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 }
683