Как перехватить внешнюю связь STA COM с STA COM или уменьшение подсчета ссылок? - PullRequest
0 голосов
/ 21 марта 2012

Мой пример: PowerPoint запускает Excel при вставке встроенной книги. Я продолжаю работать с PowerPoint. Excel остается скрытым процессом. Когда я закрываю PowerPoint, он говорит Excel закрыться (Excel закрывается позже, чем PowerPoint). Когда я убиваю PowerPoint, Excel становится зомби. Моя надстройка загружена в процесс Excel.

Какое сообщение (я читал, что объекты COM STA связываются через обмен сообщениями Windows) мне следует перехватить, чтобы получить уведомление о таком событии? Какой крюк я должен использовать? Есть ли какое-либо средство для уведомления о количестве ссылок на изменения COM-объекта?

UPDATE

Мой вопрос кажется неоднозначным. Я не пытаюсь разобраться с делом, когда убиваю PowerPoint. Это просто для доказательства того, что PowerPoint отправляет некоторые сообщения в Excel при нормальном закрытии. Я пытаюсь перехватить это сообщение по уважительной причине.

UPDATE2

Я не могу использовать API - потому что API не работает. Я пытаюсь решить Создание класса в новом потоке в новом домене приложения и предоставление ему объекта Office COM во встроенном режиме приводит к процессу зомби

Ответы [ 3 ]

1 голос
/ 21 марта 2012

В случае, если вы убьете «PowerPoint», в Excel не будет изменений подсчета ссылок. Вот почему он не выгружается.

Полагаю, вы можете просто опубликовать сообщение WM_QUIT в главном окне процесса Excel. Вы можете найти его, используя «Spy ++. Exe», который является инструментом в комплекте с Visual Studio. Также возможно, что вы можете получить фабрику классов для какого-либо объекта в Excel и вызвать метод LockServer с помощью fLock = FALSE. Это уменьшит количество ссылок на сервер Excel.

Но воспринимайте это как хак, поскольку надстройка не должна влиять на поведение хост-приложения.

1 голос
/ 21 марта 2012

Нет. COM на локальном сервере использует сообщения Windows, но использует личные сообщения, которые являются подробностями реализации. Все, что он на самом деле делает, это отправляет сообщения в скрытое окно, означающее «у вас есть новые события COM в вашей квартире». Скрытое окно передает это в библиотеку COM, которая использует (я полагаю) разделяемую память для связи с другими процессами и выполняет остальную часть работы, например, выяснение того, что является событием (вызов, возврат вызова и т. Д.).

Другими словами, нет конкретного сообщения о том, что «Excel Go Go прочь», и даже если бы он существовал, вы не могли на него полагаться, потому что это деталь реализации, которая может измениться.

  • Когда вы закрываете документ PowerPoint, содержащий встроенную электронную таблицу, PowerPoint вызывает IUnknown :: Release () для всех объектов, которые он содержит. Excel отслеживает внутренние объекты и закрывается, когда все они исчезнут.

Если у вас есть надстройка в процессе Excel, и вы хотите знать, когда Excel закрывается, используйте API addin, чтобы узнать.

В зависимости от типа надстройки для этого существуют разные API. Надстройки COM содержат события, на которые вы можете зарегистрироваться. Но это другой вопрос: «Как моя надстройка Excel может получить уведомление о закрытии невидимого экземпляра Excel».

Добавлено решение для актуальной проблемы / вопроса не задавалось

@asd.and.Rizzo, это происходит потому, что вы держитесь за объекты, созданные в Excel. Это означает, что Excel считает, что не может выйти, потому что кто-то использует объекты. Excel правильный, потому что этот сомон - ваша надстройка.

Такая проблема на весь срок службы типична при создании любых надстроек Office или Excel и не уникальна для надстроек .Net.

Вы хотите решить проблему, обманув счетчик ссылок Excel. Но это в ваших руках: счетчик ссылок только потому, что вы держите объекты! Если вы отпустите их, счет уменьшится до нуля без обмана.

Решение

Лучшее решение состоит в том, чтобы избежать затопления событий и не держаться за какой-либо объект. В зависимости от того, что делает ваша надстройка, это может быть невозможно.

Если вы не можете, вы должны потопить события закрытия документа. В каждом проверьте, сколько документов открыто. Когда счетчик достигнет нуля, вы должны удалить свои обработчики событий, вызвать Marshall.ReleaseComObject для каждого объекта, который вы когда-либо держали, и выгрузить надстройку. Excel завершит работу.

Но обратите внимание: вы должны вызвать Marshall.ReleaseComObject для каждого объекта, который вы когда-либо держали. Если вы этого не сделаете, Excel никогда не выйдет. Это должно произойти, если вы выгружаете домен приложений.

0 голосов
/ 16 декабря 2012

Я нашел следующее (от интерпретации COM-интерфейсов как чистых структур C + function и хаков Direct X). Работает, но не решает проблему по какой-то причине. Я должен сделать то же самое для рабочих книг, рабочих листов ...

Declare:


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: <pre> static IDispatch * application = NULL; static releasePTR* realRelease = NULL; static addrefPTR* realAddRef = NULL; static ULONG wasAdd = 0; static ULONG oldCount = 0;</p> <p>HRESULT STDMETHODCALLTYPE QueryInterfaceNativeOutOfProcSrv(REFIID riid, void **ppv){ return application->QueryInterface(riid,ppv);<br> }</p> <p>ULONG STDMETHODCALLTYPE AddRefNativeOutOfProcSrv(){ return application->AddRef(); }</p> <p>ULONG STDMETHODCALLTYPE ReleaseNativeOutOfProcSrv(){ return application->Release(); }</p> <p>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); }</p> <p>static IDispatchInterceptor interceptor = {QueryInterfaceNativeOutOfProcSrv,AddRefNativeOutOfProcSrv,ReleaseNativeOutOfProcSrv, GetTypeInfoCountSrv,GetTypeInfoSrv,GetIDsOfNamesSrv,InvokeSrv };</p> <p>ULONG __stdcall release(IDispatch *self) { </p> <pre><code>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 ++; } иначе если (wasAdd == 1) { Log-> logWrite ("ADDREF FAKE% d", c); wasAdd ++; } иначе если (wasAdd == 2) { Log-> logWrite ("ADDREF FAKE% d", c); wasAdd ++; } еще { c = realAddRef (self); }
Log-> logWrite ("ADDREF% d", 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 старый; VirtualProtect (v1,4, PAGE_EXECUTE_READWRITE, и старый);

*v1 = (unsigned int) release;

//while(obj->Release() > 0){};
* *} Тысяча двадцать-один

void InterceptAddRef (IDispatch obj) { void iunknown_vtable = (void *) ((unsigned int ) obj); void * idispatch_vtable = (void *) (((unsigned int) iunknown_vtable) +4); unsigned int * v1 = (unsigned int *) idispatch_vtable; realAddRef = (addrefPTR *) * v1; DWORD старый; VirtualProtect (v1,4, PAGE_EXECUTE_READWRITE, и старые); * v1 = (без знака int) addref; }
Применение:


 IDispatch * app  = Application;
 InterceptRelease(app);
 InterceptAddRef(app);
...