Microsoft MVP성태의 닷넷 이야기
.NET Framework: 1095. C# COM 개체를 C++에서 사용하는 예제 [링크 복사], [링크+제목 복사],
조회: 8270
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 5개 있습니다.)
(시리즈 글이 13개 있습니다.)
.NET Framework: 397. C# - OCX 컨트롤에 구현된 메서드에 배열을 in, out으로 전달하는 방법
; https://www.sysnet.pe.kr/2/0/1547

.NET Framework: 652. C# 개발자를 위한 C++ COM 객체의 기본 구현 방식 설명
; https://www.sysnet.pe.kr/2/0/11175

.NET Framework: 792. C# COM 서버에 구독한 COM 이벤트를 C++에서 받는 방법
; https://www.sysnet.pe.kr/2/0/11679

.NET Framework: 907. C# DLL로부터 TLB 및 C/C++ 헤더 파일(TLH)을 생성하는 방법
; https://www.sysnet.pe.kr/2/0/12220

.NET Framework: 977. C# PInvoke - C++의 매개변수에 대한 마샬링을 tlbexp.exe를 이용해 확인하는 방법
; https://www.sysnet.pe.kr/2/0/12443

.NET Framework: 1008. 배열을 반환하는 C# COM 개체의 메서드를 C++에서 사용 시 메모리 누수 현상
; https://www.sysnet.pe.kr/2/0/12491

.NET Framework: 1064. C# COM 개체를 PIA(Primary Interop Assembly)로써 "Embed Interop Types" 참조하는 방법
; https://www.sysnet.pe.kr/2/0/12662

.NET Framework: 1069. C# - DLL Surrogate를 이용한 Out-of-process COM 개체 제작
; https://www.sysnet.pe.kr/2/0/12668

.NET Framework: 1095. C# COM 개체를 C++에서 사용하는 예제
; https://www.sysnet.pe.kr/2/0/12791

.NET Framework: 2003. C# - COM 개체의 이벤트 핸들러에서 발생하는 예외에 대한 CLR의 특별 대우
; https://www.sysnet.pe.kr/2/0/13050

닷넷: 2177. C# - (Interop DLL 없이) CoClass를 이용한 COM 개체 생성 방법
; https://www.sysnet.pe.kr/2/0/13469

닷넷: 2248. C# - 인터페이스 타입의 다중 포인터를 인자로 갖는 C/C++ 함수 연동
; https://www.sysnet.pe.kr/2/0/13607

닷넷: 2254. C# - COM 인터페이스의 상속 시 중복으로 메서드를 선언
; https://www.sysnet.pe.kr/2/0/13614




C# COM 개체를 C++에서 사용하는 예제

다음의 질문이 나온 김에,

C++ C# API 연동 관련 
; https://www.sysnet.pe.kr/3/0/5546

정리를 해보겠습니다. ^^ 또한, 네이티브가 엮이므로 반드시 x86, x64를 선택해야 하는데 이 글은 x64 기준으로 설명합니다. x86에서 필요한 경우라면, 글에 나온 x64 옵션 등을 모두 x86 기준으로 바꾸면 됩니다.




우선, C# DLL(.NET 4.0)을 다음과 같이 만들겠습니다. (.NET Core/5+ 환경이라면 ".NET Core 3/5+ 기반의 COM Server를 기존의 regasm처럼 등록하는 방법" 글도 함께 참고하세요.)

using System.Runtime.InteropServices;
using System;
using System.Windows.Forms;

namespace ClassLibrary1
{
    [ComVisible(true)]
    [Guid("23172f2f-a3d3-4180-97ae-7805f74a5a46")]
    [InterfaceType(ComInterfaceType.InterfaceIsDual)]
    public interface IMyNetCode
    {
        void ShowDialog();
    }

    [ComVisible(true)]
    [Guid("41AC8568-9230-4E63-B7C5-CAAD997EE207")]
    public class MyNetCode : IMyNetCode
    {
        public void ShowDialog()
        {
            string platform = (IntPtr.Size == 4) ? "x86" : "x64";
            MessageBox.Show($".NET Framework: {Environment.Version}, {platform}");
        }
    }
}

또한 프로젝트 설정에서 "Signing"을 선택해 "Sign the assembly"를 이용해 서명합니다. 이후, 빌드하고, 명령행에서 ./bin/Debug/ 디렉터리로 이동해 다음과 같은 명령어를 실행,

C:\temp\ClassLibrary1\ClassLibrary1\bin\Debug> tlbexp ClassLibrary1.dll /win64
Microsoft (R) .NET Framework Assembly to Type Library Converter 4.8.4084.0
Copyright (C) Microsoft Corporation.  All rights reserved.

TlbExp : warning TX00131175 : When cross-compiling, all type library references should be included on the command line to ensure the correct bit-specific type libraries are loaded.
Assembly exported to 'C:\temp\ClassLibrary1\ClassLibrary1\bin\Debug\ClassLibrary1.tlb'

tlb 파일을 구해 놓습니다. 물론 위의 과정은 프로젝트 속성 창에서 "Register for COM interop" 옵션을 켜면 생략할 수 있지만 그런 경우 Visual Studio IDE를 관리자 권한으로 실행시켜야 하기 때문에 개인적으로 선호하지 않습니다.

어차피, TLB 파일은 인터페이스가 바뀌지 않는 한 재생성할 일이 없으므로 저런 식으로 그때그때 구하시는 것을 추천합니다.




C# DLL과 TLB 파일이 준비되었다면 이제 C++ Console 프로젝트를 생성하고 x64 타깃으로 바꾼 다음, 다음과 같이 코드를 작성합니다.

#include <combaseapi.h>
#include <Windows.h>

#import "../ClassLibrary1/bin/Debug/ClassLibrary1.tlb" no_namespace named_guids

int main()
{
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);

    {
        IMyNetCode* pMyObject = NULL;
        hr = ::CoCreateInstance(CLSID_MyNetCode, NULL, CLSCTX_INPROC_SERVER, IID_IMyNetCode,
            (PVOID*)&pMyObject);

        if (hr != S_OK)
        {
            printf("Failed - CoCreateInstance: %x(%d)\n", hr, hr);
            return 1;
        }

        pMyObject->ShowDialog();
    }

    CoUninitialize();

    return 0;
}

코드 작성은 이것으로 끝입니다.




자, 이제 Release 빌드를 하고, 다음의 바이너리 파일 2개를 대상 PC에 복사합니다.

  1. 닷넷 DLL 복사(c:\temp\ClassLibrary1.dll)
  2. C++ EXE 복사(c:\temp\ConsoleApplication1.exe)

이 글에서는 "C:\temp" 디렉터리에 복사했다고 가정하고 진행하겠습니다. 일단, 현재 상태로는 내 PC든 다른 PC든 정상적으로 실행되지 않습니다.

C:\temp>ConsoleApplication1.exe
Failed - CoCreateInstance: 80040154(-2147221164)

/* 0x80040154 == Class not registered */

왜냐하면, C# DLL의 위치를 찾을 방법이 없기 때문인데, 이것을 위해서는 regasm.exe를 실행해 레지스트리에 COM 관련 설정을 등록해야 합니다. 이를 위해서는 2가지 방법이 있는데요,


gacutil을 사용하는 경우, 일반 사용자 PC에 설치되어 있지 않으므로 regasm만 실행하는 방법이 더 나은데, 이 경우 옵션에 "/codebase"를 더해주면 됩니다. 따라서 관리자 권한의 창을 띄워 다음의 명령을 수행합니다.

C:\temp> C:\Windows\Microsoft.NET\Framework64\v4.0.30319\regasm /codebase ClassLibrary1.dll
Microsoft .NET Framework 버전 4.7.3190.0용
Microsoft .NET Framework Assembly Registration Utility 버전 4.7.3190.0
Copyright (C) Microsoft Corporation.  All rights reserved.

형식이 등록되었습니다.

끝입니다. 이제 ConsoleApplication1.exe를 실행하면 정상적으로 실행되는 것을 확인할 수 있습니다.

(첨부 파일은 이 글의 예제 코드를 포함합니다. (.NET Core/5+ 프로젝트 예제는 별도의 글에 첨부했습니다.)




시간 되시면, 다음의 글도 읽어 보시고. ^^

배열을 반환하는 C# COM 개체의 메서드를 C++에서 사용 시 메모리 누수 현상
; https://www.sysnet.pe.kr/2/0/12491

regasm.exe로 어셈블리 등록 시 시스템 변경 사항 (1) - .NET 2.0 + x86/x64/AnyCPU
; https://www.sysnet.pe.kr/2/0/1286
 
regasm.exe로 어셈블리 등록 시 시스템 변경 사항 (2) - .NET 4.0 + .NET 2.0
; https://www.sysnet.pe.kr/2/0/1287

regasm.exe로 어셈블리 등록 시 시스템 변경 사항 (3) - Type Library
; https://www.sysnet.pe.kr/2/0/1288

regsvcs.exe로 어셈블리 등록 시 시스템 변경 사항
; https://www.sysnet.pe.kr/2/0/1289




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 11/30/2023]

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

비밀번호

댓글 작성자
 



2021-08-23 09시00분
[DoubleS] 혹시 다른 PC 에서 regasm 명령어를 수행하지 않고 시도하려고 노력중인데
regasm 명령어가 레지스트리 등록말고도 다른 동작을 하나요?
regasm명령어를 통해 레지스트리에 등록되는것을 모두 수동으로 등록하고 실행하면 실패하고있습니다.
[guest]
2021-08-23 09시42분
COM 개체로 활성화하는 데 필요한 것은 레지스트리 작업이 전부입니다. 그것이 regasm이 하는 일이고, 설령 다른 동작을 한다고 해도 COM 개체로 실행하는 것과는 무관할 것입니다.

실패하고 있는 것에는, 아마도 어딘가의 레지스트리 설정이 누락된 것이 있을 것입니다.
정성태
2021-08-23 09시49분
아래의 레지스트리 파일(.reg)은 c:\temp에 C# DLL이 있다고 가정하고 작성된 것입니다. 본문의 예제에 작성된 대로라면 아래의 레지스트리를 등록하는 것만으로 정상적인 COM 개체 활성화가 됩니다.

--------------------------------------------------------------

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}]
@="ClassLibrary1.MyNetCode"

[HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}\Implemented Categories]

[HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}]

[HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}\InprocServer32]
@="mscoree.dll"
"ThreadingModel"="Both"
"Class"="ClassLibrary1.MyNetCode"
"Assembly"="ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bcd368746b705e6a"
"RuntimeVersion"="v4.0.30319"
"CodeBase"="file:///C:/temp/ClassLibrary1.DLL"

[HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}\InprocServer32\1.0.0.0]
"Class"="ClassLibrary1.MyNetCode"
"Assembly"="ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bcd368746b705e6a"
"RuntimeVersion"="v4.0.30319"
"CodeBase"="file:///C:/temp/ClassLibrary1.DLL"

[HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}\ProgId]
@="ClassLibrary1.MyNetCode"
정성태

... 46  47  48  49  50  51  52  53  54  [55]  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12274정성태7/21/202012363개발 환경 구성: 496. Azure - Blob Storage Account의 Location 이전 방법 [1]파일 다운로드1
12273정성태7/18/202014059개발 환경 구성: 495. Azure - Location이 다른 웹/DB 서버의 경우 발생하는 성능 하락
12272정성태7/16/20208980.NET Framework: 929. (StrongName의 버전 구분이 필요 없는) .NET Core 어셈블리 바인딩 규칙 [2]파일 다운로드1
12271정성태7/16/202011087.NET Framework: 928. .NET Framework의 Strong-named 어셈블리 바인딩 (2) - 런타임에 바인딩 리디렉션파일 다운로드1
12270정성태7/16/202011845오류 유형: 633. SSL_CTX_use_certificate_file - error:140AB18F:SSL routines:SSL_CTX_use_certificate:ee key too small
12269정성태7/16/20209247오류 유형: 632. .NET Core 웹 응용 프로그램 - The process was terminated due to an unhandled exception.
12268정성태7/15/202011006오류 유형: 631. .NET Core 웹 응용 프로그램 오류 - HTTP Error 500.35 - ANCM Multiple In-Process Applications in same Process
12267정성태7/15/202012603.NET Framework: 927. C# - 윈도우 프로그램에서 Credential Manager를 이용한 보안 정보 저장파일 다운로드1
12266정성태7/14/202010340오류 유형: 630. 사용자 계정을 지정해 CreateService API로 서비스를 등록한 경우 "Error 1069: The service did not start due to a logon failure." 오류발생
12265정성태7/10/20209437오류 유형: 629. Visual Studio - 웹 애플리케이션 실행 시 "Unable to connect to web server 'IIS Express'." 오류 발생
12264정성태7/9/202018572오류 유형: 628. docker: Error response from daemon: Conflict. The container name "..." is already in use by container "...".
12261정성태7/9/202011524VS.NET IDE: 148. 윈도우 10에서 .NET Core 응용 프로그램을 리눅스 환경에서 실행하는 2가지 방법 - docker, WSL 2 [5]
12260정성태7/8/20209760.NET Framework: 926. C# - ETW를 이용한 ThreadPool 스레드 감시파일 다운로드1
12259정성태7/8/20209371오류 유형: 627. nvlddmkm.sys의 BAD_POOL_HEADER BSOD 문제 [1]
12258정성태7/8/202012551기타: 77. DataDog APM 간략 소개
12257정성태7/7/20209478.NET Framework: 925. C# - ETW를 이용한 Monitor Enter/Exit 감시파일 다운로드1
12256정성태7/7/20209980.NET Framework: 924. C# - Reflection으로 변경할 수 없는 readonly 정적 필드 [4]
12255정성태7/6/202010423.NET Framework: 923. C# - ETW(Event Tracing for Windows)를 이용한 Finalizer 실행 감시파일 다운로드1
12254정성태7/2/202010249오류 유형: 626. git - REMOTE HOST IDENTIFICATION HAS CHANGED!
12253정성태7/2/202011411.NET Framework: 922. C# - .NET ThreadPool의 Local/Global Queue파일 다운로드1
12252정성태7/2/202013386.NET Framework: 921. C# - I/O 스레드를 사용한 비동기 소켓 서버/클라이언트파일 다운로드2
12251정성태7/1/202011436.NET Framework: 920. C# - 파일의 비동기 처리 유무에 따른 스레드 상황 [1]파일 다운로드2
12250정성태6/30/202013983.NET Framework: 919. C# - 닷넷에서의 진정한 비동기 호출을 가능케 하는 I/O 스레드 사용법 [1]파일 다운로드1
12249정성태6/29/202010097오류 유형: 625. Microsoft SQL Server 2019 RC1 Setup - 설치 제거 시 Warning 26003 오류 발생
12248정성태6/29/20208467오류 유형: 624. SQL 서버 오류 - service-specific error code 17051
12247정성태6/29/202010071.NET Framework: 918. C# - 불린 형 상수를 반환값으로 포함하는 3항 연산자 사용 시 단축 표현 권장(IDE0075) [2]파일 다운로드1
... 46  47  48  49  50  51  52  53  54  [55]  56  57  58  59  60  ...