使用 Isolated COM 时应用程序在 CoCreateInstance 期间挂起

Application hung during CoCreateInstance when using Isolated COM

当我在 运行 之前未配置客户端 DLL (globalsd.dll) 以使用独立 COM 访问公开 COM 接口 (FSCulture.dll) 的 .NET 程序集时主可执行文件 (FCMTSYSM.EXE) 我收到一个应用程序异常(访问冲突),因为 CoCreateInstance 在 DLL (syslev0.dll) 的静态初始化期间失败,该 DLL (syslev0.dll) 调用客户端 DLL (globalsd.dll)启动(代码继续尝试 运行 并使用不存在的 COM 对象)。我理解并可以处理这些。我需要改进错误处理 and/or 使用 regasm 注册 COM 对象 and/or 配置 globalsd.dll 用于隔离 COM。

然而,当我为 Isolated COM 配置 globalsd.dll,给它一个 TLB 文件和 FSCulture.DLL 文件名时,整个应用程序只是在 globalsd.dll 中的 CoCreateInstance 期间挂起,我可以弄清楚它跑到哪里去了。我尝试在 FSCulture.dll 中创建的对象的构造函数中设置断点,但它永远不会被击中。这就像我需要调试 Isolated COM 框架的内部结构。调查和解决此问题的最佳方法是什么?

从符号服务器加载符号后,调用堆栈可能更有意义,但对我来说仍然不是特别有用:

ntdll.dll!_ZwDelayExecution@8() Unknown
ntdll.dll!__LdrpInitialize@8()  Unknown
ntdll.dll!_LdrInitializeThunk@8()   Unknown

作为测试,我尝试创建一个 VB6 客户端 EXE,使用带有隔离 COM 的相同 COM 服务器和 运行 解决不同的问题。我得到

Run-time error '-2146234304 (80131040)': Automation error

当我重命名服务器 .manifest 文件并重新 运行 客户端时,我得到了同样的错误。然后,如果我触摸客户端清单(文件内容没有变化,只是重新保存同一个文件)错误变为:

The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail.

我运行 sxstrace 看看发生了什么,毫不奇怪,它找不到服务器清单文件,所以它出错了。现在,如果我将服务器清单文件恢复为其原始名称并重新 运行 客户端,我又会收到错误 80131040。如果我 运行 sxstrace 在这种情况下,解析的输出文件是空的。

此时如果我运行regasm注册服务器,我仍然得到错误80131040。但是如果我重命名client清单,客户端测试 运行 没问题。

我不明白为什么只触摸清单文件(不做任何更改)有任何效果。我也不明白为什么 SXSTrace 在出现 80131040 错误的情况下不输出任何内容。客户端清单如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   <assemblyIdentity type="win32" name="Project1" version="1.0.0.0" />
   <dependency>
   <dependentAssembly>
   <assemblyIdentity type="win32" name="SoftBrands.FourthShift.FSCulture" version="1.0.0.0" />
   </dependentAssembly>
   </dependency>
</assembly>

服务器清单如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" 
  manifestVersion="1.0">
<assemblyIdentity type="win32" name="SoftBrands.FourthShift.FSCulture" version="1.0.0.0" />
<clrClass
            clsid="{23D4FF3D-EEDF-4F68-AD65-749958EE3B2A}"
            progid="SoftBrands.FourthShift.FSCulture.FSCulture"
            threadingModel="Both"
            name="SoftBrands.FourthShift.FSCulture.FSCulture" >
</clrClass>
</assembly>

编辑 我意识到在我原来的场景中一定有另一个线程对调用堆栈更有帮助,所以我再次尝试。原来有。这是主线程上的调用堆栈:

ntdll.dll!_ZwAlpcSendWaitReceivePort@32()   Unknown
rpcrt4.dll!LRPC_CASSOCIATION::AlpcSendWaitReceivePort(unsigned long,struct _PORT_MESSAGE *,struct _ALPC_MESSAGE_ATTRIBUTES *,struct _PORT_MESSAGE *,unsigned long *,struct _ALPC_MESSAGE_ATTRIBUTES *,union _LARGE_INTEGER *)   Unknown
rpcrt4.dll!LRPC_BASE_CCALL::DoSendReceive(void) Unknown
rpcrt4.dll!LRPC_BASE_CCALL::SendReceive(struct _RPC_MESSAGE *)  Unknown
rpcrt4.dll!_I_RpcSendReceive@4()    Unknown
rpcrt4.dll!_NdrSendReceive@8()  Unknown
rpcrt4.dll!@NdrpSendReceive@4() Unknown
rpcrt4.dll!_NdrClientCall2()    Unknown
ole32.dll!ServerAllocateOXIDAndOIDs(void * hServer, void * phProcess, unsigned __int64 * poxidServer, long fApartment, unsigned long cOids, unsigned __int64 * aOid, unsigned long * pcOidsAllocated, tagOXID_INFO * poxidInfo, tagDUALSTRINGARRAY * pdsaStringBindings, tagDUALSTRINGARRAY * pdsaSecurityBindings, unsigned __int64 * pdwOrBindingsID, tagDUALSTRINGARRAY * * ppdsaOrBindings) Line 246    C
ole32.dll!CRpcResolver::ServerRegisterOXID(tagOXID_INFO & oxidInfo, unsigned __int64 * poxid, unsigned long * pcOidsToAllocate, unsigned __int64 * arNewOidList) Line 1028  C++
ole32.dll!OXIDEntry::RegisterOXIDAndOIDs(unsigned long * pcOids, unsigned __int64 * pOids) Line 1303    C++
ole32.dll!OXIDEntry::AllocOIDs(unsigned long * pcOidsAlloc, unsigned __int64 * pOidsAlloc, unsigned long cOidsReturn, unsigned __int64 * pOidsReturn)   C++
ole32.dll!CComApartment::CallTheResolver() Line 639 C++
ole32.dll!CComApartment::InitRemoting() Line 996    C++
ole32.dll!CComApartment::StartServer() Line 1204    C++
ole32.dll!InitChannelIfNecessary() Line 1021    C++
ole32.dll!CDllHost::GetApartmentToken(unsigned long & hActivator) Line 461  C++
ole32.dll!DoSTMTApartmentCreate(unsigned long & hActivator) Line 211    C++
ole32.dll!CClassCache::GetActivatorFromDllHost(int fSixteenBit, unsigned long dwDllThreadModel, unsigned long * phActivator)    C++
ole32.dll!CClassCache::GetOrCreateApartment(const ACTIVATION_PROPERTIES & ap, DLL_INSTANTIATION_PROPERTIES * pdip, unsigned long * phActivator) Line 4892   C++
ole32.dll!FindOrCreateApartment(const _GUID & Clsid, unsigned long actvflags, DLL_INSTANTIATION_PROPERTIES * pdip, unsigned long * phActivator) Line 2653   C++
ole32.dll!CProcessActivator::GetApartmentActivator(ActivationPropertiesIn * pInActProperties, ISystemActivator * * ppActivator) Line 1214   C++
ole32.dll!CProcessActivator::CCICallback(unsigned long dwContext, IUnknown * pUnkOuter, ActivationPropertiesIn * pActIn, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties) Line 1703 C++
ole32.dll!CProcessActivator::AttemptActivation(ActivationPropertiesIn * pActIn, IUnknown * pUnkOuter, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties, HRESULT (unsigned long, IUnknown *, ActivationPropertiesIn *, IActivationPropertiesIn *, IActivationPropertiesOut * *) * pfnCtxActCallback, unsigned long dwContext) Line 1630   C++
ole32.dll!CProcessActivator::ActivateByContext(ActivationPropertiesIn * pActIn, IUnknown * pUnkOuter, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties, HRESULT (unsigned long, IUnknown *, ActivationPropertiesIn *, IActivationPropertiesIn *, IActivationPropertiesOut * *) * pfnCtxActCallback) Line 1487    C++
ole32.dll!CProcessActivator::CreateInstance(IUnknown * pUnkOuter, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties) Line 1377    C++
ole32.dll!ActivationPropertiesIn::DelegateCreateInstance(IUnknown * pUnkOuter, IActivationPropertiesOut * * ppActPropsOut) Line 1917    C++
ole32.dll!CClientContextActivator::CreateInstance(IUnknown * pUnkOuter, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties) Line 685   C++
ole32.dll!ActivationPropertiesIn::DelegateCreateInstance(IUnknown * pUnkOuter, IActivationPropertiesOut * * ppActPropsOut) Line 1917    C++
ole32.dll!ICoCreateInstanceEx(const _GUID & Clsid, IUnknown * punkOuter, unsigned long dwClsCtx, _COSERVERINFO * pServerInfo, unsigned long dwCount, unsigned long dwActvFlags, tagMULTI_QI * pResults, ActivationPropertiesIn * pActIn) Line 1334  C++
ole32.dll!CComActivator::DoCreateInstance(const _GUID & Clsid, IUnknown * punkOuter, unsigned long dwClsCtx, _COSERVERINFO * pServerInfo, unsigned long dwCount, tagMULTI_QI * pResults, ActivationPropertiesIn * pActIn) Line 332  C++
ole32.dll!CoCreateInstanceEx(const _GUID & Clsid, IUnknown * punkOuter, unsigned long dwClsCtx, _COSERVERINFO * pServerInfo, unsigned long dwCount, tagMULTI_QI * pResults) Line 157    C++
ole32.dll!CoCreateInstance(const _GUID & rclsid, IUnknown * pUnkOuter, unsigned long dwContext, const _GUID & riid, void * * ppv) Line 110  C++

尝试在非托管客户端和托管服务器之间设置隔离 COM 时会遇到很多陷阱。其中之一是隔离的 COM 设置不适用于这种情况(我相信它们用于引用非托管服务器)。我怀疑另一个原因是,在没有嵌入正确的清单文件的情况下尝试调试此配置可能会导致发布模式和调试模式之间出现不同的结果,因为清单文件的生成位置、方式和时间不同。 .

中涵盖了解决此问题中场景的所有答案

更新:在与 Microsoft 就相关问题进行讨论后发现了有关此问题根本原因的更多详细信息

即,在 DLL 中创建需要动态初始化的对象的静态实例时必须小心,因为如果 /clr 编译器开关未应用于源文件,这些对象将 运行 在 DllMain 期间, 如果在其范围内引用托管代码,则 DllMain 可能会导致死锁,因为在 https://msdn.microsoft.com/en-us/library/ms173266(v=vs.120).aspx

中更详细地记录了加载程序锁

一个简单的解决方案(我在另一个上下文中再次遇到此问题时使用的解决方案)是将 /clr 编译器开关应用于源文件,该源文件具有对象的静态实例,其构造函数引用了托管代码 COM 对象。