Уф, удалось таки раскопать эту тему. К сожалению через ROT взаимодействовать с wmplayer.exe не получится, т.к. он регает в ROT только два объекта для поддержки технологии AutoPlay (с CD, camera, etc) — вот они: wmp!CWMPAutoplay и wmp!CAutoPlayDeviceHandler. Они не предоставляют нужных мне интерфейсов для взаимодействия с wmplayer из другого процесса (исследовал на Windows 7 x86).
Решить задачу помог MS-овский Office Communicator. У него есть галка "Pause Windows Media Player for calls, video calls, and conferences", работает это так: когда происходит входящий звонок в communicator.exe, а в wmplayer проигрывается видео, то при поднятии трубки видео паузится. Дизассемблирование это механики позволило открыть следующее: communicator.exe использует IServiceProvider и IWMPRemoteMediaServices для взаимодействия с работающим wmplayer.exe — это все называется "
Remoting the Windows Media Player Control". Хитрость была в создании IOleObject для взаимодействия с wmplayer.exe и задании ему IOleClientSite. Вот такой получился PoC (WmplayerStateMonitor.exe, обработка ошибок опущена):
int _tmain(int argc, _TCHAR* argv[])
{
HRESULT hr = CoInitialize(NULL);
// создаем CLSCTX_INPROC_SERVER, но взаимодействуем с COM-сервером, работающим в отдельном процессе wmplayer.exe, т.е. мы не эмбедим контрол плеера:
CComPtr<IOleObject> oleObject;
hr = CoCreateInstance(CLSID_WindowsMediaPlayer, NULL, CLSCTX_INPROC_SERVER, __uuidof(IOleObject), reinterpret_cast<void**>(&oleObject));
// задаем клиент-сайт, без этого взаимодействие не работает
MyWmplayerRemoteHost host;
hr = oleObject->SetClientSite(&host);
// подключаем наш слушатель событий
CComQIPtr<IConnectionPointContainer> connPointContainer = oleObject;
CComPtr<IConnectionPoint> connPoint;
hr = connPointContainer->FindConnectionPoint(__uuidof(IWMPEvents), &connPoint);
MyWmplayerEventHandler handler;
DWORD handlerCookie;
hr = connPoint->Advise(&handler, &handlerCookie);
for (;;) {
// прокачиваем события, т.к. наше взаимодействие с wmplayer.exe происходит через STA:
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
...
Класс MyWmplayerRemoteHost реализует интерфейсы IOleClientSite и IServiceProvider:
class MyWmplayerRemoteHost: public IOleClientSite, public IServiceProvider, public IWMPRemoteMediaServices
{
// IUnknown:
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) {
// поддерживаем IID_IUnknown и IID_IServiceProvider
}
...
// IOleClientSite: все методы возвращают E_NOTIMPL
virtual HRESULT STDMETHODCALLTYPE SaveObject(void) { return E_NOTIMPL; }
...
// IServiceProvider:
virtual HRESULT STDMETHODCALLTYPE QueryService(REFGUID, REFIID riid, void **ppvObject) {
// если riid==__uuidof(IWMPRemoteMediaServices), возвращаем IWMPRemoteMediaServices
}
// IWMPRemoteMediaServices:
virtual HRESULT STDMETHODCALLTYPE GetServiceType(BSTR *pbstrType)
{
*pbstrType = SysAllocString(L"Remote"); // т.о. мы указываем что мы удаленный клиент без UI
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetApplicationName(BSTR *pbstrName)
{
*pbstrName = SysAllocString(L"Wmplayer State Monitor"); // название нашего приложения
return S_OK;
}
virtual HRESULT STDMETHODCALLTYPE GetScriptableObject(BSTR *pbstrName, IDispatch **ppDispatch) { return E_NOTIMPL; }
virtual HRESULT STDMETHODCALLTYPE GetCustomUIMode(BSTR *pbstrFile) { return E_NOTIMPL; }
};
и класс MyWmplayerEventHandler — это наш обработчик событий об изменениях в wmplayer.exe:
class MyWmplayerEventHandler : public IWMPEvents
{
// IUnknown:
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) {
// поддерживаем IID_IUnknown и IID_IWMPEvents
}
...
// IWMPEvents: нас интересует только изменение playState, остальные обработчики - заглушки
virtual void STDMETHODCALLTYPE OpenStateChange(long NewState) {}
virtual void STDMETHODCALLTYPE PlayStateChange(long NewState) {
std::wcout << "New play state: " << PrintPlayState(static_cast<WMPPlayState>(NewState)) << std::endl;
}
...
};
Меня интересовало именно получение событий от работающего wmplayer.exe. Но описанным способом можно и управлять им:
CComQIPtr<IWMPPlayer> player = oleObject;
CComPtr<IWMPControls> controls;
hr = player->get_controls(&controls);
hr = controls->pause();
Особенности работы описанного механизма: если wmplayer.exe запущен до WmplayerStateMonitor.exe, то монитор при старте коннектится к нему. Если wmplayer завершится раньше, то WmplayerStateMonitor продолжает ужерживать COM-сервер вместе с процессом wmplayer.exe. Если WmplayerStateMonitor запущен раньше чем wmplayer, то при старте wmplayer реюзает COM-сервер в процессе wmplayer.exe, запущенный WmplayerStateMonitor. Чтобы пользователь не беспокоился о том, почему у него запущен wmplayer.exe, хотя он его не запускал, можно подключаться к wmplayer только когда он уже запущен и отключаться когда процесс остановлен. Но мониторинг старта/стопа процессов уже выходит за рамки рассмотренной темы исследования.