代码之家  ›  专栏  ›  技术社区  ›  Fozi

封送已封送的接口是否为代理或原始接口提供封送处理程序?

  •  5
  • Fozi  · 技术社区  · 14 年前

    下面是一个具体的例子:

    我创建了一个 IWeBrowser2 通过调用的接口 wb.CoCreateInstance(CLSID_InternetExplorer, 0, CLSCTX_SERVER); . 这给了我一个从我的进程到运行中的iexplore.exe进程中的任何一个进程的封送接口,该进程恰好在我的 A线 .

    现在我用 IGlobalInterfaceTable 要获取此接口的cookie,请将其传递给我的 螺纹B 并从那里请求已封送的接口。

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

    在我看来,我将得到一个实例的直接代理,并引用它,
    然而 :

    如果我结束线程A,我在那里创建的cookie将变为无效,我无法检索(并关闭)指向我创建的Web浏览器的接口指针。除非线程中有一声雷鸣,当线程退出时就会被破坏,否则这是没有意义的。

    编辑: 哦,两条线都是STA。

    2 回复  |  直到 14 年前
        1
  •  2
  •   Fozi    14 年前

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

    // 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进程的代理,而不是另一个线程的对象。

        2
  •  1
  •   Hans Passant    14 年前

    自从请求进程外服务器以来,线程A上已经有了一个代理。接下来会发生什么取决于线程a所在的单元的类型,coInitializeEx()的参数。如果它是MTA,那么在线程B中您肯定会得到相同的代理,假设它也是MTA。如果线程A退出,则添加的引用计数应使其保持活动状态。如果是斯塔,我不是100%肯定,但我认为你应该买一个新的。简单的测试btw,只要使用线程A中的一个,如果必须创建一个新的线程,您将得到rpc eu错误的线程。

    对于线程A出口杀死线程B的代理的原因,我没有很好的解释。除非您调用iglobalInterfaceTable::RevokeInterfaceFromGlobal()。你通常会这么做。