[OLE32_WINETEST] Sync with Wine Staging 4.18. CORE-16441
[reactos.git] / modules / rostests / winetests / ole32 / propvariant.c
1 /*
2 * PropVariant Tests
3 *
4 * Copyright 2004 Robert Shearman
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #define COBJMACROS
22 #ifdef __REACTOS__
23 #define CONST_VTABLE
24 #endif
25
26 #include "windows.h"
27 #include "wtypes.h"
28 #include "ddeml.h"
29
30 #include "wine/test.h"
31
32 /* invalid in all versions */
33 #define PROP_INV 0x7f
34 /* valid in v0 and above (NT4+) */
35 #define PROP_V0 0
36 /* valid in v1 and above (Win2k+) */
37 #define PROP_V1 1
38 /* valid in v1a and above (WinXP+) */
39 #define PROP_V1A 2
40 #define PROP_TODO 0x80
41
42 static const struct valid_mapping
43 {
44 BYTE simple;
45 BYTE with_array;
46 BYTE with_vector;
47 BYTE byref;
48 } valid_types[] =
49 {
50 { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_EMPTY */
51 { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_NULL */
52 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_I2 */
53 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_I4 */
54 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_R4 */
55 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_R8 */
56 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_CY */
57 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_DATE */
58 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_BSTR */
59 { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 | PROP_TODO }, /* VT_DISPATCH */
60 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_ERROR */
61 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_BOOL */
62 { PROP_V1 | PROP_TODO , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_VARIANT */
63 { PROP_V1 , PROP_V1, PROP_INV, PROP_V1 | PROP_TODO }, /* VT_UNKNOWN */
64 { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 | PROP_TODO }, /* VT_DECIMAL */
65 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 15 */
66 { PROP_V1 , PROP_V1 , PROP_V1 , PROP_V1 | PROP_TODO }, /* VT_I1 */
67 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_UI1 */
68 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_UI2 */
69 { PROP_V0 , PROP_V1 , PROP_V0 , PROP_V1 | PROP_TODO }, /* VT_UI4 */
70 { PROP_V0 , PROP_V1A | PROP_TODO, PROP_V0 , PROP_V1A | PROP_TODO }, /* VT_I8 */
71 { PROP_V0 , PROP_V1A | PROP_TODO, PROP_V0 , PROP_V1A | PROP_TODO }, /* VT_UI8 */
72 { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 | PROP_TODO }, /* VT_INT */
73 { PROP_V1 , PROP_V1 , PROP_INV, PROP_V1 | PROP_TODO }, /* VT_UINT */
74 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_VOID */
75 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_HRESULT */
76 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_PTR */
77 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_SAFEARRAY */
78 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_CARRAY */
79 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_USERDEFINED */
80 { PROP_V0 , PROP_INV, PROP_V0 , PROP_INV }, /* VT_LPSTR */
81 { PROP_V0 , PROP_INV, PROP_V0 , PROP_INV }, /* VT_LPWSTR */
82 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 32 */
83 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 33 */
84 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 34 */
85 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 35 */
86 { PROP_V1 | PROP_TODO , PROP_V1 | PROP_TODO , PROP_INV, PROP_V1 | PROP_TODO }, /* VT_RECORD */
87 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_INT_PTR */
88 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* VT_UINT_PTR */
89 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 39 */
90 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 40 */
91 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 41 */
92 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 42 */
93 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 43 */
94 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 44 */
95 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 45 */
96 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 46 */
97 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 47 */
98 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 48 */
99 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 49 */
100 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 50 */
101 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 51 */
102 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 52 */
103 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 53 */
104 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 54 */
105 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 55 */
106 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 56 */
107 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 57 */
108 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 58 */
109 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 59 */
110 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 60 */
111 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 61 */
112 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 62 */
113 { PROP_INV, PROP_INV, PROP_INV, PROP_INV }, /* 63 */
114 { PROP_V0 , PROP_INV, PROP_V0 , PROP_INV }, /* VT_FILETIME */
115 { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_BLOB */
116 { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_STREAM */
117 { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_STORAGE */
118 { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_STREAMED_OBJECT */
119 { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_STORED_OBJECT */
120 { PROP_V0 , PROP_INV, PROP_INV, PROP_INV }, /* VT_BLOB_OBJECT */
121 { PROP_V0 , PROP_INV, PROP_V0 , PROP_INV } /* VT_CF */
122 };
123
124 static const char* wine_vtypes[VT_CLSID+1] =
125 {
126 "VT_EMPTY","VT_NULL","VT_I2","VT_I4","VT_R4","VT_R8","VT_CY","VT_DATE",
127 "VT_BSTR","VT_DISPATCH","VT_ERROR","VT_BOOL","VT_VARIANT","VT_UNKNOWN",
128 "VT_DECIMAL","15","VT_I1","VT_UI1","VT_UI2","VT_UI4","VT_I8","VT_UI8",
129 "VT_INT","VT_UINT","VT_VOID","VT_HRESULT","VT_PTR","VT_SAFEARRAY",
130 "VT_CARRAY","VT_USERDEFINED","VT_LPSTR","VT_LPWSTR","32","33","34","35",
131 "VT_RECORD","VT_INT_PTR","VT_UINT_PTR","39","40","41","42","43","44","45",
132 "46","47","48","49","50","51","52","53","54","55","56","57","58","59","60",
133 "61","62","63","VT_FILETIME","VT_BLOB","VT_STREAM","VT_STORAGE",
134 "VT_STREAMED_OBJECT","VT_STORED_OBJECT","VT_BLOB_OBJECT","VT_CF","VT_CLSID"
135 };
136
137
138 static void expect(HRESULT hr, VARTYPE vt, BOOL copy, int line)
139 {
140 int idx = vt & VT_TYPEMASK;
141 BYTE flags;
142 const char *modifier;
143
144 if(vt & VT_BYREF)
145 {
146 flags = valid_types[idx].byref;
147 modifier = "byref";
148 }
149 else if(vt & VT_ARRAY)
150 {
151 flags = valid_types[idx].with_array;
152 modifier = "array";
153 }
154 else if(vt & VT_VECTOR)
155 {
156 flags = valid_types[idx].with_vector;
157 modifier = "vector";
158 }
159 else
160 {
161 flags = valid_types[idx].simple;
162 modifier = "simple";
163 }
164
165 if(flags == PROP_INV)
166 {
167 if (copy && (vt & VT_VECTOR))
168 ok(hr == DISP_E_BADVARTYPE || hr == STG_E_INVALIDPARAMETER, "%s (%s): got %08x (line %d)\n", wine_vtypes[idx], modifier, hr, line);
169 else
170 ok(hr == (copy ? DISP_E_BADVARTYPE : STG_E_INVALIDPARAMETER), "%s (%s): got %08x (line %d)\n", wine_vtypes[idx], modifier, hr, line);
171 }
172 else if(flags == PROP_V0)
173 ok(hr == S_OK, "%s (%s): got %08x\n", wine_vtypes[idx], modifier, hr);
174 else todo_wine_if(flags & PROP_TODO)
175 {
176 if(hr != S_OK)
177 win_skip("%s (%s): unsupported\n", wine_vtypes[idx], modifier);
178 else ok(hr == S_OK, "%s (%s): got %08x\n", wine_vtypes[idx], modifier, hr);
179 }
180 }
181
182 static void test_validtypes(void)
183 {
184 PROPVARIANT propvar, copy, uninit;
185 HRESULT hr;
186 unsigned int i, ret;
187
188 memset(&uninit, 0x77, sizeof(uninit));
189
190 memset(&propvar, 0x55, sizeof(propvar));
191 hr = PropVariantClear(&propvar);
192 ok(hr == STG_E_INVALIDPARAMETER, "expected STG_E_INVALIDPARAMETER, got %08x\n", hr);
193 ok(propvar.vt == 0, "expected 0, got %d\n", propvar.vt);
194 ok(U(propvar).uhVal.QuadPart == 0, "expected 0, got %#x/%#x\n",
195 U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart);
196
197 for (i = 0; i < ARRAY_SIZE(valid_types); i++)
198 {
199 VARTYPE vt;
200
201 memset(&propvar, 0x55, sizeof(propvar));
202 if (i == VT_RECORD)
203 memset(&propvar, 0, sizeof(propvar));
204 else if (i == VT_BLOB || i == VT_BLOB_OBJECT)
205 {
206 U(propvar).blob.cbSize = 0;
207 U(propvar).blob.pBlobData = NULL;
208 }
209 else
210 U(propvar).pszVal = NULL;
211 vt = propvar.vt = i;
212 memset(&copy, 0x77, sizeof(copy));
213 hr = PropVariantCopy(&copy, &propvar);
214 expect(hr, vt, TRUE, __LINE__);
215 if (hr == S_OK)
216 {
217 ok(copy.vt == propvar.vt, "expected %d, got %d\n", propvar.vt, copy.vt);
218 ok(U(copy).uhVal.QuadPart == U(propvar).uhVal.QuadPart, "%u: expected %#x/%#x, got %#x/%#x\n",
219 i, U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart,
220 U(copy).uhVal.u.LowPart, U(copy).uhVal.u.HighPart);
221 }
222 else
223 {
224 ret = memcmp(&copy, &uninit, sizeof(copy));
225 ok(!ret || broken(ret) /* win2000 */, "%d: copy should stay unchanged\n", i);
226 }
227 hr = PropVariantClear(&propvar);
228 expect(hr, vt, FALSE, __LINE__);
229 ok(propvar.vt == 0, "expected 0, got %d\n", propvar.vt);
230 ok(U(propvar).uhVal.QuadPart == 0, "%u: expected 0, got %#x/%#x\n",
231 i, U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart);
232
233 memset(&propvar, 0x55, sizeof(propvar));
234 U(propvar).pszVal = NULL;
235 vt = propvar.vt = i | VT_ARRAY;
236 memset(&copy, 0x77, sizeof(copy));
237 hr = PropVariantCopy(&copy, &propvar);
238 expect(hr, vt, TRUE, __LINE__);
239 if (hr == S_OK)
240 {
241 ok(copy.vt == propvar.vt, "expected %d, got %d\n", propvar.vt, copy.vt);
242 ok(U(copy).uhVal.QuadPart == 0, "%u: expected 0, got %#x/%#x\n",
243 i, U(copy).uhVal.u.LowPart, U(copy).uhVal.u.HighPart);
244 }
245 else
246 {
247 ret = memcmp(&copy, &uninit, sizeof(copy));
248 ok(!ret || broken(ret) /* win2000 */, "%d: copy should stay unchanged\n", i);
249 }
250 hr = PropVariantClear(&propvar);
251 expect(hr, vt, FALSE, __LINE__);
252 ok(propvar.vt == 0, "expected 0, got %d\n", propvar.vt);
253 ok(U(propvar).uhVal.QuadPart == 0, "%u: expected 0, got %#x/%#x\n",
254 i, U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart);
255
256 memset(&propvar, 0x55, sizeof(propvar));
257 U(propvar).caub.cElems = 0;
258 U(propvar).caub.pElems = NULL;
259 vt = propvar.vt = i | VT_VECTOR;
260 memset(&copy, 0x77, sizeof(copy));
261 hr = PropVariantCopy(&copy, &propvar);
262 expect(hr, vt, TRUE, __LINE__);
263 if (hr == S_OK)
264 {
265 ok(copy.vt == propvar.vt, "expected %d, got %d\n", propvar.vt, copy.vt);
266 ok(!U(copy).caub.cElems, "%u: expected 0, got %d\n", i, U(copy).caub.cElems);
267 ok(!U(copy).caub.pElems, "%u: expected NULL, got %p\n", i, U(copy).caub.pElems);
268 }
269 else
270 {
271 ret = memcmp(&copy, &uninit, sizeof(copy));
272 ok(!ret || broken(ret) /* win2000 */, "%d: copy should stay unchanged\n", i);
273 }
274 hr = PropVariantClear(&propvar);
275 expect(hr, vt, FALSE, __LINE__);
276 ok(propvar.vt == 0, "expected 0, got %d\n", propvar.vt);
277 ok(U(propvar).uhVal.QuadPart == 0, "%u: expected 0, got %#x/%#x\n",
278 i, U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart);
279
280 memset(&propvar, 0x55, sizeof(propvar));
281 U(propvar).pszVal = NULL;
282 vt = propvar.vt = i | VT_BYREF;
283 memset(&copy, 0x77, sizeof(copy));
284 hr = PropVariantCopy(&copy, &propvar);
285 expect(hr, vt, TRUE, __LINE__);
286 if (hr == S_OK)
287 {
288 ok(copy.vt == propvar.vt, "expected %d, got %d\n", propvar.vt, copy.vt);
289 ok(U(copy).uhVal.QuadPart == U(propvar).uhVal.QuadPart, "%u: expected %#x/%#x, got %#x/%#x\n",
290 i, U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart,
291 U(copy).uhVal.u.LowPart, U(copy).uhVal.u.HighPart);
292 }
293 else
294 {
295 ret = memcmp(&copy, &uninit, sizeof(copy));
296 ok(!ret || broken(ret) /* win2000 */, "%d: copy should stay unchanged\n", i);
297 }
298 hr = PropVariantClear(&propvar);
299 expect(hr, vt, FALSE, __LINE__);
300 ok(propvar.vt == 0, "expected 0, got %d\n", propvar.vt);
301 ok(U(propvar).uhVal.QuadPart == 0, "%u: expected 0, got %#x/%#x\n",
302 i, U(propvar).uhVal.u.LowPart, U(propvar).uhVal.u.HighPart);
303 }
304 }
305
306 struct unk_impl
307 {
308 IUnknown IUnknown_iface;
309 LONG ref;
310 };
311
312 static inline struct unk_impl *impl_from_IUnknown(IUnknown *iface)
313 {
314 return CONTAINING_RECORD(iface, struct unk_impl, IUnknown_iface);
315 }
316
317 static HRESULT WINAPI unk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
318 {
319 struct unk_impl *This = impl_from_IUnknown(iface);
320 if(winetest_debug > 1)
321 trace("Call to unk_QueryInterface()\n");
322 *ppv = &This->IUnknown_iface;
323 IUnknown_AddRef(iface);
324 return S_OK;
325 }
326
327 static ULONG WINAPI unk_AddRef(IUnknown *iface)
328 {
329 struct unk_impl *This = impl_from_IUnknown(iface);
330 if(winetest_debug > 1)
331 trace("Call to unk_AddRef()\n");
332 return InterlockedIncrement(&This->ref);
333 }
334
335 static ULONG WINAPI unk_Release(IUnknown *iface)
336 {
337 struct unk_impl *This = impl_from_IUnknown(iface);
338 if(winetest_debug > 1)
339 trace("Call to unk_Release()\n");
340 return InterlockedDecrement(&This->ref);
341 }
342
343 static const IUnknownVtbl unk_vtbl =
344 {
345 unk_QueryInterface,
346 unk_AddRef,
347 unk_Release
348 };
349
350 static void test_copy(void)
351 {
352 static char szTestString[] = "Test String";
353 static WCHAR wszTestString[] = {'T','e','s','t',' ','S','t','r','i','n','g',0};
354 struct unk_impl unk_obj = {{&unk_vtbl}, 1};
355 PROPVARIANT propvarSrc;
356 PROPVARIANT propvarDst;
357 SAFEARRAY *sa;
358 SAFEARRAYBOUND sabound;
359 LONG saindex;
360 HRESULT hr;
361
362 propvarSrc.vt = VT_BSTR;
363 U(propvarSrc).bstrVal = SysAllocString(wszTestString);
364
365 hr = PropVariantCopy(&propvarDst, &propvarSrc);
366 ok(hr == S_OK, "PropVariantCopy(...VT_BSTR...) failed\n");
367 ok(!lstrcmpW(U(propvarSrc).bstrVal, U(propvarDst).bstrVal), "BSTR not copied properly\n");
368 hr = PropVariantClear(&propvarSrc);
369 ok(hr == S_OK, "PropVariantClear(...VT_BSTR...) failed\n");
370 hr = PropVariantClear(&propvarDst);
371 ok(hr == S_OK, "PropVariantClear(...VT_BSTR...) failed\n");
372
373 propvarSrc.vt = VT_LPWSTR;
374 U(propvarSrc).pwszVal = wszTestString;
375 hr = PropVariantCopy(&propvarDst, &propvarSrc);
376 ok(hr == S_OK, "PropVariantCopy(...VT_LPWSTR...) failed\n");
377 ok(!lstrcmpW(U(propvarSrc).pwszVal, U(propvarDst).pwszVal), "Wide string not copied properly\n");
378 hr = PropVariantClear(&propvarDst);
379 ok(hr == S_OK, "PropVariantClear(...VT_LPWSTR...) failed\n");
380 memset(&propvarSrc, 0, sizeof(propvarSrc));
381
382 propvarSrc.vt = VT_LPSTR;
383 U(propvarSrc).pszVal = szTestString;
384 hr = PropVariantCopy(&propvarDst, &propvarSrc);
385 ok(hr == S_OK, "PropVariantCopy(...VT_LPSTR...) failed\n");
386 ok(!strcmp(U(propvarSrc).pszVal, U(propvarDst).pszVal), "String not copied properly\n");
387 hr = PropVariantClear(&propvarDst);
388 ok(hr == S_OK, "PropVariantClear(...VT_LPSTR...) failed\n");
389 memset(&propvarSrc, 0, sizeof(propvarSrc));
390
391 propvarSrc.vt = VT_UNKNOWN;
392 U(propvarSrc).punkVal = &unk_obj.IUnknown_iface;
393 hr = PropVariantCopy(&propvarDst, &propvarSrc);
394 ok(hr == S_OK, "PropVariantCopy(...VT_UNKNOWN...) failed: 0x%08x.\n", hr);
395 ok(U(propvarDst).punkVal == &unk_obj.IUnknown_iface, "Got wrong IUnknown pointer\n");
396 ok(unk_obj.ref == 2, "got wrong refcount: %d.\n", unk_obj.ref);
397 hr = PropVariantClear(&propvarDst);
398 ok(hr == S_OK, "PropVariantClear(...VT_UNKNOWN...) failed: 0x%08x.\n", hr);
399 ok(unk_obj.ref == 1, "got wrong refcount: %d.\n", unk_obj.ref);
400 memset(&propvarSrc, 0, sizeof(propvarSrc));
401
402 sabound.lLbound = 0;
403 sabound.cElements = 2;
404 sa = SafeArrayCreate(VT_UNKNOWN, 1, &sabound);
405 saindex = 0;
406 SafeArrayPutElement(sa, &saindex, &unk_obj.IUnknown_iface);
407 saindex = 1;
408 SafeArrayPutElement(sa, &saindex, &unk_obj.IUnknown_iface);
409 ok(unk_obj.ref == 3, "got wrong refcount: %d.\n", unk_obj.ref);
410
411 propvarSrc.vt = VT_ARRAY | VT_UNKNOWN;
412 U(propvarSrc).parray = sa;
413 hr = PropVariantCopy(&propvarDst, &propvarSrc);
414 ok(hr == S_OK, "PropVariantCopy(...VT_ARRAY|VT_UNKNOWN...) failed: 0x%08x.\n", hr);
415 ok(unk_obj.ref == 5, "got wrong refcount: %d.\n", unk_obj.ref);
416 hr = PropVariantClear(&propvarDst);
417 ok(hr == S_OK, "PropVariantClear(...VT_ARRAY|VT_UNKNOWN...) failed: 0x%08x.\n", hr);
418 ok(unk_obj.ref == 3, "got wrong refcount: %d.\n", unk_obj.ref);
419 hr = PropVariantClear(&propvarSrc);
420 ok(hr == S_OK, "PropVariantClear(...VT_ARRAY|VT_UNKNOWN...) failed: 0x%08x.\n", hr);
421 ok(unk_obj.ref == 1, "got wrong refcount: %d.\n", unk_obj.ref);
422 memset(&propvarSrc, 0, sizeof(propvarSrc));
423 }
424
425 struct _PMemoryAllocator_vtable {
426 void *Allocate; /* virtual void* Allocate(ULONG cbSize); */
427 void *Free; /* virtual void Free(void *pv); */
428 };
429
430 typedef struct _PMemoryAllocator {
431 struct _PMemoryAllocator_vtable *vt;
432 } PMemoryAllocator;
433
434 static void * WINAPI PMemoryAllocator_Allocate(PMemoryAllocator *_this, ULONG cbSize)
435 {
436 return CoTaskMemAlloc(cbSize);
437 }
438
439 static void WINAPI PMemoryAllocator_Free(PMemoryAllocator *_this, void *pv)
440 {
441 CoTaskMemFree(pv);
442 }
443
444 #ifdef __i386__
445
446 #include "pshpack1.h"
447 typedef struct
448 {
449 BYTE pop_eax; /* popl %eax */
450 BYTE push_ecx; /* pushl %ecx */
451 BYTE push_eax; /* pushl %eax */
452 BYTE jmp_func; /* jmp $func */
453 DWORD func;
454 } THISCALL_TO_STDCALL_THUNK;
455 #include "poppack.h"
456
457 static THISCALL_TO_STDCALL_THUNK *wrapperCodeMem = NULL;
458
459 static void fill_thunk(THISCALL_TO_STDCALL_THUNK *thunk, void *fn)
460 {
461 thunk->pop_eax = 0x58;
462 thunk->push_ecx = 0x51;
463 thunk->push_eax = 0x50;
464 thunk->jmp_func = 0xe9;
465 thunk->func = (char*)fn - (char*)(&thunk->func + 1);
466 }
467
468 static void setup_vtable(struct _PMemoryAllocator_vtable *vtable)
469 {
470 wrapperCodeMem = VirtualAlloc(NULL, 2 * sizeof(*wrapperCodeMem),
471 MEM_COMMIT, PAGE_EXECUTE_READWRITE);
472
473 fill_thunk(&wrapperCodeMem[0], PMemoryAllocator_Allocate);
474 fill_thunk(&wrapperCodeMem[1], PMemoryAllocator_Free);
475
476 vtable->Allocate = &wrapperCodeMem[0];
477 vtable->Free = &wrapperCodeMem[1];
478 }
479
480 #else
481
482 static void setup_vtable(struct _PMemoryAllocator_vtable *vtable)
483 {
484 vtable->Allocate = PMemoryAllocator_Allocate;
485 vtable->Free = PMemoryAllocator_Free;
486 }
487
488 #endif
489
490 static const char serialized_empty[] = {
491 0,0, /* VT_EMPTY */
492 0,0, /* padding */
493 };
494
495 static const char serialized_null[] = {
496 1,0, /* VT_NULL */
497 0,0, /* padding */
498 };
499
500 static const char serialized_i4[] = {
501 3,0, /* VT_I4 */
502 0,0, /* padding */
503 0xef,0xcd,0xab,0xfe
504 };
505
506 static const char serialized_bstr_wc[] = {
507 8,0, /* VT_BSTR */
508 0,0, /* padding */
509 10,0,0,0, /* size */
510 't',0,'e',0,
511 's',0,'t',0,
512 0,0,0,0
513 };
514
515 static const char serialized_bstr_mb[] = {
516 8,0, /* VT_BSTR */
517 0,0, /* padding */
518 5,0,0,0, /* size */
519 't','e','s','t',
520 0,0,0,0
521 };
522
523 static void test_propertytovariant(void)
524 {
525 HANDLE hole32;
526 BOOLEAN (__stdcall *pStgConvertPropertyToVariant)(const SERIALIZEDPROPERTYVALUE*,USHORT,PROPVARIANT*,PMemoryAllocator*);
527 PROPVARIANT propvar;
528 PMemoryAllocator allocator;
529 struct _PMemoryAllocator_vtable vtable;
530 BOOLEAN ret;
531 static const WCHAR test_string[] = {'t','e','s','t',0};
532
533 hole32 = GetModuleHandleA("ole32");
534
535 pStgConvertPropertyToVariant = (void*)GetProcAddress(hole32, "StgConvertPropertyToVariant");
536
537 if (!pStgConvertPropertyToVariant)
538 {
539 win_skip("StgConvertPropertyToVariant not available\n");
540 return;
541 }
542
543 setup_vtable(&vtable);
544 allocator.vt = &vtable;
545
546 ret = pStgConvertPropertyToVariant((SERIALIZEDPROPERTYVALUE*)serialized_empty,
547 CP_WINUNICODE, &propvar, &allocator);
548
549 ok(ret == 0, "StgConvertPropertyToVariant returned %i\n", ret);
550 ok(propvar.vt == VT_EMPTY, "unexpected vt %x\n", propvar.vt);
551
552 ret = pStgConvertPropertyToVariant((SERIALIZEDPROPERTYVALUE*)serialized_null,
553 CP_WINUNICODE, &propvar, &allocator);
554
555 ok(ret == 0, "StgConvertPropertyToVariant returned %i\n", ret);
556 ok(propvar.vt == VT_NULL, "unexpected vt %x\n", propvar.vt);
557
558 ret = pStgConvertPropertyToVariant((SERIALIZEDPROPERTYVALUE*)serialized_i4,
559 CP_WINUNICODE, &propvar, &allocator);
560
561 ok(ret == 0, "StgConvertPropertyToVariant returned %i\n", ret);
562 ok(propvar.vt == VT_I4, "unexpected vt %x\n", propvar.vt);
563 ok(U(propvar).lVal == 0xfeabcdef, "unexpected lVal %x\n", U(propvar).lVal);
564
565 ret = pStgConvertPropertyToVariant((SERIALIZEDPROPERTYVALUE*)serialized_bstr_wc,
566 CP_WINUNICODE, &propvar, &allocator);
567
568 ok(ret == 0, "StgConvertPropertyToVariant returned %i\n", ret);
569 ok(propvar.vt == VT_BSTR, "unexpected vt %x\n", propvar.vt);
570 ok(!lstrcmpW(U(propvar).bstrVal, test_string), "unexpected string value\n");
571 PropVariantClear(&propvar);
572
573 ret = pStgConvertPropertyToVariant((SERIALIZEDPROPERTYVALUE*)serialized_bstr_mb,
574 CP_UTF8, &propvar, &allocator);
575
576 ok(ret == 0, "StgConvertPropertyToVariant returned %i\n", ret);
577 ok(propvar.vt == VT_BSTR, "unexpected vt %x\n", propvar.vt);
578 ok(!lstrcmpW(U(propvar).bstrVal, test_string), "unexpected string value\n");
579 PropVariantClear(&propvar);
580 }
581
582 static void test_varianttoproperty(void)
583 {
584 HANDLE hole32;
585 PROPVARIANT propvar;
586 SERIALIZEDPROPERTYVALUE *propvalue, *own_propvalue;
587 SERIALIZEDPROPERTYVALUE* (__stdcall *pStgConvertVariantToProperty)(
588 const PROPVARIANT*,USHORT,SERIALIZEDPROPERTYVALUE*,ULONG*,PROPID,BOOLEAN,ULONG*);
589 ULONG len;
590 static const WCHAR test_string[] = {'t','e','s','t',0};
591 BSTR test_string_bstr;
592
593 hole32 = GetModuleHandleA("ole32");
594
595 pStgConvertVariantToProperty = (void*)GetProcAddress(hole32, "StgConvertVariantToProperty");
596
597 if (!pStgConvertVariantToProperty)
598 {
599 win_skip("StgConvertVariantToProperty not available\n");
600 return;
601 }
602
603 own_propvalue = HeapAlloc(GetProcessHeap(), 0, sizeof(SERIALIZEDPROPERTYVALUE) + 20);
604
605 PropVariantInit(&propvar);
606
607 propvar.vt = VT_I4;
608 U(propvar).lVal = 0xfeabcdef;
609
610 len = 0xdeadbeef;
611 propvalue = pStgConvertVariantToProperty(&propvar, CP_WINUNICODE, NULL, &len,
612 0, FALSE, 0);
613
614 ok(propvalue == NULL, "got nonnull propvalue\n");
615 todo_wine ok(len == 8, "unexpected length %d\n", len);
616
617 if (len == 0xdeadbeef)
618 {
619 HeapFree(GetProcessHeap(), 0, own_propvalue);
620 return;
621 }
622
623 len = 20;
624 propvalue = pStgConvertVariantToProperty(&propvar, CP_WINUNICODE, own_propvalue, &len,
625 0, FALSE, 0);
626
627 ok(propvalue == own_propvalue, "unexpected propvalue %p\n", propvalue);
628 ok(len == 8, "unexpected length %d\n", len);
629 ok(!memcmp(propvalue, serialized_i4, 8), "got wrong data\n");
630
631 propvar.vt = VT_EMPTY;
632 len = 20;
633 own_propvalue->dwType = 0xdeadbeef;
634 propvalue = pStgConvertVariantToProperty(&propvar, CP_WINUNICODE, own_propvalue, &len,
635 0, FALSE, 0);
636
637 ok(propvalue == own_propvalue, "unexpected propvalue %p\n", propvalue);
638 ok(len == 4 || broken(len == 0) /* before Vista */, "unexpected length %d\n", len);
639 if (len) ok(!memcmp(propvalue, serialized_empty, 4), "got wrong data\n");
640 else ok(propvalue->dwType == 0xdeadbeef, "unexpected type %d\n", propvalue->dwType);
641
642 propvar.vt = VT_NULL;
643 len = 20;
644 propvalue = pStgConvertVariantToProperty(&propvar, CP_WINUNICODE, own_propvalue, &len,
645 0, FALSE, 0);
646
647 ok(propvalue == own_propvalue, "unexpected propvalue %p\n", propvalue);
648 ok(len == 4, "unexpected length %d\n", len);
649 ok(!memcmp(propvalue, serialized_null, 4), "got wrong data\n");
650
651 test_string_bstr = SysAllocString(test_string);
652
653 propvar.vt = VT_BSTR;
654 U(propvar).bstrVal = test_string_bstr;
655 len = 20;
656 propvalue = pStgConvertVariantToProperty(&propvar, CP_WINUNICODE, own_propvalue, &len,
657 0, FALSE, 0);
658
659 ok(propvalue == own_propvalue, "unexpected propvalue %p\n", propvalue);
660 ok(len == 20, "unexpected length %d\n", len);
661 ok(!memcmp(propvalue, serialized_bstr_wc, 20), "got wrong data\n");
662
663 len = 20;
664 propvalue = pStgConvertVariantToProperty(&propvar, CP_UTF8, own_propvalue, &len,
665 0, FALSE, 0);
666
667 ok(propvalue == own_propvalue, "unexpected propvalue %p\n", propvalue);
668 ok(len == 16, "unexpected length %d\n", len);
669 ok(!memcmp(propvalue, serialized_bstr_mb, 16), "got wrong data\n");
670
671 SysFreeString(test_string_bstr);
672
673 HeapFree(GetProcessHeap(), 0, own_propvalue);
674 }
675
676 START_TEST(propvariant)
677 {
678 test_validtypes();
679 test_copy();
680 test_propertytovariant();
681 test_varianttoproperty();
682 }