我的例子: 插入嵌入式工作簿时PowerPoint启动Excel。 我继续使用PowerPoint。 Excel保持为隐藏进程。 当我关闭PowerPoint时,它说Excel关闭自己(Excel稍后关闭,然后PowerPoint)。 当我杀了PowerPoint时,Excel变成了僵尸。 我的加载项已加载到Excel进程中。
什么消息(我读到COM STA对象通过Windows消息传递通信)我应该拦截以通知此类事件?我应该使用什么钩子?是否有任何关于COM对象更改引用计数的通知?
更新
我的问题似乎含糊不清。当我杀死PowerPoint时,我并不想处理案件。它只是为了证明PowerPoint正常关闭时向Excel发送一些消息。我试图拦截这个消息是有充分理由的。
UPDATE2
我无法使用API - 因为API失败。我正在尝试解决Creating class in new thread in new app domain and providing it with Office COM object in embedded mode leads to zombie process
答案 0 :(得分:1)
无。本地服务器上的COM确实使用Windows消息,但它使用私有消息,这是一个实现细节。所有它真正做的是将消息发布到隐藏的窗口,这意味着“你的公寓里有新的COM事件”。隐藏窗口将此传递给COM库,该库使用(我相信)共享内存与其他进程通信,并完成其余工作,例如查找事件是什么(调用,调用返回等)。
换句话说,没有具体的消息可以说“excel go away”,即使你不能依赖它,因为它是一个可以改变的实现细节。
当您关闭包含嵌入电子表格的PowerPoint文档时,PowerPoint将在其拥有的所有对象上调用IUnknown :: Release()。 Excel保留了对未完成对象的内部跟踪,并在它们全部消失时关闭。
如果excel过程中有加载项,并想知道excel何时关闭,请使用addin api查找。
根据加载项的类型,有不同的API。 COM加载项包含您可以注册的事件。但这是一个不同的问题:“我的Excel加载项如何获得关于不可见的Excel实例正在关闭的通知”。
为未提出的实际问题/问题添加了解决方案
@ asd.and.Rizzo,这是因为您持有Excel创建的对象。这意味着Excel认为它无法退出,因为有人正在使用这些对象。 Excel是正确的,因为该somone是您的加载项。
这种生命周期问题在创建任何Office或Excel加载项时都很常见,并且不是.Net加载项所特有的。
您想通过欺骗Excel的引用计数来解决问题。但它掌握在你的手中:引用计数只是因为你拿着物体!如果你释放它们,计数将下降到零,没有作弊。
<强>解决方案强>
最好的解决方案是避免下沉事件并避免持有任何物体。根据您的加载项的功能,这可能无法实现。
如果不能,则必须将文档接收到关闭事件。在每个中,检查打开的文档数量。当计数达到零时,你应该删除你的事件处理程序,在你曾经拥有的每个对象上调用Marshall.ReleaseComObject,并卸载你的加载项。然后Excel将退出。
但请注意:您 必须 在您持有的每个对象上调用Marshall.ReleaseComObject。如果不这样做,Excel将永远不会退出。如果您卸载AppDomain,则应该。
答案 1 :(得分:1)
如果您杀死“PowerPoint”,Excel中将不会有引用计数更改。这就是它没有被卸载的原因。
我相信您可以简单地将WM_QUIT消息发布到Excel进程的主窗口。您可以使用“Spy ++。exe”找到它,它是与Visual Studio捆绑在一起的工具。此外,您可以在Excel中获取某个对象的类工厂,并使用fLock = FALSE调用“LockServer”方法。这将减少Excel服务器的引用计数。
但是把它当作黑客,因为加载项不应该影响主机应用程序的行为。
答案 2 :(得分:0)
我发现了下一步(从将COM接口解释为纯C结构+函数和Direct X黑客攻击)。工作,但由于某种原因不能解决问题。我应该为Workbooks,Worksheets做同样的事情......
宣告:
typedef HRESULT STDMETHODCALLTYPE QueryInterfacePtr( REFIID, void **);
typedef ULONG STDMETHODCALLTYPE AddRefPtr( );
typedef ULONG STDMETHODCALLTYPE ReleasePtr( );
typedef ULONG STDMETHODCALLTYPE GetTypeInfoCountPtr( UINT *) ;
typedef ULONG STDMETHODCALLTYPE GetTypeInfoPtr ( UINT, LCID, ITypeInfo **) ;
typedef ULONG STDMETHODCALLTYPE GetIDsOfNamesPtr ( REFIID, LPOLESTR *,
UINT, LCID, DISPID *) ;
typedef ULONG STDMETHODCALLTYPE InvokePtr ( DISPID, REFIID,
LCID, WORD, DISPPARAMS *,
VARIANT *, EXCEPINFO *, UINT *) ;
typedef struct {
// IUnknown functions
QueryInterfacePtr * QueryInterface;
AddRefPtr * AddRef;
ReleasePtr * Release;
// IDispatch functions
GetTypeInfoCountPtr* GetTypeInfoCount;
GetTypeInfoPtr* GetTypeInfo;
GetIDsOfNamesPtr* GetIDsOfNames;
InvokePtr* Invoke;
} IDispatchInterceptor;
typedef ULONG __stdcall releasePTR(IDispatch *self);
typedef ULONG __stdcall addrefPTR(IDispatch *self);
接下来我做了Excel:
适用:static IDispatch * application = NULL; static releasePTR* realRelease = NULL; static addrefPTR* realAddRef = NULL; static ULONG wasAdd = 0; static ULONG oldCount = 0;
HRESULT STDMETHODCALLTYPE QueryInterfaceNativeOutOfProcSrv(REFIID riid, void **ppv){ return application->QueryInterface(riid,ppv);
}ULONG STDMETHODCALLTYPE AddRefNativeOutOfProcSrv(){ return application->AddRef(); }
ULONG STDMETHODCALLTYPE ReleaseNativeOutOfProcSrv(){ return application->Release(); }
ULONG STDMETHODCALLTYPE GetTypeInfoCountSrv( UINT * count) { return application->GetTypeInfoCount(count); } ULONG STDMETHODCALLTYPE GetTypeInfoSrv ( UINT n, LCID id, ITypeInfo ** inf) { return application->GetTypeInfo(n,id,inf); } ULONG STDMETHODCALLTYPE GetIDsOfNamesSrv ( REFIID a, LPOLESTR * b, UINT c, LCID d, DISPID * e) { return application->GetIDsOfNames(a,b,c,d,e); } ULONG STDMETHODCALLTYPE InvokeSrv ( DISPID a, REFIID b, LCID c, WORD d, DISPPARAMS * e, VARIANT * i, EXCEPINFO * j, UINT *k) { return application->Invoke(a,b,c,d,e,i,j,k); }
static IDispatchInterceptor interceptor = {QueryInterfaceNativeOutOfProcSrv,AddRefNativeOutOfProcSrv,ReleaseNativeOutOfProcSrv, GetTypeInfoCountSrv,GetTypeInfoSrv,GetIDsOfNamesSrv,InvokeSrv };
ULONG __stdcall release(IDispatch *self) {
ULONG c = realRelease(self); Log->logWrite("release %d",c); if ( c == 1) { if (instance != NULL) { instance->OnBeginShutdown(NULL); Log->logWrite("OnBeginShutdown %d",c); instance->OnEmbeddedDisconnection(); Log->logWrite("OnEmbeddedDisconnection %d",c); instance = NULL; } } //if (c == 2) { // c = realRelease(self); // c = realRelease(self); //} return c;
}
ULONG __stdcall addref(IDispatch *self) {
ULONG c = oldCount; if (wasAdd == 0) { c = realAddRef(self); oldCount = c; wasAdd++; } else if (wasAdd == 1) { Log->logWrite("ADDREF FAKE %d",c); wasAdd++; } else if (wasAdd == 2) { Log->logWrite("ADDREF FAKE %d",c); wasAdd++; } else { c = realAddRef(self); }
Log->logWrite("ADDREF %d",c); return c; }void InterceptRelease( IDispatch obj){ void iunknown_vtable= (void*)((unsigned int)obj); void* idispatch_vtable = (void*)(((unsigned int)iunknown_vtable)+8); unsigned int* v1 = (unsigned int*)idispatch_vtable; realRelease = (releasePTR*)*v1; DWORD old; VirtualProtect(v1,4,PAGE_EXECUTE_READWRITE,&old);
*v1 = (unsigned int) release; //while(obj->Release() > 0){};
}
ULONG c = realRelease(self); Log->logWrite("release %d",c); if ( c == 1) { if (instance != NULL) { instance->OnBeginShutdown(NULL); Log->logWrite("OnBeginShutdown %d",c); instance->OnEmbeddedDisconnection(); Log->logWrite("OnEmbeddedDisconnection %d",c); instance = NULL; } } //if (c == 2) { // c = realRelease(self); // c = realRelease(self); //} return c;
*v1 = (unsigned int) release;
//while(obj->Release() > 0){};