| /* 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 <stdio.h> | |
| #include <stdarg.h> | |
| #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, %s, %p) called\n", This, wine_dbgstr_guid(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(); | |
| } |