.NET 6+ 기반의 COM Server 내에 Type Library를 내장하는 방법
보통, C# COM DLL을 C++에서 사용하려면 다음과 같은 과정을 거칩니다.
- C# COM DLL 제작
- dscom을 이용해 DLL로부터 TLB 파일 생성
- C++ 프로젝트에서는 #import 명령어로 TLB 파일 사용
그런데, 위에서 2번 과정이 별로 매끄럽지 않습니다. 게다가 C/C++에서 사용하려면 DLL과 함께 항상 TLB 파일도 함께 배포해야 하는 것도 마음에 들지 않습니다.
이를 위해 한 가지 대안이라면 2번 과정을 msbuild task로 자동화하는 정도가 있습니다.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0-windows</TargetFramework>
<OutputType>Library</OutputType>
<UseWindowsForms>true</UseWindowsForms>
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
<EnableComHosting>true</EnableComHosting>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Platform>x64</Platform>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>true</SignAssembly>
</PropertyGroup>
<PropertyGroup>
<AssemblyOriginatorKeyFile>test.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="dscom tlbexport "$(TargetPath)"" />
</Target>
</Project>
일단 위와 같이 build event로 tlb 파일을 만들었으면 ItemGroup에
ComHostTypeLibrary를 이용해 TLB를 내장할 수 있습니다.
<Project Sdk="Microsoft.NET.Sdk">
<!-- ...[생략]... -->
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="dscom tlbexport "$(TargetPath)"" />
</Target>
<ItemGroup>
<ComHostTypeLibrary Include="$(AssemblyName).tlb" Id="1"/>
</ItemGroup>
</Project>
아쉬운 점이 하나 있다면, 저 프로젝트를 맨 처음 빌드할 때는 tlb 파일이 없어 DLL 생성에 실패하게 되고, 결국 PostBuildEvent가 실행되지 않아 닭이 먼저냐/달걀이 먼저냐 하는 문제에 빠집니다.
그나마 이런 문제를 완화하기 위해 Condition을 줄 수 있지만,
<Project Sdk="Microsoft.NET.Sdk">
<!-- ...[생략]... -->
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="dscom tlbexport "$(TargetPath)"" />
</Target>
<ItemGroup Condition="Exists('$(AssemblyName).tlb')">
<ComHostTypeLibrary Include="$(AssemblyName).tlb" Id="1"/>
</ItemGroup>
</Project>
제대로 된 TLB를 생성하기 위해 빌드를 반드시 2번 해야 한다는 것을 잊으면 안 됩니다. 그래도 대개의 경우 TLB가 변경될 정도로 COM 규약이 변경되는 경우는 많지 않으므로 큰 제약은 아닙니다.
여기서 재미있는 것은, TLB 파일을 내장하는 DLL이 ClassLibrary1.dll 파일이 아닌 ClassLibrary1.comhost.dll 파일이라는 점입니다. 그래서 <EnableComHosting>true</EnableComHosting> 옵션을 사용하지 않았다면 저렇게 ComHostTypeLibrary 처리도 의미가 없습니다.
마지막으로, C/C++에서 "#import" 구문을 이용해 tlh/tli 파일을 이용하려는 경우 다음과 같이 comhost DLL을 편리하게 사용할 수 있습니다.
#include <combaseapi.h>
#include <Windows.h>
#import "..\ClassLibrary1\bin\Debug\ClassLibrary1.comhost.dll" 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;
}
(
첨부 파일은 이 글의 예제 코드를 포함합니다.)
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]