代码改变世界

Windows Mobile多媒体开发总结之Media Player Plugins(续)

2009-05-01 21:58  王克伟  阅读(3178)  评论(13编辑  收藏  举报

在文章“Windows Mobile多媒体开发总结之Media Player Plugins ”中总结了在WM(Windows Mobile)中扩展WMP(Windows Media Player)的几种方法。发布之后有很多朋友询问具体做法,所以我乘机也总结下相关知识,刚好也补一下我这方面的差缺。

需求

在WM开发中如果不是单独开发自己的播放器或者使用第三方播放器,你就只能使用WMP,但是你可能需要在别的应用程序或者驱动中控制WMP,或者需要获得WMP的播放状态,那么怎样做呢?你可能会想到向WMP对应的按键发送消息,或者模拟键盘消息。实际上这些都不是好的解决方案。解决方法就是使用User Interface Background Plug-ins,这在上一篇文章中提到过了。

 

涉及到的知识

User Interface Background Plug-ins需要实现的接口和操作WMP的方法,COM进程内服务器的编写(在Windows下扩展微软本身软件,比如扩展IE,基本都以COM的形式),Today Plug-ins的编写等。(当然你使用ATL会更方便,就不需要自己这样一步步的实现COM了。这里只是为了深入的了解下COM内部原理。)

 

动手做

第一步

在Windows Mobile中开发WMP相关头文件在AKU中的位置如下图(比如AKU 6.15):
未命名 
在你的项目中需要使用wmp.h和wmpplug.h头文件。

第二步

实现作为COM进程内服务器需要的方法,这也是需要在DLL中导出的方法:
DllGetClassObject
DllCanUnloadNow
DllRegisterServer
先简单了解下COM服务器在创建过程中的位置,为了偷懒,图就直接引用VC知识库的,组件DLL即是所要编写的COM服务器端,客户端就是WMP:

d
关于更多COM的原理知识和使用可以参考《COM本质论》、《深入解析ATL》(我对ATL/WTL还是知之甚少,这本书已经放我枕头边很久了,一直在学习Windows操作系统而耽误了学习ATL)或VC知识库等。

对上面的图再解释一下,当客户端调用CoCreateInstance方法(这是WMP去做的,我们不管)时,CoCreateInstance实际上完成了下列三步:

CoGetClassObject(rclsid, dwClsContext, NULL, IID_IClassFactory, (void **)&pCF); //此方法调用我们要实现的DllGetClassObject方法,获得工厂对象的指针 
pCF->CreateInstance(pUnkOuter, riid, ppvObject);//CMediaPlayerPluginClassFactory::CreateInstance方法此时被调用 
pCF->Release();

看一下我们要实现的这3个方法代码是怎样的:

STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, VOID ** ppv) 
{ 
    HRESULT                             hr; 
    CMediaPlayerPluginClassFactory *    pcf; 

    *ppv = NULL; 

    if ((pcf = new CMediaPlayerPluginClassFactory()) == NULL) 
        return E_OUTOFMEMORY; 

    if (FAILED(hr = pcf->QueryInterface(riid, ppv)))//把插件工厂对象的指针传递给WMP 
    { 
        delete pcf; 
        return hr; 
    } 

    return S_OK; 
}

STDAPI DllCanUnloadNow() 
{ 
    if (g_pServer->CanUnload()) 
        return S_OK; 
    else 
        return S_FALSE; 
} 

STDAPI DllRegisterServer()//当你安装插件时系统调用这个方法,在这里你把你的COM服务器注册到注册表的CLSID键上。 
{ 
    TCHAR   szModulePath[MAX_PATH]; 

    // Get our module path. 
    if (!GetModuleFileName(GetModuleHandle(s_szModuleName), szModulePath, MAX_PATH)) 
        return E_FAIL; 

    szModulePath[MAX_PATH - 1] = _T('\0'); 

    // register this COM object.  
    // 
    // IMPORTANT: 
    // be sure to update this GUID with another if you create another plug-in.  Each plug-in must have a unique GUID. 
    return DllRegisterServerImplementation(_T("{009B9B8A-5080-4d09-8F74-9BD96A3558D4}"), s_szRegDescription, szModulePath, _T("Free")); 
}

来看看CMediaPlayerPluginClassFactory相关的实现,这个类实现IClassFactory接口,我们看下它最关键的一个方法是怎样实现的:

HRESULT CMediaPlayerPluginClassFactory::CreateInstance(IUnknown * pUnkOuter, REFIID riid, VOID ** ppvObject) 
{ 
    HRESULT                 hr; 
    CMediaPlayerPlugin *    pPlugin;  

    *ppvObject = NULL;  

    if (pUnkOuter != NULL) 
        return CLASS_E_NOAGGREGATION; 

    // 创建插件对象 
    if ((pPlugin = new CMediaPlayerPlugin()) == NULL) 
        return E_OUTOFMEMORY;  

    pPlugin->InternalAddRef(); 
    hr = pPlugin->FinalConstruct();//在此注册和创建插件窗口,实际上这个窗口是不可见的,仅仅是实现一个消息循环,用于其它应用程序向它发送消息。 
    pPlugin->InternalRelease(); 

    if (FAILED(hr)) 
    { 
        delete pPlugin; 
        return hr; 
    }  

    if (FAILED(hr = pPlugin->QueryInterface(riid, ppvObject)))//传递插件对象的指针 
    { 
        delete pPlugin; 
        return hr; 
    } 

    return S_OK; 
}

另外还有像CServer类的实现,具体见附件的项目。

第三步

实现IWMPPluginUI等接口

class CMediaPlayerPlugin: 
    public IWMPPluginUI 

在上一篇文章中我列出了这个接口下的方法:

    STDMETHODIMP Create(HWND hwndParent, HWND * phwndWindow); 
    STDMETHODIMP Destroy(); 
    STDMETHODIMP DisplayPropertyPage(HWND hwndParent); 
    STDMETHODIMP GetProperty(LPCWSTR wszName, VARIANT * pvarProperty); 
    STDMETHODIMP SetCore(IWMPCore * pCore); 
    STDMETHODIMP SetProperty(LPCWSTR wszName, const VARIANT * pvarProperty); 
    STDMETHODIMP TranslateAccelerator(MSG * pMsg);

对于这些方法,实现你需要的,不感兴趣的仅仅让它为空即可,比如:

HRESULT CMediaPlayerPlugin::Create(HWND hwndParent, HWND * phwndWindow) 
{ 
    // As per the IWMPPluginUI documentation, this method will never be called 
    // because we're a background plugin (our registry Capabilities flags include 
    // PLUGIN_TYPE_BACKGROUND = 0x00000001). 

    return E_NOTIMPL; 
}

SetCore是个关键方法,因为我们需要通过它获得操作WMP的接口指针,这里我们把获得的指针存放在m_pCore中:

HRESULT CMediaPlayerPlugin::SetCore(IWMPCore * pCore) 
{ 
    // This method is called shortly after this instance is created, and allows 
    // us to hook up to Windows Media Player. Save the interface pointer. 
    // 
    // When WMP is shutting down, it calls SetCore(NULL) so we have to handle that 
    // as well. 
    if (m_pCore != NULL) 
    { 
        m_pCore->Release(); 
    } 

    m_pCore = pCore; 

    if (m_pCore != NULL) 
    { 
        m_pCore->AddRef(); 
    } 

    return S_OK; 
}

然后我们可以像这样进一步通过m_pCore获得其它接口指针,类似这里的get_contronls方法的还有get_settings等,更详细的可以查看附件的项目。

VOID CMediaPlayerPlugin::ControlPlayer(UINT uMsg) 
{ 
    IWMPControls *  pControls; 

    // Get the 'IWMPControls' interface. 
    if (SUCCEEDED(m_pCore->get_controls(&pControls))) 
    { 
        switch (uMsg) 
        { 
            // for the toggle message 
            case WM_WMPTOGGLE: 
                WMPPlayState wmpps; 

                // get the current state 
                if (SUCCEEDED(m_pCore->get_playState(&wmpps))) 
                { 
                  // if it playing a track, pause it 
                  if (wmpps == wmppsPlaying) 
                  { 
                    pControls->pause(); 
                  } 
                  // otherwise try to play the track 
                  else 
                  { 
                    pControls->play(); 
                  } 

                } 
                break; 

            case WM_WMPPREVIOUS: 
                pControls->previous(); 
                break; 

            case WM_WMPNEXT: 
                pControls->next(); 
                break; 

            default: 
                break; 
        } 

        pControls->Release(); 
    } 
}

文章上个星期就写的差不多了,一直拖到现在才发布,实在抱歉。下一篇文章我总结下Today Plugin的开发。随便介绍下通过Today Plugin控制WMP。希望对你有所帮助。

项目的环境是:
Win32/Windows Mobile 6 Professional(CHS)/Visual Studio 2008(CHS) 
/Files/wangkewei/WMPlayerPlugin.rar