Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일

C#에서 만든 COM 객체를 C/C++로 P/Invoke Interop 시 메모리 누수(Memory Leak) 발생

닷넷 객체를 COM 방식으로 접근할 수 있도록 다음과 같이 만들 수 있습니다.

[ComVisible(true)]
[Guid("34CF251B-EAED-428D-9686-C5A5711D3A3E")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IMyUnk
{
    void Test();
}

[ComVisible(true)]
[Guid("c9f685ea-3388-3ff5-958a-3234d08587c1")]
[ClassInterface(ClassInterfaceType.None)]
public class MyUnkObject : IMyUnk
{
    public void Test()
    {
        // Console.WriteLine("Call: Test()");
    }
}

그럼 C++에서 이런 식으로 함수를 정의하면,

extern "C"
{
    interface IMyUnk : IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE Test() = 0;
    };

    __declspec(dllexport) void __cdecl UseIUnk(IUnknown *pUnk, bool doRelease)
    {
        if (pUnk == nullptr)
        {
            return;
        }

        IID iid;
        IIDFromString(L"{34CF251B-EAED-428D-9686-C5A5711D3A3E}", &iid);
        
        IMyUnk* myPtr = nullptr;
        if (pUnk->QueryInterface(iid, (LPVOID*)&myPtr) != S_OK)
        {
            return;
        }

        myPtr->Test();
        myPtr->Release();

        if (doRelease == true)
        {
            pUnk->Release();
        }
    }
}

C#에서 C++로 MyObject 객체를 다음과 같이 전달해 사용할 수 있습니다.

using DetourFunc.ClrType;
using System;
using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    class Program
    {
        [DllImport("IUnkSample.dll")]
        internal static unsafe extern void UseIUnk([MarshalAs(UnmanagedType.Interface)] object unk, bool doRelease);

        static void Main(string[] args)
        {
            MyObject obj = new MyObject();
            UseIUnk(obj, true);
        }
    }
}




문제는 저렇게 interop을 하는 경우 메모리 누수가 발생한다는 겁니다. 실제로 아래와 같이 실행해 보면,

static void Main(string[] args)
{
    int i = 0;
    while (true)
    {
        TestHandleLeak();

        i++;

        if (i > 1000)
        {
            GC.Collect();
            GC.Collect();
            i = 0;
        }
    }
}

private unsafe static void TestHandleLeak()
{
    MyUnkObject obj = new MyUnkObject();
    UseIUnk(obj, true);
}

메모리가 증가하는 것을 확인할 수 있습니다. 얼핏 보면 이번에도 이전에 설명했던 것들과 비슷한 상황인 듯한데,

C# - Marshal.GetNativeVariantForObject 사용 시 메모리 누수(Memory Leak) 발생 및 해결 방법
; https://www.sysnet.pe.kr/2/0/12157

C# - Marshal.GetIUnknownForObject/GetIDispatchForObject 사용 시 메모리 누수(Memory Leak) 발생
; https://www.sysnet.pe.kr/2/0/12158

근본적인 원인은 다른 것 같습니다. 왜냐하면, !gchandles의 "Ref Count Handles"가 증가하지 않고 순수하게 닷넷 객체가 해제되지 않아 쌓이기 때문입니다. 게다가 C++ 측에서 VARIANT로 받아 VariantClear를 호출하는 식의 방법도,

// C++
__declspec(dllexport) void __cdecl UseObjectToVariant(VARIANT variant)
{
    VariantClear(&variant);
}


// C#
[DllImport("IUnkSample.dll")]
internal static unsafe extern void UseObjectToVariant(object variant);

private unsafe static void TestHandleLeak2()
{
    // object obj = new object();
    MyUnkObject obj = new MyUnkObject();
    UseObjectToVariant(obj);
            
    // UseObjectToVariant(5.5); // VARIANT의 데이터 필드에 저장할 수 있는 데이터는 메모리 누수가 없음
}

이번에는 해결책이 되지 못했습니다.




문제 유형은 다르지만, 그래도 해결 방식은 "C# - Marshal.GetIUnknownForObject/GetIDispatchForObject 사용 시 메모리 누수(Memory Leak) 발생" 글과 마찬가지로 다룰 수 있습니다.

해당 글에서 만든 (ObjectInterface 타입을 이름 변경한) VariantMarshaller 타입을 정리해 DetourFunc 라이브러리에 넣어 두었으니 NuGet을 통해,

Install-Package DetourFunc -Version 1.1.1

// 소스 코드: github - https://github.com/stjeong/DotNetSamples/tree/master/WinConsole/PEFormat/DetourFunc

참조 추가하면 다음과 같이 사용할 수 있습니다.

[DllImport("IUnkSample.dll")]
internal static unsafe extern void UseIntPtrToVariant(IntPtr variant);

private unsafe static void TestHandleWithoutLeak()
{
    MyUnkObject obj = new MyUnkObject();

    using (VariantMarshaller oi = new VariantMarshaller(obj))
    {
        UseIntPtrToVariant(oi.Variant);
    }
}

그리고 대응하는 C++ 함수에서는 그냥 사용만 하면 됩니다. (이후 실행해 확인하면 메모리 누수가 발생하지 않습니다.)

__declspec(dllexport) void __cdecl UseIntPtrToVariant(VARIANT *variant)
{
    // variant->punkVal // (IUnknown *)
}




그런데, 이런 식의 메모리 누수가 현실적으로 문제로 인식되는 경우는 많지 않습니다. 왜냐하면, .NET 객체를 COM 인터페이스로 Native에 넘기는 경우도 많지 않을 뿐더러, 그렇다고 해도 웬만큼 반복하지 않는 한 메모리 누수가 크게 부각되기까지는 꽤나 시간이 걸릴 것이기 때문입니다.

게다가 그 문제를 완화시키는 또 다른 사유로는, 이런 문제가 (Managed COM 객체가 아닌) Native COM 객체를 pinvoke로 전달할 때는 발생하지 않는다는 점입니다. 예를 들어, 아래의 코드는 메모리 누수가 발생하지 않습니다.

private unsafe static void TestHandleWithXmlDoc()
{
    Type type = Type.GetTypeFromProgID("Microsoft.XMLDOM");

    object xmlDoc = Activator.CreateInstance(type); // native COM 객체 생성
    UseIUnk(xmlDoc, false);
}

(첨부 파일은 이 글의 예제 코드를 포함합니다.)




참고로, pinvoke에서 object 인자의 마샬링 과정을 살펴보면 최초 UseIUnk 호출 시,

C:\ConsoleApp1\ConsoleApp1\Program.cs @ 47:
00007ffc`7cf40985 488b4df0        mov     rcx,qword ptr [rbp-10h]
00007ffc`7cf40989 e802fbffff      call    00007ffc`7cf40490 (ConsoleApp1.Program.UseIUnk(System.Object), mdToken: 0000000006000001)
00007ffc`7cf4098e 90              nop

마샬링을 위한 코드가 JIT 컴파일되는 과정을 동일하게 거쳐,

00007ffc`7cf40490 49ba005ae37cfc7f0000 mov r10,7FFC7CE35A00h
00007ffc`7cf4049a 40e96043545f    jmp     clr!ThePreStub (00007ffc`dc484800)

clr!ThePreStub:
00007ffc`dc484800 4157            push    r15
00007ffc`dc484802 4156            push    r14
00007ffc`dc484804 4155            push    r13
00007ffc`dc484806 4154            push    r12
00007ffc`dc484808 55              push    rbp
00007ffc`dc484809 53              push    rbx
00007ffc`dc48480a 56              push    rsi
00007ffc`dc48480b 57              push    rdi
00007ffc`dc48480c 4883ec68        sub     rsp,68h
00007ffc`dc484810 48898c24b0000000 mov     qword ptr [rsp+0B0h],rcx
00007ffc`dc484818 48899424b8000000 mov     qword ptr [rsp+0B8h],rdx
00007ffc`dc484820 4c898424c0000000 mov     qword ptr [rsp+0C0h],r8
00007ffc`dc484828 4c898c24c8000000 mov     qword ptr [rsp+0C8h],r9
00007ffc`dc484830 660f7f442420    movdqa  xmmword ptr [rsp+20h],xmm0
00007ffc`dc484836 660f7f4c2430    movdqa  xmmword ptr [rsp+30h],xmm1
00007ffc`dc48483c 660f7f542440    movdqa  xmmword ptr [rsp+40h],xmm2
00007ffc`dc484842 660f7f5c2450    movdqa  xmmword ptr [rsp+50h],xmm3
00007ffc`dc484848 488d4c2468      lea     rcx,[rsp+68h]
00007ffc`dc48484d 498bd2          mov     rdx,r10
00007ffc`dc484850 e8eba30000      call    clr!PreStubWorker (00007ffc`dc48ec40)
00007ffc`dc484855 660f6f442420    movdqa  xmm0,xmmword ptr [rsp+20h]
00007ffc`dc48485b 660f6f4c2430    movdqa  xmm1,xmmword ptr [rsp+30h]
00007ffc`dc484861 660f6f542440    movdqa  xmm2,xmmword ptr [rsp+40h]
00007ffc`dc484867 660f6f5c2450    movdqa  xmm3,xmmword ptr [rsp+50h]
00007ffc`dc48486d 488b8c24b0000000 mov     rcx,qword ptr [rsp+0B0h]
00007ffc`dc484875 488b9424b8000000 mov     rdx,qword ptr [rsp+0B8h]
00007ffc`dc48487d 4c8b8424c0000000 mov     r8,qword ptr [rsp+0C0h]
00007ffc`dc484885 4c8b8c24c8000000 mov     r9,qword ptr [rsp+0C8h]
00007ffc`dc48488d 4883c468        add     rsp,68h
00007ffc`dc484891 5f              pop     rdi
00007ffc`dc484892 5e              pop     rsi
00007ffc`dc484893 5b              pop     rbx
00007ffc`dc484894 5d              pop     rbp
00007ffc`dc484895 415c            pop     r12
00007ffc`dc484897 415d            pop     r13
00007ffc`dc484899 415e            pop     r14
00007ffc`dc48489b 415f            pop     r15
00007ffc`dc48489d 48ffe0          jmp     rax  // JIT 컴파일 시켜 jmp 코드로 바뀐 원래의 호출 위치로 다시 JMP (rax == 00007ffc`7cf40490)

00007ffc`7cf40490 49ba005ae37cfc7f0000 mov r10,7FFC7CE35A00h
00007ffc`7cf4049a 40e910050000    jmp     00007ffc`7cf409b0  // Jit된 대상 메서드로 JMP

PInvoke 층의 메서드 내에서 InterfaceMarshaler__ConvertToNative 함수를 통해 "object" 인자를 native로 전달하기 위한 마샬링 작업을 수행합니다.

00007ffc`7cf409b0 55              push    rbp
00007ffc`7cf409b1 4157            push    r15
00007ffc`7cf409b3 4156            push    r14
00007ffc`7cf409b5 4155            push    r13
00007ffc`7cf409b7 4154            push    r12
00007ffc`7cf409b9 57              push    rdi
00007ffc`7cf409ba 56              push    rsi
00007ffc`7cf409bb 53              push    rbx
00007ffc`7cf409bc 4881ec98000000  sub     rsp,98h
00007ffc`7cf409c3 488dac24d0000000 lea     rbp,[rsp+0D0h]
00007ffc`7cf409cb 4c8955a0        mov     qword ptr [rbp-60h],r10
00007ffc`7cf409cf 4889a550ffffff  mov     qword ptr [rbp-0B0h],rsp
00007ffc`7cf409d6 48894d10        mov     qword ptr [rbp+10h],rcx
00007ffc`7cf409da 488d8d68ffffff  lea     rcx,[rbp-98h]
00007ffc`7cf409e1 498bd2          mov     rdx,r10
00007ffc`7cf409e4 e85744545f      call    clr!JIT_InitPInvokeFrame (00007ffc`dc484e40)
00007ffc`7cf409e9 488945a8        mov     qword ptr [rbp-58h],rax
00007ffc`7cf409ed 488bcc          mov     rcx,rsp
00007ffc`7cf409f0 48894d88        mov     qword ptr [rbp-78h],rcx
00007ffc`7cf409f4 488bcd          mov     rcx,rbp
00007ffc`7cf409f7 48894d98        mov     qword ptr [rbp-68h],rcx
00007ffc`7cf409fb 488b4da8        mov     rcx,qword ptr [rbp-58h]
00007ffc`7cf409ff 488d8568ffffff  lea     rax,[rbp-98h]
00007ffc`7cf40a06 48894110        mov     qword ptr [rcx+10h],rax
00007ffc`7cf40a0a 488b4da0        mov     rcx,qword ptr [rbp-60h]
00007ffc`7cf40a0e e87d06685f      call    clr!StubHelpers::DemandPermission (00007ffc`dc5c1090)
00007ffc`7cf40a13 4533c0          xor     r8d,r8d
00007ffc`7cf40a16 448945c4        mov     dword ptr [rbp-3Ch],r8d
00007ffc`7cf40a1a 90              nop
00007ffc`7cf40a1b 4533c0          xor     r8d,r8d
00007ffc`7cf40a1e 4d63c0          movsxd  r8,r8d
00007ffc`7cf40a21 33d2            xor     edx,edx
00007ffc`7cf40a23 4863d2          movsxd  rdx,edx
00007ffc`7cf40a26 488b4d10        mov     rcx,qword ptr [rbp+10h]
00007ffc`7cf40a2a 41b910000000    mov     r9d,10h
00007ffc`7cf40a30 e85b8c625f      call    clr!StubHelpers::InterfaceMarshaler__ConvertToNative (00007ffc`dc569690)
00007ffc`7cf40a35 488945b8        mov     qword ptr [rbp-48h],rax
00007ffc`7cf40a39 c745c401000000  mov     dword ptr [rbp-3Ch],1
00007ffc`7cf40a40 90              nop
00007ffc`7cf40a41 488b4da0        mov     rcx,qword ptr [rbp-60h]
00007ffc`7cf40a45 41bb20000000    mov     r11d,20h
...[생략]...

InterfaceMarshaler__ConvertToNative 호출 시점의 rcx에는 object 인자의 GC Heap 주소가 담겨 있습니다. InterfaceMarshaler__ConvertToNative 함수가 기존의 Marshal.GetIUnknownForObject, Marshal.GetNativeVariantForObject와 다른 점은 내부에서 "clr!MarshalObjectToInterface" 함수를 부른다는 정도입니다.

clr!StubHelpers::InterfaceMarshaler__ConvertToNative:
00007ffc`dc569690 44894c2420      mov     dword ptr [rsp+20h],r9d ss:00000000`003ee7b8=02502df0
00007ffc`dc569695 4c89442418      mov     qword ptr [rsp+18h],r8
00007ffc`dc56969a 4889542410      mov     qword ptr [rsp+10h],rdx
00007ffc`dc56969f 53              push    rbx
00007ffc`dc5696a0 56              push    rsi
00007ffc`dc5696a1 57              push    rdi
00007ffc`dc5696a2 4154            push    r12
00007ffc`dc5696a4 4155            push    r13
00007ffc`dc5696a6 4156            push    r14
00007ffc`dc5696a8 4157            push    r15
00007ffc`dc5696aa 4881ec60010000  sub     rsp,160h
00007ffc`dc5696b1 48c7442458feffffff mov   qword ptr [rsp+58h],0FFFFFFFFFFFFFFFEh
00007ffc`dc5696ba 458bf1          mov     r14d,r9d
00007ffc`dc5696bd 4d8bf8          mov     r15,r8
00007ffc`dc5696c0 4c8be2          mov     r12,rdx
00007ffc`dc5696c3 488d35c6ffffff  lea     rsi,[clr!StubHelpers::InterfaceMarshaler__ConvertToNative (00007ffc`dc569690)]
00007ffc`dc5696ca 4889742438      mov     qword ptr [rsp+38h],rsi
00007ffc`dc5696cf 4885c9          test    rcx,rcx
00007ffc`dc5696d2 0f84d0000000    je      clr!StubHelpers::InterfaceMarshaler__ConvertToNative+0x118 (00007ffc`dc5697a8)
00007ffc`dc5696d8 488364243000    and     qword ptr [rsp+30h],0

00007ffc`dc5696de 48894c2428      mov     qword ptr [rsp+28h],rcx
00007ffc`dc5696e3 c784249000000040000000 mov dword ptr [rsp+90h],40h
00007ffc`dc5696ee 4889b424a0000000 mov     qword ptr [rsp+0A0h],rsi
00007ffc`dc5696f6 488d05fb886e00  lea     rax,[clr!HelperMethodFrame_1OBJ::`vftable' (00007ffc`dcc51ff8)]
00007ffc`dc5696fd 4889442478      mov     qword ptr [rsp+78h],rax
00007ffc`dc569702 488d442428      lea     rax,[rsp+28h]
00007ffc`dc569707 4889842450010000 mov     qword ptr [rsp+150h],rax
00007ffc`dc56970f 488d8c24a8000000 lea     rcx,[rsp+0A8h]
00007ffc`dc569717 e8d4b7f1ff      call    clr!LazyMachStateCaptureState (00007ffc`dc484ef0)
00007ffc`dc56971c 488d4c2478      lea     rcx,[rsp+78h]
00007ffc`dc569721 e80ab8f1ff      call    clr!HelperMethodFrame::Push (00007ffc`dc484f30)
00007ffc`dc569726 488b8c2498000000 mov     rcx,qword ptr [rsp+98h]
00007ffc`dc56972e 33ff            xor     edi,edi
00007ffc`dc569730 4532ed          xor     r13b,r13b
00007ffc`dc569733 8a05ff989200    mov     al,byte ptr [clr!g_StackProbingEnabled (00007ffc`dce93038)]
00007ffc`dc569739 84c0            test    al,al
00007ffc`dc56973b 0f851b3e3000    jne     clr!StubHelpers::InterfaceMarshaler__ConvertToNative+0x303ecc (00007ffc`dc86d55c)

00007ffc`dc569741 458bce          mov     r9d,r14d
00007ffc`dc569744 4d8bc7          mov     r8,r15
00007ffc`dc569747 498bd4          mov     rdx,r12
00007ffc`dc56974a 488d4c2428      lea     rcx,[rsp+28h]
00007ffc`dc56974f e864000000      call    clr!MarshalObjectToInterface (00007ffc`dc5697b8)
00007ffc`dc569754 488bd8          mov     rbx,rax
00007ffc`dc569757 4889442430      mov     qword ptr [rsp+30h],rax
00007ffc`dc56975c c644244800      mov     byte ptr [rsp+48h],0
00007ffc`dc569761 803dd098920000  cmp     byte ptr [clr!g_StackProbingEnabled (00007ffc`dce93038)],0
00007ffc`dc569768 0f85123e3000    jne     clr!StubHelpers::InterfaceMarshaler__ConvertToNative+0x303ef0 (00007ffc`dc86d580)
00007ffc`dc56976e 4584ed          test    r13b,r13b
00007ffc`dc569771 7539            jne     clr!StubHelpers::InterfaceMarshaler__ConvertToNative+0x11c (00007ffc`dc5697ac)
00007ffc`dc569773 488d4c2478      lea     rcx,[rsp+78h]
00007ffc`dc569778 e8f3b7f1ff      call    clr!HelperMethodFrame::Pop (00007ffc`dc484f70)
00007ffc`dc56977d 488d8c24a8000000 lea     rcx,[rsp+0A8h]
00007ffc`dc569785 e846acf1ff      call    clr!HelperMethodFrameRestoreState (00007ffc`dc4843d0)
00007ffc`dc56978a 85c0            test    eax,eax
00007ffc`dc56978c 0f8551ffffff    jne     clr!StubHelpers::InterfaceMarshaler__ConvertToNative+0x53 (00007ffc`dc5696e3)
00007ffc`dc569792 488bc3          mov     rax,rbx
00007ffc`dc569795 4881c460010000  add     rsp,160h
00007ffc`dc56979c 415f            pop     r15
00007ffc`dc56979e 415e            pop     r14
00007ffc`dc5697a0 415d            pop     r13
00007ffc`dc5697a2 415c            pop     r12
00007ffc`dc5697a4 5f              pop     rdi
00007ffc`dc5697a5 5e              pop     rsi
00007ffc`dc5697a6 5b              pop     rbx
00007ffc`dc5697a7 c3              ret

실제로 rcx에 object의 [주소를 가리키는 주솟값]을 담아 MarshalObjectToInterface 호출하고 반환받은 rax값을 살펴보면,

00007ffc`dc56974f e864000000      call    clr!MarshalObjectToInterface (00007ffc`dc5697b8)
                        // 결괏값: rax == 00000000`007c0018

0:000> dq @rax L1
00000000`007c0018 0000000000960938 // IDispatch

IDispatch 인터페이스를 가리키는 포인터가 담겨 있음을 알 수 있습니다.

0:000> dq 0000000000960938 L7
00000000`00960938 00007ffcdc5692f0 00007ffcdc4d7920 00007ffcdc568e50 00007ffcdc9dc1a0
00000000`00960958 00007ffcdc9dc370 00007ffcdc9dbf70 00007ffcdc9dc5b0 

00007ffcdc5692f0부터 00007ffcdc9dc5b0까지의 7개 주소는 IDispatch의 정의 따라 각각 다음의 함수 진입점을 가리키고 있습니다.

clr!Unknown_QueryInterface:
clr!Unknown_AddRef:
clr!Unknown_Release:
clr!Dispatch_GetTypeInfoCount_Wrapper:
clr!Dispatch_GetTypeInfo_Wrapper:
clr!Dispatch_GetIDsOfNames_Wrapper:
clr!Dispatch_Invoke_Wrapper:

이는 IUnknown/IDispatch에서 정의한 함수의 순서와 정확히 일치합니다.

MIDL_INTERFACE("00000000-0000-0000-C000-000000000046")
IUnknown
{
public:
    BEGIN_INTERFACE
    virtual HRESULT STDMETHODCALLTYPE QueryInterface( 
        /* [in] */ REFIID riid,
        /* [annotation][iid_is][out] */ 
        _COM_Outptr_  void **ppvObject) = 0;
        
    virtual ULONG STDMETHODCALLTYPE AddRef( void) = 0;
        
    virtual ULONG STDMETHODCALLTYPE Release( void) = 0;
        
    END_INTERFACE
};

MIDL_INTERFACE("00020400-0000-0000-C000-000000000046")
IDispatch : public IUnknown
{
public:
    virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount( 
        /* [out] */ __RPC__out UINT *pctinfo) = 0;
        
    virtual HRESULT STDMETHODCALLTYPE GetTypeInfo( 
        /* [in] */ UINT iTInfo,
        /* [in] */ LCID lcid,
        /* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo) = 0;
        
    virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames( 
        /* [in] */ __RPC__in REFIID riid,
        /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
        /* [range][in] */ __RPC__in_range(0,16384) UINT cNames,
        /* [in] */ LCID lcid,
        /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId) = 0;
        
    virtual /* [local] */ HRESULT STDMETHODCALLTYPE Invoke( 
        /* [annotation][in] */ 
        _In_  DISPID dispIdMember,
        /* [annotation][in] */ 
        _In_  REFIID riid,
        /* [annotation][in] */ 
        _In_  LCID lcid,
        /* [annotation][in] */ 
        _In_  WORD wFlags,
        /* [annotation][out][in] */ 
        _In_  DISPPARAMS *pDispParams,
        /* [annotation][out] */ 
        _Out_opt_  VARIANT *pVarResult,
        /* [annotation][out] */ 
        _Out_opt_  EXCEPINFO *pExcepInfo,
        /* [annotation][out] */ 
        _Out_opt_  UINT *puArgErr) = 0;
};




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]


donaricano-btn



[최초 등록일: ]
[최종 수정일: 2/28/2020 ]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 쓴 사람
 



2021-01-14 06시27분
[안녕하세요] 해당 문제로 고민중인 사람입니다. C# 으로 작성한 dll 파일을 C++ 에서 사용중인데 작은 메모리 누수가 발생합니다. 글을 읽었는데 제 코드에 어떻게 적용해야 될 지 모르겠어서 질문드립니다.
Nuget Package 적용 부분에 있는게 C# 라이브러리 클래스를 C++로 가져오는 부분으로 이해했는데 저는 아래처럼 가져옵니다.

HRESULT hr = CoInitializeEx(NULL, tagCOINIT::COINIT_MULTITHREADED);        

hr = CoCreateInstance(CLSID, NULL, CLSCTX_INPROC_SERVER, IID, reinterpret_cast<void**>(&instanceName));

여기에 저 패키지를 적용할 수 있을까요?
[손님]
2021-01-14 10시00분
Nuget package라는 게 이 글에서 공개한 DetourFunc을 의미하는 건가요? 그 라이브러리는 C# 측에서 사용하는 것입니다.
정성태
2021-01-15 09시51분
[안녕하세요] C# 측에서는 기본적인 GUID 설정해서 COM 객체 만드는 식으로 사용합니다.

[Guid("GUID")]
    public interface Interface
    {
        functions
    }

    [Guid("GUID")]
    public class Class : Interface, IDisposable
    {
        functions
    }

사실 제가 만든 C# 라이브러리에서 리턴하는 값들이 객체가 아니라 string 이나 int 배열 같은 것들인데 단순하게 [return: MarshalAs(UnmanagedType.BStr)] 같이 리턴값을 마샬링해서 해결할 수 있는 문제는 아닌건가요?
[손님]
2021-01-15 11시59분
그러니까, 지금 메모리 누수가 발생한다는 소스 코드는 이 글의 경우와 전혀 상관없는 유형인 거죠?

그럼, 질문 게시판을 통해 처음부터 다시 (아무것도 모르는 사람에게 설명하듯이) 올려주세요. 그리고 메모리 누수가 발생하는 최소한의 재현 프로젝트를 함께 첨부해야 합니다. (위의 내용만으로는 어떤 것을 질문하려는 것인지 잘 모르겠습니다.)
정성태
2021-01-15 01시18분
[안녕하세요] 제 소스와 본문 내용이 다른 부분은 객체를 리턴하는가 아닌가 정도의 차이인 것 같습니다.
본문 내용이 C#에서 만든 dll을 C++에서 사용할 때 메모리누수 문제에 대한 것으로 봤는데 맞나요?

소스는 카메라가 있어야해서 정리해서 질문 드리겠습니다.
[손님]
2021-01-15 01시45분
좀 더 특별한 경우입니다. 본문의 경우, C#에서 C++ 코드를 사용할 때 C++ 측으로 C# 인스턴스를 전달했을 때 발생하는 메모리 누수입니다. 질문하신 분의 경우로는 (어떤 특별함이 있는지는 모르겠지만 일반적으로) 메모리 누수가 발생하지 않습니다.
정성태

... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
12216정성태2/20/20211594.NET Framework: 904. USB/IP PROJECT를 이용해 C#으로 USB Keyboard 가상 장치 만들기 [13]파일 다운로드1
12215정성태5/12/20203261개발 환경 구성: 490. C# - (Wireshark의) USBPcap을 이용한 USB 패킷 모니터링 [1]파일 다운로드1
12214정성태5/5/20201240개발 환경 구성: 489. 정식 인증서가 있는 경우 Device Driver 서명하는 방법 (2) - UEFI/SecureBoot
12213정성태1/28/20211850개발 환경 구성: 488. (코드로 가상 USB 장치를 만들 수 있는) USB/IP PROJECT 소개
12212정성태5/1/20201094개발 환경 구성: 487. UEFI / Secure Boot 상태인지 확인하는 방법
12211정성태4/27/20201268개발 환경 구성: 486. WSL에서 Makefile로 공개된 리눅스 환경의 C/C++ 소스 코드 빌드
12210정성태4/20/20201662.NET Framework: 903. .NET Framework의 Strong-named 어셈블리 바인딩 (1) - app.config을 이용한 바인딩 리디렉션 [1]파일 다운로드1
12209정성태4/13/20201131오류 유형: 614. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우 (2)
12208정성태4/12/20201404Linux: 29. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우
12207정성태4/2/20201174스크립트: 19. Windows PowerShell의 NonInteractive 모드
12206정성태4/2/20201297오류 유형: 613. 파일 잠금이 바로 안 풀린다면? - The process cannot access the file '...' because it is being used by another process.
12205정성태4/2/20201115스크립트: 18. Powershell에서는 cmd.exe의 명령어를 지원하진 않습니다.
12204정성태4/1/20201046스크립트: 17. Powershell 명령어에 ';' (semi-colon) 문자가 포함된 경우
12203정성태3/18/20201470오류 유형: 612. warning: 'C:\ProgramData/Git/config' has a dubious owner: '...'.
12202정성태3/18/20201850개발 환경 구성: 486. .NET Framework 프로젝트를 위한 GitLab CI/CD Runner 구성
12201정성태3/18/20201397오류 유형: 611. git-credential-manager.exe: Using credentials for username "Personal Access Token".
12200정성태11/5/20201787VS.NET IDE: 145. NuGet + Github 라이브러리 디버깅 관련 옵션 3가지 - "Enable Just My Code" / "Enable Source Link support" / "Suppress JIT optimization on module load (Managed only)"
12199정성태3/17/20201083오류 유형: 610. C# - CodeDomProvider 사용 시 Unhandled Exception: System.IO.DirectoryNotFoundException: Could not find a part of the path '...\f2_6uod0.tmp'.
12198정성태3/17/20201314오류 유형: 609. SQL 서버 접속 시 "Cannot open user default database. Login failed."
12197정성태3/17/20201262VS.NET IDE: 144. .NET Core 콘솔 응용 프로그램을 배포(publish) 시 docker image 자동 생성 - 두 번째 이야기
12196정성태3/17/20201113오류 유형: 608. The ServicedComponent being invoked is not correctly configured (Use regsvcs to re-register).
12195정성태3/17/20201428.NET Framework: 902. C# - 프로세스의 모든 핸들을 열람 - 세 번째 이야기
12194정성태3/16/20201721오류 유형: 607. PostgreSQL - Npgsql.NpgsqlException: sorry, too many clients already
12193정성태3/16/20201237개발 환경 구성: 485. docker - SAP Adaptive Server Enterprise 컨테이너 실행
12192정성태3/14/20201543개발 환경 구성: 484. docker - Sybase Anywhere 16 컨테이너 실행
12191정성태3/14/20202218개발 환경 구성: 483. docker - OracleXE 컨테이너 실행 [1]
... [16]  17  18  19  20  21  22  23  24  25  26  27  28  29  30  ...