2010-10-26 5 views
5

Oto konkretny przykład:Czy marszałek interfejsu marszałka da mi koordynatora lub pierwotny interfejs?

Tworzę interfejs IWeBrowser2, dzwoniąc pod numer wb.CoCreateInstance(CLSID_InternetExplorer, 0, CLSCTX_SERVER);. To daje mi marszowy interfejs z mojego procesu do tego, który z procesów działających iexplore.exe zdarza się zawierać tę kartę przeglądarki w moim wątku A.

Teraz używam IGlobalInterfaceTable dostać ciasteczko dla tego interfejsu, należy przekazać go do mojego gwintu B i zażądać marshaled interfejs stamtąd.

Pytanie: Dostanę pełnomocnictwa pełnomocnika w moim wątku A lub bezpośrednio na przykład w procesie IE?

Wydaje się rozsądne, aby mi się, że będę się bezpośredni pełnomocnictwa do instancji z własnym odniesieniu do niego,
jednak:

Jeżeli kończę nitkowym, cookie stworzyłem tam staje się nieważny i Nie mogę pobrać (i zamknąć) wskaźników interfejsu do utworzonych już przeglądarek internetowych. To nie ma sensu, chyba że w tym wątku jest thunk, który jest niszczony, gdy wątek się kończy.

Edytuj: Och, oba wątki są STA.

Odpowiedz

2

W końcu miałem trochę czasu, aby dowiedzieć się, co się dzieje, więc napisałem krótki test, aby zobaczyć, co się dzieje.

// 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; 
} 

Najważniejsze jest to,

  • Zakończenie nitki, który zarejestrował interfejs unieważnia cookie.
  • Interfejs już skonfiskowany pozostaje ważny. (W tym przypadku oznacza to).

Oznacza to, że otrzymuję proxy do procesu IE zamiast do obiektu innego wątku.

1

Masz już proxy na wątku A, ponieważ poprosiłeś o serwer poza procesem. To, co dzieje się dalej, zależy od rodzaju mieszkania, w którym żyje wątek A, argumentu CoInitializeEx(). Jeśli jest to MTA, to na pewno otrzymasz takie samo proxy w wątku B, zakładając, że jest to również MTA. Dodana liczba odniesień powinna utrzymać ją przy życiu, jeśli wątek A zostanie zakończony. Jeśli to jest STA, to nie jestem w 100% pewny, ale myślę, że powinieneś dostać nowy. Łatwo przetestować btw, po prostu użyj tego z wątku A, a otrzymasz RPC_E_WRONGTHREAD, jeśli musiałby zostać utworzony nowy.

Nie mam wielkiego wyjaśnienia, dlaczego wyjście wątku A zabija proxy dla wątku B. O ile nie wywołasz IGlobalInterfaceTable :: RevokeInterfaceFromGlobal(). Które normalnie byś zrobił.

+0

Oba wątki to STA. Ponadto nie nazywam RevokeInterfaceFromGlobal. GetInterfaceFromGlobal po prostu zwraca E_INVALIDARG, gdy wywołuję go po zakończeniu wątku A. – Fozi

+0

Bardzo nietypowy. Gdzie dzwonisz RevokeInterfaceFromGlobal? –

+0

Mam odwołany liczony obiekt, który obsługuje plik cookie. Kiedy wszystkie referencje znikną, zostanie odwołane. Pliki cookie są przechowywane w puli i wypuszczane przy wyjściu. – Fozi

Powiązane problemy