1 /* IPropertyStorage unit tests
2  * Copyright 2005 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 #include <stdio.h>
19 #define COBJMACROS
20 #include "objbase.h"
21 #include "wine/test.h"
22 
23 #ifndef PID_BEHAVIOR
24 #define PID_BEHAVIOR 0x80000003
25 #endif
26 
27 static HRESULT (WINAPI *pFmtIdToPropStgName)(const FMTID *, LPOLESTR);
28 static HRESULT (WINAPI *pPropStgNameToFmtId)(const LPOLESTR, FMTID *);
29 static HRESULT (WINAPI *pStgCreatePropSetStg)(IStorage *, DWORD, IPropertySetStorage **);
30 static HRESULT (WINAPI *pStgCreatePropStg)(IUnknown *, REFFMTID, const CLSID *, DWORD, DWORD, IPropertyStorage **);
31 static HRESULT (WINAPI *pStgOpenPropStg)(IUnknown *, REFFMTID, DWORD, DWORD, IPropertyStorage **);
32 
33 static void init_function_pointers(void)
34 {
35     HMODULE hmod = GetModuleHandleA("ole32.dll");
36     pFmtIdToPropStgName = (void*)GetProcAddress(hmod, "FmtIdToPropStgName");
37     pPropStgNameToFmtId = (void*)GetProcAddress(hmod, "PropStgNameToFmtId");
38     pStgCreatePropSetStg = (void*)GetProcAddress(hmod, "StgCreatePropSetStg");
39     pStgCreatePropStg = (void*)GetProcAddress(hmod, "StgCreatePropStg");
40     pStgOpenPropStg = (void*)GetProcAddress(hmod, "StgOpenPropStg");
41 }
42 
43 /* FIXME: this creates an ANSI storage, try to find conditions under which
44  * Unicode translation fails
45  */
46 static void testPropsHelper(IPropertySetStorage **propSetStorage)
47 {
48     static const WCHAR szDot[] = { '.',0 };
49     static const WCHAR szPrefix[] = { 's','t','g',0 };
50     static const WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
51         'I','n','f','o','r','m','a','t','i','o','n',0 };
52     static WCHAR propName[] = { 'p','r','o','p',0 };
53     static char val[] = "l33t auth0r";
54     WCHAR filename[MAX_PATH];
55     HRESULT hr;
56     IStorage *storage = NULL;
57     IStream *stream = NULL;
58     IPropertyStorage *propertyStorage = NULL;
59     PROPSPEC spec;
60     PROPVARIANT var;
61     CLIPDATA clipdata;
62     unsigned char clipcontent[] = "foobar";
63     GUID anyOldGuid = { 0x12345678,0xdead,0xbeef, {
64      0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07 } };
65 
66     if(propSetStorage)
67         trace("Testing property storage with a set...\n");
68     else
69         trace("Testing property storage without a set...\n");
70 
71     if(!GetTempFileNameW(szDot, szPrefix, 0, filename))
72         return;
73 
74     DeleteFileW(filename);
75 
76     hr = StgCreateDocfile(filename,
77      STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage);
78     ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
79 
80     if(propSetStorage)
81     {
82         if(!pStgCreatePropSetStg)
83         {
84             IStorage_Release(storage);
85             DeleteFileW(filename);
86             return;
87         }
88         hr = pStgCreatePropSetStg(storage, 0, propSetStorage);
89         ok(hr == S_OK, "StgCreatePropSetStg failed: 0x%08x\n", hr);
90 
91         hr = IPropertySetStorage_Create(*propSetStorage,
92          &FMTID_SummaryInformation, NULL, PROPSETFLAG_ANSI,
93          STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
94          &propertyStorage);
95         ok(hr == S_OK, "IPropertySetStorage_Create failed: 0x%08x\n", hr);
96     }
97     else
98     {
99         hr = IStorage_CreateStream(storage, szSummaryInfo,
100          STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
101         ok(hr == S_OK, "IStorage_CreateStream failed: 0x%08x\n", hr);
102 
103         if(!pStgCreatePropStg)
104         {
105             IStorage_Release(storage);
106             IUnknown_Release(stream);
107             DeleteFileW(filename);
108             return;
109         }
110         hr = pStgCreatePropStg((IUnknown *)stream, &FMTID_SummaryInformation,
111          NULL, PROPSETFLAG_ANSI, 0, &propertyStorage);
112         ok(hr == S_OK, "StgCreatePropStg failed: 0x%08x\n", hr);
113     }
114 
115     hr = IPropertyStorage_WriteMultiple(propertyStorage, 0, NULL, NULL, 0);
116     ok(hr == S_OK, "WriteMultiple with 0 args failed: 0x%08x\n", hr);
117     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, NULL, NULL, 0);
118     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr);
119 
120     /* test setting one that I can't set */
121     spec.ulKind = PRSPEC_PROPID;
122     U(spec).propid = PID_DICTIONARY;
123     var.vt = VT_I4;
124     U(var).lVal = 1;
125     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
126     ok(hr == STG_E_INVALIDPARAMETER,
127      "Expected STG_E_INVALIDPARAMETER, got 0x%08x\n", hr);
128 
129     /* test setting one by name with an invalid propidNameFirst */
130     spec.ulKind = PRSPEC_LPWSTR;
131     U(spec).lpwstr = propName;
132     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var,
133      PID_DICTIONARY);
134     ok(hr == STG_E_INVALIDPARAMETER,
135      "Expected STG_E_INVALIDPARAMETER, got 0x%08x\n", hr);
136 
137     /* test setting behavior (case-sensitive) */
138     spec.ulKind = PRSPEC_PROPID;
139     U(spec).propid = PID_BEHAVIOR;
140     U(var).lVal = 1;
141     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
142     ok(hr == STG_E_INVALIDPARAMETER,
143      "Expected STG_E_INVALIDPARAMETER, got 0x%08x\n", hr);
144 
145     /* set one by value.. */
146     spec.ulKind = PRSPEC_PROPID;
147     U(spec).propid = PID_FIRST_USABLE;
148     U(var).lVal = 1;
149     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
150     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
151 
152     /* set one by name */
153     spec.ulKind = PRSPEC_LPWSTR;
154     U(spec).lpwstr = propName;
155     U(var).lVal = 2;
156     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var,
157      PID_FIRST_USABLE);
158     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
159 
160     /* set a string value */
161     spec.ulKind = PRSPEC_PROPID;
162     U(spec).propid = PIDSI_AUTHOR;
163     var.vt = VT_LPSTR;
164     U(var).pszVal = val;
165     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
166     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
167 
168     /* set a clipboard value */
169     spec.ulKind = PRSPEC_PROPID;
170     U(spec).propid = PIDSI_THUMBNAIL;
171     var.vt = VT_CF;
172     clipdata.cbSize = sizeof clipcontent + sizeof (ULONG);
173     clipdata.ulClipFmt = CF_ENHMETAFILE;
174     clipdata.pClipData = clipcontent;
175     U(var).pclipdata = &clipdata;
176     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
177     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
178 
179 
180     /* check reading */
181     hr = IPropertyStorage_ReadMultiple(propertyStorage, 0, NULL, NULL);
182     ok(hr == S_FALSE, "ReadMultiple with 0 args failed: 0x%08x\n", hr);
183     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, NULL, NULL);
184     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr);
185     /* read by propid */
186     spec.ulKind = PRSPEC_PROPID;
187     U(spec).propid = PID_FIRST_USABLE;
188     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
189     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
190     ok(var.vt == VT_I4 && U(var).lVal == 1,
191      "Didn't get expected type or value for property (got type %d, value %d)\n",
192      var.vt, U(var).lVal);
193     /* read by name */
194     spec.ulKind = PRSPEC_LPWSTR;
195     U(spec).lpwstr = propName;
196     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
197     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
198     ok(var.vt == VT_I4 && U(var).lVal == 2,
199      "Didn't get expected type or value for property (got type %d, value %d)\n",
200      var.vt, U(var).lVal);
201     /* read string value */
202     spec.ulKind = PRSPEC_PROPID;
203     U(spec).propid = PIDSI_AUTHOR;
204     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
205     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
206     ok(var.vt == VT_LPSTR && !lstrcmpA(U(var).pszVal, val),
207      "Didn't get expected type or value for property (got type %d, value %s)\n",
208      var.vt, U(var).pszVal);
209     PropVariantClear(&var);
210 
211     /* read clipboard format */
212     spec.ulKind = PRSPEC_PROPID;
213     U(spec).propid = PIDSI_THUMBNAIL;
214     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
215     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
216     ok(var.vt == VT_CF, "variant type wrong\n");
217     ok(U(var).pclipdata->ulClipFmt == CF_ENHMETAFILE,
218         "clipboard type wrong\n");
219     ok(U(var).pclipdata->cbSize == sizeof clipcontent + sizeof (ULONG),
220         "clipboard size wrong\n");
221     ok(!memcmp(U(var).pclipdata->pClipData, clipcontent, sizeof clipcontent),
222         "clipboard contents wrong\n");
223     ok(S_OK == PropVariantClear(&var), "failed to clear variant\n");
224 
225     /* check deleting */
226     hr = IPropertyStorage_DeleteMultiple(propertyStorage, 0, NULL);
227     ok(hr == S_OK, "DeleteMultiple with 0 args failed: 0x%08x\n", hr);
228     hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, NULL);
229     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr);
230     /* contrary to what the docs say, you can't delete the dictionary */
231     spec.ulKind = PRSPEC_PROPID;
232     U(spec).propid = PID_DICTIONARY;
233     hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, &spec);
234     ok(hr == STG_E_INVALIDPARAMETER,
235      "Expected STG_E_INVALIDPARAMETER, got 0x%08x\n", hr);
236     /* now delete the first value.. */
237     U(spec).propid = PID_FIRST_USABLE;
238     hr = IPropertyStorage_DeleteMultiple(propertyStorage, 1, &spec);
239     ok(hr == S_OK, "DeleteMultiple failed: 0x%08x\n", hr);
240     /* and check that it's no longer readable */
241     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
242     ok(hr == S_FALSE, "Expected S_FALSE, got 0x%08x\n", hr);
243 
244     hr = IPropertyStorage_Commit(propertyStorage, STGC_DEFAULT);
245     ok(hr == S_OK, "Commit failed: 0x%08x\n", hr);
246 
247     /* check reverting */
248     spec.ulKind = PRSPEC_PROPID;
249     U(spec).propid = PID_FIRST_USABLE;
250     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
251     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
252     hr = IPropertyStorage_Revert(propertyStorage);
253     ok(hr == S_OK, "Revert failed: 0x%08x\n", hr);
254     /* now check that it's still not there */
255     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
256     ok(hr == S_FALSE, "Expected S_FALSE, got 0x%08x\n", hr);
257     /* set an integer value again */
258     spec.ulKind = PRSPEC_PROPID;
259     U(spec).propid = PID_FIRST_USABLE;
260     var.vt = VT_I4;
261     U(var).lVal = 1;
262     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
263     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
264     /* commit it */
265     hr = IPropertyStorage_Commit(propertyStorage, STGC_DEFAULT);
266     ok(hr == S_OK, "Commit failed: 0x%08x\n", hr);
267     /* set it to a string value */
268     var.vt = VT_LPSTR;
269     U(var).pszVal = val;
270     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
271     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
272     /* revert it */
273     hr = IPropertyStorage_Revert(propertyStorage);
274     ok(hr == S_OK, "Revert failed: 0x%08x\n", hr);
275     /* Oddly enough, there's no guarantee that a successful revert actually
276      * implies the value wasn't saved.  Maybe transactional mode needs to be
277      * used for that?
278      */
279 
280     IPropertyStorage_Release(propertyStorage);
281     if(propSetStorage) IPropertySetStorage_Release(*propSetStorage);
282     IStorage_Release(storage);
283     if(stream) IUnknown_Release(stream);
284 
285     /* now open it again */
286     hr = StgOpenStorage(filename, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
287      NULL, 0, &storage);
288     ok(hr == S_OK, "StgOpenStorage failed: 0x%08x\n", hr);
289 
290     if(propSetStorage)
291     {
292         hr = pStgCreatePropSetStg(storage, 0, propSetStorage);
293         ok(hr == S_OK, "StgCreatePropSetStg failed: 0x%08x\n", hr);
294 
295         hr = IPropertySetStorage_Open(*propSetStorage, &FMTID_SummaryInformation,
296          STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &propertyStorage);
297         ok(hr == S_OK, "IPropertySetStorage_Open failed: 0x%08x\n", hr);
298     }
299     else
300     {
301         hr = IStorage_OpenStream(storage, szSummaryInfo,
302          0, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
303         ok(hr == S_OK, "IStorage_OpenStream failed: 0x%08x\n", hr);
304 
305         if(!pStgOpenPropStg)
306         {
307             IStorage_Release(storage);
308             IUnknown_Release(stream);
309             DeleteFileW(filename);
310             return;
311         }
312         hr = pStgOpenPropStg((IUnknown *)stream, &FMTID_SummaryInformation,
313          PROPSETFLAG_DEFAULT, 0, &propertyStorage);
314         ok(hr == S_OK, "StgOpenPropStg failed: 0x%08x\n", hr);
315     }
316 
317     /* check properties again */
318     spec.ulKind = PRSPEC_LPWSTR;
319     U(spec).lpwstr = propName;
320     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
321     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
322     ok(var.vt == VT_I4 && U(var).lVal == 2,
323      "Didn't get expected type or value for property (got type %d, value %d)\n",
324      var.vt, U(var).lVal);
325     spec.ulKind = PRSPEC_PROPID;
326     U(spec).propid = PIDSI_AUTHOR;
327     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
328     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
329     ok(var.vt == VT_LPSTR && !lstrcmpA(U(var).pszVal, val),
330      "Didn't get expected type or value for property (got type %d, value %s)\n",
331      var.vt, U(var).pszVal);
332     PropVariantClear(&var);
333 
334     IPropertyStorage_Release(propertyStorage);
335     if(propSetStorage) IPropertySetStorage_Release(*propSetStorage);
336     IStorage_Release(storage);
337     if(stream) IUnknown_Release(stream);
338 
339     DeleteFileW(filename);
340 
341     /* Test creating a property set storage with a random GUID */
342     hr = StgCreateDocfile(filename,
343      STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage);
344     ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
345 
346     if(propSetStorage)
347     {
348         hr = pStgCreatePropSetStg(storage, 0, propSetStorage);
349         ok(hr == S_OK, "StgCreatePropSetStg failed: 0x%08x\n", hr);
350 
351         hr = IPropertySetStorage_Create(*propSetStorage,
352          &anyOldGuid, NULL, PROPSETFLAG_ANSI,
353          STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
354          &propertyStorage);
355         ok(hr == S_OK, "IPropertySetStorage_Create failed: 0x%08x\n", hr);
356     }
357     else
358     {
359         hr = IStorage_CreateStream(storage, szSummaryInfo,
360          STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stream);
361         ok(hr == S_OK, "IStorage_CreateStream failed: 0x%08x\n", hr);
362 
363         hr = pStgCreatePropStg((IUnknown *)stream, &anyOldGuid, NULL,
364          PROPSETFLAG_DEFAULT, 0, &propertyStorage);
365         ok(hr == S_OK, "StgCreatePropStg failed: 0x%08x\n", hr);
366     }
367 
368     spec.ulKind = PRSPEC_PROPID;
369     U(spec).propid = PID_FIRST_USABLE;
370     var.vt = VT_I4;
371     U(var).lVal = 1;
372     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
373     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
374 
375     hr = IPropertyStorage_Commit(propertyStorage, STGC_DEFAULT);
376     ok(hr == S_OK, "Commit failed: 0x%08x\n", hr);
377 
378     IPropertyStorage_Release(propertyStorage);
379     if(propSetStorage) IPropertySetStorage_Release(*propSetStorage);
380     IStorage_Release(storage);
381     if(stream) IUnknown_Release(stream);
382 
383     /* now open it again */
384     hr = StgOpenStorage(filename, NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE,
385      NULL, 0, &storage);
386     ok(hr == S_OK, "StgOpenStorage failed: 0x%08x\n", hr);
387 
388     if(propSetStorage)
389     {
390         hr = pStgCreatePropSetStg(storage, 0, propSetStorage);
391         ok(hr == S_OK, "StgCreatePropSetStg failed: 0x%08x\n", hr);
392 
393         hr = IPropertySetStorage_Open(*propSetStorage, &anyOldGuid,
394          STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &propertyStorage);
395         ok(hr == S_OK, "IPropertySetStorage_Open failed: 0x%08x\n", hr);
396     }
397     else
398     {
399         hr = IStorage_OpenStream(storage, szSummaryInfo,
400          0, STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &stream);
401         ok(hr == S_OK, "IStorage_OpenStream failed: 0x%08x\n", hr);
402 
403         hr = pStgOpenPropStg((IUnknown *)stream, &anyOldGuid,
404          PROPSETFLAG_DEFAULT, 0, &propertyStorage);
405         ok(hr == S_OK, "StgOpenPropStg failed: 0x%08x\n", hr);
406     }
407 
408     spec.ulKind = PRSPEC_PROPID;
409     U(spec).propid = PID_FIRST_USABLE;
410     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
411     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
412 
413     ok(var.vt == VT_I4 && U(var).lVal == 1,
414      "Didn't get expected type or value for property (got type %d, value %d)\n",
415      var.vt, U(var).lVal);
416 
417     IPropertyStorage_Release(propertyStorage);
418     if(propSetStorage) IPropertySetStorage_Release(*propSetStorage);
419     IStorage_Release(storage);
420     if(stream) IUnknown_Release(stream);
421 
422     DeleteFileW(filename);
423 }
424 
425 static void testProps(void)
426 {
427     IPropertySetStorage *propSetStorage = NULL;
428 
429     testPropsHelper(&propSetStorage);
430     testPropsHelper(NULL);
431 }
432 
433 static void testCodepage(void)
434 {
435     static const WCHAR szDot[] = { '.',0 };
436     static const WCHAR szPrefix[] = { 's','t','g',0 };
437     static CHAR aval[] = "hi";
438     static WCHAR wval[] = { 'h','i',0 };
439     HRESULT hr;
440     IStorage *storage = NULL;
441     IPropertySetStorage *propSetStorage = NULL;
442     IPropertyStorage *propertyStorage = NULL;
443     PROPSPEC spec;
444     PROPVARIANT var;
445     WCHAR fileName[MAX_PATH];
446 
447     if(!GetTempFileNameW(szDot, szPrefix, 0, fileName))
448         return;
449 
450     hr = StgCreateDocfile(fileName,
451      STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage);
452     ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
453 
454     if(!pStgCreatePropSetStg)
455     {
456         IStorage_Release(storage);
457         DeleteFileW(fileName);
458         return;
459     }
460     hr = pStgCreatePropSetStg(storage, 0, &propSetStorage);
461     ok(hr == S_OK, "StgCreatePropSetStg failed: 0x%08x\n", hr);
462 
463     hr = IPropertySetStorage_Create(propSetStorage,
464      &FMTID_SummaryInformation, NULL, PROPSETFLAG_DEFAULT,
465      STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
466      &propertyStorage);
467     ok(hr == S_OK, "IPropertySetStorage_Create failed: 0x%08x\n", hr);
468 
469     PropVariantInit(&var);
470     spec.ulKind = PRSPEC_PROPID;
471     U(spec).propid = PID_CODEPAGE;
472     /* check code page before it's been explicitly set */
473     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
474     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
475     ok(var.vt == VT_I2 && U(var).iVal == 1200,
476      "Didn't get expected type or value for property\n");
477     /* Set the code page to ascii */
478     var.vt = VT_I2;
479     U(var).iVal = 1252;
480     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
481     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
482     /* check code page */
483     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
484     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
485     ok(var.vt == VT_I2 && U(var).iVal == 1252,
486      "Didn't get expected type or value for property\n");
487     /* Set code page to Unicode */
488     U(var).iVal = 1200;
489     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
490     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
491     /* check code page */
492     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
493     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
494     ok(var.vt == VT_I2 && U(var).iVal == 1200,
495      "Didn't get expected type or value for property\n");
496     /* Set a string value */
497     spec.ulKind = PRSPEC_PROPID;
498     U(spec).propid = PID_FIRST_USABLE;
499     var.vt = VT_LPSTR;
500     U(var).pszVal = aval;
501     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
502     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
503     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
504     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
505     ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, "hi"),
506      "Didn't get expected type or value for property\n");
507     PropVariantClear(&var);
508     /* This seemingly non-sensical test is to show that the string is indeed
509      * interpreted according to the current system code page, not according to
510      * the property set's code page.  (If the latter were true, the whole
511      * string would be maintained.  As it is, only the first character is.)
512      */
513     var.vt = VT_LPSTR;
514     U(var).pszVal = (LPSTR)wval;
515     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
516     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
517     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
518     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
519     ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, "h"),
520      "Didn't get expected type or value for property\n");
521     PropVariantClear(&var);
522 
523     /* now that a property's been set, you can't change the code page */
524     spec.ulKind = PRSPEC_PROPID;
525     U(spec).propid = PID_CODEPAGE;
526     var.vt = VT_I2;
527     U(var).iVal = 1200;
528     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
529     ok(hr == STG_E_INVALIDPARAMETER,
530      "Expected STG_E_INVALIDPARAMETER, got 0x%08x\n", hr);
531 
532     IPropertyStorage_Release(propertyStorage);
533     IPropertySetStorage_Release(propSetStorage);
534     IStorage_Release(storage);
535 
536     DeleteFileW(fileName);
537 
538     /* same tests, but with PROPSETFLAG_ANSI */
539     hr = StgCreateDocfile(fileName,
540      STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, &storage);
541     ok(hr == S_OK, "StgCreateDocfile failed: 0x%08x\n", hr);
542 
543     hr = pStgCreatePropSetStg(storage, 0, &propSetStorage);
544     ok(hr == S_OK, "StgCreatePropSetStg failed: 0x%08x\n", hr);
545 
546     hr = IPropertySetStorage_Create(propSetStorage,
547      &FMTID_SummaryInformation, NULL, PROPSETFLAG_ANSI,
548      STGM_READWRITE | STGM_CREATE | STGM_SHARE_EXCLUSIVE,
549      &propertyStorage);
550     ok(hr == S_OK, "IPropertySetStorage_Create failed: 0x%08x\n", hr);
551 
552     /* check code page before it's been explicitly set */
553     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
554     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
555     ok(var.vt == VT_I2, "Didn't get expected type for property (%u)\n", var.vt);
556     /* Set code page to Unicode */
557     U(var).iVal = 1200;
558     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
559     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
560     /* check code page */
561     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
562     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
563     ok(var.vt == VT_I2 && U(var).iVal == 1200,
564      "Didn't get expected type or value for property\n");
565     /* This test is commented out for documentation.  It fails under Wine,
566      * and I expect it would under Windows as well, yet it succeeds.  There's
567      * obviously something about string conversion I don't understand.
568      */
569     if(0) {
570     static unsigned char strVal[] = { 0x81, 0xff, 0x04, 0 };
571     /* Set code page to 950 (Traditional Chinese) */
572     U(var).iVal = 950;
573     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
574     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
575     /* Try writing an invalid string: lead byte 0x81 is unused in Traditional
576      * Chinese.
577      */
578     spec.ulKind = PRSPEC_PROPID;
579     U(spec).propid = PID_FIRST_USABLE;
580     var.vt = VT_LPSTR;
581     U(var).pszVal = (LPSTR)strVal;
582     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
583     ok(hr == S_OK, "WriteMultiple failed: 0x%08x\n", hr);
584     /* Check returned string */
585     hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
586     ok(hr == S_OK, "ReadMultiple failed: 0x%08x\n", hr);
587     ok(var.vt == VT_LPSTR && !strcmp(U(var).pszVal, (LPCSTR)strVal),
588      "Didn't get expected type or value for property\n");
589     }
590 
591     IPropertyStorage_Release(propertyStorage);
592     IPropertySetStorage_Release(propSetStorage);
593     IStorage_Release(storage);
594 
595     DeleteFileW(fileName);
596 }
597 
598 static void testFmtId(void)
599 {
600     WCHAR szSummaryInfo[] = { 5,'S','u','m','m','a','r','y',
601         'I','n','f','o','r','m','a','t','i','o','n',0 };
602     WCHAR szDocSummaryInfo[] = { 5,'D','o','c','u','m','e','n','t',
603         'S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',
604         0 };
605     WCHAR szIID_IPropSetStg[] = { 5,'0','j','a','a','a','a','a',
606         'a','A','a','a','a','a','a','d','a','A','a','a','a','a','a','a','a','G',
607         'c',0 };
608     WCHAR name[32];
609     FMTID fmtid;
610     HRESULT hr;
611 
612     if (pFmtIdToPropStgName) {
613     hr = pFmtIdToPropStgName(NULL, name);
614     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr);
615     hr = pFmtIdToPropStgName(&FMTID_SummaryInformation, NULL);
616     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr);
617     hr = pFmtIdToPropStgName(&FMTID_SummaryInformation, name);
618     ok(hr == S_OK, "FmtIdToPropStgName failed: 0x%08x\n", hr);
619     ok(!memcmp(name, szSummaryInfo, (lstrlenW(szSummaryInfo) + 1) *
620      sizeof(WCHAR)), "Got wrong name for FMTID_SummaryInformation\n");
621     hr = pFmtIdToPropStgName(&FMTID_DocSummaryInformation, name);
622     ok(hr == S_OK, "FmtIdToPropStgName failed: 0x%08x\n", hr);
623     ok(!memcmp(name, szDocSummaryInfo, (lstrlenW(szDocSummaryInfo) + 1) *
624      sizeof(WCHAR)), "Got wrong name for FMTID_DocSummaryInformation\n");
625     hr = pFmtIdToPropStgName(&FMTID_UserDefinedProperties, name);
626     ok(hr == S_OK, "FmtIdToPropStgName failed: 0x%08x\n", hr);
627     ok(!memcmp(name, szDocSummaryInfo, (lstrlenW(szDocSummaryInfo) + 1) *
628      sizeof(WCHAR)), "Got wrong name for FMTID_DocSummaryInformation\n");
629     hr = pFmtIdToPropStgName(&IID_IPropertySetStorage, name);
630     ok(hr == S_OK, "FmtIdToPropStgName failed: 0x%08x\n", hr);
631     ok(!memcmp(name, szIID_IPropSetStg, (lstrlenW(szIID_IPropSetStg) + 1) *
632      sizeof(WCHAR)), "Got wrong name for IID_IPropertySetStorage\n");
633     }
634 
635     if(pPropStgNameToFmtId) {
636     /* test args first */
637     hr = pPropStgNameToFmtId(NULL, NULL);
638     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr);
639     hr = pPropStgNameToFmtId(NULL, &fmtid);
640     ok(hr == STG_E_INVALIDNAME, "Expected STG_E_INVALIDNAME, got 0x%08x\n",
641      hr);
642     hr = pPropStgNameToFmtId(szDocSummaryInfo, NULL);
643     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got 0x%08x\n", hr);
644     /* test the known format IDs */
645     hr = pPropStgNameToFmtId(szSummaryInfo, &fmtid);
646     ok(hr == S_OK, "PropStgNameToFmtId failed: 0x%08x\n", hr);
647     ok(!memcmp(&fmtid, &FMTID_SummaryInformation, sizeof(fmtid)),
648      "Got unexpected FMTID, expected FMTID_SummaryInformation\n");
649     hr = pPropStgNameToFmtId(szDocSummaryInfo, &fmtid);
650     ok(hr == S_OK, "PropStgNameToFmtId failed: 0x%08x\n", hr);
651     ok(!memcmp(&fmtid, &FMTID_DocSummaryInformation, sizeof(fmtid)),
652      "Got unexpected FMTID, expected FMTID_DocSummaryInformation\n");
653     /* test another GUID */
654     hr = pPropStgNameToFmtId(szIID_IPropSetStg, &fmtid);
655     ok(hr == S_OK, "PropStgNameToFmtId failed: 0x%08x\n", hr);
656     ok(!memcmp(&fmtid, &IID_IPropertySetStorage, sizeof(fmtid)),
657      "Got unexpected FMTID, expected IID_IPropertySetStorage\n");
658     /* now check case matching */
659     CharUpperW(szDocSummaryInfo + 1);
660     hr = pPropStgNameToFmtId(szDocSummaryInfo, &fmtid);
661     ok(hr == S_OK, "PropStgNameToFmtId failed: 0x%08x\n", hr);
662     ok(!memcmp(&fmtid, &FMTID_DocSummaryInformation, sizeof(fmtid)),
663      "Got unexpected FMTID, expected FMTID_DocSummaryInformation\n");
664     CharUpperW(szIID_IPropSetStg + 1);
665     hr = pPropStgNameToFmtId(szIID_IPropSetStg, &fmtid);
666     ok(hr == S_OK, "PropStgNameToFmtId failed: 0x%08x\n", hr);
667     ok(!memcmp(&fmtid, &IID_IPropertySetStorage, sizeof(fmtid)),
668      "Got unexpected FMTID, expected IID_IPropertySetStorage\n");
669     }
670 }
671 
672 START_TEST(stg_prop)
673 {
674     init_function_pointers();
675     testProps();
676     testCodepage();
677     testFmtId();
678 }
679