Microsoft MVP성태의 닷넷 이야기
.NET Framework: 979. C# - CoCreateInstanceEx 사용 예제 코드 [링크 복사], [링크+제목 복사]
조회: 11083
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)

C# - CoCreateInstanceEx 사용 예제 코드

CoCreateInstanceEx Win32 API는,

CoCreateInstanceEx function (combaseapi.h)
; https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstanceex

C#에서 대체 방법을 제공하므로 직접 사용해야 할 필요는 거의 없습니다.

Guid clsid = new Guid("{469afbdf-084f-4dc9-904f-9e824c48bc37}");
 
{
    Type targetType = Type.GetTypeFromCLSID(clsid);
    object comObj = Activator.CreateInstance(targetType);
    Console.WriteLine($"{comObj}");
}

단지, 지금까지 다뤘던 Interop 예제와는 다른 면이 있기 때문에,

Win32 Interop - 크기가 정해지지 않은 배열을 C++에서 C#으로 전달하는 경우
; https://www.sysnet.pe.kr/2/0/737

C#에서 Union 구조체 다루기
; https://www.sysnet.pe.kr/2/0/728

How to Interop DISPPARAMS
; https://www.sysnet.pe.kr/2/0/617

[in,out] 배열을 C#에서 C/C++로 넘기는 방법 - 두 번째 이야기
; https://www.sysnet.pe.kr/2/0/811

[in,out] 배열을 C#에서 C/C++로 넘기는 방법
; https://www.sysnet.pe.kr/2/0/810

살펴보겠습니다. ^^




가장 중요한 코드는 C++의 MULTI_QI 타입인데,

typedef struct tagMULTI_QI
{
    const IID *pIID; // [입력] IID의 값이 담겨 있는 주소에 대한 포인터
    IUnknown *pItf; // [출력] COM 개체의 IID에 해당하는 인터페이스 포인터
    HRESULT hr;
} MULTI_QI;

보는 바와 같이 지난 글에서 다룬 GUID의 포인터가,

C# - GUID 타입 전용의 UnmanagedType.LPStruct
; https://www.sysnet.pe.kr/2/0/12444

들어 있어 unsafe 구문의 마샬링을 해야 합니다. 그런데, PInvoke 사이트에는 다음과 같이 C# 구조체를 정의하고 있습니다.

// https://www.pinvoke.net/default.aspx/Structures/MULTI_QI.html
[StructLayout(LayoutKind.Sequential)]
struct MULTI_QI
{
    [MarshalAs(UnmanagedType.LPStruct)]  public Guid pIID;
    [MarshalAs(UnmanagedType.Interface)] public object pItf;
    public int hr;
}

이상하죠? 분명히 LPStruct는 구조체 내에서 정의한 GUID를 마샬링하지 못합니다. 실제로 PInovke에 나온 MULTI_QI와 CoCreateInstanceEx 코드로 예제를 구성하면,

[DllImport("ole32.dll")]
static extern int CoCreateInstance([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);
}

예의 그 예외가 발생합니다.

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




어쨌든, 정상적인 PInvoke 호출을 하려면 MULTI_QI 구조체를 이런 식으로 구성해야 합니다.

/*
typedef struct tagMULTI_QI
{
    const IID *pIID;
    IUnknown *pItf;
    HRESULT hr;
} MULTI_QI;
*/

internal struct MULTI_QI_PTR
{
    public IntPtr pIID;
    public IntPtr pItf;
    public int hr;
}

그리고 호출 시 pIID 멤버를 포인터 처리하는 작업을 직접 코딩해야 합니다. 이와 관련해 지난 예제에서 구조체 처리를 (LPStruct는 불가능하니) 2가지 방식으로 다뤘는데요, 다음은 첫 번째 방식으로 처리한 것이고,

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,
           IntPtr pResults);

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

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

반면, 두 번째 방식으로 포인터의 힘을 빌리면 좀 더 간단하게 처리할 수 있습니다.

unsafe internal struct MULTI_QI
{
    public Guid* pIID;
    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,
    MULTI_QI* pResults);

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

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




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 10/27/2023]

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

비밀번호

댓글 작성자
 




... 16  17  18  19  20  21  22  23  [24]  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13027정성태4/12/20227073.NET Framework: 1192. C# - 환경 변수의 변화를 알리는 WM_SETTINGCHANGE Win32 메시지 사용법파일 다운로드1
13026정성태4/11/20228596.NET Framework: 1191. C 언어로 작성된 FFmpeg Examples의 C# 포팅 전체 소스 코드 [3]
13025정성태4/11/20227940.NET Framework: 1190. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 vaapi_encode.c, vaapi_transcode.c 예제 포팅
13024정성태4/7/20226428.NET Framework: 1189. C# - 런타임 환경에 따라 달라진 AppDomain.GetCurrentThreadId 메서드
13023정성태4/6/20226730.NET Framework: 1188. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 transcoding.c 예제 포팅 [3]
13022정성태3/31/20226626Windows: 202. 윈도우 11 업그레이드 - "PC Health Check"를 통과했지만 여전히 업그레이드가 안 되는 경우 해결책
13021정성태3/31/20226803Windows: 201. Windows - INF 파일을 이용한 장치 제거 방법
13020정성태3/30/20226533.NET Framework: 1187. RDP 접속 시 WPF UserControl의 Unloaded 이벤트 발생파일 다운로드1
13019정성태3/30/20226542.NET Framework: 1186. Win32 Message를 Code로부터 메시지 이름 자체를 구하고 싶다면?파일 다운로드1
13018정성태3/29/20227078.NET Framework: 1185. C# - Unsafe.AsPointer가 반환한 포인터는 pinning 상태일까요? [5]
13017정성태3/28/20226886.NET Framework: 1184. C# - GC Heap에 위치한 참조 개체의 주소를 알아내는 방법 - 두 번째 이야기 [3]
13016정성태3/27/20227728.NET Framework: 1183. C# 11에 추가된 ref 필드의 (우회) 구현 방법파일 다운로드1
13015정성태3/26/20229086.NET Framework: 1182. C# 11 - ref struct에 ref 필드를 허용 [1]
13014정성태3/23/20227676VC++: 155. CComPtr/CComQIPtr과 Conformance mode 옵션의 충돌 [1]
13013정성태3/22/20226004개발 환경 구성: 641. WSL 우분투 인스턴스에 파이썬 2.7 개발 환경 구성하는 방법
13012정성태3/21/20225327오류 유형: 803. C# - Local '...' or its members cannot have their address taken and be used inside an anonymous method or lambda expression
13011정성태3/21/20226793오류 유형: 802. 윈도우 운영체제에서 웹캠 카메라 인식이 안 되는 경우
13010정성태3/21/20225727오류 유형: 801. Oracle.ManagedDataAccess.Core - GetTypes 호출 시 "Could not load file or assembly 'System.DirectoryServices.Protocols...'" 오류
13009정성태3/20/20227338개발 환경 구성: 640. docker - ibmcom/db2 컨테이너 실행
13008정성태3/19/20226640VS.NET IDE: 176. 비주얼 스튜디오 - 솔루션 탐색기에서 프로젝트를 선택할 때 csproj 파일이 열리지 않도록 만드는 방법
13007정성태3/18/20226244.NET Framework: 1181. C# - Oracle.ManagedDataAccess의 Pool 및 그것의 연결 개체 수를 알아내는 방법파일 다운로드1
13006정성태3/17/20227312.NET Framework: 1180. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 remuxing.c 예제 포팅
13005정성태3/17/20226185오류 유형: 800. C# - System.InvalidOperationException: Late bound operations cannot be performed on fields with types for which Type.ContainsGenericParameters is true.
13004정성태3/16/20226192디버깅 기술: 182. windbg - 닷넷 메모리 덤프에서 AppDomain에 걸친 정적(static) 필드 값을 조사하는 방법
13003정성태3/15/20226345.NET Framework: 1179. C# - (.NET Framework를 위한) Oracle.ManagedDataAccess 패키지의 성능 카운터 설정 방법
13002정성태3/14/20227116.NET Framework: 1178. C# - ffmpeg(FFmpeg.AutoGen)를 이용한 http_multiclient.c 예제 포팅
... 16  17  18  19  20  21  22  23  [24]  25  26  27  28  29  30  ...