Microsoft MVP성태의 닷넷 이야기
.NET Framework: 979. C# - CoCreateInstanceEx 사용 예제 코드 [링크 복사], [링크+제목 복사],
조회: 21098
글쓴 사람
정성태 (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;
    }
}

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




If you know what interface you want, just pass it directly to CoCreateInstance
; https://devblogs.microsoft.com/oldnewthing/20240520-00/?p=109782

Reducing chattiness by querying for multiple interfaces at once, part 1
; https://devblogs.microsoft.com/oldnewthing/20220315-00/?p=106350

Reducing chattiness by querying for multiple interfaces at once, part 2
; https://devblogs.microsoft.com/oldnewthing/20220316-00/?p=106353




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 5/21/2024]

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

비밀번호

댓글 작성자
 




[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
13945정성태6/7/2025345오류 유형: 960. 파이썬 + conda - mysqlclient 사용 시 "NameError: name '_mysql' is not defined" 에러
13944정성태6/7/2025341오류 유형: 959. The trust relationship between this workstation and the primary domain failed. - 네 번째 이야기
13943정성태6/6/2025481개발 환경 구성: 748. Windows + Foundry Local - 로컬에서 AI 모델 활용
13942정성태6/5/2025666오류 유형: 958. winget 설치 시 "0x80d02002 : unknown error"
13941정성태6/2/2025910닷넷: 2334. C# - cpuid 명령어를 이용한 CPU 제조사 문자열 가져오기파일 다운로드1
13940정성태6/1/20251317C/C++: 188. C++의 32비트 + Release 어셈블리 코드를 .NET으로 포팅할 때 주의할 점파일 다운로드1
13939정성태5/29/20251602오류 유형: 957. NVIDIA Triton Inference Server - version `GLIBCXX_3.4.32' not found (required by /opt/tritonserver/backends/python/triton_python_backend_stub)
13938정성태5/29/20251361개발 환경 구성: 747. 파이썬 - WSL/docker에 구성한 Triton 예제 개발 환경
13937정성태5/24/20251344개발 환경 구성: 746. Windows + WSL2 환경에서 (tensorflow 등의) NVIDIA GPU 인식
13936정성태5/23/20251175개발 환경 구성: 745. Linux / WSL 환경에 Miniconda 설치하기
13935정성태5/20/20251187파이썬 - pip 사용 시 "ImportError: cannot import name 'html5lib' from 'pip._vendor'" 오류
13934정성태5/20/20251702스크립트: 77. 파이썬 - 'urllib.request' 모듈의 명시적/암시적 로딩 차이
13933정성태5/19/20251270오류 유형: 956. Visual Studio 2022가 17.12 버전부터 업데이트 되지 않는다면?
13932정성태5/18/20251476스크립트: 76. 파이썬 - Version 문자열 다루기(semver 패키지)
13931정성태5/17/20251765스크립트: 75. 파이썬 - Cython 기본 예제 및 컴파일
13930정성태5/17/20251445개발 환경 구성: 744. 파이썬 - Windows embeddable package 환경에서 외부 패키지 사용하는 방법(ex: UFO² 환경 구성)
13929정성태5/16/20251473오류 유형: 955. 파이썬 - "Windows embeddable package" REPL 환경에서 "NameError: name 'exit' is not defined"
13928정성태5/15/20251524오류 유형: 954. UFO² - "'Invalid URL (POST /v1/chat/completions/chat/completions)'"
13927정성태5/15/20251521오류 유형: 953. OpenAI - The API request of HOST_AGENT failed: OpenAI API request exceeded rate limit: Error code: 429
13926정성태5/14/20251900개발 환경 구성: 743. LLM과 윈도우의 만남 - Desktop AgentOS UFO² 기본 환경 구성
13925정성태5/12/20251999닷넷: 2333. C# - (Console 유형의 프로젝트에서) Clipboard 연동파일 다운로드1
13924정성태5/8/20251736닷넷: 2332. C# - (JetBrains Omea Reader 대상으로) 런타임 시에 메서드 가로채기 [2]파일 다운로드1
13923정성태5/5/20251490스크립트: 74. 파이썬 - C# - Python.NET의 RunSimpleScript, Exec, Eval 차이점파일 다운로드1
13922정성태5/3/20251724스크립트: 73. 파이썬 - Windows embeddable package 버전에서 tkinter 환경 구성
13921정성태5/3/20252223오류 유형: 952. 듀얼 채널 메모리 정렬을 지키지 않은 컴퓨터의 Windows 비정상 종료 현상(Blue Screen) [2]
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...