为什么我的 IWMPEvents 函数从未被调用?

Why my IWMPEvents functions are never called?

我对 IWMPEvents 有疑问,我建议成功但从未提出。这是我如何创建嵌入 Windows Media Player:

HRESULT MyWMP::setURL(wchar_t* url)
{
    return pMediaPlayer->put_URL(url);   // Load and play songs successfully;
}

bool MyWMP::CreatePlayer()
{
    HRESULT hr;
    const CLSID CLSID_WindowsMediaPlayer = { 0x6BF52A52, 0x394A, 0x11d3,{ 0xB1, 0x53, 0x00, 0xC0, 0x4F, 0x79, 0xFA, 0xA6 } };
    hr = ::OleCreate(CLSID_WindowsMediaPlayer, IID_IOleObject, OLERENDER_DRAW, 0, this, this, (void**)&oleObject);
    if (SUCCEEDED(hr) && oleObject)
    {
        if (SUCCEEDED(hr)) hr = oleObject->SetClientSite(this);
        if (SUCCEEDED(hr)) hr = OleSetContainedObject(oleObject, TRUE);

        if (SUCCEEDED(hr))
        {
            RECT posRect;
            ::SetRect(&posRect, -300, -300, 300, 300);
            hr = oleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, -1, this->hwnd, &posRect);
        }
        hr = oleObject->QueryInterface(&pMediaPlayer);
    }
    if (FAILED(hr) || !pMediaPlayer)
    {
        MessageBox(NULL, L"Create Browser failed!", L"Error", MB_ICONERROR);
        return false;
    }
    return true;
}

MyWMP::MyWMP(HWND parentHWND, HINSTANCE hInstance) :
    isFullyCreated(false)
{
    OleInitialize(NULL);
    HRESULT hr = E_FAIL;
    IConnectionPointContainer* container = nullptr;
    IUnknown* punk = nullptr;

    this->parentHWND = parentHWND;
    this->hwnd = CreateWindow(L"Static", NULL, WS_CHILD | WS_VISIBLE, 221, 0, 300, 300, parentHWND, NULL, hInstance, 0);

    iComRefCount = 0;
    ::SetRect(&rObject, 0, 0, 300, 300);

    if (CreatePlayer())
    {
        hr = pMediaPlayer->get_settings(&pMediaPlayerSettings);
        if (SUCCEEDED(hr) && pMediaPlayerSettings)
        {
            pMediaPlayerSettings->put_autoStart(VARIANT_TRUE);
            pMediaPlayerSettings->put_volume(100);
        }
        hr = pMediaPlayer->QueryInterface(IID_IConnectionPointContainer, (void**)&container);
        if (SUCCEEDED(hr) && container) hr = container->FindConnectionPoint(__uuidof(IWMPEvents), &callback);
        if (FAILED(hr) && container)
        {
            hr = container->FindConnectionPoint(__uuidof(_WMPOCXEvents), &callback);
        }
        if (SUCCEEDED(hr) && callback)
        {
            CWMPEventDispatch *cw = new CWMPEventDispatch();
            IUnknown *cwUnk = NULL;
            if (SUCCEEDED(cw->QueryInterface(IID_IUnknown, (void**)&cwUnk)))
            {
                if (SUCCEEDED(hr) && container)
                {
                    hr = callback->Advise(cwUnk, &eventCookie);
                    if (SUCCEEDED(hr) && eventCookie)
                    {
                        isFullyCreated = true; // Set breakpoint here and is triggered
                                               // by debugger
                                               // hr = S_OK
                    }
                }
            }
            if (cwUnk) cwUnk->Release();
        }
        if (punk) punk->Release();
        if (container) container->Release();
    }
}

下面是CWMPEventDispatchclass。我在所有 IDispatch 函数上设置了断点,其中 none 被调试器触发。当我加载新歌曲时,play/pause(在嵌入式 UI 控制按钮上),这些函数永远不会被调用。

// CWMPEventDispatch.h : Declaration of the event dispatcher
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//

#include "stdafx.h"
#include "wmpids.h"
#include "wmp.h"

class CWMPEventDispatch :
    public IWMPEvents,
    public _WMPOCXEvents
{
public:

    // ----- IUnknown -----
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void**ppvObject) override;
    virtual ULONG STDMETHODCALLTYPE AddRef(void);
    virtual ULONG STDMETHODCALLTYPE Release(void);

    // IDispatch methods
    STDMETHOD(GetIDsOfNames)(REFIID /*riid*/,
        __in_ecount(cNames) LPOLESTR FAR * /*rgszNames*/,
        unsigned int /*cNames*/,
        LCID /*lcid*/,
        DISPID FAR * /*rgDispId*/)
    {
        return(E_NOTIMPL);
    }

    STDMETHOD(GetTypeInfo)(unsigned int /*iTInfo*/,
        LCID /*lcid*/,
        ITypeInfo FAR *FAR * /*ppTInfo*/)
    {
        return(E_NOTIMPL);
    }

    STDMETHOD(GetTypeInfoCount)(unsigned int FAR * /*pctinfo*/)
    {
        return(E_NOTIMPL);
    }

    STDMETHOD(Invoke)(DISPID  dispIdMember,
        REFIID  /*riid*/,
        LCID  /*lcid*/,
        WORD  /*wFlags*/,
        DISPPARAMS FAR*  pDispParams,
        VARIANT FAR*  /*pVarResult*/,
        EXCEPINFO FAR*  /*pExcepInfo*/,
        unsigned int FAR*  /*puArgErr*/);


private:
    int iComRefCount;
};

#include "stdafx.h"
#include "myWMPEventDispatch.h"

#pragma region ----- IUnknown -----
ULONG STDMETHODCALLTYPE CWMPEventDispatch::AddRef(void) { return ++iComRefCount; }
ULONG STDMETHODCALLTYPE CWMPEventDispatch::Release(void) { return --iComRefCount; }

HRESULT STDMETHODCALLTYPE CWMPEventDispatch::QueryInterface(REFIID riid, void**ppvObject)
{
    if (riid == __uuidof(IUnknown))        *ppvObject = static_cast<IDispatch*>(this);
    else if (riid == __uuidof(IDispatch))       *ppvObject = static_cast<IDispatch*>(this);
    else if (riid == __uuidof(IWMPEvents))  *ppvObject = static_cast<IWMPEvents*>(this);
    else if (riid == __uuidof(_WMPOCXEvents)) *ppvObject = static_cast<_WMPOCXEvents*>(this);
    else
        return E_NOINTERFACE;

    AddRef();
    return S_OK;
}
#pragma endregion

HRESULT CWMPEventDispatch::Invoke(
    DISPID  dispIdMember,
    REFIID  /*riid*/,
    LCID  /*lcid*/,
    WORD  /*wFlags*/,
    DISPPARAMS FAR*  pDispParams,
    VARIANT FAR*  /*pVarResult*/,
    EXCEPINFO FAR*  /*pExcepInfo*/,
    unsigned int FAR*  /*puArgErr*/)
{
    if (!pDispParams)
        return E_POINTER;

    if (pDispParams->cNamedArgs != 0)
        return DISP_E_NONAMEDARGS;

    HRESULT hr = DISP_E_MEMBERNOTFOUND;


    return(hr);
}

问题:

为什么我的 IWMPEvents 函数从未被调用,如何解决?

我将完整的源代码作为 7z 存档进行攻击。可以下载here.

环境:Win 10 x64,VS 2017 社区

这段代码:

if (SUCCEEDED(hr) && container) hr = container->FindConnectionPoint(__uuidof(IWMPEvents), &callback);
if (FAILED(hr) && container)
{
    hr = container->FindConnectionPoint(__uuidof(_WMPOCXEvents), &callback);
}

会在第一行成功,所以你只会挂钩 IWMPEvents 上的事件,你不会继续挂钩 _WMPOCXEvents.

IWMPEvents 是早期绑定(IUnknown 派生)事件,因此媒体播放器确实会调用 IWMPEvents::PlayStateChange(...) IWMPEvents::StatusChange(...) 等,但它不会使用相应的 DISPID 调用 IDispatch::Invoke。

如果您需要 IDispatch 事件,只需删除第一个 FindConnectionPoint,或调用两者。