0:044> ~~[138c]s win32u!NtUserMessageCall+0x14: 00007ffc`5c891184 c3 ret 0:061> k # Child-SP RetAddr Call Site 00 0000008c`00ffec68 00007ffc`5f21bfbe win32u!NtUserMessageCall+0x14 01 0000008c`00ffec70 00007ffc`5f21be38 user32!SendMessageWorker+0x11e 02 0000008c`00ffed10 00007ffc`124fd4af user32!SendMessageW+0xf8 03 0000008c`00ffed70 00007ffc`125e943b xxx!DllUnregisterServer+0x3029f 04 0000008c`00ffeda0 00007ffc`125e9685 xxx!DllUnregisterServer+0x11c22b 05 0000008c`00ffede0 00007ffc`600b50e7 xxx!DllUnregisterServer+0x11c475 06 0000008c`00ffee20 00007ffc`60093ccd ntdll!LdrpCallInitRoutine+0x6f 07 0000008c`00ffee90 00007ffc`60092eef ntdll!LdrpProcessDetachNode+0xf5 08 0000008c`00ffef60 00007ffc`600ae319 ntdll!LdrpUnloadNode+0x3f 09 0000008c`00ffefb0 00007ffc`600ae293 ntdll!LdrpDecrementModuleLoadCountEx+0x71 0a 0000008c`00ffefe0 00007ffc`5cd7c00e ntdll!LdrUnloadDll+0x93 0b 0000008c`00fff010 00007ffc`5d47cf78 KERNELBASE!FreeLibrary+0x1e 0c 0000008c`00fff040 00007ffc`5d447aa3 combase!CClassCache::CDllPathEntry::CFinishObject::Finish+0x28 [onecore\com\combase\objact\dllcache.cxx @ 3420] 0d 0000008c`00fff070 00007ffc`5d4471a9 combase!CClassCache::CFinishComposite::Finish+0x4b [onecore\com\combase\objact\dllcache.cxx @ 3530] 0e 0000008c`00fff0a0 00007ffc`5d3f1499 combase!CClassCache::FreeUnused+0xdd [onecore\com\combase\objact\dllcache.cxx @ 6547] 0f 0000008c`00fff650 00007ffc`5d3f13c7 combase!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`6008a019 combase!CDllHost::MTADllUnloadCallback+0x17 [onecore\com\combase\objact\dllhost.cxx @ 929] 12 0000008c`00fff6c0 00007ffc`6008bec4 ntdll!TppTimerpExecuteCallback+0xa9 13 0000008c`00fff710 00007ffc`5f167e94 ntdll!TppWorkerThread+0x644 14 0000008c`00fffa00 00007ffc`600d7ad1 kernel32!BaseThreadInitThunk+0x14 15 0000008c`00fffa30 00000000`00000000 ntdll!RtlUserThreadStart+0x21为了做一个简单的梳理,我们搭建一个简单的多语言 COM 互操作。
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}"; } } }这里简单说一下:
// 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)].生成签名
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\x64 C:\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 /CodeBase Microsoft .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>从输出中可以看到已成功注册,并且生成了一个 FlyCom.tlb 代理文件,接下来可以到注册表中验证一下 GUID=270C3ED3-053D-4324-9176-9C3FA2BE58A7 注册项以及别名为 FlyCom.Show 的注册项。
#include <Windows.h> #include <string.h> #include <iostream> #import "FlyCom.tlb" named_guids raw_interface_only using namespace std; int main() { CoInitialize(NULL); FlyCom::BaseFlyPtr ptr; ptr.CreateInstance("FlyCom.Show"); wchar_t* c = ptr->Show(L"hello world"); wprintf(L"%s", c); getchar(); }将程序跑起来后,真的很完美。