如何拦截out-of-proc桌面STA COM到STA COM通信或引用计数递减?

时间:2012-03-21 08:57:11

标签: winapi com ipc

我的例子: 插入嵌入式工作簿时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

3 个答案:

答案 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){};