diff --git a/dlls/wmp/events.c b/dlls/wmp/events.c index a908834010d..412eb307d1a 100644 --- a/dlls/wmp/events.c +++ b/dlls/wmp/events.c @@ -400,3 +400,14 @@ void ConnectionPointContainer_Destroy(WindowsMediaPlayer *wmp) { ConnectionPoint_Destroy(wmp->wmpocx); } + +void call_sink(ConnectionPoint *This, DISPID dispid, DISPPARAMS *dispparams) +{ + DWORD i; + + for(i=0; isinks_size; i++) { + if(This->sinks[i]) + IDispatch_Invoke(This->sinks[i], dispid, &IID_NULL, LOCALE_SYSTEM_DEFAULT, + DISPATCH_METHOD, dispparams, NULL, NULL, NULL); + } +} diff --git a/dlls/wmp/oleobj.c b/dlls/wmp/oleobj.c index cc0e9a9d983..cbf183c1f6d 100644 --- a/dlls/wmp/oleobj.c +++ b/dlls/wmp/oleobj.c @@ -307,8 +307,8 @@ static ULONG WINAPI OleObject_Release(IOleObject *iface) if(!ref) { release_client_site(This); - ConnectionPointContainer_Destroy(This); destroy_player(This); + ConnectionPointContainer_Destroy(This); heap_free(This); } diff --git a/dlls/wmp/player.c b/dlls/wmp/player.c index 3d0df96bd86..8e012007883 100644 --- a/dlls/wmp/player.c +++ b/dlls/wmp/player.c @@ -20,9 +20,26 @@ #include "wine/debug.h" #include +#include "wmpids.h" WINE_DEFAULT_DEBUG_CHANNEL(wmp); +static void update_state(WindowsMediaPlayer *wmp, LONG type, LONG state) +{ + DISPPARAMS dispparams; + VARIANTARG params[1]; + + dispparams.cArgs = 1; + dispparams.cNamedArgs = 0; + dispparams.rgdispidNamedArgs = NULL; + dispparams.rgvarg = params; + + V_VT(params) = VT_UI4; + V_UI4(params) = state; + + call_sink(wmp->wmpocx, type, &dispparams); +} + static inline WMPMedia *impl_from_IWMPMedia(IWMPMedia *iface) { return CONTAINING_RECORD(iface, WMPMedia, IWMPMedia_iface); @@ -125,14 +142,21 @@ static HRESULT WINAPI WMPPlayer4_put_URL(IWMPPlayer4 *iface, BSTR url) if(url == NULL) { return E_POINTER; } + hres = create_media_from_url(url, &media); + if (SUCCEEDED(hres)) { + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsTransitioning); hres = IWMPPlayer4_put_currentMedia(iface, media); IWMPMedia_Release(media); /* put will addref */ } - if (SUCCEEDED(hres) && This->auto_start) { - hres = IWMPControls_play(&This->IWMPControls_iface); + if (SUCCEEDED(hres)) { + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsReady); + if (This->auto_start == VARIANT_TRUE) { + hres = IWMPControls_play(&This->IWMPControls_iface); + } } + return hres; } @@ -191,9 +215,13 @@ static HRESULT WINAPI WMPPlayer4_put_currentMedia(IWMPPlayer4 *iface, IWMPMedia if(pMedia == NULL) { return E_POINTER; } + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistChanging); if(This->wmpmedia != NULL) { IWMPMedia_Release(This->wmpmedia); } + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistChanged); + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistOpenNoMedia); + This->wmpmedia = pMedia; IWMPMedia_AddRef(This->wmpmedia); return S_OK; @@ -1388,19 +1416,32 @@ static HRESULT WINAPI WMPControls_play(IWMPControls *iface) CLSCTX_INPROC_SERVER, &IID_IGraphBuilder, (void **)&This->filter_graph); + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposOpeningUnknownURL); + if (SUCCEEDED(hres)) hres = IGraphBuilder_RenderFile(This->filter_graph, media->url, NULL); if (SUCCEEDED(hres)) hres = IGraphBuilder_QueryInterface(This->filter_graph, &IID_IMediaControl, (void**)&This->media_control); + if (SUCCEEDED(hres)) + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposMediaOpen); } + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsTransitioning); + if (SUCCEEDED(hres)) hres = IMediaControl_Run(This->media_control); if (hres == S_FALSE) { hres = S_OK; /* S_FALSE will mean that graph is transitioning and that is fine */ } + + if (SUCCEEDED(hres)) { + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsPlaying); + } else { + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsUndefined); + } + return hres; } @@ -1419,6 +1460,8 @@ static HRESULT WINAPI WMPControls_stop(IWMPControls *iface) IGraphBuilder_Release(This->filter_graph); This->filter_graph = NULL; This->media_control = NULL; + update_state(This, DISPID_WMPCOREEVENT_OPENSTATECHANGE, wmposPlaylistOpenNoMedia); + update_state(This, DISPID_WMPCOREEVENT_PLAYSTATECHANGE, wmppsStopped); return hres; } diff --git a/dlls/wmp/tests/media.c b/dlls/wmp/tests/media.c index 6e4a0c170f7..a7f611c8263 100644 --- a/dlls/wmp/tests/media.c +++ b/dlls/wmp/tests/media.c @@ -19,9 +19,54 @@ #include #include #include +#include +#include +#include #include "wine/test.h" +#define DEFINE_EXPECT(kind) \ + static DWORD expect_ ## kind = 0, called_ ## kind = 0 + +#define SET_EXPECT(kind, index) \ + do { \ + assert(index < 8 * sizeof(expect_ ## kind)); \ + expect_ ## kind |= (1 << index); \ + }while(0) + +#define CHECK_EXPECT(kind, index) \ + do { \ + ok(expect_ ##kind & (1 << index), "unexpected event for " #kind ", index:%d\n", index); \ + called_ ## kind |= (1 << index); \ + }while(0) + +#define CHECK_CALLED(kind, index) \ + do { \ + ok(called_ ## kind & (1 << index), "expected " #kind ", %d\n", index); \ + expect_ ## kind &= ~(1 << index); \ + called_ ## kind &= ~(1 << index); \ + }while(0) + +#define CHECK_CALLED_OR_BROKEN(kind, index) \ + do { \ + ok(called_ ## kind & (1 << index) || broken(1), "expected " #kind ", %d\n", index); \ + expect_ ## kind &= ~(1 << index); \ + called_ ## kind &= ~(1 << index); \ + }while(0) + +#define CHECK_NOT_CALLED(kind, index) \ + do { \ + ok(!(called_ ## kind & (1 << index)), "not expected " #kind ", %d\n", index); \ + expect_ ## kind &= ~(1 << index); \ + called_ ## kind &= ~(1 << index); \ + }while(0) + +DEFINE_EXPECT(PLAYSTATE); +DEFINE_EXPECT(OPENSTATE); + +static HANDLE playing_event; +static DWORD main_thread_id; + static const WCHAR mp3file[] = {'t','e','s','t','.','m','p','3',0}; static inline WCHAR *load_resource(const WCHAR *name) { @@ -48,14 +93,132 @@ static inline WCHAR *load_resource(const WCHAR *name) return pathW; } +static ULONG WINAPI Dispatch_AddRef(IDispatch *iface) +{ + return 2; +} + +static ULONG WINAPI Dispatch_Release(IDispatch *iface) +{ + return 1; +} + +static HRESULT WINAPI Dispatch_GetTypeInfoCount(IDispatch *iface, UINT *pctinfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Dispatch_GetTypeInfo(IDispatch *iface, UINT iTInfo, LCID lcid, + ITypeInfo **ppTInfo) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI Dispatch_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *rgszNames, + UINT cNames, LCID lcid, DISPID *rgDispId) +{ + ok(0, "unexpected call\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI WMPOCXEvents_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + + if(IsEqualGUID(&IID__WMPOCXEvents, riid) || IsEqualGUID(&IID_IDispatch, riid)) { + *ppv = iface; + return S_OK; + } + + ok(0, "unexpected riid %s\n", wine_dbgstr_guid(riid)); + return E_NOINTERFACE; +} + +static HRESULT WINAPI WMPOCXEvents_Invoke(IDispatch *iface, DISPID dispIdMember, REFIID riid, + LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + ok(main_thread_id == GetCurrentThreadId(), "Got notification outside of main thread!\n"); + switch(dispIdMember) { + case DISPID_WMPCOREEVENT_OPENSTATECHANGE: + CHECK_EXPECT(OPENSTATE, V_UI4(pDispParams->rgvarg)); + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_OPENSTATECHANGE, %d\n", V_UI4(pDispParams->rgvarg)); + break; + case DISPID_WMPCOREEVENT_PLAYSTATECHANGE: + CHECK_EXPECT(PLAYSTATE, V_UI4(pDispParams->rgvarg)); + if (V_UI4(pDispParams->rgvarg) == wmppsPlaying) { + SetEvent(playing_event); + } + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_PLAYSTATECHANGE, %d\n", V_UI4(pDispParams->rgvarg)); + break; + case DISPID_WMPCOREEVENT_MEDIACHANGE: + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_MEDIACHANGE\n"); + break; + case DISPID_WMPCOREEVENT_CURRENTITEMCHANGE: + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_CURRENTITEMCHANGE\n"); + break; + case DISPID_WMPCOREEVENT_STATUSCHANGE: + if (winetest_debug > 1) + trace("DISPID_WMPCOREEVENT_STATUSCHANGE\n"); + break; + default: + if (winetest_debug > 1) + trace("event: %d\n", dispIdMember); + break; + } + + return E_NOTIMPL; +} + +static IDispatchVtbl WMPOcxEventsVtbl = { + WMPOCXEvents_QueryInterface, + Dispatch_AddRef, + Dispatch_Release, + Dispatch_GetTypeInfoCount, + Dispatch_GetTypeInfo, + Dispatch_GetIDsOfNames, + WMPOCXEvents_Invoke, +}; + +static IDispatch WMPOCXEvents = { &WMPOcxEventsVtbl }; + +static HRESULT pump_messages(DWORD timeout, DWORD count, const HANDLE *handles) { + MSG msg; + HRESULT res; + DWORD start_time = GetTickCount(); + do { + DWORD now = GetTickCount(); + res = MsgWaitForMultipleObjectsEx(count, handles, start_time + timeout - now, + QS_ALLINPUT ,MWMO_ALERTABLE | MWMO_INPUTAVAILABLE); + if (res == WAIT_OBJECT_0 + 1) { + GetMessageW(&msg, 0, 0, 0); + if (winetest_debug > 1) + trace("Dispatching %d\n", msg.message); + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + while (res == WAIT_OBJECT_0 + 1); + return res; +} + static void test_wmp(void) { + DWORD res = 0; IWMPPlayer4 *player4; IWMPControls *controls; HRESULT hres; BSTR filename; - + IConnectionPointContainer *container; + IConnectionPoint *point; IOleObject *oleobj; + static DWORD dw = 100; IWMPSettings *settings; hres = CoCreateInstance(&CLSID_WindowsMediaPlayer, NULL, CLSCTX_INPROC_SERVER, &IID_IOleObject, (void**)&oleobj); @@ -65,6 +228,18 @@ static void test_wmp(void) } ok(hres == S_OK, "Could not create CLSID_WindowsMediaPlayer instance: %08x\n", hres); + hres = IOleObject_QueryInterface(oleobj, &IID_IConnectionPointContainer, (void**)&container); + ok(hres == S_OK, "QueryInterface(IID_IConnectionPointContainer) failed: %08x\n", hres); + if(FAILED(hres)) + return; + + hres = IConnectionPointContainer_FindConnectionPoint(container, &IID__WMPOCXEvents, &point); + IConnectionPointContainer_Release(container); + ok(hres == S_OK, "FindConnectionPoint failed: %08x\n", hres); + + hres = IConnectionPoint_Advise(point, (IUnknown*)&WMPOCXEvents, &dw); + ok(hres == S_OK, "Advise failed: %08x\n", hres); + hres = IOleObject_QueryInterface(oleobj, &IID_IWMPPlayer4, (void**)&player4); ok(hres == S_OK, "Could not get IWMPPlayer4 iface: %08x\n", hres); @@ -87,22 +262,70 @@ static void test_wmp(void) filename = SysAllocString(load_resource(mp3file)); + SET_EXPECT(OPENSTATE, wmposPlaylistChanging); + SET_EXPECT(OPENSTATE, wmposPlaylistOpenNoMedia); + SET_EXPECT(OPENSTATE, wmposPlaylistChanged); + SET_EXPECT(PLAYSTATE, wmppsTransitioning); + SET_EXPECT(PLAYSTATE, wmppsReady); hres = IWMPPlayer4_put_URL(player4, filename); ok(hres == S_OK, "IWMPPlayer4_put_URL failed: %08x\n", hres); + CHECK_CALLED(OPENSTATE, wmposPlaylistChanging); + CHECK_CALLED(OPENSTATE, wmposPlaylistChanged); + CHECK_CALLED(OPENSTATE, wmposPlaylistOpenNoMedia); + CHECK_CALLED(PLAYSTATE, wmppsTransitioning); + CHECK_CALLED(PLAYSTATE, wmppsReady); + SET_EXPECT(OPENSTATE, wmposOpeningUnknownURL); + SET_EXPECT(OPENSTATE, wmposMediaOpen); + SET_EXPECT(OPENSTATE, wmposMediaOpening); + SET_EXPECT(PLAYSTATE, wmppsPlaying); + SET_EXPECT(PLAYSTATE, wmppsTransitioning); hres = IWMPControls_play(controls); ok(hres == S_OK, "IWMPControls_play failed: %08x\n", hres); + res = pump_messages(5000, 1, &playing_event); + ok(res == WAIT_OBJECT_0 || broken(res == WAIT_TIMEOUT), "Timed out while waiting for media to become ready\n"); + if (res == WAIT_TIMEOUT) { + /* This happens on Vista Ultimate 64 vms + * I have been unable to find out source of this behaviour */ + win_skip("Failed to transition media to playing state.\n"); + goto playback_skip; + } + CHECK_CALLED(OPENSTATE, wmposOpeningUnknownURL); + CHECK_CALLED(OPENSTATE, wmposMediaOpen); + /* MediaOpening happens only on xp, 2003 */ + todo_wine CHECK_CALLED_OR_BROKEN(OPENSTATE, wmposMediaOpening); + CHECK_CALLED(PLAYSTATE, wmppsPlaying); + CHECK_CALLED(PLAYSTATE, wmppsTransitioning); + SET_EXPECT(PLAYSTATE, wmppsStopped); + /* The following happens on wine only since we close media on stop */ + SET_EXPECT(OPENSTATE, wmposPlaylistOpenNoMedia); hres = IWMPControls_stop(controls); ok(hres == S_OK, "IWMPControls_stop failed: %08x\n", hres); + CHECK_CALLED(PLAYSTATE, wmppsStopped); + todo_wine CHECK_NOT_CALLED(OPENSTATE, wmposPlaylistOpenNoMedia); /* Already Stopped */ hres = IWMPControls_stop(controls); ok(hres == NS_S_WMPCORE_COMMAND_NOT_AVAILABLE, "IWMPControls_stop is available: %08x\n", hres); + SET_EXPECT(PLAYSTATE, wmppsPlaying); + /* The following happens on wine only since we close media on stop */ + SET_EXPECT(OPENSTATE, wmposOpeningUnknownURL); + SET_EXPECT(OPENSTATE, wmposMediaOpen); + SET_EXPECT(PLAYSTATE, wmppsTransitioning); hres = IWMPControls_play(controls); ok(hres == S_OK, "IWMPControls_play failed: %08x\n", hres); + CHECK_CALLED(PLAYSTATE, wmppsPlaying); + todo_wine CHECK_NOT_CALLED(OPENSTATE, wmposOpeningUnknownURL); + todo_wine CHECK_NOT_CALLED(OPENSTATE, wmposMediaOpen); + todo_wine CHECK_NOT_CALLED(PLAYSTATE, wmppsTransitioning); +playback_skip: + hres = IConnectionPoint_Unadvise(point, dw); + ok(hres == S_OK, "Unadvise failed: %08x\n", hres); + + IConnectionPoint_Release(point); IWMPControls_Release(controls); IWMPPlayer4_Release(player4); IOleObject_Release(oleobj); @@ -114,7 +337,11 @@ START_TEST(media) { CoInitialize(NULL); + main_thread_id = GetCurrentThreadId(); + playing_event = CreateEventW(NULL, FALSE, FALSE, NULL); test_wmp(); + CloseHandle(playing_event); + CoUninitialize(); } diff --git a/dlls/wmp/wmp_main.c b/dlls/wmp/wmp_main.c index 29b096f7fd1..9a33b2762bc 100644 --- a/dlls/wmp/wmp_main.c +++ b/dlls/wmp/wmp_main.c @@ -25,6 +25,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(wmp); HINSTANCE wmp_instance; +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) { diff --git a/dlls/wmp/wmp_private.h b/dlls/wmp/wmp_private.h index 05ad5889e2c..9e84d56ea88 100644 --- a/dlls/wmp/wmp_private.h +++ b/dlls/wmp/wmp_private.h @@ -83,6 +83,7 @@ WMPMedia *unsafe_impl_from_IWMPMedia(IWMPMedia *iface) DECLSPEC_HIDDEN; HRESULT create_media_from_url(BSTR url, IWMPMedia **ppMedia) DECLSPEC_HIDDEN; void ConnectionPointContainer_Init(WindowsMediaPlayer *wmp) DECLSPEC_HIDDEN; void ConnectionPointContainer_Destroy(WindowsMediaPlayer *wmp) DECLSPEC_HIDDEN; +void call_sink(ConnectionPoint *This, DISPID dispid, DISPPARAMS *dispparams) DECLSPEC_HIDDEN; HRESULT WINAPI WMPFactory_CreateInstance(IClassFactory*,IUnknown*,REFIID,void**) DECLSPEC_HIDDEN; diff --git a/include/Makefile.in b/include/Makefile.in index d5a3eeb4fd5..d816bbf4e42 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -706,6 +706,7 @@ SOURCES = \ wmium.h \ wmiutils.idl \ wmp.idl \ + wmpids.h \ wmsbuffer.idl \ wmsdk.h \ wmsdkidl.idl \ diff --git a/include/wmpids.h b/include/wmpids.h new file mode 100644 index 00000000000..a8500409ac4 --- /dev/null +++ b/include/wmpids.h @@ -0,0 +1,24 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* WMPCoreEvents */ +#define DISPID_WMPCOREEVENT_OPENSTATECHANGE 5001 +#define DISPID_WMPCOREEVENT_STATUSCHANGE 5002 + +#define DISPID_WMPCOREEVENT_PLAYSTATECHANGE 5101 + +#define DISPID_WMPCOREEVENT_MEDIACHANGE 5802 +#define DISPID_WMPCOREEVENT_CURRENTITEMCHANGE 5806