1

提前对太长的帖子表示抱歉,但考虑到涉及的部分很多,我不确定我会在哪里犯错,因此,我需要发布所有涉及的部分。请不要假设任何事情,我可能会在这里犯任何类型的错误。

问题:

1)真正的问题:刚刚学习COM。

2) 我需要将 IDispatch 指针从 C++ 中的控制台客户端 ( ConsoleClient ) 作为参数传递给 Windows 服务 ( WinService ) 中的 COM 函数。此 IDispatch 指针充当回调。WinService 知道要调用的函数的名称。我所有的尝试都没有成功。我收到不同的错误,例如:未实现、RPC 不可用等。

注意:以下错误是正确的!我在@Igor 指出我的错误后进行了编辑:在 STA 线程中有一个 do-while(我从 main() 中删除)。我在 main() 线程(而不是 do-while)中添加了一个消息泵(GetMessage、TranslateMessage 和 DispatchMessage),我的问题得到了解决!

我有一个 WinService 公开以下 COM 接口(WinService 的 idl 文件):

[
object,
uuid(DBE8BC31-9D2B-4F4B-903A-B40473408DE9),
dual,
nonextensible,
pointer_default(unique)
]
interface IWinService : IDispatch
{
///Here there are more functions..
    [id(4), helpstring("Interface HELP")] 
    HRESULT WinServiceCOMfunction( [in] VARIANT vCallback, [out, retval] LONG* pReturn );
    //Here there even more functions...
 };  

[
    uuid(DEF3BFAE-ADF4-493B-8D01-E47A279225C5),
    version(1.0),
    helpstring("LIB HELP")
]
library WinServiceLib
{
    importlib("stdole2.tlb");

[       
    uuid(AC290DC9-8CB4-4502-A73C-2BDAEC4B215A)      
]
coclass CoWinService
    {
        [default] interface IWinService;
    };
}

WinService 内部期望vCallBack .vt = VT_DISPATCH,即当我从 ConsoleClient.EXE 应用程序调用 WinServiceCOM 函数时需要传入的回调指针。WinService 知道充当回调的函数的名称。即,WinService 从IDispatch-pointer-callback GetIDsOfName 调用以“ FunctionCallback ”作为参数。但是,在这里我在 WinService 中遇到不同的错误,例如:未实现,RPC 不可用等。然后没有执行回调(ConsoleClient 没有收到任何返回)。

到目前为止我所做的(以下所有源代码都在 ConsoleClient.EXE 中找到)ConsoleClient.EXE main.cpp:

int _tmain(int argc, _TCHAR* argv[])
{
    CoInitialize(NULL);

    //To be brief: Let's think that I already have an IDispatch pointer to the WinService interface IWinService:
    CComPtr<IDispatch> pIWinService; //pIWinService
    //Here I queried the IWinService interface...It was successful!!!

    OLECHAR * winServiceNameFunction = L"WinServiceCOMfunction";

    DISPID dispid;
    hr = pIWinService->GetIDsOfNames( IID_NULL, &winServiceNameFunction, 1, LOCALE_USER_DEFAULT, &dispid );

    if ( FAILED( hr ) )
    {
        wprintf( L"GetIDsOfNames failed" );
        return 1;
    }
    else
    {
        wprintf( L"GetIDsOfNames succeeded!" );
    }

    CComPtr<IDispatch> consoleClientCallback( new CConsoleClientInterface() );
    CConsoleClientInterface *sanity = dynamic_cast<CConsoleClientInterface *>( consoleClientCallback.p );

    if ( nullptr == pAutoCallback )
    {
        wprintf( L"CConsoleClientInterface pointer failed\n" );
        return 1;
    }
    else
    {
        wprintf( L"CConsoleClientInterface pointer succeeded\n" );
    }

    DWORD dwRegister;
    hr = CoRegisterClassObject(CLSID_CoConsoleClient, consoleClientCallback.p, 
        CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegister);

    if(FAILED(hr))
    {
        wprintf(L"CoRegisterClassObject Failed\n");
        return 1;
    }
    else
    {
        wprintf(L"CoRegisterClassObject Succeeded\n");
    }

    CComVariant paramCallback( consoleClientCallback.Detach() );
    VARIANTARG varParams[] = { paramCallback };
    DISPPARAMS dispparams = { vArgs, NULL, 1, 0 };

    hr = pPtr->Invoke(
        dispid,
        IID_NULL,
        LOCALE_SYSTEM_DEFAULT,
        DISPATCH_METHOD,
        &dispparams,
        NULL,
        NULL,
        NULL);

    if ( FAILED( hr ) )
    {
        wprintf( L"Invoke failed" );
        return 1;
    }
    else
    {
        wprintf( L"Invoke Succeeded" );
    }

    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    CoRevokeClassObject(dwRegister);
    CoUninitialize();
    return 0;
}

所有主要的都成功执行。但是,我从来没有从回调中得到任何东西。当 WinService 尝试从 IDispatch 指针回调中查询接口时,它会抛出以下错误:未实现,RPC 不可用,等等......

创建 ConsoleClient IDispatch 接口的模板类来自:http: //blogs.msdn.com/b/oldnewthing/archive/2013/06/12/10425215.aspx

disInterfaceBase.h:

template<typename DispInterface>
class CDispInterfaceBase : public DispInterface
{
public:
CDispInterfaceBase() : m_cRef(1), m_dwCookie(0) { }

/* IUnknown */
IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv)
{
    *ppv = nullptr;
    HRESULT hr = E_NOINTERFACE;
    if (riid == IID_IUnknown || riid == IID_IDispatch ||
        riid == __uuidof(DispInterface))
    {
        *ppv = static_cast<DispInterface *>
            (static_cast<IDispatch*>(this));
        AddRef();
        hr = S_OK;
    }
    return hr;
}

IFACEMETHODIMP_(ULONG) AddRef()
{
    return InterlockedIncrement(&m_cRef);
}

IFACEMETHODIMP_(ULONG) Release()
{
    LONG cRef = InterlockedDecrement(&m_cRef);
    if (cRef == 0) delete this;
    return cRef;
}

// *** IDispatch ***
IFACEMETHODIMP GetTypeInfoCount(UINT *pctinfo)
{
    *pctinfo = 0;
    return E_NOTIMPL;
}

IFACEMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid,
    ITypeInfo **ppTInfo)
{
    *ppTInfo = nullptr;
    return E_NOTIMPL;
}

IFACEMETHODIMP GetIDsOfNames(REFIID, LPOLESTR *rgszNames,
    UINT cNames, LCID lcid,
    DISPID *rgDispId)
{
    return E_NOTIMPL;
}

IFACEMETHODIMP Invoke(
    DISPID dispid, REFIID riid, LCID lcid, WORD wFlags,
    DISPPARAMS *pdispparams, VARIANT *pvarResult,
    EXCEPINFO *pexcepinfo, UINT *puArgErr)
{
    if (pvarResult) VariantInit(pvarResult);
    return SimpleInvoke(dispid, pdispparams, pvarResult);
}

// Derived class must implement SimpleInvoke
virtual HRESULT SimpleInvoke(DISPID dispid,
    DISPPARAMS *pdispparams, VARIANT *pvarResult) = 0;

public:
    HRESULT Connect(IUnknown *punk)
    {
        HRESULT hr = S_OK;
        CComPtr<IConnectionPointContainer> spcpc;
        if (SUCCEEDED(hr)) {
            hr = punk->QueryInterface(IID_PPV_ARGS(&spcpc));
        }
        if (SUCCEEDED(hr)) {
            hr = spcpc->FindConnectionPoint(__uuidof(DispInterface), &m_spcp);
        }
        if (SUCCEEDED(hr)) {
            hr = m_spcp->Advise(this, &m_dwCookie);
        }
        return hr;
    }

void Disconnect()
{
    if (m_dwCookie) {
        m_spcp->Unadvise(m_dwCookie);
        m_spcp.Release();
        m_dwCookie = 0;
    }
}

private:
    LONG m_cRef;
    CComPtr<IConnectionPoint> m_spcp;
    DWORD m_dwCookie;
};

ConsoleClient ConsoleClient.h 中的实际 IDispatch 实现

class CConsoleClientInterface : public CDispInterfaceBase<ICONSOLEINTERFACE>
{
public:

    CConsoleClientInterface() { }
    ~CConsoleClientInterface() { }

STDMETHODIMP GetIDsOfNames(REFIID, LPOLESTR *rgszNames,
        UINT cNames, LCID lcid,
        DISPID *rgDispId)
    {    
        HRESULT hr = E_FAIL;

    if(_wcsicmp(*rgszNames, L"FunctionCallback") == 0)
    {
        *rgDispId = 1;
        hr= S_OK;
    }
    else
    {
        hr= DISP_E_UNKNOWNINTERFACE;
    }

    if(FAILED(hr))
    {
        std::cout << L"FAILED\n";
    }

    return hr;
}

    HRESULT SimpleInvoke(
        DISPID dispid, DISPPARAMS *pdispparams, VARIANT *pvarResult)
    {
//      switch (dispid) 
//      {
//      case 4:
            std::cout << L"SimpleInvoke" << std::endl; //This is never printed (for the error that I am trying to figure out)
            HRESULT hr = FunctionCallback( pdispparams->rgvarg[1].intVal, pdispparams->rgvarg[0].parray );
//          break;
//      }
        return hr;
    }

    HRESULT FunctionCallback( LONG longValue, LPSAFEARRAY safearray )
    {
        //So simple, I just want that this value is printed in the ConsoleClient's console! Unfortunately this is not happening!
        std::cout << longValue << std::endl;

        return S_OK;
    }
};

和 ConsoleClient.idl

[
    object,
    uuid(CD08B160-558A-4251-885C-173A08A461F1),
    dual,
    nonextensible,
    helpstring("ICONSOLEINTERFACE Interface"),
    pointer_default(unique)
]
interface ICONSOLEINTERFACE : IDispatch{
    [id(1), helpstring("method FunctionCallback")] 
    HRESULT FunctionCallback([in] LONG longValue, [in] LPSAFEARRAY safeArray );
};
[
    uuid(F3445A9E-555B-4729-952B-8B72B8DB2E37),
    version(1.0),
    helpstring("ConsoleClientLib Help Lib")
]
library ConsoleClientLib 
{
    importlib("stdole2.tlb");
    [
        uuid(EFF9EC78-3031-4558-9BA3-5B2641CCB304),
        helpstring("CoConsoleClient Class")
    ]
    coclass ConsoleClientInterface 
    {
        [default] interface ICONSOLEINTERFACE;
    };
};

我创造了:

HKEY_CLASSES_ROOT\CLSID\{EFF9EC78-3031-4558-9BA3-5B2641CCB304} //CLSID
(Default) REG_SZ = C:\Program Files\Common Files\ConsoleClient.EXE
HKEY_CLASSES_ROOT\CLSID\{EFF9EC78-3031-4558-9BA3-5B2641CCB304}\LocalServer32
ThreadingModel REG_SZ = Both

我还使用 regtlibv12.exe 注册了 tlb

很抱歉这篇文章太长了,但是 COM 强迫我这样做,特别是我是 COM 的新手,我不知道我会在哪里犯错。

注意:我确信调用(从 ConsoleClient.EXE)到 WinService COM 函数(WinServiceCOMfunction)是成功的(我调试了它并在调用之后点击了这个函数)。

NOTE2:我确信使用回调的 WinService 有效。还有其他实现(在不同的编程语言中)通过 WinService 中的相同功能(WinServiceCOMfunction)使用此机制。

有什么帮助吗?提前致谢!

4

0 回答 0