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

C# - CoCreateInstanceEx 사용 예제 코드

CoCreateInstanceEx Win32 API는,

CoCreateInstanceEx function (combaseapi.h)
; https://docs.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", 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;
    }
}

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




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



donaricano-btn



[최초 등록일: ]
[최종 수정일: 12/10/2020 ]

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)
12426정성태11/24/2020927오류 유형: 685. WinDbg Preview - error InitTypeRead
12425정성태11/24/2020950VC++: 141. Visual C++ - "Treat Warnings As Errors" 옵션이 꺼져 있는데도 일부 경고가 에러 처리되는 경우
12424정성태11/24/20201128VC++: 140. C++의 연산자 동의어(operator synonyms), 대체 토큰
12423정성태11/22/2020977.NET Framework: 974. C# 9.0 - (16) 제약 조건이 없는 형식 매개변수 주석(Unconstrained type parameter annotations)파일 다운로드1
12422정성태11/21/2020954.NET Framework: 973. .NET 5, .NET Framework에서만 허용하는 UnmanagedCallersOnly 사용예파일 다운로드1
12421정성태11/23/2020957.NET Framework: 972. DNNE가 출력한 NE DLL을 직접 생성하는 방법파일 다운로드1
12420정성태11/19/2020880오류 유형: 684. Visual C++ - MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance
12419정성태11/23/2020954VC++: 139. Visual C++ - .NET Core의 nethost.lib와 정적 링크파일 다운로드1
12418정성태11/19/2020860오류 유형: 683. Visual C++ - error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MT_StaticRelease' doesn't match value 'MDd_DynamicDebug'파일 다운로드1
12417정성태11/19/2020687오류 유형: 682. Visual C++ - warning LNK4099: PDB '...pdb' was not found with '...lib(pch.obj)' or at '...pdb'; linking object as if no debug info
12416정성태11/19/2020787오류 유형: 681. Visual C++ - error LNK2001: unresolved external symbol _CrtDbgReport
12415정성태11/19/2020819.NET Framework: 971. UnmanagedCallersOnly 특성과 DNNE 사용파일 다운로드1
12414정성태11/20/2020866VC++: 138. x64 빌드에서 extern "C"가 아닌 경우 ___cdecl name mangling 적용 [4]파일 다운로드1
12413정성태11/17/20201015.NET Framework: 970. .NET 5 / .NET Core - UnmanagedCallersOnly 특성을 사용한 함수 내보내기파일 다운로드1
12412정성태11/21/20201123.NET Framework: 969. .NET Framework 및 .NET 5 - UnmanagedCallersOnly 특성 사용파일 다운로드1
12411정성태11/12/20201009오류 유형: 680. C# 9.0 - Error CS8889 The target runtime doesn't support extensible or runtime-environment default calling conventions.
12410정성태11/12/2020912디버깅 기술: 174. windbg - System.TypeLoadException 예외 분석 사례
12409정성태11/12/20201187.NET Framework: 968. C# 9.0의 Function pointer를 이용한 함수 주소 구하는 방법파일 다운로드1
12408정성태3/17/20212406도서: 시작하세요! C# 9.0 프로그래밍 [6]
12407정성태11/30/20201050.NET Framework: 967. "clr!JIT_DbgIsJustMyCode" 호출이 뭘까요?
12406정성태11/22/20201460.NET Framework: 966. C# 9.0 - (15) 최상위 문(Top-level statements) [2]파일 다운로드1
12405정성태11/22/20201109.NET Framework: 965. C# 9.0 - (14) 부분 메서드에 대한 새로운 기능(New features for partial methods)파일 다운로드1
12404정성태11/22/20201105.NET Framework: 964. C# 9.0 - (13) 모듈 이니셜라이저(Module initializers)파일 다운로드1
12403정성태11/22/20201173.NET Framework: 963. C# 9.0 - (12) foreach 루프에 대한 GetEnumerator 확장 메서드 지원(Extension GetEnumerator)파일 다운로드1
12402정성태11/22/20201417.NET Framework: 962. C# 9.0 - (11) 공변 반환 형식(Covariant return types) [1]파일 다운로드1
12401정성태11/5/2020931VS.NET IDE: 153. 닷넷 응용 프로그램에서의 "My Code" 범위와 "Enable Just My Code"의 역할
1  2  3  4  5  6  7  8  9  [10]  11  12  13  14  15  ...