1 /* Tests for Thread and SHGlobalCounter functions
2  *
3  * Copyright 2010 Detlef Riekenberg
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19 
20 #include "precomp.h"
21 
22 static HRESULT (WINAPI *pSHCreateThreadRef)(LONG*, IUnknown**);
23 static HRESULT (WINAPI *pSHGetThreadRef)(IUnknown**);
24 static HRESULT (WINAPI *pSHSetThreadRef)(IUnknown*);
25 
26 static DWORD AddRef_called;
27 
28 typedef struct
29 {
30   IUnknown IUnknown_iface;
31   LONG  *ref;
32 } threadref;
33 
34 static inline threadref *impl_from_IUnknown(IUnknown *iface)
35 {
36   return CONTAINING_RECORD(iface, threadref, IUnknown_iface);
37 }
38 
39 static HRESULT WINAPI threadref_QueryInterface(IUnknown *iface, REFIID riid, LPVOID *ppvObj)
40 {
41     threadref * This = impl_from_IUnknown(iface);
42 
43     trace("unexpected QueryInterface(%p, %s, %p) called\n", This, wine_dbgstr_guid(riid), ppvObj);
44     *ppvObj = NULL;
45     return E_NOINTERFACE;
46 }
47 
48 static ULONG WINAPI threadref_AddRef(IUnknown *iface)
49 {
50     threadref * This = impl_from_IUnknown(iface);
51 
52     AddRef_called++;
53     return InterlockedIncrement(This->ref);
54 }
55 
56 static ULONG WINAPI threadref_Release(IUnknown *iface)
57 {
58     threadref * This = impl_from_IUnknown(iface);
59 
60     trace("unexpected Release(%p) called\n", This);
61     return InterlockedDecrement(This->ref);
62 }
63 
64 /* VTable */
65 static const IUnknownVtbl threadref_vt =
66 {
67   threadref_QueryInterface,
68   threadref_AddRef,
69   threadref_Release
70 };
71 
72 static void init_threadref(threadref* iface, LONG *refcount)
73 {
74   iface->IUnknown_iface.lpVtbl = &threadref_vt;
75   iface->ref = refcount;
76 }
77 
78 /* ##### */
79 
80 static void test_SHCreateThreadRef(void)
81 {
82     IUnknown *pobj;
83     IUnknown *punk;
84     LONG refcount;
85     HRESULT hr;
86 
87     /* Not present before IE 6_XP_sp2 */
88     if (!pSHCreateThreadRef) {
89         win_skip("SHCreateThreadRef not found\n");
90         return;
91     }
92 
93     /* start with a clean state */
94     hr = pSHSetThreadRef(NULL);
95     ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
96 
97     pobj = NULL;
98     refcount = 0xdeadbeef;
99     hr = pSHCreateThreadRef(&refcount, &pobj);
100     ok((hr == S_OK) && pobj && (refcount == 1),
101         "got 0x%x and %p with %d (expected S_OK and '!= NULL' with 1)\n",
102         hr, pobj, refcount);
103 
104     /* the object is not automatic set as ThreadRef */
105     punk = NULL;
106     hr = pSHGetThreadRef(&punk);
107     ok( (hr == E_NOINTERFACE) && (punk == NULL),
108         "got 0x%x and %p (expected E_NOINTERFACE and NULL)\n", hr, punk);
109 
110     /* set the object */
111     hr = pSHSetThreadRef(pobj);
112     ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
113 
114     /* read back */
115     punk = NULL;
116     hr = pSHGetThreadRef(&punk);
117     ok( (hr == S_OK) && (punk == pobj) && (refcount == 2),
118         "got 0x%x and %p with %d (expected S_OK and %p with 2)\n",
119         hr, punk, refcount, pobj);
120 
121     /* free the ref from SHGetThreadRef */
122     if (SUCCEEDED(hr)) {
123         hr = IUnknown_Release(pobj);
124         ok((hr == 1) && (hr == refcount),
125             "got %d with %d (expected 1 with 1)\n", hr, refcount);
126     }
127 
128     /* free the object */
129     if (pobj) {
130         hr = IUnknown_Release(pobj);
131         ok((hr == 0) && (hr == refcount),
132             "got %d with %d (expected 0 with 0)\n", hr, refcount);
133     }
134 
135     if (0) {
136         /* the ThreadRef has still the pointer,
137            but the object no longer exist after the *_Release */
138         punk = NULL;
139         hr = pSHGetThreadRef(&punk);
140         trace("got 0x%x and %p with %d\n", hr, punk, refcount);
141     }
142 
143     /* remove the dead object pointer */
144     hr = pSHSetThreadRef(NULL);
145     ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
146 
147     /* parameter check */
148     if (0) {
149         /* vista: E_INVALIDARG, XP: crash */
150         pobj = NULL;
151         hr = pSHCreateThreadRef(NULL, &pobj);
152         ok(hr == E_INVALIDARG, "got 0x%x (expected E_INVALIDARG)\n", hr);
153 
154         refcount = 0xdeadbeef;
155         /* vista: E_INVALIDARG, XP: crash */
156         hr = pSHCreateThreadRef(&refcount, NULL);
157         ok( (hr == E_INVALIDARG) && (refcount == 0xdeadbeef),
158             "got 0x%x with 0x%x (expected E_INVALIDARG and oxdeadbeef)\n",
159             hr, refcount);
160     }
161 }
162 
163 
164 static void test_SHGetThreadRef(void)
165 {
166     IUnknown *punk;
167     HRESULT hr;
168 
169     /* Not present before IE 5 */
170     if (!pSHGetThreadRef) {
171         win_skip("SHGetThreadRef not found\n");
172         return;
173     }
174 
175     punk = NULL;
176     hr = pSHGetThreadRef(&punk);
177     ok( (hr == E_NOINTERFACE) && (punk == NULL),
178         "got 0x%x and %p (expected E_NOINTERFACE and NULL)\n", hr, punk);
179 
180     if (0) {
181         /* this crash on Windows */
182         pSHGetThreadRef(NULL);
183     }
184 }
185 
186 static void test_SHSetThreadRef(void)
187 {
188     threadref ref;
189     IUnknown *punk;
190     HRESULT hr;
191     LONG refcount;
192 
193     /* Not present before IE 5 */
194     if (!pSHSetThreadRef) {
195         win_skip("SHSetThreadRef not found\n");
196         return;
197     }
198 
199     /* start with a clean state */
200     hr = pSHSetThreadRef(NULL);
201     ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
202 
203     /* build and set out object */
204     init_threadref(&ref, &refcount);
205     AddRef_called = 0;
206     refcount = 1;
207     hr = pSHSetThreadRef(&ref.IUnknown_iface);
208     ok( (hr == S_OK) && (refcount == 1) && (!AddRef_called),
209         "got 0x%x with %d, %d (expected S_OK with 1, 0)\n",
210         hr, refcount, AddRef_called);
211 
212     /* read back our object */
213     AddRef_called = 0;
214     refcount = 1;
215     punk = NULL;
216     hr = pSHGetThreadRef(&punk);
217     ok( (hr == S_OK) && (punk == &ref.IUnknown_iface) && (refcount == 2) && (AddRef_called == 1),
218         "got 0x%x and %p with %d, %d (expected S_OK and %p with 2, 1)\n",
219         hr, punk, refcount, AddRef_called, &ref);
220 
221     /* clear the object pointer */
222     hr = pSHSetThreadRef(NULL);
223     ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
224 
225     /* verify, that our object is no longer known as ThreadRef */
226     hr = pSHGetThreadRef(&punk);
227     ok( (hr == E_NOINTERFACE) && (punk == NULL),
228         "got 0x%x and %p (expected E_NOINTERFACE and NULL)\n", hr, punk);
229 
230 }
231 
232 START_TEST(thread)
233 {
234     HMODULE hshlwapi = GetModuleHandleA("shlwapi.dll");
235 
236     pSHCreateThreadRef = (void *) GetProcAddress(hshlwapi, "SHCreateThreadRef");
237     pSHGetThreadRef = (void *) GetProcAddress(hshlwapi, "SHGetThreadRef");
238     pSHSetThreadRef = (void *) GetProcAddress(hshlwapi, "SHSetThreadRef");
239 
240     test_SHCreateThreadRef();
241     test_SHGetThreadRef();
242     test_SHSetThreadRef();
243 
244 }
245