聊一聊被 .NET程序員 遺忘的 COM 組件

一:背景1.講故事最近遇到了好幾起和 COM 相關的Dump,由于對 COM 整體運作不是很了解,所以分析此類dump還是比較頭疼的,比如下面這個經典的 COM 調用棧 。
0:044> ~~[138c]swin32u!NtUserMessageCall+0x14:00007ffc`5c891184 c3ret0:061> k # Child-SPRetAddrCall Site00 0000008c`00ffec68 00007ffc`5f21bfbewin32u!NtUserMessageCall+0x1401 0000008c`00ffec70 00007ffc`5f21be38user32!SendMessageWorker+0x11e02 0000008c`00ffed10 00007ffc`124fd4afuser32!SendMessageW+0xf803 0000008c`00ffed70 00007ffc`125e943bxxx!DllUnregisterServer+0x3029f04 0000008c`00ffeda0 00007ffc`125e9685xxx!DllUnregisterServer+0x11c22b05 0000008c`00ffede0 00007ffc`600b50e7xxx!DllUnregisterServer+0x11c47506 0000008c`00ffee20 00007ffc`60093ccdntdll!LdrpCallInitRoutine+0x6f07 0000008c`00ffee90 00007ffc`60092eefntdll!LdrpProcessDetachNode+0xf508 0000008c`00ffef60 00007ffc`600ae319ntdll!LdrpUnloadNode+0x3f09 0000008c`00ffefb0 00007ffc`600ae293ntdll!LdrpDecrementModuleLoadCountEx+0x710a 0000008c`00ffefe0 00007ffc`5cd7c00entdll!LdrUnloadDll+0x930b 0000008c`00fff010 00007ffc`5d47cf78KERNELBASE!FreeLibrary+0x1e0c 0000008c`00fff040 00007ffc`5d447aa3combase!CClassCache::CDllPathEntry::CFinishObject::Finish+0x28 [onecore\com\combase\objact\dllcache.cxx @ 3420]0d 0000008c`00fff070 00007ffc`5d4471a9combase!CClassCache::CFinishComposite::Finish+0x4b [onecore\com\combase\objact\dllcache.cxx @ 3530]0e 0000008c`00fff0a0 00007ffc`5d3f1499combase!CClassCache::FreeUnused+0xdd [onecore\com\combase\objact\dllcache.cxx @ 6547]0f 0000008c`00fff650 00007ffc`5d3f13c7combase!CoFreeUnusedLibrariesEx+0x89 [onecore\com\combase\objact\dllapi.cxx @ 117]10 (Inline Function) --------`--------combase!CoFreeUnusedLibraries+0xa [onecore\com\combase\objact\dllapi.cxx @ 74]11 0000008c`00fff690 00007ffc`6008a019combase!CDllHost::MTADllUnloadCallback+0x17 [onecore\com\combase\objact\dllhost.cxx @ 929]12 0000008c`00fff6c0 00007ffc`6008bec4ntdll!TppTimerpExecuteCallback+0xa913 0000008c`00fff710 00007ffc`5f167e94ntdll!TppWorkerThread+0x64414 0000008c`00fffa00 00007ffc`600d7ad1kernel32!BaseThreadInitThunk+0x1415 0000008c`00fffa30 00000000`00000000ntdll!RtlUserThreadStart+0x21為了做一個簡單的梳理,我們搭建一個簡單的多語言 COM 互操作 。
二:COM 多語言互操作1. 背景可能很多新生代的程序員都不知道 COM,最多也只聽過這個名詞,其實在 Windows 上有海量的 COM 組件,這些組件信息都是注冊在 HKEY_CLASSES_ROOT\CLSID 節點目錄 , 截圖如下:

聊一聊被 .NET程序員 遺忘的 COM 組件

文章插圖
這個和微服務中的 注冊中心 是一個道理,這一篇我們用 C# 寫一個COM組件,用 C++ 去調用 。
2. C# 寫一個 COM 組件寫一個 .NET Framework 4.8 下的 32bitFlyCom 組件,一個接口,一個實現類 , 具體原理后續再分析,先搭建嘗嘗鮮 ,  C# 代碼如下:
namespace FlyCom{[Guid("31A3CED7-B4F1-4D59-881A-EA1D7ABCC4CF")]public interface BaseFly{[DispId(1)]string Show(string str);}[Guid("270C3ED3-053D-4324-9176-9C3FA2BE58A7")][ProgId("FlyCom.Show")]public class Fly : BaseFly{public string Show(string str){return $"str={str}, length={str.Length}";}}}這里簡單說一下:
  1. Guid
一個是接口(BaseFly) 的唯一碼 , 即 IID 信息 ,  一個是 COM組件的 唯一碼,叫做 CLSID 。
  1. ProgId
因為 GUID 不方便記憶,所以給這個 COM組件 取一個別名叫 FlyCom.Show。
  1. DispId
這個是為了遵循 COM多語言互通下的 vtable調用標準,表示第一個接口方法是 Show,后續再聊 。
【聊一聊被 .NET程序員 遺忘的 COM 組件】有了代碼,接下來還要做三個配置 。
  • 對 COM 的可見性
修改 AssemblyInfo.cs 中的 ComVisible = true,參考如下:
// Setting ComVisible to false makes the types in this assembly not visible// to COM components.If you need to access a type in this assembly from// COM, set the ComVisible attribute to true on that type.[assembly: ComVisible(true)]
  • 生成簽名
一般來說,將 com 放到 注冊表,最好都生成一個強簽名 , 否則會有警告提示 。
聊一聊被 .NET程序員 遺忘的 COM 組件

文章插圖
  • 注冊 com 互操作
在屬性面板中,選擇 Build 選項卡,選中 Register for COM interop 選項即可 。
聊一聊被 .NET程序員 遺忘的 COM 組件

文章插圖
3. 注冊 COM 到注冊表要將 com組件 放到注冊表,需要使用注冊表編輯工具 regasm 。
Microsoft Windows [版本 10.0.19042.746](c) 2020 Microsoft Corporation. 保留所有權利 。C:\Users\Administrator>cd /d C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64>C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exe D:\net6\ConsoleApp1\FlyCom\bin\Debug\FlyCom.dll /tlb:FlyCom.tlb/CodeBaseMicrosoft .NET Framework 程序集注冊實用工具版本 4.8.4084.0(適用于 Microsoft .NET Framework 版本 4.8.4084.0)版權所有 (C) Microsoft Corporation 。保留所有權利 。成功注冊了類型成功注冊了導出到“D:\net6\ConsoleApp1\FlyCom\bin\Debug\FlyCom.tlb”的程序集和類型庫C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64>

推薦閱讀