2010-10-26 15 views
5

Ecco un esempio concreto:Il marshalling di un'interfaccia di marshalling mi fornisce un marshaller per il proxy o l'interfaccia originale?

Creare un'interfaccia IWeBrowser2 chiamando wb.CoCreateInstance(CLSID_InternetExplorer, 0, CLSCTX_SERVER);. Questo mi dà un'interfaccia marshaled dal mio processo in qualsiasi dei processi in esecuzione iexplore.exe sembra contenere questa scheda del browser nel mio thread A.

Ora utilizzo lo IGlobalInterfaceTable per ottenere un cookie per questa interfaccia, passarlo al mio thread B e richiedere l'interfaccia di marshalling da lì.

Domanda: faccio a ottenere un proxy per il proxy nel mio thread A o direttamente all'istanza nel processo di IE?

Sembra ragionevole a me che mi metterò un proxy diretto all'istanza con il proprio riferimento ad esso,
tuttavia:

Se finisco il mio thread A, il cookie ho creato lì diventa invalido e Non riesco a recuperare (e chiudere) i puntatori dell'interfaccia ai browser Web che ho creato più. Questo non ha senso a meno che non ci sia un thunk in quel thread che viene distrutto quando il thread si chiude.

Modifica: Oh, entrambi i thread sono STA.

risposta

2

Ho finalmente avuto un po 'di tempo per capire cosa sta succedendo, così ho scritto un breve test per vedere cosa sta succedendo.

// MarshalTest.cpp : Defines the entry point for the console application. 
// 

#include "stdafx.h" 

enum { WM_THEREYOUGO = WM_USER+1, WM_THANKYOU, WM_YOURWELCOME }; 

DWORD WINAPI TheOtherThread(DWORD * main_thread_id) 
{ 
    MSG msg = { 0 }; 
    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 
    assert(SUCCEEDED(hr)); 

    { 
     // create web browser 
     CComPtr<IWebBrowser2> wb; 
     hr = wb.CoCreateInstance(CLSID_InternetExplorer, 0, CLSCTX_SERVER); 
     assert(SUCCEEDED(hr) && wb); 

     // navigate 
     hr = wb->Navigate2(&CComVariant(_T("stackoverflow.com")), &CComVariant(0), &CComVariant(_T("")), &CComVariant(), &CComVariant()); 
     assert(SUCCEEDED(hr)); 
     hr = wb->put_Visible(VARIANT_TRUE); 
     assert(SUCCEEDED(hr)); 

     // Marshal 
     DWORD the_cookie = 0; 
     { 
      CComPtr<IGlobalInterfaceTable> com_broker; 
      hr = com_broker.CoCreateInstance(CLSID_StdGlobalInterfaceTable); 
      assert(SUCCEEDED(hr)); 
      hr = com_broker->RegisterInterfaceInGlobal(wb, __uuidof(IWebBrowser2), &the_cookie); 
     } 

     // notify main thread 
     PostThreadMessage(*main_thread_id, WM_THEREYOUGO, the_cookie, NULL); 

     // message loop 
     while(GetMessage(&msg, 0, 0, 0)) { 
      if(msg.hwnd == NULL) { 
       // thread message 
       switch(msg.message) { 
        case WM_THANKYOU: 
         PostQuitMessage(0); 
         break; 
       } 
      } else { 
       TranslateMessage(&msg); 
       DispatchMessage(&msg); 
      } 
     } 
    } 

    CoUninitialize(); 

    PostThreadMessage(*main_thread_id, WM_YOURWELCOME, 0, NULL); 
    return msg.wParam; 
} 


int _tmain(int argc, _TCHAR* argv[]) 
{ 
    MSG msg = {0}; 
    DWORD main_thread_id = GetCurrentThreadId(); 

    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 
    assert(SUCCEEDED(hr)); 
    { 
     DWORD ThreadId = 0; 
     HANDLE hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)TheOtherThread, &main_thread_id, 0, &ThreadId); 

     DWORD the_cookie = 0; 

     CComPtr<IWebBrowser2> wb, wb2; 

     while(GetMessage(&msg, 0, 0, 0)) { 
      if(msg.hwnd == NULL) { 
       // thread message 
       switch(msg.message) { 
        case WM_THEREYOUGO: 
         // we got the cookie. 
         the_cookie = msg.wParam; 

         // get the browser. This should work. 
         { 
          CComPtr<IGlobalInterfaceTable> com_broker; 
          hr = com_broker.CoCreateInstance(CLSID_StdGlobalInterfaceTable); 
          assert(SUCCEEDED(hr)); 
          hr = com_broker->GetInterfaceFromGlobal(the_cookie, __uuidof(IWebBrowser2), (void**)&wb); 
          assert(SUCCEEDED(hr) && wb); 
         } 

         // do something with it. 
         hr = wb->put_FullScreen(VARIANT_TRUE); 
         assert(SUCCEEDED(hr)); 

         // signal the other thread. 
         PostThreadMessage(ThreadId, WM_THANKYOU, 0, NULL); 
         break; 

        case WM_YOURWELCOME: 
         // the other thread has ended. 
         PostQuitMessage(0); 
         break; 
       } 
      } else { 
       TranslateMessage(&msg); 
       DispatchMessage(&msg); 
      } 
     } 

     // the other thread has ended. Try getting the interface again. 
     { 
      CComPtr<IGlobalInterfaceTable> com_broker; 
      hr = com_broker.CoCreateInstance(CLSID_StdGlobalInterfaceTable); 
      assert(SUCCEEDED(hr)); 
      hr = com_broker->GetInterfaceFromGlobal(the_cookie, __uuidof(IWebBrowser2), (void**)&wb2); 
      //assert(SUCCEEDED(hr) && wb2); // this fails, hr == E_INVALIDARG. 

      // clean up, will not be executed. 
      if(SUCCEEDED(hr)) { 
       hr = com_broker->RevokeInterfaceFromGlobal(the_cookie); 
      } 
     } 

     // try using it 
     if(wb2) { 
      hr = wb2->put_FullScreen(VARIANT_FALSE); 
      assert(SUCCEEDED(hr)); 
     } else if(wb) { 
      // this succeeds 
      hr = wb->put_FullScreen(VARIANT_FALSE); 
      assert(SUCCEEDED(hr)); 
     } 

     CloseHandle(hThread); 
    } 

    CoUninitialize(); 
    return msg.wParam; 
} 

La linea di fondo è questo:

  • Terminare il filo che ha registrato l'interfaccia invalida il cookie.
  • L'interfaccia già condivisa resta valida. (In questo caso, vale a dire.)

Ciò significa che ottengo un proxy per il processo di IE invece che per l'altro oggetto del thread.

1

Hai già un proxy sul thread A poiché hai richiesto un server fuori processo. Quello che succede dopo dipende dal tipo di appartamento in cui vive A, l'argomento di CoInitializeEx(). Se si tratta di MTA, si otterrà lo stesso proxy nel thread B, supponendo che sia anche MTA. Il conteggio dei riferimenti aggiunto dovrebbe mantenerlo attivo se il thread A termina. Se è STA, non sono sicuro al 100%, ma penso che dovresti prenderne uno nuovo. Facile da testare btw, basta usare quello del thread A e otterrai RPC_E_WRONGTHREAD se ne dovresti creare uno nuovo.

Non ho una grande spiegazione del motivo per cui l'uscita del thread A uccide il proxy per il thread B. A meno che non si chiami IGlobalInterfaceTable :: RevokeInterfaceFromGlobal(). Che faresti normalmente.

+0

Entrambi i thread sono STA. Inoltre, non chiamo RevokeInterfaceFromGlobal. GetInterfaceFromGlobal restituisce semplicemente E_INVALIDARG quando lo chiamo dopo che il thread A è terminato. – Fozi

+0

Molto insolito. Dove stai chiamando RevokeInterfaceFromGlobal? –

+0

Ho un oggetto conteggiato di riferimento che gestisce il cookie. Quando tutti i riferimenti sono andati, viene revocato. I cookie sono tenuti in una piscina e rilasciati all'uscita. – Fozi

Problemi correlati