msi: Added tests for MsiSetTargetPath.
[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     msiobj_release( &si->db->hdr );
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( 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 %ld 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 != %ld\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 %ld\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 %ld\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 %ld %ld\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, LPFILETIME 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( 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( 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( MSIDATABASE *db, UINT uiUpdateCount )
409 {
410     IStream *stm = NULL;
411     MSISUMMARYINFO *si;
412     DWORD grfMode;
413     HRESULT r;
414
415     TRACE("%p %d\n", db, uiUpdateCount );
416
417     si = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO, 
418                   sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo );
419     if( !si )
420         return si;
421
422     msiobj_addref( &db->hdr );
423     si->db = db;
424     memset( &si->property, 0, sizeof si->property );
425     si->update_count = uiUpdateCount;
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->db->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, uiUpdateCount );
466     if (si)
467     {
468         *pHandle = alloc_msihandle( &si->hdr );
469         if( *pHandle )
470             ret = ERROR_SUCCESS;
471         msiobj_release( &si->hdr );
472     }
473
474     if( db )
475         msiobj_release( &db->hdr );
476
477     return ret;
478 }
479
480 UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase, 
481               LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle)
482 {
483     LPWSTR szwDatabase = NULL;
484     UINT ret;
485
486     TRACE("%ld %s %d %p\n", hDatabase, debugstr_a(szDatabase), 
487           uiUpdateCount, pHandle);
488
489     if( szDatabase )
490     {
491         szwDatabase = strdupAtoW( szDatabase );
492         if( !szwDatabase )
493             return ERROR_FUNCTION_FAILED;
494     }
495
496     ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, pHandle);
497
498     msi_free( szwDatabase );
499
500     return ret;
501 }
502
503 UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, UINT *pCount)
504 {
505     MSISUMMARYINFO *si;
506
507     TRACE("%ld %p\n",hSummaryInfo, pCount);
508
509     si = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO );
510     if( !si )
511         return ERROR_INVALID_HANDLE;
512
513     if( pCount )
514         *pCount = get_property_count( si->property );
515     msiobj_release( &si->hdr );
516
517     return ERROR_SUCCESS;
518 }
519
520 static UINT get_prop( MSIHANDLE handle, UINT uiProperty, UINT *puiDataType,
521           INT *piValue, FILETIME *pftValue, awstring *str, DWORD *pcchValueBuf)
522 {
523     MSISUMMARYINFO *si;
524     PROPVARIANT *prop;
525     UINT ret = ERROR_SUCCESS;
526
527     TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
528           piValue, pftValue, str, pcchValueBuf);
529
530     si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
531     if( !si )
532         return ERROR_INVALID_HANDLE;
533
534     if ( uiProperty >= MSI_MAX_PROPS )
535     {
536         *puiDataType = VT_EMPTY;
537         return ret;
538     }
539
540     prop = &si->property[uiProperty];
541
542     if( puiDataType )
543         *puiDataType = prop->vt;
544
545     switch( prop->vt )
546     {
547     case VT_I2:
548         if( piValue )
549             *piValue = prop->u.iVal;
550         break;
551     case VT_I4:
552         if( piValue )
553             *piValue = prop->u.lVal;
554         break;
555     case VT_LPSTR:
556         if( pcchValueBuf )
557         {
558             DWORD len = 0;
559
560             if( str->unicode )
561             {
562                 len = MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1,
563                                            str->str.w, *pcchValueBuf );
564                 len--;
565             }
566             else
567             {
568                 len = lstrlenA( prop->u.pszVal );
569                 if( str->str.a )
570                     lstrcpynA(str->str.a, prop->u.pszVal, *pcchValueBuf );
571             }
572             if (len >= *pcchValueBuf)
573                 ret = ERROR_MORE_DATA;
574             *pcchValueBuf = len;
575         }
576         break;
577     case VT_FILETIME:
578         if( pftValue )
579             memcpy(pftValue, &prop->u.filetime, sizeof (FILETIME) );
580         break;
581     case VT_EMPTY:
582         break;
583     default:
584         FIXME("Unknown property variant type\n");
585         break;
586     }
587     msiobj_release( &si->hdr );
588     return ret;
589 }
590
591 LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty )
592 {
593     PROPVARIANT *prop;
594
595     if ( uiProperty >= MSI_MAX_PROPS )
596         return NULL;
597     prop = &si->property[uiProperty];
598     if( prop->vt != VT_LPSTR )
599         return NULL;
600     return strdupAtoW( prop->u.pszVal );
601 }
602
603 UINT WINAPI MsiSummaryInfoGetPropertyA(
604       MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue,
605       FILETIME *pftValue, LPSTR szValueBuf, DWORD *pcchValueBuf)
606 {
607     awstring str;
608
609     TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
610           piValue, pftValue, szValueBuf, pcchValueBuf );
611
612     str.unicode = FALSE;
613     str.str.a = szValueBuf;
614
615     return get_prop( handle, uiProperty, puiDataType, piValue,
616                      pftValue, &str, pcchValueBuf );
617 }
618
619 UINT WINAPI MsiSummaryInfoGetPropertyW(
620       MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue,
621       FILETIME *pftValue, LPWSTR szValueBuf, DWORD *pcchValueBuf)
622 {
623     awstring str;
624
625     TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType,
626           piValue, pftValue, szValueBuf, pcchValueBuf );
627
628     str.unicode = TRUE;
629     str.str.w = szValueBuf;
630
631     return get_prop( handle, uiProperty, puiDataType, piValue,
632                      pftValue, &str, pcchValueBuf );
633 }
634
635 static UINT set_prop( MSIHANDLE handle, UINT uiProperty, UINT uiDataType,
636                INT iValue, FILETIME* pftValue, awcstring *str )
637 {
638     MSISUMMARYINFO *si;
639     PROPVARIANT *prop;
640     UINT type, len, ret = ERROR_SUCCESS;
641
642     TRACE("%ld %u %u %i %p %p\n", handle, uiProperty, uiDataType,
643           iValue, pftValue, str );
644
645     type = get_type( uiProperty );
646     if( type == VT_EMPTY || type != uiDataType )
647         return ERROR_DATATYPE_MISMATCH;
648
649     if( uiDataType == VT_LPSTR && !str->str.w )
650         return ERROR_INVALID_PARAMETER;
651
652     if( uiDataType == VT_FILETIME && !pftValue )
653         return ERROR_INVALID_PARAMETER;
654
655     si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
656     if( !si )
657         return ERROR_INVALID_HANDLE;
658
659     prop = &si->property[uiProperty];
660
661     if( prop->vt == VT_EMPTY )
662     {
663         if( !si->update_count )
664         {
665             ret = ERROR_FUNCTION_FAILED;
666             goto end;
667         }
668         si->update_count--;
669     }
670     else if( prop->vt != type )
671         goto end;
672
673     free_prop( prop );
674     prop->vt = type;
675     switch( type )
676     {
677     case VT_I4:
678         prop->u.lVal = iValue;
679         break;
680     case VT_I2:
681         prop->u.iVal = iValue;
682         break;
683     case VT_FILETIME:
684         memcpy( &prop->u.filetime, pftValue, sizeof prop->u.filetime );
685         break;
686     case VT_LPSTR:
687         if( str->unicode )
688         {
689             len = WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
690                                        NULL, 0, NULL, NULL );
691             prop->u.pszVal = msi_alloc( len );
692             WideCharToMultiByte( CP_ACP, 0, str->str.w, -1,
693                                  prop->u.pszVal, len, NULL, NULL );
694         }
695         else
696         {
697             len = lstrlenA( str->str.a ) + 1;
698             prop->u.pszVal = msi_alloc( len );
699             lstrcpyA( prop->u.pszVal, str->str.a );
700         }
701         break;
702     }
703
704 end:
705     msiobj_release( &si->hdr );
706     return ret;
707 }
708
709 UINT WINAPI MsiSummaryInfoSetPropertyW( MSIHANDLE handle, UINT uiProperty,
710                UINT uiDataType, INT iValue, FILETIME* pftValue, LPCWSTR szValue )
711 {
712     awcstring str;
713
714     TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType,
715           iValue, pftValue, debugstr_w(szValue) );
716
717     str.unicode = TRUE;
718     str.str.w = szValue;
719     return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str );
720 }
721
722 UINT WINAPI MsiSummaryInfoSetPropertyA( MSIHANDLE handle, UINT uiProperty,
723                UINT uiDataType, INT iValue, FILETIME* pftValue, LPCSTR szValue )
724 {
725     awcstring str;
726
727     TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType,
728           iValue, pftValue, debugstr_a(szValue) );
729
730     str.unicode = FALSE;
731     str.str.a = szValue;
732     return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str );
733 }
734
735 UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle )
736 {
737     IStream *stm = NULL;
738     MSISUMMARYINFO *si;
739     DWORD grfMode;
740     HRESULT r;
741     UINT ret = ERROR_FUNCTION_FAILED;
742
743     TRACE("%ld\n", handle );
744
745     si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
746     if( !si )
747         return ERROR_INVALID_HANDLE;
748
749     grfMode = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE;
750     r = IStorage_CreateStream( si->db->storage, szSumInfo, grfMode, 0, 0, &stm );
751     if( SUCCEEDED(r) )
752     {
753         ret = save_summary_info( si, stm );
754         IStream_Release( stm );
755     }
756     msiobj_release( &si->hdr );
757
758     return ret;
759 }