2010-04-19 00:45:17 +02:00
|
|
|
/* 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
|
2011-01-06 22:22:09 +01:00
|
|
|
#define CONST_VTABLE
|
|
|
|
|
2010-04-19 00:45:17 +02:00
|
|
|
#include "windef.h"
|
|
|
|
#include "winbase.h"
|
|
|
|
#include "winerror.h"
|
|
|
|
#include "ole2.h"
|
|
|
|
#include "shlwapi.h"
|
|
|
|
|
|
|
|
#include "wine/test.h"
|
|
|
|
|
2010-04-19 23:26:49 +02:00
|
|
|
static HRESULT (WINAPI *pSHCreateThreadRef)(LONG*, IUnknown**);
|
2010-04-19 00:45:17 +02:00
|
|
|
static HRESULT (WINAPI *pSHGetThreadRef)(IUnknown**);
|
2010-04-19 00:45:18 +02:00
|
|
|
static HRESULT (WINAPI *pSHSetThreadRef)(IUnknown*);
|
|
|
|
|
|
|
|
static DWORD AddRef_called;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
2011-01-06 22:22:09 +01:00
|
|
|
IUnknown IUnknown_iface;
|
2010-04-19 00:45:18 +02:00
|
|
|
LONG *ref;
|
|
|
|
} threadref;
|
|
|
|
|
2011-01-06 22:22:09 +01:00
|
|
|
static inline threadref *impl_from_IUnknown(IUnknown *iface)
|
|
|
|
{
|
|
|
|
return CONTAINING_RECORD(iface, threadref, IUnknown_iface);
|
|
|
|
}
|
|
|
|
|
2010-04-19 23:26:49 +02:00
|
|
|
static HRESULT WINAPI threadref_QueryInterface(IUnknown *iface, REFIID riid, LPVOID *ppvObj)
|
2010-04-19 00:45:18 +02:00
|
|
|
{
|
2011-01-06 22:22:09 +01:00
|
|
|
threadref * This = impl_from_IUnknown(iface);
|
2010-04-19 23:26:49 +02:00
|
|
|
|
2016-07-20 10:30:56 +02:00
|
|
|
trace("unexpected QueryInterface(%p, %s, %p) called\n", This, wine_dbgstr_guid(riid), ppvObj);
|
2010-04-19 00:45:18 +02:00
|
|
|
*ppvObj = NULL;
|
|
|
|
return E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
|
2010-04-19 23:26:49 +02:00
|
|
|
static ULONG WINAPI threadref_AddRef(IUnknown *iface)
|
2010-04-19 00:45:18 +02:00
|
|
|
{
|
2011-01-06 22:22:09 +01:00
|
|
|
threadref * This = impl_from_IUnknown(iface);
|
2010-04-19 23:26:49 +02:00
|
|
|
|
2010-04-19 00:45:18 +02:00
|
|
|
AddRef_called++;
|
|
|
|
return InterlockedIncrement(This->ref);
|
|
|
|
}
|
|
|
|
|
2010-04-19 23:26:49 +02:00
|
|
|
static ULONG WINAPI threadref_Release(IUnknown *iface)
|
2010-04-19 00:45:18 +02:00
|
|
|
{
|
2011-01-06 22:22:09 +01:00
|
|
|
threadref * This = impl_from_IUnknown(iface);
|
2010-04-19 23:26:49 +02:00
|
|
|
|
2010-04-19 00:45:18 +02:00
|
|
|
trace("unexpected Release(%p) called\n", This);
|
|
|
|
return InterlockedDecrement(This->ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* VTable */
|
2010-04-19 23:26:49 +02:00
|
|
|
static const IUnknownVtbl threadref_vt =
|
2010-04-19 00:45:18 +02:00
|
|
|
{
|
|
|
|
threadref_QueryInterface,
|
|
|
|
threadref_AddRef,
|
|
|
|
threadref_Release
|
|
|
|
};
|
|
|
|
|
|
|
|
static void init_threadref(threadref* iface, LONG *refcount)
|
|
|
|
{
|
2011-01-06 22:22:09 +01:00
|
|
|
iface->IUnknown_iface.lpVtbl = &threadref_vt;
|
2010-04-19 00:45:18 +02:00
|
|
|
iface->ref = refcount;
|
|
|
|
}
|
2010-04-19 00:45:17 +02:00
|
|
|
|
|
|
|
/* ##### */
|
|
|
|
|
2010-04-19 23:26:49 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-04-19 00:45:17 +02:00
|
|
|
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 */
|
2011-02-09 22:44:37 +01:00
|
|
|
pSHGetThreadRef(NULL);
|
2010-04-19 00:45:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-19 00:45:18 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2010-04-19 00:45:19 +02:00
|
|
|
/* 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 */
|
2010-04-19 00:45:18 +02:00
|
|
|
init_threadref(&ref, &refcount);
|
|
|
|
AddRef_called = 0;
|
|
|
|
refcount = 1;
|
2011-01-06 22:22:09 +01:00
|
|
|
hr = pSHSetThreadRef(&ref.IUnknown_iface);
|
2010-04-19 00:45:18 +02:00
|
|
|
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);
|
|
|
|
|
2010-04-19 00:45:19 +02:00
|
|
|
/* read back our object */
|
2010-04-19 00:45:18 +02:00
|
|
|
AddRef_called = 0;
|
|
|
|
refcount = 1;
|
|
|
|
punk = NULL;
|
|
|
|
hr = pSHGetThreadRef(&punk);
|
2011-01-06 22:22:09 +01:00
|
|
|
ok( (hr == S_OK) && (punk == &ref.IUnknown_iface) && (refcount == 2) && (AddRef_called == 1),
|
2010-04-19 00:45:18 +02:00
|
|
|
"got 0x%x and %p with %d, %d (expected S_OK and %p with 2, 1)\n",
|
|
|
|
hr, punk, refcount, AddRef_called, &ref);
|
|
|
|
|
2010-04-19 20:05:37 +02:00
|
|
|
/* clear the object pointer */
|
2010-04-19 00:45:19 +02:00
|
|
|
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);
|
|
|
|
|
2010-04-19 00:45:18 +02:00
|
|
|
}
|
|
|
|
|
2010-04-19 00:45:17 +02:00
|
|
|
START_TEST(thread)
|
|
|
|
{
|
|
|
|
HMODULE hshlwapi = GetModuleHandleA("shlwapi.dll");
|
|
|
|
|
2010-04-19 23:26:49 +02:00
|
|
|
pSHCreateThreadRef = (void *) GetProcAddress(hshlwapi, "SHCreateThreadRef");
|
2010-04-19 00:45:17 +02:00
|
|
|
pSHGetThreadRef = (void *) GetProcAddress(hshlwapi, "SHGetThreadRef");
|
2010-04-19 00:45:18 +02:00
|
|
|
pSHSetThreadRef = (void *) GetProcAddress(hshlwapi, "SHSetThreadRef");
|
2010-04-19 00:45:17 +02:00
|
|
|
|
2010-04-19 23:26:49 +02:00
|
|
|
test_SHCreateThreadRef();
|
2010-04-19 00:45:17 +02:00
|
|
|
test_SHGetThreadRef();
|
2010-04-19 00:45:18 +02:00
|
|
|
test_SHSetThreadRef();
|
2010-04-19 00:45:17 +02:00
|
|
|
|
|
|
|
}
|