성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
[공진영] 안녕하세요 좋은글 감사합니다. 현재 제가 wpf로 관제 모...
글쓰기
제목
이름
암호
전자우편
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# - GUID 타입 전용의 UnmanagedType.LPStruct</h1> <p> LPStruct 옵션에 대한 좋은 글이 있군요. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > The confusing UnmanagedType.LPStruct marshaling directive ; <a target='tab' href='https://learn.microsoft.com/en-us/archive/blogs/adam_nathan/the-confusing-unmanagedtype-lpstruct-marshaling-directive'>https://learn.microsoft.com/en-us/archive/blogs/adam_nathan/the-confusing-unmanagedtype-lpstruct-marshaling-directive</a> </pre> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> <span style='color: blue; font-weight: bold'>UnmanagedType.LPStruct is only supported for one specific case</span>: <span style='color: blue; font-weight: bold'>treating a System.Guid</span> value type as an unmanaged GUID with an extra level of indirection. In other words, this directive makes the marshaler add a level of indirection to System.Guid when marshaling it from managed to unmanaged code, and remove a level of indirection from GUID when marshaling it from unmanaged to managed code. </div><br /> <br /> 글에서도 나오지만, 하위 호환성만 아니면 이름을 바꿨을 거라고 합니다. 또한 다음의 공식 문서에서도,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Native interoperability best practices ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/standard/native-interop/best-practices'>https://learn.microsoft.com/en-us/dotnet/standard/native-interop/best-practices</a> </pre> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> <span style='color: blue; font-weight: bold'>GUIDs</span> <br /> GUIDs are usable directly in signatures. Many Windows APIs take GUID& type aliases like REFIID. When passed by ref, they can either be passed by ref or with the [MarshalAs(UnmanagedType.LPStruct)] attribute.<br /> <br /> <span style='color: blue; font-weight: bold'>DO NOT Use [MarshalAs(UnmanagedType.LPStruct)] for anything other than ref GUID parameters.</span> </div><br /> <br /> 분명하게 <a target='tab' href='https://www.sysnet.pe.kr/2/1/957'>GUID</a>를 위한 옵션이라고 강조하고 있습니다. 그런데 사실, 이게 좀 더 문제가 되는 것은 UnmanagedType enum 문서에,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > UnmanagedType Enum ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedtype'>https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedtype</a> </pre> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> <span style='color: blue; font-weight: bold'>LPStruct</span> <br /> A pointer to a C-style structure that you use to marshal managed formatted classes. This member is valid for platform invoke methods only.<br /> </div><br /> <br /> LPStruct가 일반적인 타입에도 사용할 수 있을 것처럼 쓰여 있기 때문입니다. 실제로 어느 쪽이 맞는지 간단하게 테스트를 한 번 해볼까요? ^^<br /> <br /> <hr style='width: 50%' /><br /> <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 __stdcall GuidFunc(<span style='color: blue; font-weight: bold'>GUID* pGuid</span>) { wchar_t buf[1024] = { 0 }; StringFromGUID2(*pGuid, buf, 1024); wcout << buf << endl; } </pre> <br /> GUID 타입이기 때문에 C# 측에서는 다음과 같이 다양하게 interop 옵션으로 C++ 코드와 연동할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [DllImport("Dll1.dll", EntryPoint = "GuidFunc")] static extern void GuidFunc1(<span style='color: blue; font-weight: bold'>IntPtr ptr</span>); [DllImport("Dll1.dll", EntryPoint = "GuidFunc")] unsafe static extern void GuidFunc2(<span style='color: blue; font-weight: bold'>Guid* ptr</span>); [DllImport("Dll1.dll", EntryPoint = "GuidFunc")] static extern void GuidFunc3(<span style='color: blue; font-weight: bold'>[MarshalAs(UnmanagedType.LPStruct)] Guid ptr</span>); { Guid iUnk = new Guid("{00000000-0000-0000-C000-000000000046}"); { IntPtr pGuid = Marshal.AllocHGlobal(16); Marshal.StructureToPtr(iUnk, pGuid, true); <span style='color: blue; font-weight: bold'>GuidFunc1(pGuid);</span> Marshal.FreeHGlobal(pGuid); } unsafe { <span style='color: blue; font-weight: bold'>GuidFunc2(&iUnk);</span> } { <span style='color: blue; font-weight: bold'>GuidFunc3(iUnk);</span> } } </pre> <br /> 사실 가장 직관적인 연동은 두 번째에 해당하는 "Guid*"이지만, LPStruct를 이용하면 포인터임에도 불구하고 마치 값 형식처럼 간단하게 전달할 수 있고 이에 대한 처리는 CLR 측의 마샬러가 자동으로 해줍니다.<br /> <br /> <hr style='width: 50%' /><br /> <a name='ptr_to_struct'></a> <br /> 혹시 동일하게 16바이트로 구성한 구조체라면 LPStruct를 사용할 수 있지 않을까요? 그래서 C++ 측의 코드를,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > #pragma pack(push, 1) typedef struct tagMyStruct { int a; int b; int c; int d; } MyStruct; #pragma pack(pop) __declspec(dllexport) void __stdcall StructFunc(MyStruct* pGuid) { cout << pGuid->a << ", " << pGuid->b << ", " << pGuid->c << ", " << pGuid->d << endl; } </pre> <br /> GUID 예제와 마찬가지로 C#에서 다음과 같이 interop하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [DllImport("Dll1.dll", EntryPoint = "StructFunc")] static extern void StructFunc1(<span style='color: blue; font-weight: bold'>IntPtr ptr</span>); [DllImport("Dll1.dll", EntryPoint = "StructFunc")] unsafe static extern void StructFunc2(<span style='color: blue; font-weight: bold'>MyStruct* ptr</span>); [DllImport("Dll1.dll", EntryPoint = "StructFunc")] static extern void StructFunc3(<span style='color: blue; font-weight: bold'>[MarshalAs(UnmanagedType.LPStruct)] MyStruct MyStruct</span>); { MyStruct instance = new MyStruct(); { instance.a = 5; instance.b = 6; instance.c = 7; instance.d = 8; int size = Marshal.SizeOf(typeof(MyStruct)); IntPtr pInstance = Marshal.AllocHGlobal(size); Marshal.StructureToPtr(instance, pInstance, true); <span style='color: blue; font-weight: bold'>StructFunc1(pInstance);</span> Marshal.FreeHGlobal(pInstance); } unsafe { <span style='color: blue; font-weight: bold'>StructFunc2(&instance);</span> } { <span style='color: blue; font-weight: bold'>StructFunc3(instance);</span> } } </pre> <a name='invalid_marshal'></a> <br /> 3번째 UnmanagedType.LPStruct를 사용한 메서드에서 다음과 같은 오류가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Unhandled Exception: System.Runtime.InteropServices.MarshalDirectiveException: Cannot marshal 'parameter #1': Invalid managed/unmanaged type combination (this value type must be paired with Struct). at lpStruct.Program.StructFunc3(MyStruct MyStruct) at lpStruct.Program.Main(String[] args) in C:\lpStruct\lpStruct\Program.cs:line 84 </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# PInvoke - C++의 매개변수에 대한 마샬링을 tlbexp.exe를 이용해 확인하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12443'>https://www.sysnet.pe.kr/2/0/12443</a> </pre> <br /> tlbexp.exe를 실행시켜 보면 마찬가지의 경고 메시지가 뜨면서 해당 메서드 자체를 누락시켜 버립니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> TlbExp : warning TX801311A6 : Type library exporter warning processing '...'. Warning: The method or field has an invalid managed/unmanaged type combination, check the MarshalAs directive.<br /> </div><br /> <br /> <hr style='width: 50%' /><br /> <a name='lpstruct_guid_field'></a> <br /> 그런데, UnmanagedType 문서의 설명에서,<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> <span style='color: blue; font-weight: bold'>LPStruct</span> <br /> A pointer to a C-style structure that you use to marshal managed formatted classes. <span style='color: blue; font-weight: bold'>This member is valid for platform invoke methods only.</span> </div><br /> <br /> 저렇게 "only"로 끝나는 제약이 약간 마음에 걸립니다. 즉, LPStruct + GUID는 메서드의 직접적인 인자로 전달할 때만 정상적인 마샬링 혜택을 받고 그 외의 경우에는 아니라는 의미인 듯합니다. 실제로, 필드로 정의한 GUID인 경우 LPStruct를 지정하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct MyGuid { <span style='color: blue; font-weight: bold'>[MarshalAs(UnmanagedType.LPStruct)] public Guid CLSID;</span> } [DllImport("Dll1.dll")] static extern void GuidStructFunc(MyGuid instance); /* // tlbexp로 확인히면, struct tagMyGuid { GUID* CLSID; } MyGuid; */ </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;' > Unhandled Exception: System.TypeLoadException: Cannot marshal field 'CLSID' of type 'lpStruct.MyGuid': Invalid managed/unmanaged type combination (this value type must be paired with Struct). at System.StubHelpers.ValueClassMarshaler.ConvertToNative(IntPtr dst, IntPtr src, IntPtr pMT, CleanupWorkList& pCleanupWorkList) at lpStruct.Program.GuidStructFunc(MyGuid instance) at lpStruct.Program.Main(String[] args) in C:\temp\lpStruct\Program.cs:line 113 </pre> <br /> 또한 SizeOf 연산조차도 할 수 없습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // System.ArgumentException: 'Type 'lpStruct.MyGuid' cannot be marshaled as an unmanaged structure; no meaningful size or offset can be computed.' int size = Marshal.SizeOf(instance); </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1674&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 정리해 보면, 일반적인 구조체인 경우 LPStruct를 이용해 포인터를 경유한 마샬링은 할 수 없습니다. 또한, LPStruct의 마샬링 혜택을 받는 GUID라고 해도 다른 구조체의 멤버로 있을 때는 예외가 발생합니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
4410
(왼쪽의 숫자를 입력해야 합니다.)