diff --git a/dlls/ddraw/ddraw.c b/dlls/ddraw/ddraw.c index f6be8bce74..cec78214a3 100644 --- a/dlls/ddraw/ddraw.c +++ b/dlls/ddraw/ddraw.c @@ -131,7 +131,13 @@ IDirectDrawImpl_QueryInterface(IDirectDraw7 *iface, TRACE("(%p) Returning IDirectDraw interface at %p\n", This, *obj); } - /* Direct3D */ + /* Direct3D + * The refcount unit test revealed that an IDirect3D7 interface can only be queried + * from a DirectDraw object that was created as an IDirectDraw7 interface. No idea + * who had this idea and why. The older interfaces can query and IDirect3D version + * because they are all created as IDirectDraw(1). This isn't really crucial behavior, + * and messy to implement with the common creation function, so it has been left out here. + */ else if ( IsEqualGUID( &IID_IDirect3D , refiid ) || IsEqualGUID( &IID_IDirect3D2 , refiid ) || IsEqualGUID( &IID_IDirect3D3 , refiid ) || @@ -197,17 +203,30 @@ IDirectDrawImpl_QueryInterface(IDirectDraw7 *iface, /***************************************************************************** * IDirectDraw7::AddRef * - * Increases the interfaces refcount. Used for version 1, 2, 4 and 7 + * Increases the interfaces refcount, basically + * + * DDraw refcounting is a bit tricky. The different DirectDraw interface + * versions have individual refcounts, but the IDirect3D interfaces do not. + * All interfaces are from one object, that means calling QueryInterface on an + * IDirectDraw7 interface for an IDirectDraw4 interface does not create a new + * IDirectDrawImpl object. + * + * That means all AddRef and Release implementations of IDirectDrawX work + * with their own counter, and IDirect3DX::AddRef thunk to IDirectDraw (1), + * except of IDirect3D7 which thunks to IDirectDraw7 * * Returns: The new refcount + * *****************************************************************************/ static ULONG WINAPI IDirectDrawImpl_AddRef(IDirectDraw7 *iface) { ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw7, iface); - ULONG ref = InterlockedIncrement(&This->ref); + ULONG ref = InterlockedIncrement(&This->ref7); - TRACE("(%p) : incrementing from %lu.\n", This, ref -1); + TRACE("(%p) : incrementing IDirectDraw7 refcount from %lu.\n", This, ref -1); + + if(ref == 1) InterlockedIncrement(&This->numIfaces); return ref; } @@ -215,8 +234,8 @@ IDirectDrawImpl_AddRef(IDirectDraw7 *iface) /***************************************************************************** * IDirectDrawImpl_Destroy * - * Destroys a ddraw object. This is to share code between normal Release - * and the dll unload cleanup code + * Destroys a ddraw object if all refcounts are 0. This is to share code + * between the IDirectDrawX::Release functions * * Params: * This: DirectDraw object to destroy @@ -225,45 +244,49 @@ IDirectDrawImpl_AddRef(IDirectDraw7 *iface) void IDirectDrawImpl_Destroy(IDirectDrawImpl *This) { - IDirectDrawImpl *prev; + IDirectDrawImpl *prev; - TRACE("(%p)\n", This); + /* Clear the cooplevel to restore window and display mode */ + IDirectDraw7_SetCooperativeLevel(ICOM_INTERFACE(This, IDirectDraw7), + NULL, + DDSCL_NORMAL); - /* Destroy the device window if we created one */ - if(This->devicewindow != 0) - { - TRACE(" (%p) Destroying the device window %p\n", This, This->devicewindow); - DestroyWindow(This->devicewindow); - This->devicewindow = 0; - } + /* Destroy the device window if we created one */ + if(This->devicewindow != 0) + { + TRACE(" (%p) Destroying the device window %p\n", This, This->devicewindow); + DestroyWindow(This->devicewindow); + This->devicewindow = 0; + } - /* Unregister the window class */ - UnregisterClassA(This->classname, 0); + /* Unregister the window class */ + UnregisterClassA(This->classname, 0); - /* Unchain it from the ddraw list */ - if(ddraw_list == This) - { - ddraw_list = This->next; - /* No need to search for a predecessor here */ - } + /* Unchain it from the ddraw list */ + if(ddraw_list == This) + { + ddraw_list = This->next; + /* No need to search for a predecessor here */ + } + else + { + for(prev = ddraw_list; prev; prev = prev->next) + if(prev->next == This) break; + + if(prev) + prev->next = This->next; else - { - for(prev = ddraw_list; prev; prev = prev->next) - if(prev->next == This) break; + ERR("Didn't find the previous ddraw element in the list\n"); + } - if(prev) - prev->next = This->next; - else - ERR("Didn't find the previous ddraw element in the list\n"); - } + /* Release the attached WineD3D stuff */ + IWineD3DDevice_Release(This->wineD3DDevice); + IWineD3D_Release(This->wineD3D); - /* Release the attached WineD3D stuff */ - IWineD3DDevice_Release(This->wineD3DDevice); - IWineD3D_Release(This->wineD3D); - - /* Now free the object */ - HeapFree(GetProcessHeap(), 0, This); + /* Now free the object */ + HeapFree(GetProcessHeap(), 0, This); } + /***************************************************************************** * IDirectDraw7::Release * @@ -275,21 +298,14 @@ static ULONG WINAPI IDirectDrawImpl_Release(IDirectDraw7 *iface) { ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw7, iface); - ULONG ref = InterlockedDecrement(&This->ref); + ULONG ref = InterlockedDecrement(&This->ref7); - TRACE("(%p)->() decrementing from %lu.\n", This, ref +1); + TRACE("(%p)->() decrementing IDirectDraw7 refcount from %lu.\n", This, ref +1); - if (ref == 0) + if(ref == 0) { - /* No need to restore the display mode - it's done by SetCooperativeLevel */ - - IDirectDraw7_SetCooperativeLevel(ICOM_INTERFACE(This, IDirectDraw7), - NULL, - DDSCL_NORMAL); - - /* This is for the dll cleanup code in DllMain() */ - if(!This->DoNotDestroy) - IDirectDrawImpl_Destroy(This); + ULONG ifacecount = InterlockedDecrement(&This->numIfaces); + if(ifacecount == 0) IDirectDrawImpl_Destroy(This); } return ref; @@ -2337,6 +2353,7 @@ IDirectDrawImpl_CreateSurface(IDirectDraw7 *iface, /* Addref the ddraw interface to keep an reference for each surface */ IDirectDraw7_AddRef(iface); + object->ifaceToRelease = (IUnknown *) iface; /* If the implementation is OpenGL and there's no d3ddevice, attach a d3ddevice * But attach the d3ddevice only if the currently created surface was diff --git a/dlls/ddraw/ddraw_private.h b/dlls/ddraw/ddraw_private.h index fc571ed8af..74bed0e4b7 100644 --- a/dlls/ddraw/ddraw_private.h +++ b/dlls/ddraw/ddraw_private.h @@ -90,7 +90,8 @@ struct IDirectDrawImpl ICOM_VFIELD_MULTI(IDirect3D2); ICOM_VFIELD_MULTI(IDirect3D); - LONG ref; + /* See comment in IDirectDraw::AddRef */ + LONG ref7, ref4, ref2, ref1, numIfaces; /* WineD3D linkage */ IWineD3D *wineD3D; @@ -143,7 +144,6 @@ struct IDirectDrawImpl /* For the dll unload cleanup code */ IDirectDrawImpl *next; - BOOL DoNotDestroy; LONG surfaces; }; @@ -202,6 +202,7 @@ struct IDirectDrawSurfaceImpl ICOM_VFIELD_MULTI(IDirect3DTexture); LONG ref; + IUnknown *ifaceToRelease; int version; diff --git a/dlls/ddraw/ddraw_thunks.c b/dlls/ddraw/ddraw_thunks.c index 883411310d..7ccc65d79f 100644 --- a/dlls/ddraw/ddraw_thunks.c +++ b/dlls/ddraw/ddraw_thunks.c @@ -17,15 +17,32 @@ */ #include "config.h" +#include "wine/port.h" +#include #include +#include +#include + +#define COBJMACROS +#define NONAMELESSUNION #include "windef.h" #include "winbase.h" +#include "winnls.h" +#include "winerror.h" #include "wingdi.h" +#include "wine/exception.h" +#include "excpt.h" + #include "ddraw.h" +#include "d3d.h" + #include "ddraw_private.h" -#include "ddcomimpl.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(ddraw_thunk); +WINE_DECLARE_DEBUG_CHANNEL(ddraw); static HRESULT WINAPI IDirectDrawImpl_QueryInterface(LPDIRECTDRAW This, REFIID iid, LPVOID *ppObj) @@ -56,51 +73,93 @@ IDirectDraw4Impl_QueryInterface(LPDIRECTDRAW4 This, REFIID iid, LPVOID *ppObj) } static ULONG WINAPI -IDirectDrawImpl_AddRef(LPDIRECTDRAW This) +IDirectDrawImpl_AddRef(LPDIRECTDRAW iface) { - return IDirectDraw7_AddRef(COM_INTERFACE_CAST(IDirectDrawImpl, - IDirectDraw, IDirectDraw7, - This)); + ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw, iface); + ULONG ref = InterlockedIncrement(&This->ref1); + + TRACE("(%p) : incrementing IDirectDraw refcount from %lu.\n", This, ref -1); + + if(ref == 1) InterlockedIncrement(&This->numIfaces); + + return ref; } static ULONG WINAPI -IDirectDraw2Impl_AddRef(LPDIRECTDRAW2 This) +IDirectDraw2Impl_AddRef(LPDIRECTDRAW2 iface) { - return IDirectDraw7_AddRef(COM_INTERFACE_CAST(IDirectDrawImpl, - IDirectDraw2, IDirectDraw7, - This)); + ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw2, iface); + ULONG ref = InterlockedIncrement(&This->ref2); + + TRACE("(%p) : incrementing IDirectDraw2 refcount from %lu.\n", This, ref -1); + + if(ref == 1) InterlockedIncrement(&This->numIfaces); + + return ref; } static ULONG WINAPI -IDirectDraw4Impl_AddRef(LPDIRECTDRAW4 This) +IDirectDraw4Impl_AddRef(LPDIRECTDRAW4 iface) { - return IDirectDraw7_AddRef(COM_INTERFACE_CAST(IDirectDrawImpl, - IDirectDraw4, IDirectDraw7, - This)); + ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw4, iface); + ULONG ref = InterlockedIncrement(&This->ref4); + + TRACE("(%p) : incrementing IDirectDraw4 refcount from %lu.\n", This, ref -1); + + if(ref == 1) InterlockedIncrement(&This->numIfaces); + + return ref; } static ULONG WINAPI -IDirectDrawImpl_Release(LPDIRECTDRAW This) +IDirectDrawImpl_Release(LPDIRECTDRAW iface) { - return IDirectDraw7_Release(COM_INTERFACE_CAST(IDirectDrawImpl, - IDirectDraw, IDirectDraw7, - This)); + ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw, iface); + ULONG ref = InterlockedDecrement(&This->ref1); + + TRACE_(ddraw)("(%p)->() decrementing IDirectDraw refcount from %lu.\n", This, ref +1); + + if(ref == 0) + { + ULONG ifacecount = InterlockedDecrement(&This->numIfaces); + if(ifacecount == 0) IDirectDrawImpl_Destroy(This); + } + + return ref; } static ULONG WINAPI -IDirectDraw2Impl_Release(LPDIRECTDRAW2 This) +IDirectDraw2Impl_Release(LPDIRECTDRAW2 iface) { - return IDirectDraw7_Release(COM_INTERFACE_CAST(IDirectDrawImpl, - IDirectDraw2, IDirectDraw7, - This)); + ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw2, iface); + ULONG ref = InterlockedDecrement(&This->ref2); + + TRACE_(ddraw)("(%p)->() decrementing IDirectDraw2 refcount from %lu.\n", This, ref +1); + + if(ref == 0) + { + ULONG ifacecount = InterlockedDecrement(&This->numIfaces); + if(ifacecount == 0) IDirectDrawImpl_Destroy(This); + } + + return ref; } static ULONG WINAPI -IDirectDraw4Impl_Release(LPDIRECTDRAW4 This) +IDirectDraw4Impl_Release(LPDIRECTDRAW4 iface) { - return IDirectDraw7_Release(COM_INTERFACE_CAST(IDirectDrawImpl, - IDirectDraw4, IDirectDraw7, - This)); + ICOM_THIS_FROM(IDirectDrawImpl, IDirectDraw4, iface); + ULONG ref = InterlockedDecrement(&This->ref4); + + TRACE_(ddraw)("(%p)->() decrementing IDirectDraw4 refcount from %lu.\n", This, ref +1); + + if(ref == 0) + { + ULONG ifacecount = InterlockedDecrement(&This->numIfaces); + if(ifacecount == 0) IDirectDrawImpl_Destroy(This); + } + + return ref; } static HRESULT WINAPI @@ -169,11 +228,20 @@ IDirectDrawImpl_CreatePalette(LPDIRECTDRAW This, DWORD dwFlags, LPDIRECTDRAWPALETTE *ppPalette, IUnknown *pUnkOuter) { - return IDirectDraw7_CreatePalette(COM_INTERFACE_CAST(IDirectDrawImpl, + HRESULT hr; + hr = IDirectDraw7_CreatePalette(COM_INTERFACE_CAST(IDirectDrawImpl, IDirectDraw, IDirectDraw7, This), dwFlags, pEntries, ppPalette, pUnkOuter); + if(SUCCEEDED(hr) && *ppPalette) + { + IDirectDraw7_Release(COM_INTERFACE_CAST(IDirectDrawImpl, + IDirectDraw, + IDirectDraw7, + This)); + } + return hr; } static HRESULT WINAPI @@ -182,11 +250,19 @@ IDirectDraw2Impl_CreatePalette(LPDIRECTDRAW2 This, DWORD dwFlags, LPDIRECTDRAWPALETTE *ppPalette, IUnknown *pUnkOuter) { + HRESULT hr; return IDirectDraw7_CreatePalette(COM_INTERFACE_CAST(IDirectDrawImpl, IDirectDraw2, IDirectDraw7, This), dwFlags, pEntries, ppPalette, pUnkOuter); + if(SUCCEEDED(hr) && *ppPalette) + { + IDirectDraw7_Release(COM_INTERFACE_CAST(IDirectDrawImpl, + IDirectDraw, + IDirectDraw7, + This)); + } } static HRESULT WINAPI @@ -195,11 +271,19 @@ IDirectDraw4Impl_CreatePalette(LPDIRECTDRAW4 This, DWORD dwFlags, LPDIRECTDRAWPALETTE *ppPalette, IUnknown *pUnkOuter) { + HRESULT hr; return IDirectDraw7_CreatePalette(COM_INTERFACE_CAST(IDirectDrawImpl, IDirectDraw4, IDirectDraw7, This), dwFlags, pEntries, ppPalette, pUnkOuter); + if(SUCCEEDED(hr) && *ppPalette) + { + IDirectDraw7_Release(COM_INTERFACE_CAST(IDirectDrawImpl, + IDirectDraw, + IDirectDraw7, + This)); + } } static HRESULT WINAPI @@ -226,9 +310,15 @@ IDirectDrawImpl_CreateSurface(LPDIRECTDRAW This, LPDDSURFACEDESC pSDesc, pSurface7); impl = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7, pSurface7); - if(impl) + if(SUCCEEDED(hr) && impl) { impl->version = 1; + IDirectDraw7_Release(COM_INTERFACE_CAST(IDirectDrawImpl, + IDirectDraw, + IDirectDraw7, + This)); + IDirectDraw_AddRef(This); + impl->ifaceToRelease = (IUnknown *) This; } return hr; @@ -256,9 +346,15 @@ IDirectDraw2Impl_CreateSurface(LPDIRECTDRAW2 This, LPDDSURFACEDESC pSDesc, pSurface7); impl = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7, pSurface7); - if(impl) + if(SUCCEEDED(hr) && impl) { impl->version = 2; + IDirectDraw7_Release(COM_INTERFACE_CAST(IDirectDrawImpl, + IDirectDraw2, + IDirectDraw7, + This)); + IDirectDraw2_AddRef(This); + impl->ifaceToRelease = (IUnknown *) This; } return hr; @@ -280,9 +376,15 @@ IDirectDraw4Impl_CreateSurface(LPDIRECTDRAW4 This, LPDDSURFACEDESC2 pSDesc, (LPDIRECTDRAWSURFACE7 *)ppSurface, pUnkOuter); impl = ICOM_OBJECT(IDirectDrawSurfaceImpl, IDirectDrawSurface7, *ppSurface); - if(impl) + if(SUCCEEDED(hr) && impl) { impl->version = 4; + IDirectDraw7_Release(COM_INTERFACE_CAST(IDirectDrawImpl, + IDirectDraw4, + IDirectDraw7, + This)); + IDirectDraw4_AddRef(This); + impl->ifaceToRelease = (IUnknown *) This; } return hr; } diff --git a/dlls/ddraw/direct3d.c b/dlls/ddraw/direct3d.c index 679dd2fb8d..6e8c61036d 100644 --- a/dlls/ddraw/direct3d.c +++ b/dlls/ddraw/direct3d.c @@ -43,10 +43,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(d3d7); /***************************************************************************** - * IUnknown Methods. Common for Version 1, 2, 3 and 7 + * IDirect3D7::QueryInterface * - * These are thunks which relay to IDirectDraw. See ddraw.c for - * details + * QueryInterface implementation with thunks to IDirectDraw7 * *****************************************************************************/ static HRESULT WINAPI @@ -101,11 +100,30 @@ Thunk_IDirect3DImpl_1_QueryInterface(IDirect3D *iface, obj); } +/***************************************************************************** + * IDirect3D7::AddRef + * + * DirectDraw refcounting is a bit odd. Every version of the ddraw interface + * has its own refcount, but IDirect3D 1/2/3 refcounts are linked to + * IDirectDraw, and IDirect3D7 is linked to IDirectDraw7 + * + * IDirect3D7 -> IDirectDraw7 + * IDirect3D3 -> IDirectDraw + * IDirect3D2 -> IDirectDraw + * IDirect3D -> IDirectDraw + * + * So every AddRef implementation thunks to a different interface, and the + * IDirectDrawX::AddRef implementations have different counters... + * + * Returns + * The new refcount + * + *****************************************************************************/ static ULONG WINAPI Thunk_IDirect3DImpl_7_AddRef(IDirect3D7 *iface) { ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D7, iface); - TRACE("(%p) : Thunking to IDirectDraw7\n", This); + TRACE("(%p) : Thunking to IDirectDraw7.\n", This); return IDirectDraw7_AddRef(ICOM_INTERFACE(This, IDirectDraw7)); } @@ -114,34 +132,42 @@ static ULONG WINAPI Thunk_IDirect3DImpl_3_AddRef(IDirect3D3 *iface) { ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D3, iface); - TRACE("(%p) : Thunking to IDirectDraw7\n", This); + TRACE("(%p) : Thunking to IDirectDraw.\n", This); - return IDirectDraw7_AddRef(ICOM_INTERFACE(This, IDirectDraw7)); + return IDirectDraw_AddRef(ICOM_INTERFACE(This, IDirectDraw)); } static ULONG WINAPI Thunk_IDirect3DImpl_2_AddRef(IDirect3D2 *iface) { ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D2, iface); - TRACE("(%p) : Thunking to IDirectDraw7\n", This); + TRACE("(%p) : Thunking to IDirectDraw.\n", This); - return IDirectDraw7_AddRef(ICOM_INTERFACE(This, IDirectDraw7)); + return IDirectDraw_AddRef(ICOM_INTERFACE(This, IDirectDraw)); } static ULONG WINAPI Thunk_IDirect3DImpl_1_AddRef(IDirect3D *iface) { ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D, iface); - TRACE("(%p) : Thunking to IDirectDraw7\n", This); + TRACE("(%p) : Thunking to IDirectDraw.\n", This); - return IDirectDraw7_AddRef(ICOM_INTERFACE(This, IDirectDraw7)); + return IDirectDraw_AddRef(ICOM_INTERFACE(This, IDirectDraw)); } +/***************************************************************************** + * IDirect3D7::Release + * + * Same story as IDirect3D7::AddRef + * + * Returns: The new refcount + * + *****************************************************************************/ static ULONG WINAPI Thunk_IDirect3DImpl_7_Release(IDirect3D7 *iface) { ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D7, iface); - TRACE("(%p) : Thunking to IDirectDraw7", This); + TRACE("(%p) : Thunking to IDirectDraw7.\n", This); return IDirectDraw7_Release(ICOM_INTERFACE(This, IDirectDraw7)); } @@ -150,27 +176,27 @@ static ULONG WINAPI Thunk_IDirect3DImpl_3_Release(IDirect3D3 *iface) { ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D3, iface); - TRACE("(%p) : Thunking to IDirectDraw7", This); + TRACE("(%p) : Thunking to IDirectDraw.\n", This); - return IDirectDraw7_Release(ICOM_INTERFACE(This, IDirectDraw7)); + return IDirectDraw_Release(ICOM_INTERFACE(This, IDirectDraw)); } static ULONG WINAPI Thunk_IDirect3DImpl_2_Release(IDirect3D2 *iface) { ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D2, iface); - TRACE("(%p) : Thunking to IDirectDraw7", This); + TRACE("(%p) : Thunking to IDirectDraw.\n", This); - return IDirectDraw7_Release(ICOM_INTERFACE(This, IDirectDraw7)); + return IDirectDraw_Release(ICOM_INTERFACE(This, IDirectDraw)); } static ULONG WINAPI Thunk_IDirect3DImpl_1_Release(IDirect3D *iface) { ICOM_THIS_FROM(IDirectDrawImpl, IDirect3D, iface); - TRACE("(%p) : Thunking to IDirectDraw7", This); + TRACE("(%p) : Thunking to IDirectDraw.\n", This); - return IDirectDraw7_Release(ICOM_INTERFACE(This, IDirectDraw7)); + return IDirectDraw_Release(ICOM_INTERFACE(This, IDirectDraw)); } /***************************************************************************** diff --git a/dlls/ddraw/main.c b/dlls/ddraw/main.c index ca1ff82ac3..a9bec78841 100644 --- a/dlls/ddraw/main.c +++ b/dlls/ddraw/main.c @@ -135,7 +135,6 @@ DDRAW_Create(GUID *guid, ICOM_INIT_INTERFACE(This, IDirect3D2, IDirect3D2_Vtbl); ICOM_INIT_INTERFACE(This, IDirect3D3, IDirect3D3_Vtbl); ICOM_INIT_INTERFACE(This, IDirect3D7, IDirect3D7_Vtbl); - This->ref = 1; /* See comments in IDirectDrawImpl_CreateNewSurface for a description * of this member. @@ -285,9 +284,10 @@ DDRAW_Create(GUID *guid, This->next = ddraw_list; ddraw_list = This; - /* Call QueryInterface to get the pointer to the requested interface */ + /* Call QueryInterface to get the pointer to the requested interface. This also initializes + * The required refcount + */ hr = IDirectDraw7_QueryInterface( ICOM_INTERFACE(This, IDirectDraw7), iid, DD); - IDirectDraw7_Release( ICOM_INTERFACE(This, IDirectDraw7) ); if(SUCCEEDED(hr)) return DD_OK; err_out: @@ -843,9 +843,13 @@ DllMain(HINSTANCE hInstDLL, DDSURFACEDESC2 desc; int i; - WARN("DDraw %p has a refcount of %ld\n", ddraw, ddraw->ref); + WARN("DDraw %p has a refcount of %ld\n", ddraw, ddraw->ref7 + ddraw->ref4 + ddraw->ref2 + ddraw->ref1); - ddraw->DoNotDestroy = TRUE; /* Avoid to destroy the object too early */ + /* Add references to each interface to avoid freeing them unexpectadely */ + IDirectDraw_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw)); + IDirectDraw2_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw2)); + IDirectDraw4_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw4)); + IDirectDraw7_AddRef(ICOM_INTERFACE(ddraw, IDirectDraw7)); /* Does a D3D device exist? Destroy it * TODO: Destroy all Vertex buffers, Lights, Materials @@ -877,12 +881,13 @@ DllMain(HINSTANCE hInstDLL, if(ddraw->surfaces > 0) ERR("DDraw %p still has %ld surfaces attached\n", ddraw, ddraw->surfaces); - /* Restore the cooperative level */ - IDirectDraw7_SetCooperativeLevel(ICOM_INTERFACE(ddraw, IDirectDraw7), - NULL, - DDSCL_NORMAL); - ddraw->DoNotDestroy = FALSE; - IDirectDrawImpl_Destroy(ddraw); + /* Release all hanging references to destroy the objects. This + * restores the screen mode too + */ + while(IDirectDraw_Release(ICOM_INTERFACE(ddraw, IDirectDraw))); + while(IDirectDraw2_Release(ICOM_INTERFACE(ddraw, IDirectDraw2))); + while(IDirectDraw4_Release(ICOM_INTERFACE(ddraw, IDirectDraw4))); + while(IDirectDraw7_Release(ICOM_INTERFACE(ddraw, IDirectDraw7))); } } } diff --git a/dlls/ddraw/surface.c b/dlls/ddraw/surface.c index 72e0eddf08..a9086d2f4f 100644 --- a/dlls/ddraw/surface.c +++ b/dlls/ddraw/surface.c @@ -291,6 +291,7 @@ IDirectDrawSurfaceImpl_Release(IDirectDrawSurface7 *iface) IDirectDrawSurfaceImpl *surf; IDirectDrawImpl *ddraw; + IUnknown *ifaceToRelease = This->ifaceToRelease; /* Destroy all complex attached surfaces * Therefore, start with the first surface, @@ -385,7 +386,7 @@ IDirectDrawSurfaceImpl_Release(IDirectDrawSurface7 *iface) IDirectDrawSurfaceImpl_Destroy(This); /* Reduce the ddraw refcount */ - IDirectDraw7_Release(ICOM_INTERFACE(ddraw, IDirectDraw7)); + IUnknown_Release(ifaceToRelease); } return ref; @@ -1627,13 +1628,32 @@ IDirectDrawSurfaceImpl_GetDDInterface(IDirectDrawSurface7 *iface, { ICOM_THIS_FROM(IDirectDrawSurfaceImpl, IDirectDrawSurface7, iface); - TRACE("(%p)->(%p)\n",This,DD); + /* It is not quite correct to use the same lpVtable for the different + * IDirectDrawSurface versions because the GetDDInterface return different interfaces + */ + FIXME("(%p)->(%p)\n",This,DD); if(!DD) return DDERR_INVALIDPARAMS; - *((IDirectDraw7 **) DD) = ICOM_INTERFACE(This->ddraw, IDirectDraw7); - IDirectDraw7_AddRef( (IDirectDraw7 *) *DD); + switch(This->version) + { + case 7: + *((IDirectDraw7 **) DD) = ICOM_INTERFACE(This->ddraw, IDirectDraw7); + IDirectDraw7_AddRef(*(IDirectDraw7 **) DD); + break; + + case 4: + *((IDirectDraw4 **) DD) = ICOM_INTERFACE(This->ddraw, IDirectDraw4); + IDirectDraw4_AddRef(*(IDirectDraw4 **) DD); + + case 2: + case 1: + *((IDirectDraw **) DD) = ICOM_INTERFACE(This->ddraw, IDirectDraw); + IDirectDraw_AddRef( *(IDirectDraw **) DD); + break; + + } return DD_OK; } diff --git a/dlls/ddraw/tests/refcount.c b/dlls/ddraw/tests/refcount.c index bcdc535e9a..6e27665b97 100644 --- a/dlls/ddraw/tests/refcount.c +++ b/dlls/ddraw/tests/refcount.c @@ -43,7 +43,7 @@ unsigned long getRefcount(IUnknown *iface) return IUnknown_Release(iface); } -static void test_ddraw(void) +static void test_ddraw_objects(void) { HRESULT hr; unsigned long ref; @@ -135,6 +135,248 @@ static void test_ddraw(void) IDirectDraw7_Release(DDraw); } +static void test_iface_refcnt(void) +{ + HRESULT hr; + IDirectDraw *DDraw1; + IDirectDraw2 *DDraw2; + IDirectDraw4 *DDraw4; + IDirectDraw7 *DDraw7; + IDirect3D7 *D3D7; + IDirect3D3 *D3D3; + IDirect3D2 *D3D2; + IDirect3D *D3D1; + long ref; + + hr = pDirectDrawCreateEx(NULL, (void **) &DDraw7, &IID_IDirectDraw7, NULL); + ok(hr == DD_OK || hr==DDERR_NODIRECTDRAWSUPPORT, "DirectDrawCreateEx returned: %lx\n", hr); + if(!DDraw7) + { + trace("Couldn't create DDraw interface, skipping tests\n"); + return; + } + + ref = getRefcount( (IUnknown *) DDraw7); + ok(ref == 1, "Initial IDirectDraw7 reference count is %ld\n", ref); + + hr = IDirectDraw7_QueryInterface(DDraw7, &IID_IDirectDraw4, (void **) &DDraw4); + ok(hr == DD_OK, "IDirectDraw7_QueryInterface returned %08lx\n", hr); + hr = IDirectDraw7_QueryInterface(DDraw7, &IID_IDirectDraw2, (void **) &DDraw2); + ok(hr == DD_OK, "IDirectDraw7_QueryInterf&ace returned %08lx\n", hr); + hr = IDirectDraw7_QueryInterface(DDraw7, &IID_IDirectDraw, (void **) &DDraw1); + ok(hr == DD_OK, "IDirectDraw7_QueryInterface returned %08lx\n", hr); + + /* All interfaces now have refcount 1! */ + ref = getRefcount( (IUnknown *) DDraw7); + ok(ref == 1, "IDirectDraw7 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw7); + ok(ref == 1, "IDirectDraw7 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw4); + ok(ref == 1, "IDirectDraw4 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw2); + ok(ref == 1, "IDirectDraw2 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw1); + ok(ref == 1, "IDirectDraw reference count is %ld\n", ref); + + hr = IDirectDraw7_QueryInterface(DDraw7, &IID_IDirect3D7, (void **) &D3D7); + ok(hr == DD_OK, "IDirectDraw7_QueryInterface returned %08lx\n", hr); + + /* Apparently IDirectDrawX and IDirect3DX are linked together */ + ref = getRefcount( (IUnknown *) D3D7); + ok(ref == 2, "IDirect3D7 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw7); + ok(ref == 2, "IDirectDraw7 reference count is %ld\n", ref); + + IDirectDraw7_AddRef(DDraw7); + ref = getRefcount( (IUnknown *) D3D7); + ok(ref == 3, "IDirect3D7 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw7); + ok(ref == 3, "IDirectDraw7 reference count is %ld\n", ref); + + IDirect3D7_Release(D3D7); + ref = getRefcount( (IUnknown *) D3D7); + ok(ref == 2, "IDirect3D7 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw7); + ok(ref == 2, "IDirectDraw7 reference count is %ld\n", ref); + + /* Can't get older d3d interfaces. WHY????? */ + hr = IDirectDraw7_QueryInterface(DDraw4, &IID_IDirect3D3, (void **) &D3D3); + todo_wine ok(hr == E_NOINTERFACE, "IDirectDraw7_QueryInterface returned %08lx\n", hr); + if(hr == DD_OK && D3D3) IDirect3D3_Release(D3D3); + + hr = IDirectDraw4_QueryInterface(DDraw4, &IID_IDirect3D3, (void **) &D3D3); + todo_wine ok(hr == E_NOINTERFACE, "IDirectDraw4_QueryInterface returned %08lx\n", hr); + if(hr == DD_OK && D3D3) IDirect3D3_Release(D3D3); + + hr = IDirectDraw7_QueryInterface(DDraw7, &IID_IDirect3D2, (void **) &D3D2); + todo_wine ok(hr == E_NOINTERFACE, "IDirectDraw7_QueryInterface returned %08lx\n", hr); + if(hr == DD_OK && D3D2) IDirect3D2_Release(D3D2); + + hr = IDirectDraw2_QueryInterface(DDraw2, &IID_IDirect3D2, (void **) &D3D2); + todo_wine ok(hr == E_NOINTERFACE, "IDirectDraw2_QueryInterface returned %08lx\n", hr); + if(hr == DD_OK && D3D2) IDirect3D2_Release(D3D2); + + hr = IDirectDraw7_QueryInterface(DDraw7, &IID_IDirect3D, (void **) &D3D1); + todo_wine ok(hr == E_NOINTERFACE, "IDirectDraw7_QueryInterface returned %08lx\n", hr); + if(hr == DD_OK && D3D1) IDirect3D_Release(D3D1); + + hr = IDirectDraw_QueryInterface(DDraw1, &IID_IDirect3D, (void **) &D3D1); + todo_wine ok(hr == E_NOINTERFACE, "IDirectDraw_QueryInterface returned %08lx\n", hr); + if(hr == DD_OK && D3D1) IDirect3D_Release(D3D1); + + hr = IDirect3D7_QueryInterface(D3D7, &IID_IDirect3D, (void **) &D3D1); + todo_wine ok(hr == E_NOINTERFACE, "IDirect3D7_QueryInterface returned %08lx\n", hr); + if(hr == DD_OK && D3D1) IDirect3D_Release(D3D1); + + /* Try an AddRef, it only affects the AddRefed interface */ + IDirectDraw4_AddRef(DDraw4); + ref = getRefcount( (IUnknown *) DDraw7); + ok(ref == 2, "IDirectDraw7 reference count is %ld\n", ref); /* <-- From the d3d query */ + ref = getRefcount( (IUnknown *) DDraw4); + ok(ref == 2, "IDirectDraw4 reference count is %ld\n", ref); /* <-- The AddRef call */ + ref = getRefcount( (IUnknown *) DDraw2); + ok(ref == 1, "IDirectDraw2 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw1); + ok(ref == 1, "IDirectDraw reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) D3D7); + ok(ref == 2, "IDirect3D7 reference count is %ld\n", ref); /* <-- From the d3d query */ + IDirectDraw4_Release(DDraw4); + + /* Make sure that they are one object, not different ones */ + hr = IDirectDraw4_SetCooperativeLevel(DDraw4, GetDesktopWindow(), DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN); + ok(hr == DD_OK, "IDirectDraw4::SetCooperativeLevel returned %08lx\n", hr); + /* After an window has been set, DDSCL_SETFOCUSWINDOW should return DDERR_HWNDALREADYSET, see the mode test */ + hr = IDirectDraw7_SetCooperativeLevel(DDraw7, NULL, DDSCL_SETFOCUSWINDOW); + ok(hr == DDERR_HWNDALREADYSET, "IDirectDraw7::SetCooperativeLevel returned %08lx\n", hr); + + /* All done, release all interfaces */ + IDirectDraw7_Release(DDraw7); + IDirectDraw4_Release(DDraw4); + IDirectDraw2_Release(DDraw2); + IDirectDraw_Release(DDraw1); + IDirect3D7_Release(D3D7); +} + +static void test_d3d_ifaces(void) +{ + IDirectDraw *DDraw1; + IDirectDraw2 *DDraw2; + IDirectDraw4 *DDraw4; + IDirect3D *D3D1; + IDirect3D2 *D3D2; + IDirect3D3 *D3D3; + IDirect3D7 *D3D7; + HRESULT hr; + long ref; + + hr = DirectDrawCreate(NULL, &DDraw1, NULL); + ok(hr == DD_OK, "DirectDrawCreate returned %08lx\n", hr); + if(!DDraw1) + { + trace("DirectDrawCreate failed with %08lx\n", hr); + return; + } + + hr = IDirectDraw_QueryInterface(DDraw1, &IID_IDirectDraw2, (void **) &DDraw2); + ok(hr == DD_OK, "IDirectDraw_QueryInterface returned %08lx\n", hr); + hr = IDirectDraw_QueryInterface(DDraw1, &IID_IDirectDraw4, (void **) &DDraw4); + ok(hr == DD_OK, "IDirectDraw_QueryInterface returned %08lx\n", hr); + + ref = getRefcount( (IUnknown *) DDraw4); + ok(ref == 1, "IDirectDraw4 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw2); + ok(ref == 1, "IDirectDraw2 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw1); + ok(ref == 1, "IDirectDraw reference count is %ld\n", ref); + + hr = IDirectDraw_QueryInterface(DDraw1, &IID_IDirect3D, (void **) &D3D1); + ok(hr == DD_OK, "IDirectDraw_QueryInterface returned %08lx\n", hr); + ref = getRefcount( (IUnknown *) DDraw4); + ok(ref == 1, "IDirectDraw4 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw2); + ok(ref == 1, "IDirectDraw2 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw1); + ok(ref == 2, "IDirectDraw reference count is %ld\n", ref); + IDirect3D_Release(D3D1); + + hr = IDirectDraw2_QueryInterface(DDraw2, &IID_IDirect3D2, (void **) &D3D2); + ok(hr == DD_OK, "IDirectDraw_QueryInterface returned %08lx\n", hr); + ref = getRefcount( (IUnknown *) DDraw4); + ok(ref == 1, "IDirectDraw4 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw2); + ok(ref == 1, "IDirectDraw2 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw1); + ok(ref == 2, "IDirectDraw reference count is %ld\n", ref); + IDirect3D2_Release(D3D2); + + hr = IDirectDraw4_QueryInterface(DDraw4, &IID_IDirect3D3, (void **) &D3D3); + ok(hr == DD_OK, "IDirectDraw_QueryInterface returned %08lx\n", hr); + ref = getRefcount( (IUnknown *) DDraw4); + ok(ref == 1, "IDirectDraw4 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw2); + ok(ref == 1, "IDirectDraw2 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw1); + ok(ref == 2, "IDirectDraw reference count is %ld\n", ref); + IDirect3D3_Release(D3D3); + + /* Try to AddRef the D3D3 interface that has been released already */ + IDirect3D3_AddRef(D3D3); + ref = getRefcount( (IUnknown *) DDraw1); + ok(ref == 2, "IDirectDraw reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) D3D3); + ok(ref == 2, "IDirect3D3 reference count is %ld\n", ref); + /* The newer interfaces remain untouched */ + ref = getRefcount( (IUnknown *) DDraw4); + ok(ref == 1, "IDirectDraw4 reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw2); + ok(ref == 1, "IDirectDraw2 reference count is %ld\n", ref); + IDirect3D3_Release(D3D3); + ref = getRefcount( (IUnknown *) DDraw1); + ok(ref == 1, "IDirectDraw reference count is %ld\n", ref); + ref = getRefcount( (IUnknown *) DDraw1); + ok(ref == 1, "IDirectDraw reference count is %ld\n", ref); + + /* It is possible to query any IDirect3D interfaces from any IDirectDraw interface, + * Except IDirect3D7, it can only be returned by IDirectDraw7(which can't return older ifaces) + */ + hr = IDirectDraw_QueryInterface(DDraw2, &IID_IDirect3D, (void **) &D3D1); + ok(hr == DD_OK, "IDirectDraw2_QueryInterface returned %08lx\n", hr); + IDirect3D_Release(D3D1); + hr = IDirectDraw4_QueryInterface(DDraw4, &IID_IDirect3D, (void **) &D3D1); + ok(hr == DD_OK, "IDirectDraw4_QueryInterface returned %08lx\n", hr); + IDirect3D_Release(D3D1); + + hr = IDirectDraw_QueryInterface(DDraw1, &IID_IDirect3D2, (void **) &D3D2); + ok(hr == DD_OK, "IDirectDraw_QueryInterface returned %08lx\n", hr); + IDirect3D_Release(D3D2); + hr = IDirectDraw4_QueryInterface(DDraw4, &IID_IDirect3D2, (void **) &D3D2); + ok(hr == DD_OK, "IDirectDraw4_QueryInterface returned %08lx\n", hr); + IDirect3D_Release(D3D2); + + hr = IDirectDraw_QueryInterface(DDraw1, &IID_IDirect3D3, (void **) &D3D3); + ok(hr == DD_OK, "IDirectDraw_QueryInterface returned %08lx\n", hr); + IDirect3D_Release(D3D3); + hr = IDirectDraw2_QueryInterface(DDraw2, &IID_IDirect3D3, (void **) &D3D3); + ok(hr == DD_OK, "IDirectDraw2_QueryInterface returned %08lx\n", hr); + IDirect3D_Release(D3D3); + + /* This does NOT work */ + hr = IDirectDraw_QueryInterface(DDraw1, &IID_IDirect3D7, (void **) &D3D7); + todo_wine ok(hr == E_NOINTERFACE, "IDirectDraw_QueryInterface returned %08lx\n", hr); + if(D3D7) IDirect3D_Release(D3D7); + hr = IDirectDraw2_QueryInterface(DDraw2, &IID_IDirect3D7, (void **) &D3D7); + todo_wine ok(hr == E_NOINTERFACE, "IDirectDraw2_QueryInterface returned %08lx\n", hr); + if(D3D7) IDirect3D_Release(D3D7); + hr = IDirectDraw4_QueryInterface(DDraw4, &IID_IDirect3D7, (void **) &D3D7); + todo_wine ok(hr == E_NOINTERFACE, "IDirectDraw4_QueryInterface returned %08lx\n", hr); + if(D3D7) IDirect3D_Release(D3D7); + + /* Release the interfaces */ + IDirectDraw4_Release(DDraw4); + IDirectDraw2_Release(DDraw2); + IDirectDraw_Release(DDraw1); +} + START_TEST(refcount) { init_function_pointers(); @@ -143,5 +385,7 @@ START_TEST(refcount) trace("function DirectDrawCreateEx not available, skipping tests\n"); return; } - test_ddraw(); + test_ddraw_objects(); + test_iface_refcnt(); + test_d3d_ifaces(); }