连接到DllHost由DllSurrogate激活的STA Apartment COM对象的消息泵

时间:2014-05-30 19:30:15

标签: winapi visual-c++ com

我有一个相当复杂的要求。我的STA COM对象在DLL中实现(不能将其移动到进程外EXE)。通过DllSurrogate我在dllhost.exe进程中托管我的对象。我的对象有一个附加到它的UI(一个简单的无模式对话框),但我需要PreTranslateAccelerator机制,以便一些快捷方式工作,等等。因为COM激活我的对象并将其托管在默认的dllhost.com ,我显然没有控制消息泵。

在这种情况下,是否还有预先翻译邮件的方法?我怀疑COM已经预见到了这样一个特定的场景,但也许我错过了一些东西。

2 个答案:

答案 0 :(得分:1)

  

我需要将所有内容打包在一个DLL中。

在这种情况下,DllSurrogate不是执行此操作的唯一方法。还有Rundll32

INFO: Windows Rundll and Rundll32 Interface

这将允许您在DLL的EntryPoint内运行自己的消息循环,并完全控制消息处理,包括PreTranslateMessage。您可以从ATL EXE服务器复制消息循环逻辑。

请记住,还有32位和64位版本的" RunDll32.exe"在每个64位Windows操作系统中。使用与您的DLL的位相匹配的那个。

答案 1 :(得分:1)

好的,这是。我希望我没有遗漏任何重要的东西。基本上,我创建了一个自定义CMyComCreator而不是默认值。我不是只创建一个COM对象并返回一个接口指针,而是旋转一个worker UiThread。我使用MyData结构来跨线程传递数据。工作线程完成设置后,我使用CComGITPtr将编组接口指针从UiThread传回主节点。消费者(进程外)最终得到的接口指针直接与绕过主线程的UiThread对话。您可能会认为CMyDialog是一个无模式对话框,它会在销毁时发送PostQuitMessage以终止消息循环。这就是全部。可能看起来很麻烦,但效果很好。

struct MyData
{
    ATL::CComGITPtr<IUnknown> Unk;
    ATL::CEvent Event;
    HRESULT hr;

    MyData() : hr(E_OUTOFMEMORY), Event(FALSE, FALSE) { }
};

static CMessageLoop * MessageLoop;

class CMyComCreator
{
public:
    static HRESULT WINAPI CreateInstance(
        _In_opt_ void* pv,
        _In_ REFIID riid,
        _COM_Outptr_ LPVOID* ppv)
    {
        ATLASSERT(ppv != NULL);
        if (ppv == NULL)
            return E_POINTER;
        *ppv = NULL;

        HRESULT hRes = E_OUTOFMEMORY;
        MyData* data = NULL;

        ATLPREFAST_SUPPRESS(6014 28197)
            /* prefast noise VSW 489981 */
            ATLTRY(data = _ATL_NEW MyData)
        ATLPREFAST_UNSUPPRESS()

        if (data != NULL)
        {
            HANDLE thread = (HANDLE)_beginthreadex(NULL, 0, UiThread, (void *)data, 0, NULL);

            if (thread)
            {
                WaitForSingleObject(data->Event, INFINITE);
                CloseHandle(thread);

                hRes = data->hr;

                if (SUCCEEDED(hRes))
                {
                    ATL::CComPtr<IUnknown> unk;

                    hRes = data->Unk.CopyTo(&unk);

                    if (SUCCEEDED(hRes))
                    {
                        hRes = unk->QueryInterface(riid, ppv);
                    }
                }
            }

            delete data;
        }
        return hRes;
    }
};

typedef CMyComCreator _CreatorClass;

static unsigned __stdcall UiThread(void * param)
{
    CoInitializeEx(0, COINIT_APARTMENTTHREADED);

    MyData * data = (MyData *)param;

    ATL::CComObject<CMyDialog> * bb;
    data->hr = ATL::CComObject<CMyDialog>::CreateInstance(&bb);

    ATL::CComPtr<IUnknown> unk((IDispatch *) bb);

    data->Unk = unk;
    unk.Release();
    data->Event.Set();

    if (SUCCEEDED(data->hr))
    {
        CMessageLoop theLoop;
        MessageLoop = &theLoop;
        int nRet = theLoop.Run();
        MessageLoop = NULL;
    }

    CoUninitialize();

    return 0;
}