Microsoft MVP성태의 닷넷 이야기
글쓴 사람
홈페이지
첨부 파일

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;
};




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

[연관 글]





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

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

비밀번호

댓글 쓴 사람
 




1  2  3  [4]  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
12213정성태5/3/2020425개발 환경 구성: 488. (코드로 가상 USB 장치를 만들 수 있는) USB/IP PROJECT 소개
12212정성태5/1/2020245개발 환경 구성: 487. UEFI / Secure Boot 상태인지 확인하는 방법
12211정성태4/27/2020364개발 환경 구성: 486. WSL에서 Makefile로 공개된 리눅스 환경의 C/C++ 소스 코드 빌드
12210정성태4/20/2020525.NET Framework: 903. .NET Framework의 Strong-named 어셈블리 바인딩 (1) - app.config을 이용한 바인딩 리디렉션 [1]파일 다운로드1
12209정성태4/13/2020291오류 유형: 614. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우 (2)
12208정성태4/12/2020352Linux: 29. 리눅스 환경에서 C/C++ 프로그램이 Segmentation fault 에러가 발생한 경우
12207정성태4/2/2020375스크립트: 19. Windows PowerShell의 NonInteractive 모드
12206정성태4/2/2020433오류 유형: 613. 파일 잠금이 바로 안 풀린다면? - The process cannot access the file '...' because it is being used by another process.
12205정성태4/2/2020323스크립트: 18. Powershell에서는 cmd.exe의 명령어를 지원하진 않습니다.
12204정성태4/1/2020265스크립트: 17. Powershell 명령어에 ';' (semi-colon) 문자가 포함된 경우
12203정성태3/18/2020511오류 유형: 612. warning: 'C:\ProgramData/Git/config' has a dubious owner: '...'.
12202정성태3/18/2020675개발 환경 구성: 486. .NET Framework 프로젝트를 위한 GitLab CI/CD Runner 구성
12201정성태3/18/2020357오류 유형: 611. git-credential-manager.exe: Using credentials for username "Personal Access Token".
12200정성태3/18/2020700VS.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/2020302오류 유형: 610. C# - CodeDomProvider 사용 시 Unhandled Exception: System.IO.DirectoryNotFoundException: Could not find a part of the path '...\f2_6uod0.tmp'.
12198정성태3/17/2020354오류 유형: 609. SQL 서버 접속 시 "Cannot open user default database. Login failed."
12197정성태3/17/2020480VS.NET IDE: 144. .NET Core 콘솔 응용 프로그램을 배포(publish) 시 docker image 자동 생성 - 두 번째 이야기
12196정성태3/17/2020438오류 유형: 608. The ServicedComponent being invoked is not correctly configured (Use regsvcs to re-register).
12195정성태3/17/2020561.NET Framework: 902. C# - 프로세스의 모든 핸들을 열람 - 세 번째 이야기
12194정성태3/16/2020439오류 유형: 607. PostgreSQL - Npgsql.NpgsqlException: sorry, too many clients already
12193정성태3/16/2020406개발 환경 구성: 485. docker - SAP Adaptive Server Enterprise 컨테이너 실행
12192정성태3/14/2020549개발 환경 구성: 484. docker - Sybase Anywhere 16 컨테이너 실행
12191정성태3/14/2020816개발 환경 구성: 483. docker - OracleXE 컨테이너 실행 [1]
12190정성태3/14/2020294오류 유형: 606. Docker Desktop 업그레이드 시 "The process cannot access the file 'C:\Program Files\Docker\Docker\resources\dockerd.exe' because it is being used by another process."
12189정성태3/13/2020714개발 환경 구성: 482. Facebook OAuth 처리 시 상태 정보 전달 방법과 "유효한 OAuth 리디렉션 URI" 설정 규칙
12188정성태3/14/2020395Windows: 167. 부팅 시점에 실행되는 chkdsk 결과를 확인하는 방법
1  2  3  [4]  5  6  7  8  9  10  11  12  13  14  15  ...