blob: d4a178723610112e5be0b63562feb5167b02331a [file] [log] [blame]
/* 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, %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();
}