一、背景1、讲故事最近遇到几个与COM相关的dump。由于我对COM的整体运行了解不多,分析这样的dump还是很头疼的。是的,例如下面的经典COM调用堆栈。0:044>~~[138c]swin32u!SendMessageWorker+0x11e020000008c`00ffed1000007ffc`124fd4afuser32!SendMessageW+0xf8030000008c`00ffed7000007ffc`125e943bxxx!DllUnregisterServer+0x3029f040000008c`00ffeda000007ffc`125e9685xxx!DllUnregisterServer+0x11c22b050000008c`00ffede000007ffc`600b50e7xxx!DllUnregisterServer+0x11c475060000008c`00ffee2000007ffc`60093ccdntdll!LdrpCallInitRoutine+0x6f070000008c`00ffee9000007ffc`60092eefntdll!LdrpProcessDetachNode+0xf5080000008c`00ffef6000007ffc`600ae319ntdll!LdrpUnloadNode+0x3f090000008c`00ffefb000007ffc`600ae293ntdll!LdrpDecrementModuleLoadCountEx+0x710a0000008c`00ffefe000007ffc`5cd7c00entdll!LdrUnloadDll+0x930b0000008C`00FFF01000007FFC`5D47CF78kernelbase!freelibrary+0x1e0c00008C`008C`00FFF04000007FFC`5d447aaa3bombase!cclasscache!::ccllastry00007ffc`5d4471a9组合!CClassCache::CFinishComposite::Finish+0x4b[onecore\com\combase\objact\dllcache.cxx@3530]0e0000008c`00fff0a000007ffc`5d3f1499组合!CClassCache::FreecheUnused\.objcxcombasell@6547]0f0000008c`00fff65000007ffc`5d3f13c7combase!CoFreeUnusedLibrariesEx+0x89[onecore\com\combase\objact\dllapi.cxx@117]10(内联函数)------`--------combase!CoFreeUnusedLibraries+0xa[onecore\com\combase\objact\dllapi.cxx@74]110000008c`00fff69000007ffc`6008a019combase!CDllHost::MTADllUnloadCallback+0x17[onecore\com\combase\objact\dllhost.cxx@929]120000008c`00fff6c000007ffc`6008bec4ntdll!TppTimerpExecuteCallback+0xa9130000008c`00fff71000007ffc`5f167e94ntdll!TppWorkerThread+0x644140000008c`00fffa0000007ffc`600d7ad1kernel32!BaseThreadInitThunk+0x14150000008c`00fffa3000000000`00000000ntdll!RtlUserThreadStart+0x21为了做一个简单的梳理,我们搭建一个简单的多语言COM互操作二、COM多语言互操作1.背景许多新一代的程序员可能不知道COM,最多只是听说过这个名词。其实Windows上有大量的COM组件,这些组件的信息都注册在HKEY_CLASSES_ROOT\CLSID节点目录下,截图如下:这个和微服务中的注册中心是一样的。在这篇文章中,我们使用C#编写一个COM组件,并使用C++来调用它。2.用C#写一个COM组件在.NETFramework4.8下写一个32位的FlyCom组件,一个接口,一个实现类。具体原理后面会分析,C#代码如下:);}[Guid("270C3ED3-053D-4324-9176-9C3FA2BE58A7")][ProgId("FlyCom.Show")]publicclassFly:BaseFly{publicstringShow(stringstr){return$"str={str},长度={str.Length}”;}}}这里简单介绍一下:Guid是一个接口(BaseFly)唯一编码,即IID信息,一个是COM组件的唯一编码,叫做CLSID。因为ProgId的GUID不方便记忆,所以给这个COM组件起一个别名,名字叫FlyCom.Show。DispId是遵循COM多语言互通下的vtable调用标准,表示第一个接口方法是Show,后面再说。有了代码,接下来还要进行三个配置。修改AssemblyInfo.cs中的ComVisible=trueCOM可见性,参考如下://SettingComVisibletofalsemakesthetypesinthisassemblynotvisible//toCOMcomponents.如果需要从//COM访问此程序集中的类型,请将该类型的ComVisible属性设置为true。[assembly:ComVisible(true)]生成签名一般来说,com放在注册表的时候最好生成强签名,否则会有警告提示。Registercominterop在属性面板中,选择Build选项卡并选择RegisterforCOMinterop选项。3、将COM注册到注册表要将COM组件注册到注册表中,需要用到注册表编辑工具regasm。MicrosoftWindows[版本10.0.19042.746](c)2020MicrosoftCorporation。版权所有。C:\Users\Administrator>cd/dC:\ProgramFiles(x86)\MicrosoftSDKs\Windows\v10.0A\bin\NETFX4.8Tools\x64C:\ProgramFiles(x86)\MicrosoftSDKs\Windows\v10。0A\bin\NETFX4.8工具\x64>C:\Windows\Microsoft.NET\Framework\v4.0.30319\regasm.exeD:\net6\ConsoleApp1\FlyCom\bin\Debug\FlyCom.dll/tlb:FlyCom.tlb/CodeBaseMicrosoft.NETFramework程序集注册实用程序版本4.8.4084.0适用于Microsoft.NETFramework版本4.8.4084.0版权所有(C)MicrosoftCorporation。版权所有。Successfullyregisteredtypes成功注册的程序集和类型库导出到"D:\net6\ConsoleApp1\FlyCom\bin\Debug\FlyCom.tlb"C:\ProgramFiles(x86)\MicrosoftSDKs\Windows\v10.0A\bin\NETFX4.8Tools\x64>从输出可以看到注册成功,生成了一个FlyCom.tlb代理文件。接下来可以去注册表验证GUID=270C3ED3-053D-4324-9176-9C3FA2BE58A7注册项和别名为FlyCom.Show的注册项。4、使用C++调用C++用C#编写的COM组件,和RPC调用一样,直接使用自动生成的代理文件,将FlyCom.tlb复制到根目录,将程序改为Win32位,截图为如下:接下来是完整的C++代码。#include
