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.
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
Bardzo nietypowy. Gdzie dzwonisz RevokeInterfaceFromGlobal? –
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