当COM接口通过COM回调传递给.Net应用程序时,内存泄漏应用程序
我试图使用Microsoft Smooth Streaming Format SDK来实现基本上CCTV系统。 SDK有一个使用C++的示例应用程序,但我正在使用C#编写。我有代码移植,并且它能正常工作,除了在传递COM对象的COM - >。NET回调中出现明显的内存泄漏。当COM接口通过COM回调传递给.Net应用程序时,内存泄漏应用程序
ETA:使用RedGate ANTS Memory Profiler 8.10的试用版测量内存泄漏。它显示了一个稳定的攀登私人字节和工作集 - 私人图。此外,“按模块非托管内存分类”显示“CLR(估计)”不断增长。在隔夜的测试中,它在〜16小时内增长了332MB。虽然这是一个非常缓慢的增长速度,但最终会导致OutOfMemory状况。正如之前所说,这个应用程序应该全天24小时播出。
COM对象是一个自定义DirectShow样本采集器过滤器。 SSF SDK有一个样本采集器过滤器实现,但它表现出相同的行为,无论如何不会被重新分配。
样品采集滤波器IDL是这样:
[
object,
uuid(17b2823e-a24b-483f-a0b0-002edaf56035),
helpstring("ISampleGrabberCB interface"),
pointer_default(unique)
]
interface ISampleGrabberCallback : IUnknown
{
HRESULT OnSample([in] IMediaSample *pSample);
}
[
object,
uuid(9495f2d0-35fd-451f-b831-f89f1af8589f),
dual,
helpstring("ISampleGrabberFilter Interface"),
pointer_default(unique)
]
interface ISampleGrabberFilter : IDispatch
{
HRESULT SetCallback([in] ISampleGrabberCallback *callbackIf);
};
[
uuid(2bd7d268-bb30-4a6f-b715-6223c006d973),
helpstring("SampleGrabber Class")
]
coclass SampleGrabberFilter
{
[default] interface ISampleGrabberFilter;
};
IMediaSample通过DirectShow的在strmif.idl定义,并导入到IDL文件。
我定义了一个COM互操作文件(其使用用于DirectShowLib.Net IMediaSample的定义),如下所示:
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using DirectShowLib;
namespace Interop.SampleGrabber
{
[Guid("9495F2D0-35FD-451F-B831-F89F1AF8589F")]
[TypeLibType(TypeLibTypeFlags.FDual | TypeLibTypeFlags.FDispatchable)]
[ComImport]
public interface ISampleGrabberFilter
{
[DispId(1610743808)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
int SetCallback([MarshalAs(UnmanagedType.Interface), In] ISampleGrabberCallback callbackIf);
}
[Guid("17B2823E-A24B-483F-A0B0-002EDAF56035")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComImport]
public interface ISampleGrabberCallback
{
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[PreserveSig]
int OnSample([MarshalAs(UnmanagedType.Interface), In] IMediaSample pSample);
}
[CoClass(typeof(SampleGrabberFilterClass))]
[Guid("9495F2D0-35FD-451F-B831-F89F1AF8589F")]
[ComImport]
public interface SampleGrabberFilter : ISampleGrabberFilter
{
}
[TypeLibType(TypeLibTypeFlags.FCanCreate)]
[ClassInterface(ClassInterfaceType.None)]
[Guid("2BD7D268-BB30-4A6F-B715-6223C006D973")]
[ComImport]
public class SampleGrabberFilterClass : ISampleGrabberFilter, SampleGrabberFilter
{
[DispId(1610743808)]
[MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
[PreserveSig]
public extern virtual int SetCallback([MarshalAs(UnmanagedType.Interface), In] ISampleGrabberCallback callbackIf);
}
}
COM互文件是由一个调整TLBIMP生成的互操作的DLL的与编译制成DotPeek 2016.1(为了使用IMediaSample的DirectShowLib.NET版本)。自定义互操作文件在程序中直接“链接”;即它不是一个单独的组件。
的C++代码,这使得回调看起来是这样的:
// in SampleGrabberFilter.h file
CComPtr<ISampleGrabberCallback> callback;
// in SampleGrabberFilter.cpp file
HRESULT SampleGrabberFilter::SetCallback(ISampleGrabberCallback *cb)
{
callback = cb;
return S_OK;
}
HRESULT SampleGrabberFilter::DoRenderSample(
IMediaSample *pSample)
{
HRESULT hr = S_OK;
if (callback != nullptr)
{
hr = callback->OnSample(pSample);
}
pSample->Release();
return hr;
}
和实现回调的C#代码如下所示:
public class StreamContext : Interop.SampleGrabber.ISampleGrabberCallback,
{
// ...
int OnSample_(IMediaSample sample)
{
Console.WriteLine("OnSample_ : Size = {0}", sample.GetSize());
if (sample == null)
return 0;
int hr = 0;
hr = ProcessSample(sample);
//Marshal.Release(Marshal.GetIUnknownForObject(sample));
Marshal.ReleaseComObject(sample);
//int n = Marshal.FinalReleaseComObject(sample);
GC.Collect();
return hr;
}
//...
}
注意我的所有不同的尝试来释放IMediaSample 。
这段代码的所有工作都很好,除非我将一个实际的IMediaSample传递给回调方法,否则似乎会出现泄漏;即使我将上面的ProcessSample调用注释掉,使得回调成为NOOP。但是,如果我将nullptr传递给回调,则不会泄漏。
所以有些东西似乎是添加了IMediaSample而不是释放它。
我在这里错过了什么?
更新:我已经加入的另一种方法,以从IMediaSample(和其它信息)通过缓冲器中的回调接口,而不是象所以整个IMediaSample:
HRESULT OnSampleBuffer(REFERENCE_TIME startTime, REFERENCE_TIME endTime, int bufferLen, LPBYTE buffer, BOOL isSyncPoint)
当这种形式的回调作出,没有内存泄漏。
我在我的代码与COM对象的工作原理与C#应用程序事件此评论
// Call the Garbage Collector twice (only once is not enough)
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
有一个在SO为什么Under what circumstances, we need to call GC.Collect twice
我已经试过了一个问题,但GC .WaitForPendingFinalizers()在我称之为挂起时。 – wta