2016-09-15 48 views
1

我试图使用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) 

当这种形式的回调作出,没有内存泄漏。

回答

0

我在我的代码与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

+0

我已经试过了一个问题,但GC .WaitForPendingFinalizers()在我称之为挂起时。 – wta

相关问题