성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
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# - CoCreateInstanceEx 사용 예제 코드</h1> <p> CoCreateInstanceEx Win32 API는,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > CoCreateInstanceEx function (combaseapi.h) ; <a target='tab' href='https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstanceex'>https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstanceex</a> </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;' > Guid clsid = new Guid("{469afbdf-084f-4dc9-904f-9e824c48bc37}"); { Type targetType = Type.GetTypeFromCLSID(clsid); object comObj = Activator.CreateInstance(targetType); Console.WriteLine($"{comObj}"); } </pre> <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;' > Win32 Interop - 크기가 정해지지 않은 배열을 C++에서 C#으로 전달하는 경우 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/737'>https://www.sysnet.pe.kr/2/0/737</a> C#에서 Union 구조체 다루기 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/728'>https://www.sysnet.pe.kr/2/0/728</a> How to Interop DISPPARAMS ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/617'>https://www.sysnet.pe.kr/2/0/617</a> [in,out] 배열을 C#에서 C/C++로 넘기는 방법 - 두 번째 이야기 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/811'>https://www.sysnet.pe.kr/2/0/811</a> [in,out] 배열을 C#에서 C/C++로 넘기는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/810'>https://www.sysnet.pe.kr/2/0/810</a> </pre> <br /> 살펴보겠습니다. ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 가장 중요한 코드는 C++의 MULTI_QI 타입인데,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > typedef struct tagMULTI_QI { const <span style='color: blue; font-weight: bold'>IID *pIID</span>; // [입력] IID의 값이 담겨 있는 주소에 대한 포인터 <span style='color: blue; font-weight: bold'>IUnknown *pItf</span>; // [출력] COM 개체의 IID에 해당하는 인터페이스 포인터 HRESULT hr; } MULTI_QI; </pre> <br /> 보는 바와 같이 지난 글에서 다룬 GUID의 포인터가,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - GUID 타입 전용의 UnmanagedType.LPStruct ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12444'>https://www.sysnet.pe.kr/2/0/12444</a> </pre> <br /> 들어 있어 unsafe 구문의 마샬링을 해야 합니다. 그런데, <a target='tab' href='https://www.pinvoke.net/'>PInvoke 사이트</a>에는 다음과 같이 C# 구조체를 정의하고 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // <a target='tab' href='https://www.pinvoke.net/default.aspx/Structures/MULTI_QI.html'>https://www.pinvoke.net/default.aspx/Structures/MULTI_QI.html</a> [StructLayout(LayoutKind.Sequential)] struct MULTI_QI { <span style='color: blue; font-weight: bold'>[MarshalAs(UnmanagedType.LPStruct)]</span> public Guid pIID; [MarshalAs(UnmanagedType.Interface)] public object pItf; public int hr; } </pre> <br /> 이상하죠? <a target='tab' href='https://www.sysnet.pe.kr/2/0/12444#lpstruct_guid_field'>분명히 LPStruct는 구조체 내에서 정의한 GUID를 마샬링하지 못합니다</a>. 실제로 PInovke에 나온 MULTI_QI와 CoCreateInstanceEx 코드로 예제를 구성하면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [DllImport("ole32.dll")] static extern int <a target='tab' href='https://www.sysnet.pe.kr/2/0/12678'>CoCreateInstance</a>([In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, IntPtr pUnkOuter, CLSCTX dwClsCtx, [In, MarshalAs(UnmanagedType.LPStruct)] Guid riid, [In, Out] ref IntPtr ppv); [DllImport("ole32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, PreserveSig = false)] static extern void CoCreateInstanceEx( [In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, [MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, CLSCTX dwClsCtx, IntPtr pServerInfo, uint cmq, [In, Out] MULTI_QI[] pResults); public static void CoCreateInstance(Guid clsid, out object comObject) { comObject = null; Guid iUnk = new Guid("{00000000-0000-0000-C000-000000000046}"); MULTI_QI[] mqs = new MULTI_QI[1]; mqs[0].pIID = iUnk; CoCreateInstanceEx(clsid, null, CLSCTX.CLSCTX_INPROC_SERVER, IntPtr.Zero, 1, mqs); } </pre> <br /> <a target='tab' href='https://www.sysnet.pe.kr/2/0/12444#invalid_marshal'>예의 그 예외</a>가 발생합니다.<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 'pIID' of type 'CreateTest.MULTI_QI': Invalid managed/unmanaged type combination (this value type must be paired with Struct). at System.StubHelpers.MngdNativeArrayMarshaler.ConvertContentsToNative(IntPtr pMarshalState, Object& pManagedHome, IntPtr pNativeHome) at CreateTest.PInvokeSample.CoCreateInstanceEx(Guid rclsid, Object pUnkOuter, CLSCTX dwClsCtx, IntPtr pServerInfo, UInt32 cmq, MULTI_QI[] pResults) at CreateTest.PInvokeSample.CoCreateInstance(Guid clsid, Object& comObject) in C:\CreateTest\PInvokeSample.cs:line 27 at CreateTest.Program.Main(String[] args) in C:\CreateTest\Program.cs:line 20 </pre> <br /> <hr style='width: 50%' /><br /> <br /> 어쨌든, 정상적인 PInvoke 호출을 하려면 MULTI_QI 구조체를 이런 식으로 구성해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > /* typedef struct tagMULTI_QI { <span style='color: blue; font-weight: bold'>const IID *pIID;</span> IUnknown *pItf; HRESULT hr; } MULTI_QI; */ internal struct MULTI_QI_PTR { public <span style='color: blue; font-weight: bold'>IntPtr pIID</span>; public IntPtr pItf; public int hr; } </pre> <br /> 그리고 호출 시 pIID 멤버를 포인터 처리하는 작업을 직접 코딩해야 합니다. 이와 관련해 <a target='tab' href='https://www.sysnet.pe.kr/2/0/12444#ptr_to_struct'>지난 예제에서 구조체 처리를 (LPStruct는 불가능하니) 2가지 방식</a>으로 다뤘는데요, 다음은 첫 번째 방식으로 처리한 것이고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; namespace ComUtil { public static class NativeMethods { [DllImport("ole32.dll", CharSet = CharSet.Unicode, EntryPoint = "CoCreateInstanceEx")] static internal extern uint CoCreateInstanceEx([In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, [MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, CLSCTX dwClsCtx, IntPtr pServerInfo, uint cmq, <span style='color: blue; font-weight: bold'>IntPtr pResults</span>); public static uint CoCreateInstanceEx(Guid clsid, out object comObject) { Guid iUnk = new Guid("{00000000-0000-0000-C000-000000000046}"); return CoCreateInstanceEx(clsid, iUnk, out comObject); } private static uint CoCreateInstanceEx(Guid clsid, Guid qIID, out object comObject) { MULTI_QI_PTR mq = default; IntPtr pGuid = IntPtr.Zero; IntPtr pBuf = IntPtr.Zero; comObject = null; try { pGuid = Marshal.AllocHGlobal(16); pBuf = Marshal.AllocHGlobal(Marshal.SizeOf(mq)); Marshal.StructureToPtr(qIID, pGuid, true); mq.pIID = pGuid; Marshal.StructureToPtr(mq, pBuf, true); uint result = CoCreateInstanceEx(clsid, null, CLSCTX.INPROC_SERVER, IntPtr.Zero, 1, pBuf); if (result == 0) { mq = (MULTI_QI_PTR)Marshal.PtrToStructure(pBuf, typeof(MULTI_QI_PTR)); comObject = Marshal.GetObjectForIUnknown(mq.pItf); } return result; } finally { if (pGuid != IntPtr.Zero) { Marshal.FreeHGlobal(pGuid); } if (pBuf != IntPtr.Zero) { Marshal.FreeHGlobal(pBuf); } } } } } </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using ComUtil; using System; namespace CreateTest { class Program { public const string comGuid = "{469afbdf-084f-4dc9-904f-9e824c48bc37}"; [STAThread] static void Main(string[] args) { Guid clsid = new Guid(comGuid); { uint result = NativeMethods.CoCreateInstanceEx(clsid, out object comObj); Console.WriteLine($"{result.ToString("x")} {comObj}"); } } } } </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;' > unsafe internal struct MULTI_QI { public <span style='color: blue; font-weight: bold'>Guid* pIID</span>; public IntPtr pItf; public int hr; } [DllImport("ole32.dll")] static unsafe internal extern uint CoCreateInstanceEx([In, MarshalAs(UnmanagedType.LPStruct)] Guid rclsid, [MarshalAs(UnmanagedType.IUnknown)] object pUnkOuter, CLSCTX dwClsCtx, IntPtr pServerInfo, uint cmq, <span style='color: blue; font-weight: bold'>MULTI_QI* pResults</span>); private static uint CoCreateInstanceEx(Guid clsid, Guid qIID, out object comObject) { MULTI_QI mq = default; comObject = null; unsafe { mq.pIID = &qIID; uint result = CoCreateInstanceEx(clsid, null, CLSCTX.INPROC_SERVER, IntPtr.Zero, 1, &mq); if (result == 0) { comObject = Marshal.GetObjectForIUnknown(mq.pItf); } return result; } } </pre> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1676&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1829
(왼쪽의 숫자를 입력해야 합니다.)