ddraw/tests: Test invalid clipper objects.
Signed-off-by: Stefan Dösinger <stefan@codeweavers.com> Signed-off-by: Henri Verbeet <hverbeet@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
347efb985c
commit
4968ca3d79
|
@ -12293,6 +12293,156 @@ static void test_alphatest(void)
|
||||||
DestroyWindow(window);
|
DestroyWindow(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_clipper_refcount(void)
|
||||||
|
{
|
||||||
|
IDirectDrawSurface *surface;
|
||||||
|
IDirectDrawClipper *clipper, *clipper2;
|
||||||
|
DDSURFACEDESC surface_desc;
|
||||||
|
IDirectDraw *ddraw;
|
||||||
|
ULONG refcount;
|
||||||
|
HWND window;
|
||||||
|
HRESULT hr;
|
||||||
|
BOOL changed;
|
||||||
|
const IDirectDrawClipperVtbl *orig_vtbl;
|
||||||
|
|
||||||
|
window = create_window();
|
||||||
|
ddraw = create_ddraw();
|
||||||
|
ok(!!ddraw, "Failed to create a ddraw object.\n");
|
||||||
|
hr = IDirectDraw_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
memset(&surface_desc, 0, sizeof(surface_desc));
|
||||||
|
surface_desc.dwSize = sizeof(surface_desc);
|
||||||
|
surface_desc.dwFlags = DDSD_CAPS;
|
||||||
|
surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
|
||||||
|
hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface, NULL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
hr = IDirectDraw_CreateClipper(ddraw, 0, &clipper, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* Show that clipper validation doesn't somehow happen through per-clipper vtable
|
||||||
|
* pointers. */
|
||||||
|
hr = IDirectDraw_CreateClipper(ddraw, 0, &clipper2, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper->lpVtbl == clipper2->lpVtbl, "Got different clipper vtables %p and %p.\n",
|
||||||
|
clipper->lpVtbl, clipper2->lpVtbl);
|
||||||
|
orig_vtbl = clipper->lpVtbl;
|
||||||
|
IDirectDrawClipper_Release(clipper2);
|
||||||
|
|
||||||
|
/* Surfaces hold a reference to clippers. No surprises there. */
|
||||||
|
hr = IDirectDrawSurface_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface_GetClipper(surface, &clipper2);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
|
||||||
|
refcount = IDirectDrawClipper_Release(clipper2);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface_SetClipper(surface, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
refcount = IDirectDrawSurface_Release(surface);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* SetClipper with an invalid pointer crashes. */
|
||||||
|
|
||||||
|
/* Clipper methods work with a broken vtable, with the exception of Release. */
|
||||||
|
clipper->lpVtbl = (void *)0xdeadbeef;
|
||||||
|
refcount = orig_vtbl->AddRef(clipper);
|
||||||
|
todo_wine ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
ok(!refcount, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
clipper->lpVtbl = orig_vtbl;
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
todo_wine ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* Fix the refcount difference because Wine did not increase the ref in the
|
||||||
|
* AddRef call above. */
|
||||||
|
if (refcount)
|
||||||
|
{
|
||||||
|
refcount = IDirectDrawClipper_Release(clipper);
|
||||||
|
ok(!refcount, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Steal the reference and see what happens - releasing the surface works fine.
|
||||||
|
* The clipper is destroyed and not kept alive by a hidden refcount - trying to
|
||||||
|
* release it after the GetClipper call is likely to crash, and certain to crash
|
||||||
|
* if we allocate and zero as much heap memory as we can get. */
|
||||||
|
hr = IDirectDraw_CreateSurface(ddraw, &surface_desc, &surface, NULL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
hr = IDirectDraw_CreateClipper(ddraw, 0, &clipper, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
hr = IDirectDrawSurface_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
|
||||||
|
IDirectDrawClipper_Release(clipper);
|
||||||
|
IDirectDrawClipper_Release(clipper);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface_GetClipper(surface, &clipper2);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
|
||||||
|
|
||||||
|
/* Show that invoking the Release method does not crash, but don't get the
|
||||||
|
* vtable through the clipper pointer because it is no longer pointing to
|
||||||
|
* valid memory. */
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
|
||||||
|
refcount = IDirectDrawSurface_Release(surface);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
|
||||||
|
/* It looks like the protection against invalid thispointers is part of
|
||||||
|
* the IDirectDrawClipper method implementation, not IDirectDrawSurface. */
|
||||||
|
clipper = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);
|
||||||
|
ok(!!clipper, "failed to allocate memory\n");
|
||||||
|
|
||||||
|
/* Assigning the vtable to our fake clipper does NOT make a difference on
|
||||||
|
* native - there is a different member of the clipper implementation struct
|
||||||
|
* that is used to determine if a clipper is valid. */
|
||||||
|
clipper->lpVtbl = orig_vtbl;
|
||||||
|
|
||||||
|
refcount = orig_vtbl->AddRef(clipper);
|
||||||
|
todo_wine ok(!refcount, "Got refcount %u.\n", refcount);
|
||||||
|
refcount = orig_vtbl->AddRef((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef);
|
||||||
|
ok(!refcount, "Got refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
changed = 0x1234;
|
||||||
|
hr = orig_vtbl->IsClipListChanged(clipper, &changed);
|
||||||
|
todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
todo_wine ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
|
||||||
|
|
||||||
|
changed = 0x1234;
|
||||||
|
hr = orig_vtbl->IsClipListChanged((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef, &changed);
|
||||||
|
ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
|
||||||
|
|
||||||
|
/* Nope, we can't initialize our fake clipper. */
|
||||||
|
hr = orig_vtbl->Initialize(clipper, ddraw, 0);
|
||||||
|
todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
HeapFree(GetProcessHeap(), 0, clipper);
|
||||||
|
|
||||||
|
refcount = IDirectDraw_Release(ddraw);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
DestroyWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(ddraw1)
|
START_TEST(ddraw1)
|
||||||
{
|
{
|
||||||
DDDEVICEIDENTIFIER identifier;
|
DDDEVICEIDENTIFIER identifier;
|
||||||
|
@ -12401,4 +12551,5 @@ START_TEST(ddraw1)
|
||||||
test_killfocus();
|
test_killfocus();
|
||||||
test_gdi_surface();
|
test_gdi_surface();
|
||||||
test_alphatest();
|
test_alphatest();
|
||||||
|
test_clipper_refcount();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13552,6 +13552,163 @@ static void test_alphatest(void)
|
||||||
DestroyWindow(window);
|
DestroyWindow(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_clipper_refcount(void)
|
||||||
|
{
|
||||||
|
IDirectDrawSurface *surface;
|
||||||
|
IDirectDrawClipper *clipper, *clipper2;
|
||||||
|
DDSURFACEDESC surface_desc;
|
||||||
|
IDirectDraw2 *ddraw;
|
||||||
|
IDirectDraw *ddraw1;
|
||||||
|
ULONG refcount;
|
||||||
|
HWND window;
|
||||||
|
HRESULT hr;
|
||||||
|
BOOL changed;
|
||||||
|
const IDirectDrawClipperVtbl *orig_vtbl;
|
||||||
|
|
||||||
|
window = create_window();
|
||||||
|
ddraw = create_ddraw();
|
||||||
|
ok(!!ddraw, "Failed to create a ddraw object.\n");
|
||||||
|
hr = IDirectDraw2_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
memset(&surface_desc, 0, sizeof(surface_desc));
|
||||||
|
surface_desc.dwSize = sizeof(surface_desc);
|
||||||
|
surface_desc.dwFlags = DDSD_CAPS;
|
||||||
|
surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
|
||||||
|
hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &surface, NULL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
hr = IDirectDraw2_CreateClipper(ddraw, 0, &clipper, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* Show that clipper validation doesn't somehow happen through per-clipper vtable
|
||||||
|
* pointers. */
|
||||||
|
hr = IDirectDraw2_CreateClipper(ddraw, 0, &clipper2, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper->lpVtbl == clipper2->lpVtbl, "Got different clipper vtables %p and %p.\n",
|
||||||
|
clipper->lpVtbl, clipper2->lpVtbl);
|
||||||
|
orig_vtbl = clipper->lpVtbl;
|
||||||
|
IDirectDrawClipper_Release(clipper2);
|
||||||
|
|
||||||
|
/* Surfaces hold a reference to clippers. No surprises there. */
|
||||||
|
hr = IDirectDrawSurface_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface_GetClipper(surface, &clipper2);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
|
||||||
|
refcount = IDirectDrawClipper_Release(clipper2);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface_SetClipper(surface, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
refcount = IDirectDrawSurface_Release(surface);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* SetClipper with an invalid pointer crashes. */
|
||||||
|
|
||||||
|
/* Clipper methods work with a broken vtable, with the exception of Release. */
|
||||||
|
clipper->lpVtbl = (void *)0xdeadbeef;
|
||||||
|
refcount = orig_vtbl->AddRef(clipper);
|
||||||
|
todo_wine ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
ok(!refcount, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
clipper->lpVtbl = orig_vtbl;
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
todo_wine ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* Fix the refcount difference because Wine did not increase the ref in the
|
||||||
|
* AddRef call above. */
|
||||||
|
if (refcount)
|
||||||
|
{
|
||||||
|
refcount = IDirectDrawClipper_Release(clipper);
|
||||||
|
ok(!refcount, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Steal the reference and see what happens - releasing the surface works fine.
|
||||||
|
* The clipper is destroyed and not kept alive by a hidden refcount - trying to
|
||||||
|
* release it after the GetClipper call is likely to crash, and certain to crash
|
||||||
|
* if we allocate and zero as much heap memory as we can get. */
|
||||||
|
hr = IDirectDraw2_CreateSurface(ddraw, &surface_desc, &surface, NULL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
hr = IDirectDraw2_CreateClipper(ddraw, 0, &clipper, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
hr = IDirectDrawSurface_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
|
||||||
|
IDirectDrawClipper_Release(clipper);
|
||||||
|
IDirectDrawClipper_Release(clipper);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface_GetClipper(surface, &clipper2);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
|
||||||
|
|
||||||
|
/* Show that invoking the Release method does not crash, but don't get the
|
||||||
|
* vtable through the clipper pointer because it is no longer pointing to
|
||||||
|
* valid memory. */
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
|
||||||
|
refcount = IDirectDrawSurface_Release(surface);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
|
||||||
|
/* It looks like the protection against invalid thispointers is part of
|
||||||
|
* the IDirectDrawClipper method implementation, not IDirectDrawSurface. */
|
||||||
|
clipper = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);
|
||||||
|
ok(!!clipper, "failed to allocate memory\n");
|
||||||
|
|
||||||
|
/* Assigning the vtable to our fake clipper does NOT make a difference on
|
||||||
|
* native - there is a different member of the clipper implementation struct
|
||||||
|
* that is used to determine if a clipper is valid. */
|
||||||
|
clipper->lpVtbl = orig_vtbl;
|
||||||
|
|
||||||
|
refcount = orig_vtbl->AddRef(clipper);
|
||||||
|
todo_wine ok(!refcount, "Got refcount %u.\n", refcount);
|
||||||
|
refcount = orig_vtbl->AddRef((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef);
|
||||||
|
ok(!refcount, "Got refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
changed = 0x1234;
|
||||||
|
hr = orig_vtbl->IsClipListChanged(clipper, &changed);
|
||||||
|
todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
todo_wine ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
|
||||||
|
|
||||||
|
changed = 0x1234;
|
||||||
|
hr = orig_vtbl->IsClipListChanged((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef, &changed);
|
||||||
|
ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
|
||||||
|
|
||||||
|
/* Nope, we can't initialize our fake clipper. */
|
||||||
|
hr = IDirectDraw2_QueryInterface(ddraw, &IID_IDirectDraw, (void **)&ddraw1);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get ddraw1 interface, hr %#x.\n", hr);
|
||||||
|
|
||||||
|
hr = orig_vtbl->Initialize(clipper, ddraw1, 0);
|
||||||
|
todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
IDirectDraw_Release(ddraw1);
|
||||||
|
|
||||||
|
HeapFree(GetProcessHeap(), 0, clipper);
|
||||||
|
|
||||||
|
refcount = IDirectDraw2_Release(ddraw);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
DestroyWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
START_TEST(ddraw2)
|
START_TEST(ddraw2)
|
||||||
{
|
{
|
||||||
DDDEVICEIDENTIFIER identifier;
|
DDDEVICEIDENTIFIER identifier;
|
||||||
|
@ -13668,4 +13825,5 @@ START_TEST(ddraw2)
|
||||||
test_killfocus();
|
test_killfocus();
|
||||||
test_gdi_surface();
|
test_gdi_surface();
|
||||||
test_alphatest();
|
test_alphatest();
|
||||||
|
test_clipper_refcount();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15798,6 +15798,162 @@ static void test_alphatest(void)
|
||||||
DestroyWindow(window);
|
DestroyWindow(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_clipper_refcount(void)
|
||||||
|
{
|
||||||
|
IDirectDrawSurface4 *surface;
|
||||||
|
IDirectDrawClipper *clipper, *clipper2;
|
||||||
|
DDSURFACEDESC2 surface_desc;
|
||||||
|
IDirectDraw4 *ddraw;
|
||||||
|
IDirectDraw *ddraw1;
|
||||||
|
ULONG refcount;
|
||||||
|
HWND window;
|
||||||
|
HRESULT hr;
|
||||||
|
BOOL changed;
|
||||||
|
const IDirectDrawClipperVtbl *orig_vtbl;
|
||||||
|
|
||||||
|
window = create_window();
|
||||||
|
ddraw = create_ddraw();
|
||||||
|
ok(!!ddraw, "Failed to create a ddraw object.\n");
|
||||||
|
hr = IDirectDraw4_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
memset(&surface_desc, 0, sizeof(surface_desc));
|
||||||
|
surface_desc.dwSize = sizeof(surface_desc);
|
||||||
|
surface_desc.dwFlags = DDSD_CAPS;
|
||||||
|
surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
|
||||||
|
hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &surface, NULL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
hr = IDirectDraw4_CreateClipper(ddraw, 0, &clipper, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* Show that clipper validation doesn't somehow happen through per-clipper vtable
|
||||||
|
* pointers. */
|
||||||
|
hr = IDirectDraw4_CreateClipper(ddraw, 0, &clipper2, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper->lpVtbl == clipper2->lpVtbl, "Got different clipper vtables %p and %p.\n",
|
||||||
|
clipper->lpVtbl, clipper2->lpVtbl);
|
||||||
|
orig_vtbl = clipper->lpVtbl;
|
||||||
|
IDirectDrawClipper_Release(clipper2);
|
||||||
|
|
||||||
|
/* Surfaces hold a reference to clippers. No surprises there. */
|
||||||
|
hr = IDirectDrawSurface4_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface4_GetClipper(surface, &clipper2);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
|
||||||
|
refcount = IDirectDrawClipper_Release(clipper2);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface4_SetClipper(surface, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface4_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
refcount = IDirectDrawSurface4_Release(surface);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* SetClipper with an invalid pointer crashes. */
|
||||||
|
|
||||||
|
/* Clipper methods work with a broken vtable, with the exception of Release. */
|
||||||
|
clipper->lpVtbl = (void *)0xdeadbeef;
|
||||||
|
refcount = orig_vtbl->AddRef(clipper);
|
||||||
|
todo_wine ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
ok(!refcount, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
clipper->lpVtbl = orig_vtbl;
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
todo_wine ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* Fix the refcount difference because Wine did not increase the ref in the
|
||||||
|
* AddRef call above. */
|
||||||
|
if (refcount)
|
||||||
|
{
|
||||||
|
refcount = IDirectDrawClipper_Release(clipper);
|
||||||
|
ok(!refcount, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Steal the reference and see what happens - releasing the surface works fine.
|
||||||
|
* The clipper is destroyed and not kept alive by a hidden refcount - trying to
|
||||||
|
* release it after the GetClipper call is likely to crash, and certain to crash
|
||||||
|
* if we allocate and zero as much heap memory as we can get. */
|
||||||
|
hr = IDirectDraw4_CreateSurface(ddraw, &surface_desc, &surface, NULL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
hr = IDirectDraw4_CreateClipper(ddraw, 0, &clipper, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
hr = IDirectDrawSurface4_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
|
||||||
|
IDirectDrawClipper_Release(clipper);
|
||||||
|
IDirectDrawClipper_Release(clipper);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface4_GetClipper(surface, &clipper2);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
|
||||||
|
|
||||||
|
/* Show that invoking the Release method does not crash, but don't get the
|
||||||
|
* vtable through the clipper pointer because it is no longer pointing to
|
||||||
|
* valid memory. */
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
|
||||||
|
refcount = IDirectDrawSurface4_Release(surface);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
|
||||||
|
/* It looks like the protection against invalid thispointers is part of
|
||||||
|
* the IDirectDrawClipper method implementation, not IDirectDrawSurface. */
|
||||||
|
clipper = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);
|
||||||
|
ok(!!clipper, "failed to allocate memory\n");
|
||||||
|
|
||||||
|
/* Assigning the vtable to our fake clipper does NOT make a difference on
|
||||||
|
* native - there is a different member of the clipper implementation struct
|
||||||
|
* that is used to determine if a clipper is valid. */
|
||||||
|
clipper->lpVtbl = orig_vtbl;
|
||||||
|
|
||||||
|
refcount = orig_vtbl->AddRef(clipper);
|
||||||
|
todo_wine ok(!refcount, "Got refcount %u.\n", refcount);
|
||||||
|
refcount = orig_vtbl->AddRef((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef);
|
||||||
|
ok(!refcount, "Got refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
changed = 0x1234;
|
||||||
|
hr = orig_vtbl->IsClipListChanged(clipper, &changed);
|
||||||
|
todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
todo_wine ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
|
||||||
|
|
||||||
|
changed = 0x1234;
|
||||||
|
hr = orig_vtbl->IsClipListChanged((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef, &changed);
|
||||||
|
ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
|
||||||
|
|
||||||
|
/* Nope, we can't initialize our fake clipper. */
|
||||||
|
hr = IDirectDraw4_QueryInterface(ddraw, &IID_IDirectDraw, (void **)&ddraw1);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get ddraw1 interface, hr %#x.\n", hr);
|
||||||
|
|
||||||
|
hr = orig_vtbl->Initialize(clipper, ddraw1, 0);
|
||||||
|
todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
IDirectDraw_Release(ddraw1);
|
||||||
|
|
||||||
|
HeapFree(GetProcessHeap(), 0, clipper);
|
||||||
|
|
||||||
|
refcount = IDirectDraw4_Release(ddraw);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
DestroyWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(ddraw4)
|
START_TEST(ddraw4)
|
||||||
{
|
{
|
||||||
DDDEVICEIDENTIFIER identifier;
|
DDDEVICEIDENTIFIER identifier;
|
||||||
|
@ -15929,4 +16085,5 @@ START_TEST(ddraw4)
|
||||||
test_sysmem_draw();
|
test_sysmem_draw();
|
||||||
test_gdi_surface();
|
test_gdi_surface();
|
||||||
test_alphatest();
|
test_alphatest();
|
||||||
|
test_clipper_refcount();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15599,6 +15599,162 @@ static void test_alphatest(void)
|
||||||
DestroyWindow(window);
|
DestroyWindow(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_clipper_refcount(void)
|
||||||
|
{
|
||||||
|
IDirectDrawSurface7 *surface;
|
||||||
|
IDirectDrawClipper *clipper, *clipper2;
|
||||||
|
DDSURFACEDESC2 surface_desc;
|
||||||
|
IDirectDraw7 *ddraw;
|
||||||
|
IDirectDraw *ddraw1;
|
||||||
|
ULONG refcount;
|
||||||
|
HWND window;
|
||||||
|
HRESULT hr;
|
||||||
|
BOOL changed;
|
||||||
|
const IDirectDrawClipperVtbl *orig_vtbl;
|
||||||
|
|
||||||
|
window = create_window();
|
||||||
|
ddraw = create_ddraw();
|
||||||
|
ok(!!ddraw, "Failed to create a ddraw object.\n");
|
||||||
|
hr = IDirectDraw7_SetCooperativeLevel(ddraw, window, DDSCL_NORMAL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
memset(&surface_desc, 0, sizeof(surface_desc));
|
||||||
|
surface_desc.dwSize = sizeof(surface_desc);
|
||||||
|
surface_desc.dwFlags = DDSD_CAPS;
|
||||||
|
surface_desc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
|
||||||
|
hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &surface, NULL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
hr = IDirectDraw7_CreateClipper(ddraw, 0, &clipper, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* Show that clipper validation doesn't somehow happen through per-clipper vtable
|
||||||
|
* pointers. */
|
||||||
|
hr = IDirectDraw7_CreateClipper(ddraw, 0, &clipper2, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper->lpVtbl == clipper2->lpVtbl, "Got different clipper vtables %p and %p.\n",
|
||||||
|
clipper->lpVtbl, clipper2->lpVtbl);
|
||||||
|
orig_vtbl = clipper->lpVtbl;
|
||||||
|
IDirectDrawClipper_Release(clipper2);
|
||||||
|
|
||||||
|
/* Surfaces hold a reference to clippers. No surprises there. */
|
||||||
|
hr = IDirectDrawSurface7_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface7_GetClipper(surface, &clipper2);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
|
||||||
|
refcount = IDirectDrawClipper_Release(clipper2);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface7_SetClipper(surface, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface7_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
refcount = IDirectDrawSurface7_Release(surface);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
refcount = get_refcount((IUnknown *)clipper);
|
||||||
|
ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* SetClipper with an invalid pointer crashes. */
|
||||||
|
|
||||||
|
/* Clipper methods work with a broken vtable, with the exception of Release. */
|
||||||
|
clipper->lpVtbl = (void *)0xdeadbeef;
|
||||||
|
refcount = orig_vtbl->AddRef(clipper);
|
||||||
|
todo_wine ok(refcount == 2, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
ok(!refcount, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
clipper->lpVtbl = orig_vtbl;
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
todo_wine ok(refcount == 1, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
/* Fix the refcount difference because Wine did not increase the ref in the
|
||||||
|
* AddRef call above. */
|
||||||
|
if (refcount)
|
||||||
|
{
|
||||||
|
refcount = IDirectDrawClipper_Release(clipper);
|
||||||
|
ok(!refcount, "Got unexpected refcount %u.\n", refcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Steal the reference and see what happens - releasing the surface works fine.
|
||||||
|
* The clipper is destroyed and not kept alive by a hidden refcount - trying to
|
||||||
|
* release it after the GetClipper call is likely to crash, and certain to crash
|
||||||
|
* if we allocate and zero as much heap memory as we can get. */
|
||||||
|
hr = IDirectDraw7_CreateSurface(ddraw, &surface_desc, &surface, NULL);
|
||||||
|
ok(hr == DD_OK, "Got unexpected hr %#x.\n", hr);
|
||||||
|
hr = IDirectDraw7_CreateClipper(ddraw, 0, &clipper, NULL);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to create clipper, hr %#x.\n", hr);
|
||||||
|
hr = IDirectDrawSurface7_SetClipper(surface, clipper);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to set clipper, hr %#x.\n", hr);
|
||||||
|
|
||||||
|
IDirectDrawClipper_Release(clipper);
|
||||||
|
IDirectDrawClipper_Release(clipper);
|
||||||
|
|
||||||
|
hr = IDirectDrawSurface7_GetClipper(surface, &clipper2);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get clipper, hr %#x.\n", hr);
|
||||||
|
ok(clipper == clipper2, "Got clipper %p, expected %p.\n", clipper2, clipper);
|
||||||
|
|
||||||
|
/* Show that invoking the Release method does not crash, but don't get the
|
||||||
|
* vtable through the clipper pointer because it is no longer pointing to
|
||||||
|
* valid memory. */
|
||||||
|
refcount = orig_vtbl->Release(clipper);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
|
||||||
|
refcount = IDirectDrawSurface7_Release(surface);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
|
||||||
|
/* It looks like the protection against invalid thispointers is part of
|
||||||
|
* the IDirectDrawClipper method implementation, not IDirectDrawSurface. */
|
||||||
|
clipper = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 0x1000);
|
||||||
|
ok(!!clipper, "failed to allocate memory\n");
|
||||||
|
|
||||||
|
/* Assigning the vtable to our fake clipper does NOT make a difference on
|
||||||
|
* native - there is a different member of the clipper implementation struct
|
||||||
|
* that is used to determine if a clipper is valid. */
|
||||||
|
clipper->lpVtbl = orig_vtbl;
|
||||||
|
|
||||||
|
refcount = orig_vtbl->AddRef(clipper);
|
||||||
|
todo_wine ok(!refcount, "Got refcount %u.\n", refcount);
|
||||||
|
refcount = orig_vtbl->AddRef((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef);
|
||||||
|
ok(!refcount, "Got refcount %u.\n", refcount);
|
||||||
|
|
||||||
|
changed = 0x1234;
|
||||||
|
hr = orig_vtbl->IsClipListChanged(clipper, &changed);
|
||||||
|
todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
todo_wine ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
|
||||||
|
|
||||||
|
changed = 0x1234;
|
||||||
|
hr = orig_vtbl->IsClipListChanged((IDirectDrawClipper *)(ULONG_PTR)0xdeadbeef, &changed);
|
||||||
|
ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
ok(changed == 0x1234, "'changed' changed: %x.\n", changed);
|
||||||
|
|
||||||
|
/* Nope, we can't initialize our fake clipper. */
|
||||||
|
hr = IDirectDraw7_QueryInterface(ddraw, &IID_IDirectDraw, (void **)&ddraw1);
|
||||||
|
ok(SUCCEEDED(hr), "Failed to get ddraw1 interface, hr %#x.\n", hr);
|
||||||
|
|
||||||
|
hr = orig_vtbl->Initialize(clipper, ddraw1, 0);
|
||||||
|
todo_wine ok(hr == DDERR_INVALIDPARAMS, "Got unexpected hr %#x.\n", hr);
|
||||||
|
|
||||||
|
IDirectDraw_Release(ddraw1);
|
||||||
|
|
||||||
|
HeapFree(GetProcessHeap(), 0, clipper);
|
||||||
|
|
||||||
|
refcount = IDirectDraw7_Release(ddraw);
|
||||||
|
ok(!refcount, "%u references left.\n", refcount);
|
||||||
|
DestroyWindow(window);
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(ddraw7)
|
START_TEST(ddraw7)
|
||||||
{
|
{
|
||||||
DDDEVICEIDENTIFIER2 identifier;
|
DDDEVICEIDENTIFIER2 identifier;
|
||||||
|
@ -15742,4 +15898,5 @@ START_TEST(ddraw7)
|
||||||
test_gdi_surface();
|
test_gdi_surface();
|
||||||
test_multiply_transform();
|
test_multiply_transform();
|
||||||
test_alphatest();
|
test_alphatest();
|
||||||
|
test_clipper_refcount();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue