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

비밀번호

댓글 작성자
 




... 91  92  93  94  95  96  97  [98]  99  100  101  102  103  104  105  ...
NoWriterDateCnt.TitleFile(s)
11484정성태4/11/201824748.NET Framework: 737. C# - async를 Task 타입이 아닌 사용자 정의 타입에 적용하는 방법파일 다운로드1
11483정성태4/10/201828044개발 환경 구성: 358. "Let's Encrypt"에서 제공하는 무료 SSL 인증서를 IIS에 적용하는 방법 (2) [1]
11482정성태4/10/201820488VC++: 126. CUDA Core 수를 알아내는 방법
11481정성태4/10/201832118개발 환경 구성: 357. CUDA의 인덱싱 관련 용어 - blockIdx, threadIdx, blockDim, gridDim
11480정성태4/9/201822170.NET Framework: 736. C# - API를 사용해 Azure에 접근하는 방법 [2]파일 다운로드1
11479정성태4/9/201817784.NET Framework: 735. Azure - PowerShell로 Access control(IAM)에 새로운 계정 만드는 방법
11478정성태11/8/201920030디버깅 기술: 115. windbg - 덤프 파일로부터 PID와 환경변수 등의 정보를 구하는 방법 [1]
11477정성태4/8/201817476오류 유형: 460. windbg - sos 명령어 수행 시 c0000006 오류 발생
11476정성태4/8/201819038디버깅 기술: 114. windbg - !threads 출력 결과로부터 닷넷 관리 스레드(System.Threading.Thread) 객체를 구하는 방법
11475정성태3/28/201821341디버깅 기술: 113. windbg - Thread.Suspend 호출 시 응용 프로그램 hang 현상에 대한 덤프 분석
11474정성태3/27/201819454오류 유형: 459. xperf: error: TEST.Event: Invalid flags. (0x3ec).
11473정성태3/22/201824595.NET Framework: 734. C# - Thread.Suspend 호출 시 응용 프로그램 hang 현상파일 다운로드2
11472정성태3/22/201818571개발 환경 구성: 356. GTX 1070, GTX 960, GT 640M의 cudaGetDeviceProperties 출력 결과
11471정성태3/20/201821952VC++: 125. CUDA로 작성한 RGB2RGBA 성능 [1]파일 다운로드1
11470정성태3/20/201824132오류 유형: 458. Visual Studio - CUDA 프로젝트 빌드 시 오류 C1189, expression must have a constant value
11469정성태3/19/201817152오류 유형: 457. error MSB3103: Invalid Resx file. Could not load file or assembly 'System.Windows.Forms, ...' or one of its dependencies.
11468정성태3/19/201816674오류 유형: 456. 닷넷 응용 프로그램 실행 시 0x80131401 예외 발생
11467정성태3/19/201816086오류 유형: 455. Visual Studio Installer - 업데이트 실패
11466정성태3/18/201817234개발 환경 구성: 355. 한 대의 PC에서 2개 이상의 DirectX 게임을 실행하는 방법
11463정성태3/15/201819574.NET Framework: 733. 스레드 간의 read/write 시에도 lock이 필요 없는 경우파일 다운로드1
11462정성태3/14/201822444개발 환경 구성: 354. HTTPS 호출에 대한 TLS 설정 확인하는 방법 [1]
11461정성태3/13/201825069오류 유형: 454. 윈도우 업데이트 설치 오류 - 0x800705b4 [1]
11460정성태3/13/201817563디버깅 기술: 112. windbg - 닷넷 메모리 덤프에서 전역 객체의 내용을 조사하는 방법
11459정성태3/13/201818368오류 유형: 453. Debug Diagnostic Tool에서 mscordacwks.dll을 찾지 못하는 문제
11458정성태2/21/201819347오류 유형: 452. This share requires the obsolete SMB1 protocol, which is unsafe and could expose your system to attack. [1]
11457정성태2/17/201824071.NET Framework: 732. C# - Task.ContinueWith 설명 [1]파일 다운로드1
... 91  92  93  94  95  96  97  [98]  99  100  101  102  103  104  105  ...