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