c# - 应用程序在基于 .NET 的 COM 对象的 CoCreateInstance 期间挂起

标签 c# c++ .net com-interop winforms-interop

我有一个 C++ DLL,它正在创建一个在 .NET 中实现的 COM 对象的实例。在许多情况下,这工作正常,但在某些情况下,它会挂起应用程序,我看到它卡在以下调用堆栈中(这只是我的 DLL 代码级别下的部分):

ntdll.dll!_NtAlpcSendWaitReceivePort@32()
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 *)
rpcrt4.dll!LRPC_BASE_CCALL::DoSendReceive(void)
rpcrt4.dll!LRPC_BASE_CCALL::SendReceive(struct _RPC_MESSAGE *)
rpcrt4.dll!_I_RpcSendReceive@4()
rpcrt4.dll!_NdrSendReceive@8()
rpcrt4.dll!@NdrpSendReceive@4()
rpcrt4.dll!_NdrClientCall2()
combase.dll!ServerAllocateOXIDAndOIDs(void * hServer, void * phProcess, unsigned __int64 * poxidServer, unsigned long cOids, unsigned __int64 * aOid, unsigned long * pcOidsAllocated, const tagOXID_INFO * poxidInfo, tagDUALSTRINGARRAY * pdsaStringBindings, tagDUALSTRINGARRAY * pdsaSecurityBindings, unsigned __int64 * pdwOrBindingsID, tagDUALSTRINGARRAY * * ppdsaOrBindings) Line 246
combase.dll!CRpcResolver::ServerRegisterOXID(const tagOXID_INFO & oxidInfo, unsigned __int64 * poxid, unsigned long * pcOidsToAllocate, unsigned __int64 * arNewOidList) Line 1020
combase.dll!OXIDEntry::RegisterOXIDAndOIDs(unsigned long * pcOids, unsigned __int64 * pOids) Line 1631
combase.dll!OXIDEntry::AllocOIDs(unsigned long * pcOidsAlloc, unsigned __int64 * pOidsAlloc, unsigned long cOidsReturn, unsigned __int64 * pOidsReturn)
combase.dll!CComApartment::CallTheResolver() Line 856
combase.dll!CComApartment::InitRemoting() Line 1166
combase.dll!CComApartment::StartServer() Line 1386
combase.dll!InitChannelIfNecessary() Line 1393
combase.dll!ClassicSTAThreadWaitForHandles(unsigned long dwFlags, unsigned long dwTimeout, unsigned long cHandles, void * * pHandles, unsigned long * pdwIndex) Line 34
combase.dll!CoWaitForMultipleHandles(unsigned long dwFlags, unsigned long dwTimeout, unsigned long cHandles, void * * pHandles, unsigned long * lpdwindex)
mscorwks.dll!NT5WaitRoutine(int,unsigned long,int,void * *,int)
mscorwks.dll!MsgWaitHelper(int,void * *,int,unsigned long,int)
mscorwks.dll!Thread::DoAppropriateAptStateWait(int,void * *,int,unsigned long,enum WaitMode)
mscorwks.dll!Thread::DoAppropriateWaitWorker(int,void * *,int,unsigned long,enum WaitMode)
mscorwks.dll!Thread::DoAppropriateWait(int,void * *,int,unsigned long,enum WaitMode,struct PendingSync *)
mscorwks.dll!CLREvent::WaitEx(unsigned long,enum WaitMode,struct PendingSync *)
mscorwks.dll!CLREvent::Wait(unsigned long,int,struct PendingSync *)
mscorwks.dll!CExecutionEngine::WaitForEvent(void *,unsigned long,int)
mscorwks.dll!ClrWaitEvent(void *,unsigned long,int)
mscorwks.dll!FusionSink::Wait(void)
mscorwks.dll!AssemblySink::Wait(void)
mscorwks.dll!FusionBind::RemoteLoad(struct IApplicationContext *,class FusionSink *,struct IAssemblyName *,struct IAssembly *,unsigned short const *,struct IAssembly * *,struct IHostAssembly * *,struct IAssembly * *,int)
mscorwks.dll!FusionBind::LoadAssembly(struct IApplicationContext *,class FusionSink *,struct IAssembly * *,struct IHostAssembly * *,struct IAssembly * *,int)
mscorwks.dll!AssemblySpec::FindAssemblyFile(class AppDomain *,int,struct IAssembly * *,struct IHostAssembly * *,struct IAssembly * *,struct IFusionBindLog * *,enum StackCrawlMark *)
mscorwks.dll!AppDomain::BindAssemblySpec(class AssemblySpec *,int,int,enum StackCrawlMark *)
mscorwks.dll!AssemblySpec::LoadDomainAssembly(enum FileLoadLevel,class Object * *,class Object * *,int,int,int,enum StackCrawlMark *)
mscorwks.dll!AssemblySpec::LoadAssembly(enum FileLoadLevel,class Object * *,class Object * *,int,int,int,enum StackCrawlMark *)
mscorwks.dll!AppDomain::LoadAssemblyHelper(unsigned short const *,unsigned short const *)
mscorwks.dll!AppDomain::LoadCOMClass(struct _GUID,int,int *)
mscorwks.dll!GetTypeForCLSID(struct _GUID const &,int *)
mscorwks.dll!EEDllGetClassObject(struct _GUID const &,struct _GUID const &,void * *)
mscorwks.dll!_InternalDllGetClassObject@12()
mscorwks.dll!_DllGetClassObjectInternal@12()
mscoreei.dll!_DllGetClassObject@12()
combase.dll!CClassCache::CDllPathEntry::GetClassObject(const _GUID & pClsid, const _GUID & pIid, void * * ppv) Line 2691
combase.dll!CClassCache::CDllPathEntry::DllGetClassObject(const _GUID & rclsid, const _GUID & riid, IUnknown * * ppUnk, int fMakeValid) Line 3892
combase.dll!CClassCache::CDllFnPtrMoniker::BindToObjectNoSwitch(const _GUID & riid, void * * ppvResult) Line 4406
combase.dll!CClassCache::GetClassObject(const ACTIVATION_PROPERTIES & ap) Line 5816
combase.dll!CServerContextActivator::CreateInstance(IUnknown * pUnkOuter, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties) Line 999
combase.dll!ActivationPropertiesIn::DelegateCreateInstance(IUnknown * pUnkOuter, IActivationPropertiesOut * * ppActPropsOut) Line 1854
combase.dll!CApartmentActivator::CreateInstance(IUnknown * pUnkOuter, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties) Line 2323
combase.dll!CProcessActivator::CCICallback(unsigned long dwContext, IUnknown * pUnkOuter, ActivationPropertiesIn * pActIn, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties)
combase.dll!CProcessActivator::AttemptActivation(ActivationPropertiesIn * pActIn, IUnknown * pUnkOuter, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties, HRESULT (unsigned long, IUnknown *, ActivationPropertiesIn *, IActivationPropertiesIn *, IActivationPropertiesOut * *) * pfnCtxActCallback, unsigned long dwContext) Line 1673
combase.dll!CProcessActivator::ActivateByContext(ActivationPropertiesIn * pActIn, IUnknown * pUnkOuter, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties, HRESULT (unsigned long, IUnknown *, ActivationPropertiesIn *, IActivationPropertiesIn *, IActivationPropertiesOut * *) * pfnCtxActCallback) Line 1539
combase.dll!CProcessActivator::CreateInstance(IUnknown * pUnkOuter, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties) Line 1417
combase.dll!ActivationPropertiesIn::DelegateCreateInstance(IUnknown * pUnkOuter, IActivationPropertiesOut * * ppActPropsOut) Line 1854
combase.dll!CClientContextActivator::CreateInstance(IUnknown * pUnkOuter, IActivationPropertiesIn * pInActProperties, IActivationPropertiesOut * * ppOutActProperties) Line 713
combase.dll!ActivationPropertiesIn::DelegateCreateInstance(IUnknown * pUnkOuter, IActivationPropertiesOut * * ppActPropsOut)
combase.dll!ICoCreateInstanceEx(const _GUID & OriginalClsid, IUnknown * punkOuter, unsigned long dwClsCtx, _COSERVERINFO * pServerInfo, unsigned long dwCount, unsigned long dwActvFlags, tagMULTI_QI * pResults, ActivationPropertiesIn * pActIn) Line 1645
combase.dll!CComActivator::DoCreateInstance(const _GUID & Clsid, IUnknown * punkOuter, unsigned long dwClsCtx, _COSERVERINFO * pServerInfo, unsigned long dwCount, tagMULTI_QI * pResults, ActivationPropertiesIn * pActIn) Line 376
combase.dll!CoCreateInstance(const _GUID & rclsid, IUnknown * pUnkOuter, unsigned long dwContext, const _GUID & riid, void * * ppv) Line 120

挂起的情况是满足以下所有条件:

  1. 该应用程序在一个干净的 Windows 2012 R2 系统上运行,我刚刚在该系统上运行了一个安装程序,该安装程序尝试安装该应用程序运行所需的最少组件集。
  2. 在创建不相关的 COM 对象之前,应用程序未创建和初始化 Microsoft_InteropFormTools.InteropToolbox 的实例。
  3. 该应用程序以无注册方式安装,而不是使用具有更多开销的旧安装程序,并在注册表中注册 COM DLL,在 GAC 中注册 .NET DLL,并且可能包含最小安装程序忽略的文件。

如果我更改第一个条件并在我的本地 Windows 7 开发计算机而不是干净的 Windows 2012 服务器上运行,则不会出现问题。如果我更改第二个条件,以便代码在创建 COM 对象之前初始化 InteropformTools,那么问题也不会发生。如果我更改第三个条件以便使用旧的综合安装程序安装产品,则不会出现问题。

我如何追踪此问题的根源和/或修复它?

最佳答案

在 Microsoft 支持和 DebugDiag 的帮助下,我们确定问题的原因似乎与加载程序锁定有关。加载器锁在 https://msdn.microsoft.com/en-us/library/ms173266(v=vs.120).aspx 中有详细记录但基本上,有一些限制适用于在 DllMain 范围内运行的代码或静态非托管代码对象的动态初始化,其实例需要在加载 DLL 时进行动态初始化(因为它们在全局范围内)。解决这个问题的一种方法是告诉 C++ 编译器代码应该在 CLR 支持下编译,这样它就不会处理 DllMain 中的初始化,而是处理另一个不保留加载程序锁的函数。

在我们的代码中,我们有一个全局声明:

CFSCoCultureWrapper cultureWrapper;

它有一个在托管 COM 对象上调用 CoCreateInstance 的构造函数,该对象又引用了 Microsoft.InteropToolbox。将 /clr 开关应用到那个源文件允许加载 DLL 而不会挂起。

尚不清楚行为在不同部署中发生变化的原因,但正如文章链接所述,挂起不一定总是发生,因此这些问题可能很难调试。为了说明这一点,在我们遇到问题之前,即使我们的简单测试用例也是 4 级深度加载 DLL - EXE 加载 (LoadLibrary) 非托管 DLL 加载 (CoCreateInstance) 托管 DLL 加载 Microsoft DLL。我们认为这些问题所涉及的复杂程度已经足够了解,并且没有进一步了解为什么问题只发生在某些部署中。

简单的回答,不要创建在非托管代码的构造函数期间加载托管代码的对象的全局实例。使用惰性初始化或将代码文件切换为使用 /clr 开关或使用某种方法来防止在 DllMain 初始化期间执行托管代码。我们发现的另一个解决方法是将托管代码切换为使用 .NET 4.5 而不是 2.0。

关于c# - 应用程序在基于 .NET 的 COM 对象的 CoCreateInstance 期间挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35278676/

相关文章:

.net - 列表集合中的搜索值

.net - 使用 .NET 中的 Cyber​​Source API 取消/退款交易

c# - 我们可以通过编程方式将网站托管到 IIS 吗?

c# - 如何使用显示图像的处理程序检查图像控件中是否存在图像

c++ - sqlite 中一次来自两个查询的结果?

c++ - 如何确保客户端告诉服务器用户已断开连接,而不管用于关闭客户端的方法如何

c# - 如何使 XML 反序列化更快?

c# - Global.asax 仅在 Debug模式下运行

c++ - qt minimumSize() 返回 0 但在 qt designer 中它被设置为非零

.net - AutoMapper AssertConfiguration 在编译时?