성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C#에서 만든 COM 객체를 C/C++로 P/Invoke Interop 시 메모리 누수(Memory Leak) 발생</h1> <p> 닷넷 객체를 COM 방식으로 접근할 수 있도록 다음과 같이 만들 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [ComVisible(true)] [Guid("34CF251B-EAED-428D-9686-C5A5711D3A3E")] <span style='color: blue; font-weight: bold'>[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]</span> 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()"); } } </pre> <br /> 그럼 C++에서 이런 식으로 함수를 정의하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > extern "C" { <span style='color: blue; font-weight: bold'>interface IMyUnk : IUnknown { public: virtual HRESULT STDMETHODCALLTYPE Test() = 0; };</span> __declspec(dllexport) void __cdecl UseIUnk(<span style='color: blue; font-weight: bold'>IUnknown *pUnk</span>, 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; } <span style='color: blue; font-weight: bold'>myPtr->Test();</span> myPtr->Release(); if (doRelease == true) { pUnk->Release(); } } } </pre> <br /> C#에서 C++로 MyObject 객체를 다음과 같이 전달해 사용할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using DetourFunc.ClrType; using System; using System.Runtime.InteropServices; namespace ConsoleApp1 { class Program { [DllImport("IUnkSample.dll")] internal static unsafe extern void UseIUnk(<span style='color: blue; font-weight: bold'>[MarshalAs(UnmanagedType.Interface)] object unk</span>, bool doRelease); static void Main(string[] args) { MyObject obj = new MyObject(); <span style='color: blue; font-weight: bold'>UseIUnk(obj, true);</span> } } } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 문제는 저렇게 interop을 하는 경우 메모리 누수가 발생한다는 겁니다. 실제로 아래와 같이 실행해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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); } </pre> <br /> 메모리가 증가하는 것을 확인할 수 있습니다. 얼핏 보면 이번에도 이전에 설명했던 것들과 비슷한 상황인 듯한데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - Marshal.GetNativeVariantForObject 사용 시 메모리 누수(Memory Leak) 발생 및 해결 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12157'>https://www.sysnet.pe.kr/2/0/12157</a> C# - Marshal.GetIUnknownForObject/GetIDispatchForObject 사용 시 메모리 누수(Memory Leak) 발생 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12158'>https://www.sysnet.pe.kr/2/0/12158</a> </pre> <br /> 근본적인 원인은 다른 것 같습니다. 왜냐하면, !gchandles의 "Ref Count Handles"가 증가하지 않고 순수하게 닷넷 객체가 해제되지 않아 쌓이기 때문입니다. 게다가 C++ 측에서 VARIANT로 받아 VariantClear를 호출하는 식의 방법도,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // C++ __declspec(dllexport) void __cdecl UseObjectToVariant(VARIANT variant) { <span style='color: blue; font-weight: bold'>VariantClear(&variant);</span> } // 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(); <span style='color: blue; font-weight: bold'>UseObjectToVariant(obj);</span> // UseObjectToVariant(5.5); // VARIANT의 데이터 필드에 저장할 수 있는 데이터는 메모리 누수가 없음 } </pre> <br /> 이번에는 해결책이 되지 못했습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 문제 유형은 다르지만, 그래도 해결 방식은 "<a target='tab' href='https://www.sysnet.pe.kr/2/0/12158'>C# - Marshal.GetIUnknownForObject/GetIDispatchForObject 사용 시 메모리 누수(Memory Leak) 발생</a>" 글과 마찬가지로 다룰 수 있습니다. <br /> <br /> 해당 글에서 만든 (ObjectInterface 타입을 이름 변경한) VariantMarshaller 타입을 정리해 DetourFunc 라이브러리에 넣어 두었으니 NuGet을 통해,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Install-Package DetourFunc -Version 1.1.1 // 소스 코드: github - <a target='tab' href='https://github.com/stjeong/DotNetSamples/tree/master/WinConsole/PEFormat/DetourFunc'>https://github.com/stjeong/DotNetSamples/tree/master/WinConsole/PEFormat/DetourFunc</a> </pre> <br /> 참조 추가하면 다음과 같이 사용할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [DllImport("IUnkSample.dll")] internal static unsafe extern void <span style='color: blue; font-weight: bold'>UseIntPtrToVariant</span>(IntPtr variant); private unsafe static void TestHandleWithoutLeak() { MyUnkObject obj = new MyUnkObject(); <span style='color: blue; font-weight: bold'>using (VariantMarshaller oi = new VariantMarshaller(obj)) { UseIntPtrToVariant(oi.Variant); }</span> } </pre> <br /> 그리고 대응하는 C++ 함수에서는 그냥 사용만 하면 됩니다. (이후 실행해 확인하면 메모리 누수가 발생하지 않습니다.)<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > __declspec(dllexport) void __cdecl UseIntPtrToVariant(VARIANT *variant) { // variant->punkVal // (IUnknown *) } </pre> <br /> <hr style='width: 50%' /><br /> <br /> 그런데, 이런 식의 메모리 누수가 현실적으로 문제로 인식되는 경우는 많지 않습니다. 왜냐하면, .NET 객체를 COM 인터페이스로 Native에 넘기는 경우도 많지 않을 뿐더러, 그렇다고 해도 웬만큼 반복하지 않는 한 메모리 누수가 크게 부각되기까지는 꽤나 시간이 걸릴 것이기 때문입니다.<br /> <br /> 게다가 그 문제를 완화시키는 또 다른 사유로는, 이런 문제가 (Managed COM 객체가 아닌) Native COM 객체를 pinvoke로 전달할 때는 발생하지 않는다는 점입니다. 예를 들어, 아래의 코드는 메모리 누수가 발생하지 않습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private unsafe static void TestHandleWithXmlDoc() { Type type = Type.GetTypeFromProgID("Microsoft.XMLDOM"); object xmlDoc = Activator.CreateInstance(type); // native COM 객체 생성 <span style='color: blue; font-weight: bold'>UseIUnk</span>(xmlDoc, false); } </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1564&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, pinvoke에서 object 인자의 마샬링 과정을 살펴보면 최초 UseIUnk 호출 시, <br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\ConsoleApp1\ConsoleApp1\Program.cs @ 47: 00007ffc`7cf40985 488b4df0 mov rcx,qword ptr [rbp-10h] 00007ffc`7cf40989 e802fbffff <span style='color: blue; font-weight: bold'>call</span> 00007ffc`7cf40490 (<span style='color: blue; font-weight: bold'>ConsoleApp1.Program.UseIUnk</span>(System.Object), mdToken: 0000000006000001) 00007ffc`7cf4098e 90 nop </pre> <br /> 마샬링을 위한 <a target='tab' href='https://www.sysnet.pe.kr/2/0/12133'>코드가 JIT 컴파일되는 과정을 동일</a>하게 거쳐,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 00007ffc`7cf40490 49ba005ae37cfc7f0000 mov r10,7FFC7CE35A00h 00007ffc`7cf4049a 40e96043545f jmp clr!ThePreStub (00007ffc`dc484800) </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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) </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 00007ffc`7cf40490 49ba005ae37cfc7f0000 mov r10,7FFC7CE35A00h 00007ffc`7cf4049a 40e910050000 jmp 00007ffc`7cf409b0 // Jit된 대상 메서드로 JMP </pre> <br /> PInvoke 층의 메서드 내에서 InterfaceMarshaler__ConvertToNative 함수를 통해 "object" 인자를 native로 전달하기 위한 마샬링 작업을 수행합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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 <span style='color: blue; font-weight: bold'>00007ffc`7cf40a30 e85b8c625f call clr!StubHelpers::InterfaceMarshaler__ConvertToNative (00007ffc`dc569690)</span> 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 ...[생략]... </pre> <br /> InterfaceMarshaler__ConvertToNative 호출 시점의 rcx에는 object 인자의 GC Heap 주소가 담겨 있습니다. InterfaceMarshaler__ConvertToNative 함수가 기존의 <a target='tab' href='https://www.sysnet.pe.kr/2/0/12158#gifon'>Marshal.GetIUnknownForObject</a>, <a target='tab' href='https://www.sysnet.pe.kr/2/0/12158#gnvfo'>Marshal.GetNativeVariantForObject</a>와 다른 점은 내부에서 "clr!MarshalObjectToInterface" 함수를 부른다는 정도입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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] <span style='color: blue; font-weight: bold'>00007ffc`dc56974f e864000000 call clr!MarshalObjectToInterface (00007ffc`dc5697b8)</span> 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 </pre> <br /> 실제로 rcx에 object의 [주소를 가리키는 주솟값]을 담아 MarshalObjectToInterface 호출하고 반환받은 rax값을 살펴보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 00007ffc`dc56974f e864000000 call clr!MarshalObjectToInterface (00007ffc`dc5697b8) // 결괏값: rax == 00000000`007c0018 </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>dq @rax L1</span> 00000000`007c0018 <span style='color: blue; font-weight: bold'>0000000000960938</span> // IDispatch </pre> <br /> IDispatch 인터페이스를 가리키는 포인터가 담겨 있음을 알 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 0:000> <span style='color: blue; font-weight: bold'>dq 0000000000960938 L7</span> 00000000`00960938 00007ffcdc5692f0 00007ffcdc4d7920 00007ffcdc568e50 00007ffcdc9dc1a0 00000000`00960958 00007ffcdc9dc370 00007ffcdc9dbf70 00007ffcdc9dc5b0 </pre> <br /> 00007ffcdc5692f0부터 00007ffcdc9dc5b0까지의 7개 주소는 IDispatch의 정의 따라 각각 다음의 함수 진입점을 가리키고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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: </pre> <br /> 이는 IUnknown/IDispatch에서 정의한 함수의 순서와 정확히 일치합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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; }; </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8547
(왼쪽의 숫자를 입력해야 합니다.)