wined3d: Remove superfluous casts of void pointers to other pointer types.
[wine] / dlls / msi / suminfo.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002, 2005 Mike McCormack for CodeWeavers
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 #include <stdarg.h>
22
23 #define COBJMACROS
24 #define NONAMELESSUNION
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winreg.h"
29 #include "winnls.h"
30 #include "shlwapi.h"
31 #include "wine/debug.h"
32 #include "msi.h"
33 #include "msiquery.h"
34 #include "msidefs.h"
35 #include "msipriv.h"
36 #include "objidl.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(msi);
39
40 #include "pshpack1.h"
41
42 typedef struct { 
43     WORD wByteOrder;
44     WORD wFormat;
45     DWORD dwOSVer;
46     CLSID clsID;
47     DWORD reserved;
48 } PROPERTYSETHEADER;
49
50 typedef struct { 
51     FMTID fmtid;
52     DWORD dwOffset;
53 } FORMATIDOFFSET;
54
55 typedef struct { 
56     DWORD cbSection;
57     DWORD cProperties;
58 } PROPERTYSECTIONHEADER; 
59  
60 typedef struct { 
61     DWORD propid;
62     DWORD dwOffset;
63 } PROPERTYIDOFFSET; 
64
65 typedef struct {
66     DWORD type;
67     union {
68         INT i4;
69         SHORT i2;
70         FILETIME ft;
71         struct {
72             DWORD len;
73             BYTE str[1];
74         } str;
75     } u;
76 } PROPERTY_DATA;
77  
78 #include "poppack.h"
79
80 #define SECT_HDR_SIZE (sizeof(PROPERTYSECTIONHEADER))
81
82 static const WCHAR szSumInfo[] = { 5 ,'S','u','m','m','a','r','y',
83                        'I','n','f','o','r','m','a','t','i','o','n',0 };
84
85 static void free_prop( PROPVARIANT *prop )
86 {
87     if (prop->vt == VT_LPSTR )
88         msi_free( prop->u.pszVal );
89     prop->vt = VT_EMPTY;
90 }
91
92 static void MSI_CloseSummaryInfo( MSIOBJECTHDR *arg )
93 {
94     MSISUMMARYINFO *si = (MSISUMMARYINFO *) arg;
95     DWORD i;
96
97     for( i = 0; i < MSI_MAX_PROPS; i++ )
98         free_prop( &si->property[i] );
99     IStorage_Release( si->storage );
100 }
101
102 static UINT get_type( UINT uiProperty )
103 {
104     switch( uiProperty )
105     {
106     case PID_CODEPAGE:
107          return VT_I2;
108
109     case PID_SUBJECT:
110     case PID_AUTHOR:
111     case PID_KEYWORDS:
112     case PID_COMMENTS:
113     case PID_TEMPLATE:
114     case PID_LASTAUTHOR:
115     case PID_REVNUMBER:
116     case PID_APPNAME:
117     case PID_TITLE:
118          return VT_LPSTR;
119
120     case PID_LASTPRINTED:
121     case PID_CREATE_DTM:
122     case PID_LASTSAVE_DTM:
123          return VT_FILETIME;
124
125     case PID_WORDCOUNT:
126     case PID_CHARCOUNT:
127     case PID_SECURITY:
128     case PID_PAGECOUNT:
129          return VT_I4;
130     }
131     return VT_EMPTY;
132 }
133
134 static UINT get_property_count( const PROPVARIANT *property )
135 {
136     UINT i, n = 0;
137
138     if( !property )
139         return n;
140     for( i = 0; i < MSI_MAX_PROPS; i++ )
141         if( property[i].vt != VT_EMPTY )
142             n++;
143     return n;
144 }
145
146 /* FIXME: doesn't deal with endian conversion */
147 static void read_properties_from_data( PROPVARIANT *prop, LPBYTE data, DWORD sz )
148 {
149     UINT type;
150     DWORD i;
151     int size;
152     PROPERTY_DATA *propdata;
153     PROPVARIANT *property;
154     PROPERTYIDOFFSET *idofs;
155     PROPERTYSECTIONHEADER *section_hdr;
156
157     section_hdr = (PROPERTYSECTIONHEADER*) &data[0];
158     idofs = (PROPERTYIDOFFSET*) &data[SECT_HDR_SIZE];
159
160     /* now set all the properties */
161     for( i = 0; i < section_hdr->cProperties; i++ )
162     {
163         type = get_type( idofs[i].propid );
164         if( type == VT_EMPTY )
165         {
166             ERR("propid %d has unknown type\n", idofs[i].propid);
167             break;
168         }
169
170         propdata = (PROPERTY_DATA*) &data[ idofs[i].dwOffset ];
171
172         /* check the type is the same as we expect */
173         if( type != propdata->type )
174         {
175             ERR("wrong type %d != %d\n", type, propdata->type);
176             break;
177         }
178
179         /* check we don't run off the end of the data */
180         size = sz - idofs[i].dwOffset - sizeof(DWORD);
181         if( sizeof(DWORD) > size ||
182             ( type == VT_FILETIME && sizeof(FILETIME) > size ) ||
183             ( type == VT_LPSTR && (propdata->u.str.len + sizeof(DWORD)) > size ) )
184         {
185             ERR("not enough data\n");
186             break;
187         }
188
189         if( idofs[i].propid >= MSI_MAX_PROPS )
190         {
191             ERR("Unknown property ID %d\n", idofs[i].propid );
192             break;
193         }
194
195         property = &prop[ idofs[i].propid ];
196         property->vt = type;
197
198         if( type == VT_LPSTR )
199         {
200             LPSTR str = msi_alloc( propdata->u.str.len );
201             memcpy( str, propdata->u.str.str, propdata->u.str.len );
202             str[ propdata->u.str.len - 1 ] = 0;
203             property->u.pszVal = str;
204         }
205         else if( type == VT_FILETIME )
206             property->u.filetime = propdata->u.ft;
207         else if( type == VT_I2 )
208             property->u.iVal = propdata->u.i2;
209         else if( type == VT_I4 )
210             property->u.lVal = propdata->u.i4;
211     }
212 }
213
214 static UINT load_summary_info( MSISUMMARYINFO *si, IStream *stm )
215 {
216     UINT ret = ERROR_FUNCTION_FAILED;
217     PROPERTYSETHEADER set_hdr;
218     FORMATIDOFFSET format_hdr;
219     PROPERTYSECTIONHEADER section_hdr;
220     LPBYTE data = NULL;
221     LARGE_INTEGER ofs;
222     ULONG count, sz;
223     HRESULT r;
224
225     TRACE("%p %p\n", si, stm);
226
227     /* read the header */
228     sz = sizeof set_hdr;
229     r = IStream_Read( stm, &set_hdr, sz, &count );
230     if( FAILED(r) || count != sz )
231         return ret;
232
233     if( set_hdr.wByteOrder != 0xfffe )
234     {
235         ERR("property set not big-endian %04X\n", set_hdr.wByteOrder);
236         return ret;
237     }
238
239     sz = sizeof format_hdr;
240     r = IStream_Read( stm, &format_hdr, sz, &count );
241     if( FAILED(r) || count != sz )
242         return ret;
243
244     /* check the format id is correct */
245     if( !IsEqualGUID( &FMTID_SummaryInformation, &format_hdr.fmtid ) )
246         return ret;
247
248     /* seek to the location of the section */
249     ofs.QuadPart = format_hdr.dwOffset;
250     r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, NULL );
251     if( FAILED(r) )
252         return ret;
253
254     /* read the section itself */
255     sz = SECT_HDR_SIZE;
256     r = IStream_Read( stm, &section_hdr, sz, &count );
257     if( FAILED(r) || count != sz )
258         return ret;
259
260     if( section_hdr.cProperties > MSI_MAX_PROPS )
261     {
262         ERR("too many properties %d\n", section_hdr.cProperties);
263         return ret;
264     }
265
266     data = msi_alloc( section_hdr.cbSection);
267     if( !data )
268         return ret;
269
270     memcpy( data, &section_hdr, SECT_HDR_SIZE );
271
272     /* read all the data in one go */
273     sz = section_hdr.cbSection - SECT_HDR_SIZE;
274     r = IStream_Read( stm, &data[ SECT_HDR_SIZE ], sz, &count );
275     if( SUCCEEDED(r) && count == sz )
276         read_properties_from_data( si->property, data, sz + SECT_HDR_SIZE );
277     else
278         ERR("failed to read properties %d %d\n", count, sz);
279
280     msi_free( data );
281     return ret;
282 }
283
284 static DWORD write_dword( LPBYTE data, DWORD ofs, DWORD val )
285 {
286     if( data )
287     {
288         data[ofs++] = val&0xff;
289         data[ofs++] = (val>>8)&0xff;
290         data[ofs++] = (val>>16)&0xff;
291         data[ofs++] = (val>>24)&0xff;
292     }
293     return 4;
294 }
295
296 static DWORD write_filetime( LPBYTE data, DWORD ofs, const FILETIME *ft )
297 {
298     write_dword( data, ofs, ft->dwLowDateTime );
299     write_dword( data, ofs + 4, ft->dwHighDateTime );
300     return 8;
301 }
302
303 static DWORD write_string( LPBYTE data, DWORD ofs, LPCSTR str )
304 {
305     DWORD len = lstrlenA( str ) + 1;
306     write_dword( data, ofs, len );
307     if( data )
308         memcpy( &data[ofs + 4], str, len );
309     return (7 + len) & ~3;
310 }
311
312 static UINT write_property_to_data( const PROPVARIANT *prop, LPBYTE data )
313 {
314     DWORD sz = 0;
315
316     if( prop->vt == VT_EMPTY )
317         return sz;
318
319     /* add the type */
320     sz += write_dword( data, sz, prop->vt );
321     switch( prop->vt )
322     {
323     case VT_I2:
324         sz += write_dword( data, sz, prop->u.iVal );
325         break;
326     case VT_I4:
327         sz += write_dword( data, sz, prop->u.lVal );
328         break;
329     case VT_FILETIME:
330         sz += write_filetime( data, sz, &prop->u.filetime );
331         break;
332     case VT_LPSTR:
333         sz += write_string( data, sz, prop->u.pszVal );
334         break;
335     }
336     return sz;
337 }
338
339 static UINT save_summary_info( const MSISUMMARYINFO * si, IStream *stm )
340 {
341     UINT ret = ERROR_FUNCTION_FAILED;
342     PROPERTYSETHEADER set_hdr;
343     FORMATIDOFFSET format_hdr;
344     PROPERTYSECTIONHEADER section_hdr;
345     PROPERTYIDOFFSET idofs[MSI_MAX_PROPS];
346     LPBYTE data = NULL;
347     ULONG count, sz;
348     HRESULT r;
349     int i, n;
350
351     /* write the header */
352     sz = sizeof set_hdr;
353     memset( &set_hdr, 0, sz );
354     set_hdr.wByteOrder = 0xfffe;
355     set_hdr.wFormat = 0;
356     set_hdr.dwOSVer = 0x00020005; /* build 5, platform id 2 */
357     /* set_hdr.clsID is {00000000-0000-0000-0000-000000000000} */
358     set_hdr.reserved = 1;
359     r = IStream_Write( stm, &set_hdr, sz, &count );
360     if( FAILED(r) || count != sz )
361         return ret;
362
363     /* write the format header */
364     sz = sizeof format_hdr;
365     memcpy( &format_hdr.fmtid, &FMTID_SummaryInformation, sizeof (FMTID) );
366     format_hdr.dwOffset = sizeof format_hdr + sizeof set_hdr;
367     r = IStream_Write( stm, &format_hdr, sz, &count );
368     if( FAILED(r) || count != sz )
369         return ret;
370
371     /* add up how much space the data will take and calculate the offsets */
372     section_hdr.cbSection = sizeof section_hdr;
373     section_hdr.cbSection += (get_property_count( si->property ) * sizeof idofs[0]);
374     section_hdr.cProperties = 0;
375     n = 0;
376     for( i = 0; i < MSI_MAX_PROPS; i++ )
377     {
378         sz = write_property_to_data( &si->property[i], NULL );
379         if( !sz )
380             continue;
381         idofs[ section_hdr.cProperties ].propid = i;
382         idofs[ section_hdr.cProperties ].dwOffset = section_hdr.cbSection;
383         section_hdr.cProperties++;
384         section_hdr.cbSection += sz;
385     }
386
387     data = msi_alloc_zero( section_hdr.cbSection );
388
389     sz = 0;
390     memcpy( &data[sz], &section_hdr, sizeof section_hdr );
391     sz += sizeof section_hdr;
392
393     memcpy( &data[sz], idofs, section_hdr.cProperties * sizeof idofs[0] );
394     sz += section_hdr.cProperties * sizeof idofs[0];
395
396     /* write out the data */
397     for( i = 0; i < MSI_MAX_PROPS; i++ )
398         sz += write_property_to_data( &si->property[i], &data[sz] );
399
400     r = IStream_Write( stm, data, sz, &count );
401     msi_free( data );
402     if( FAILED(r) || count != sz )
403         return ret;
404
405     return ERROR_SUCCESS;
406 }
407
408 MSISUMMARYINFO *MSI_GetSummaryInformationW( IStorage *stg, UINT uiUpdateCount )
409 {
410     IStream *stm = NULL;
411     MSISUMMARYINFO *si;
412     DWORD grfMode;
413     HRESULT r;
414
415     TRACE("%p %d\n", stg, uiUpdateCount );
416
417     si = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO, 
418                   sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo );
419     if( !si )
420         return si;
421
422     memset( &si->property, 0, sizeof si->property );
423     si->update_count = uiUpdateCount;
424     IStorage_AddRef( stg );
425     si->storage = stg;
426
427     /* read the stream... if we fail, we'll start with an empty property set */
428     grfMode = STGM_READ | STGM_SHARE_EXCLUSIVE;
429     r = IStorage_OpenStream( si->storage, szSumInfo, 0, grfMode, 0, &stm );
430     if( SUCCEEDED(r) )
431     {
432         load_summary_info( si, stm );
433         IStream_Release( stm );
434     }
435
436     return si;
437 }
438
439 UINT WINAPI MsiGetSummaryInformationW( MSIHANDLE hDatabase, 
440               LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle )
441 {
442     MSISUMMARYINFO *si;
443     MSIDATABASE *db;
444     UINT ret = ERROR_FUNCTION_FAILED;
445
446     TRACE("%ld %s %d %p\n", hDatabase, debugstr_w(szDatabase),
447            uiUpdateCount, pHandle);
448
449     if( !pHandle )
450         return ERROR_INVALID_PARAMETER;
451
452     if( szDatabase )
453     {
454         ret = MSI_OpenDatabaseW( szDatabase, NULL, &db );
455         if( ret != ERROR_SUCCESS )
456             return ret;
457     }
458     else
459     {
460         db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
461         if( !db )
462             return ERROR_INVALID_PARAMETER;
463     }
464
465     si = MSI_GetSummaryInformationW( db->storage, uiUpdateCount );
466     if (si)
467     {
468         *pHandle = alloc_msihandle( &si->hdr );
469         if( *pHandle )
470             ret = ERROR_SUCCESS;
471         else
472             ret = ERROR_NOT_ENOUGH_MEMORY;
473         msiobj_release( &si->hdr );
474     }
475
476     if( db )
477         msiobj_release( &db->hdr );
478
479     return ret;
480 }
481
482 UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase, 
483               LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle)
484 {
485     LPWSTR szwDatabase = NULL;
486     UINT ret;
487
488     TRACE("%ld %s %d %p\n", hDatabase, debugstr_a(szDatabase), 
489           uiUpdateCount, pHandle);
490
491     if( szDatabase )
492     {
493         szwDatabase = strdupAtoW( szDatabase );
494         if( !szwDatabase )
495             return ERROR_FUNCTION_FAILED;
496     }
497
498     ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, pHandle);
499
500     msi_free( szwDatabase );
501
502     return ret;
503 }
504
505 UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, UINT *pCount)
506 {
507     MSISUMMARYINFO *si;
508
509     TRACE("%ld %p\n", hSummaryInfo, pCount);
510
511     si = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO );
512     if( !si )
513         return ERROR_INVALID_HANDLE;
514
515     if( pCount )
516         *pCount = get_property_count( si->property );
517     msiobj_release( &si->hdr );
518
519     return ERROR_SUCCESS;
520 }
521
522 static UINT get_prop( MSIHANDLE handle, UINT uiProperty, UINT *puiDataType,
523           INT *piValue, FILETIME *pftValue, awstring *str, DWORD *pcchValueBuf)
524 {
525     MSISUMMARYINFO *si;
526     PROPVARIANT *prop;
527     UINT ret = ERROR_SUCCESS;
528
529     TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
530           piValue, pftValue, str, pcchValueBuf);
531
532     if ( uiProperty >= MSI_MAX_PROPS )
533     {
534         if (puiDataType) *puiDataType = VT_EMPTY;
535         return ERROR_UNKNOWN_PROPERTY;
536     }
537
538     si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
539     if( !si )
540         return ERROR_INVALID_HANDLE;
541
542     prop = &si->property[uiProperty];
543
544     if( puiDataType )
545         *puiDataType = prop->vt;
546
547     switch( prop->vt )
548     {
549     case VT_I2:
550         if( piValue )
551             *piValue = prop->u.iVal;
552         break;
553     case VT_I4:
554         if( piValue )
555             *piValue = prop->u.lVal;
556         break;
557     case VT_LPSTR:
558         if( pcchValueBuf )
559         {
560             DWORD len = 0;
561
562             if( str->unicode )
563             {
564                 len = MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1,
565                                            str->str.w, *pcchValueBuf );
566                 len--;
567             }
568             else
569             {
570                 len = lstrlenA( prop->u.pszVal );
571                 if( str->str.a )
572                     lstrcpynA(str->str.a, prop->u.pszVal, *pcchValueBuf );
573             }
574             if (len >= *pcchValueBuf)
575                 ret = ERROR_MORE_DATA;
576             *pcchValueBuf = len;
577         }
578         break;
579     case VT_FILETIME:
580         if( pftValue )
581             memcpy(pftValue, &prop->u.filetime, sizeof (FILETIME) );
582         break;
583     case VT_EMPTY:
584         break;
585     default:
586         FIXME("Unknown property variant type\n");
587         break;
588     }
589     msiobj_release( &si->hdr );
590     return ret;
591 }
592
593 LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty )
594 {
595     PROPVARIANT *prop;
596
597     if ( uiProperty >= MSI_MAX_PROPS )
598         return NULL;
599     prop = &si->property[uiProperty];
600     if( prop->vt != VT_LPSTR )
601         return NULL;
602     return strdupAtoW( prop->u.pszVal );
603 }
604
605 LPWSTR msi_get_suminfo_product( IStorage *stg )
606 {
607     MSISUMMARYINFO *si;
608     LPWSTR prod;
609
610     si = MSI_GetSummaryInformationW( stg, 0 );
611     if (!si)
612     {
613         ERR("no summary information!\n");
614         return NULL;
615     }
616     prod = msi_suminfo_dup_string( si, PID_REVNUMBER );
617     msiobj_release( &si->hdr );
618     return prod;
619 }
620
621 UINT WINAPI MsiSummaryInfoGetPropertyA(
622       MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue,
623       FILETIME *pftValue, LPSTR szValueBuf, DWORD *pcchValueBuf)
624 {
625     awstring str;
626
627     TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
628           piValue, pftValue, szValueBuf, pcchValueBuf );
629
630     str.unicode = FALSE;
631     str.str.a = szValueBuf;
632
633     return get_prop( handle, uiProperty, puiDataType, piValue,
634                      pftValue, &str, pcchValueBuf );
635 }
636
637 UINT WINAPI MsiSummaryInfoGetPropertyW(
638       MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue,
639       FILETIME *pftValue, LPWSTR szValueBuf, DWORD *pcchValueBuf)
640 {
641     awstring str;
642
643     TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
644           piValue, pftValue, szValueBuf, pcchValueBuf );
645
646     str.unicode = TRUE;
647     str.str.w = szValueBuf;
648
649     return get_prop( handle, uiProperty, puiDataType, piValue,
650                      pftValue, &str, pcchValueBuf );
651 }
652
653 static UINT set_prop( MSIHANDLE handle, UINT uiProperty, UINT uiDataType,
654                INT iValue, FILETIME* pftValue, awcstring *str )
655 {
656     MSISUMMARYINFO *si;
657     PROPVARIANT *prop;
658     UINT type, len, ret = ERROR_SUCCESS;
659
660     TRACE("%ld %u %u %i %p %p\n", handle, uiProperty, uiDataType,
661           iValue, pftValue, str );
662
663     type = get_type( uiProperty );
664     if( type == VT_EMPTY || type != uiDataType )
665         return ERROR_DATATYPE_MISMATCH;
666
667     if( uiDataType == VT_LPSTR && !str->str.w )
668         return ERROR_INVALID_PARAMETER;
669
670     if( uiDataType == VT_FILETIME && !pftValue )
671         return ERROR_INVALID_PARAMETER;
672
673     si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
674     if( !si )
675         return ERROR_INVALID_HANDLE;
676
677     prop = &si->property[uiProperty];
678
679     if( prop->vt == VT_EMPTY )
680     {
681         if( !si->update_count )
682         {
683             ret = ERROR_FUNCTION_FAILED;
684             goto end;
685         }
686         si->update_count--;
687     }
688     else if( prop->vt != type )
689         goto end;
690
691     free_prop( prop );
692     prop->vt = type;
693     switch( type )
694     {
695     case VT_I4:
696         prop->u.lVal = iValue;
697         break;
698     case VT_I2:
699         prop->u.iVal = iValue;
700         break;
701     case VT_FILETIME:
702         memcpy( &prop->u.filetime, pftValue, sizeof prop->u.filetime );
703         break;
704     case VT_LPSTR:
705         if( str->unicode )
706         {
707             len = WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
708                                        NULL, 0, NULL, NULL );
709             prop->u.pszVal = msi_alloc( len );
710             WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
711                                  prop->u.pszVal, len, NULL, NULL );
712         }
713         else
714         {
715             len = lstrlenA( str->str.a ) + 1;
716             prop->u.pszVal = msi_alloc( len );
717             lstrcpyA( prop->u.pszVal, str->str.a );
718         }
719         break;
720     }
721
722 end:
723     msiobj_release( &si->hdr );
724     return ret;
725 }
726
727 UINT WINAPI MsiSummaryInfoSetPropertyW( MSIHANDLE handle, UINT uiProperty,
728                UINT uiDataType, INT iValue, FILETIME* pftValue, LPCWSTR szValue )
729 {
730     awcstring str;
731
732     TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType,
733           iValue, pftValue, debugstr_w(szValue) );
734
735     str.unicode = TRUE;
736     str.str.w = szValue;
737     return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str );
738 }
739
740 UINT WINAPI MsiSummaryInfoSetPropertyA( MSIHANDLE handle, UINT uiProperty,
741                UINT uiDataType, INT iValue, FILETIME* pftValue, LPCSTR szValue )
742 {
743     awcstring str;
744
745     TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType,
746           iValue, pftValue, debugstr_a(szValue) );
747
748     str.unicode = FALSE;
749     str.str.a = szValue;
750     return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str );
751 }
752
753 UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle )
754 {
755     IStream *stm = NULL;
756     MSISUMMARYINFO *si;
757     DWORD grfMode;
758     HRESULT r;
759     UINT ret = ERROR_FUNCTION_FAILED;
760
761     TRACE("%ld\n", handle );
762
763     si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
764     if( !si )
765         return ERROR_INVALID_HANDLE;
766
767     grfMode = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
768     r = IStorage_CreateStream( si->storage, szSumInfo, grfMode, 0, 0, &stm );
769     if( SUCCEEDED(r) )
770     {
771         ret = save_summary_info( si, stm );
772         IStream_Release( stm );
773     }
774     msiobj_release( &si->hdr );
775
776     return ret;
777 }