2016-02-08 2 views
1

У меня есть C++ DLL, которая создает экземпляр COM-объекта, который реализован в .NET. Во многих ситуациях это работает отлично, но при определенных обстоятельствах, он висит приложение, и я вижу, он застрял со стеком следующего вызова (это только часть ниже уровня кода моей библиотеки DLL):Приложение, зависающее во время CoCreateInstance .NET-объекта COM

[email protected]() 
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 *) 
[email protected]() 
[email protected]() 
[email protected]@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 * *) 
[email protected]() 
[email protected]() 
[email protected]() 
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. Приложение не создает и не инициализирует экземпляр Microsoft_InteropFormTools.InteropToolbox перед созданием несвязанного COM-объекта.
  3. Приложение установлено безрезультатно, а не с помощью старого установщика, который имеет намного больше накладных расходов и регистрирует COM DLL в реестре и .NET DLL в GAC и, возможно, включает файлы, которые игнорируют минимальный установщик.

Если я изменил первое условие и запустил на своей локальной машине разработки Windows 7 вместо чистого сервера Windows 2012, проблема не возникает. Если я изменю второе условие, чтобы код инициализировал InteropformTools перед созданием COM-объекта, то проблема также не возникает. Если я изменю третье условие, чтобы продукт был установлен с использованием старого комплексного установщика, проблема не возникает.

Как отслеживать источник этой проблемы и/или исправлять ее?

ответ

2

С помощью поддержки Microsoft и DebugDiag мы определили, что причина проблемы связана с блокировкой Loader Lock. Блокировка загрузчика подробно описана в https://msdn.microsoft.com/en-us/library/ms173266(v=vs.120).aspx, но в основном существуют определенные ограничения, которые применяются к коду, который выполняется в пределах области действия DllMain или динамической инициализации статических неконтролируемых объектов кода, экземпляры которых требуют динамической инициализации при загрузке DLL (поскольку они в глобальном масштабе). Один из способов этого - сообщить компилятору C++, что код должен быть скомпилирован с поддержкой CLR, поэтому он не обрабатывает инициализацию в DllMain, а скорее другую функцию, которая не удерживает блокировку загрузчика.

В нашем коде мы имели глобальное объявление:

CFSCoCultureWrapper cultureWrapper; 

который имел конструктор под названием CoCreateInstance на управляемый объект СОМ, который, в свою очередь, имел ссылку на Microsoft.InteropToolbox. Применение переключателя /clr к этому одному исходному файлу позволяло загружать DLL без зависания.

Непонятно, почему поведение изменилось в разных развертываниях, но, как указано в статье, зависание не обязательно всегда происходит, поэтому это может быть сложной задачей для отладки. Чтобы проиллюстрировать, даже наш простой тестовый сценарий - это 4 уровня глубокой загрузки DLL до того, как мы столкнулись с проблемой. Загрузка загруженных (LoadLibrary) неуправляемых DLL-нагрузок (CoCreateInstance) загружает DLL Microsoft. Мы решили, что уровень сложности, связанный с такими проблемами, был достаточно хорошо понят и не стал больше понимать, почему проблема возникает только в определенных вариантах развертывания.

Простой ответ, не создавайте глобальные экземпляры объектов, которые загружают управляемый код во время конструктора из неуправляемого кода.Используйте ленивую инициализацию или переключите файл кода, чтобы использовать переключатель /clr, или используйте некоторые средства предотвращения выполнения управляемого кода во время инициализации времени DllMain. Другая работа, которую мы обнаружили, заключалась в переключении управляемого кода на использование .NET 4.5 вместо 2.0.

Смежные вопросы