Source Generator 能不能直接生成 COM 互操作代码?
不能。C# 的
Source Generator在编译期运行,只能读取当前项目中的 C# 语法树和符号,它**看不到类型库(.tlb)、注册表中的 COM 类信息,也无法调用
tlbimp.exe或
comhost.dll的逻辑**。所以你无法用 Source Generator “自动生成”像
Interop.MyLib.dll那样的传统 COM 互操作程序集。
那怎么让 Source Generator 和 COM 协同工作?
可行路径是:把 COM 类型信息提前转成结构化输入(比如 JSON/YAML 描述接口、CLSID、IID),再让 Source Generator 读取该文件并生成 C# 声明 —— 本质是“手写 IDL 的轻量替代”,不是全自动绑定。
先用oleview.exe或
dumpbin /headers提取 COM 接口定义,或导出 .idl 文件 用脚本(Python/PowerShell)把
IUnknown继承链、方法签名、
[in]/
[out]标记转成 JSON,例如:
{"interface": "IMyService", "iid": "00000000-0000-0000-0000-000000000000", "methods": [{"name": "DoWork", "params": [{"name": "input", "type": "int"}]}]}
在 Source Generator 中用 AdditionalFiles读取该 JSON,用
SyntaxGenerator拼出
[ComImport]、
[Guid]、
[InterfaceType]等属性的接口声明 生成的代码仍需手动调用
Marshal.GetActiveObject或
Activator.CreateInstance(传入 CLSID)来获取实例
为什么不用 TlbImp 或 Windows SDK 的默认方式?
因为
tlbimp.exe生成的互操作程序集依赖运行时注册(
regsvr32)、容易版本漂移,且不支持 .NET 6+ 的单文件发布;而 Source Generator 生成的是纯源码,可 git 跟踪、可 diff、可定制 marshaling 行为(比如把
BSTR自动转
string,跳过某些字段)。 典型痛点:COM 接口返回
VARIANT*,默认生成器会映射成
object,但你想强制转
double?—— 这种逻辑只能靠 Source Generator 在生成时注入判断 另一个场景:多个 COM 组件共用同一组回调接口,但
tlbimp会为每个组件生成重复接口 —— Source Generator 可统一提取、去重、加命名空间前缀 注意:生成的代码里仍要保留
[UnmanagedFunctionPointer]委托、
Marshal.AllocHGlobal手动内存管理等细节,Generator 不会帮你补全运行时逻辑
实际项目中更推荐的组合方案
不要指望 Source Generator 替代 COM 互操作基础设施,而是把它当作“类型声明工厂”:用
Microsoft.Windows.CsWin32处理 Win32 API,用
COMImport手写关键接口,再用 Source Generator 填补中间层(比如把配置文件里的 CLSID 列表批量生成
GetComInstance<t>(string clsid)</t>工厂方法)。 必须引用
System.Runtime.InteropServices,且项目 SDK 要设为
Microsoft.NET.Sdk(非
Microsoft.NET.Sdk.Web等变体,否则
Generator可能被跳过) 在
.csproj中显式启用:
<PropertyGroup><EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles><CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath></PropertyGroup>调试 Generator 时,用
dotnet build -v:d > log.txt查看是否加载成功;常见失败原因是
AdditionalFiles路径没匹配到,或 JSON 格式有 BOM
真正麻烦的从来不是生成代码,而是 COM 对象生命周期、线程模型(STA/MTA)、错误码转换(
HRESULT→ 异常)、以及跨 apartment 的接口指针传递 —— 这些 Source Generator 一概不碰,得你自己用
Marshal.ThrowExceptionForHR和
CoInitializeEx补齐。
