/* Tests for Thread and SHGlobalCounter functions * * Copyright 2010 Detlef Riekenberg * * 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 */ #include #include #define COBJMACROS #define CONST_VTABLE #include "windef.h" #include "winbase.h" #include "winerror.h" #include "ole2.h" #include "shlwapi.h" #include "wine/test.h" static HRESULT (WINAPI *pSHCreateThreadRef)(LONG*, IUnknown**); static HRESULT (WINAPI *pSHGetThreadRef)(IUnknown**); static HRESULT (WINAPI *pSHSetThreadRef)(IUnknown*); static DWORD AddRef_called; typedef struct { IUnknown IUnknown_iface; LONG *ref; } threadref; static inline threadref *impl_from_IUnknown(IUnknown *iface) { return CONTAINING_RECORD(iface, threadref, IUnknown_iface); } static HRESULT WINAPI threadref_QueryInterface(IUnknown *iface, REFIID riid, LPVOID *ppvObj) { threadref * This = impl_from_IUnknown(iface); trace("unexpected QueryInterface(%p, %p, %p) called\n", This, riid, ppvObj); *ppvObj = NULL; return E_NOINTERFACE; } static ULONG WINAPI threadref_AddRef(IUnknown *iface) { threadref * This = impl_from_IUnknown(iface); AddRef_called++; return InterlockedIncrement(This->ref); } static ULONG WINAPI threadref_Release(IUnknown *iface) { threadref * This = impl_from_IUnknown(iface); trace("unexpected Release(%p) called\n", This); return InterlockedDecrement(This->ref); } /* VTable */ static const IUnknownVtbl threadref_vt = { threadref_QueryInterface, threadref_AddRef, threadref_Release }; static void init_threadref(threadref* iface, LONG *refcount) { iface->IUnknown_iface.lpVtbl = &threadref_vt; iface->ref = refcount; } /* ##### */ static void test_SHCreateThreadRef(void) { IUnknown *pobj; IUnknown *punk; LONG refcount; HRESULT hr; /* Not present before IE 6_XP_sp2 */ if (!pSHCreateThreadRef) { win_skip("SHCreateThreadRef not found\n"); return; } /* start with a clean state */ hr = pSHSetThreadRef(NULL); ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr); pobj = NULL; refcount = 0xdeadbeef; hr = pSHCreateThreadRef(&refcount, &pobj); ok((hr == S_OK) && pobj && (refcount == 1), "got 0x%x and %p with %d (expected S_OK and '!= NULL' with 1)\n", hr, pobj, refcount); /* the object is not automatic set as ThreadRef */ punk = NULL; hr = pSHGetThreadRef(&punk); ok( (hr == E_NOINTERFACE) && (punk == NULL), "got 0x%x and %p (expected E_NOINTERFACE and NULL)\n", hr, punk); /* set the object */ hr = pSHSetThreadRef(pobj); ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr); /* read back */ punk = NULL; hr = pSHGetThreadRef(&punk); ok( (hr == S_OK) && (punk == pobj) && (refcount == 2), "got 0x%x and %p with %d (expected S_OK and %p with 2)\n", hr, punk, refcount, pobj); /* free the ref from SHGetThreadRef */ if (SUCCEEDED(hr)) { hr = IUnknown_Release(pobj); ok((hr == 1) && (hr == refcount), "got %d with %d (expected 1 with 1)\n", hr, refcount); } /* free the object */ if (pobj) { hr = IUnknown_Release(pobj); ok((hr == 0) && (hr == refcount), "got %d with %d (expected 0 with 0)\n", hr, refcount); } if (0) { /* the ThreadRef has still the pointer, but the object no longer exist after the *_Release */ punk = NULL; hr = pSHGetThreadRef(&punk); trace("got 0x%x and %p with %d\n", hr, punk, refcount); } /* remove the dead object pointer */ hr = pSHSetThreadRef(NULL); ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr); /* parameter check */ if (0) { /* vista: E_INVALIDARG, XP: crash */ pobj = NULL; hr = pSHCreateThreadRef(NULL, &pobj); ok(hr == E_INVALIDARG, "got 0x%x (expected E_INVALIDARG)\n", hr); refcount = 0xdeadbeef; /* vista: E_INVALIDARG, XP: crash */ hr = pSHCreateThreadRef(&refcount, NULL); ok( (hr == E_INVALIDARG) && (refcount == 0xdeadbeef), "got 0x%x with 0x%x (expected E_INVALIDARG and oxdeadbeef)\n", hr, refcount); } } static void test_SHGetThreadRef(void) { IUnknown *punk; HRESULT hr; /* Not present before IE 5 */ if (!pSHGetThreadRef) { win_skip("SHGetThreadRef not found\n"); return; } punk = NULL; hr = pSHGetThreadRef(&punk); ok( (hr == E_NOINTERFACE) && (punk == NULL), "got 0x%x and %p (expected E_NOINTERFACE and NULL)\n", hr, punk); if (0) { /* this crash on Windows */ pSHGetThreadRef(NULL); } } static void test_SHSetThreadRef(void) { threadref ref; IUnknown *punk; HRESULT hr; LONG refcount; /* Not present before IE 5 */ if (!pSHSetThreadRef) { win_skip("SHSetThreadRef not found\n"); return; } /* start with a clean state */ hr = pSHSetThreadRef(NULL); ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr); /* build and set out object */ init_threadref(&ref, &refcount); AddRef_called = 0; refcount = 1; hr = pSHSetThreadRef(&ref.IUnknown_iface); ok( (hr == S_OK) && (refcount == 1) && (!AddRef_called), "got 0x%x with %d, %d (expected S_OK with 1, 0)\n", hr, refcount, AddRef_called); /* read back our object */ AddRef_called = 0; refcount = 1; punk = NULL; hr = pSHGetThreadRef(&punk); ok( (hr == S_OK) && (punk == &ref.IUnknown_iface) && (refcount == 2) && (AddRef_called == 1), "got 0x%x and %p with %d, %d (expected S_OK and %p with 2, 1)\n", hr, punk, refcount, AddRef_called, &ref); /* clear the object pointer */ hr = pSHSetThreadRef(NULL); ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr); /* verify, that our object is no longer known as ThreadRef */ hr = pSHGetThreadRef(&punk); ok( (hr == E_NOINTERFACE) && (punk == NULL), "got 0x%x and %p (expected E_NOINTERFACE and NULL)\n", hr, punk); } START_TEST(thread) { HMODULE hshlwapi = GetModuleHandleA("shlwapi.dll"); pSHCreateThreadRef = (void *) GetProcAddress(hshlwapi, "SHCreateThreadRef"); pSHGetThreadRef = (void *) GetProcAddress(hshlwapi, "SHGetThreadRef"); pSHSetThreadRef = (void *) GetProcAddress(hshlwapi, "SHSetThreadRef"); test_SHCreateThreadRef(); test_SHGetThreadRef(); test_SHSetThreadRef(); }