5

这是一个具体的例子:

我通过调用创建一个IWeBrowser2接口wb.CoCreateInstance(CLSID_InternetExplorer, 0, CLSCTX_SERVER);。这为我提供了一个从我的进程到任何正在运行的 iexplore.exe 进程恰好在我的线程 A中包含此浏览器选项卡的编组接口。

现在我使用IGlobalInterfaceTable为这个接口获取一个 cookie,将它传递给我的线程 B并从那里请求封送接口。

问题:我是在我的线程 A 中获得代理的代理,还是直接获得 IE 进程中的实例?

对我来说,我将获得实例的直接代理并引用它,这似乎是明智的,
但是

如果我结束我的线程 A,我在那里创建的 cookie 将变得无效,并且我无法再检索(和关闭)指向我创建的 Web 浏览器的接口指针。除非该线程中有一个 thunk 在线程退出时被销毁,否则这是没有意义的。

编辑:哦,两个线程都是 STA。

4

2 回答 2

2

我终于有时间弄清楚发生了什么,所以我写了一个简短的测试来看看发生了什么。

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

底线是这个:

  • 结束注册接口的线程会使 cookie 无效。
  • 已经封送的接口保持有效。(在这种情况下,就是这样。)

这意味着我获得了 IE 进程的代理,而不是其他线程对象的代理。

于 2010-11-19T21:39:27.030 回答
1

自从您请求进程外服务器以来,您已经在线程 A 上获得了代理。接下来发生的事情取决于线程 A 所在的单元类型,即 CoInitializeEx() 的参数。如果它是 MTA,你肯定会在线程 B 中获得相同的代理,假设它也是 MTA。如果线程 A 退出,添加的引用计数应使其保持活动状态。如果是 STA,那么我不能 100% 确定,但认为你应该买一个新的。顺便说一句,易于测试,只需使用线程 A 中的那个,如果必须创建一个新的,您将获得 RPC_E_WRONGTHREAD。

我没有很好的解释为什么线程 A 退出会杀死线程 B 的代理。除非你调用 IGlobalInterfaceTable::RevokeInterfaceFromGlobal()。你通常会这样做。

于 2010-10-26T19:19:15.110 回答