| /* | |
| * Implementation of the Microsoft Installer (msi.dll) | |
| * | |
| * Copyright 2002, 2005 Mike McCormack for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include <stdarg.h> | |
| #define COBJMACROS | |
| #define NONAMELESSUNION | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winreg.h" | |
| #include "winnls.h" | |
| #include "shlwapi.h" | |
| #include "wine/debug.h" | |
| #include "msi.h" | |
| #include "msiquery.h" | |
| #include "msidefs.h" | |
| #include "msipriv.h" | |
| #include "objidl.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(msi); | |
| #define MSI_MAX_PROPS 19 | |
| #include "pshpack1.h" | |
| typedef struct { | |
| WORD wByteOrder; | |
| WORD wFormat; | |
| DWORD dwOSVer; | |
| CLSID clsID; | |
| DWORD reserved; | |
| } PROPERTYSETHEADER; | |
| typedef struct { | |
| FMTID fmtid; | |
| DWORD dwOffset; | |
| } FORMATIDOFFSET; | |
| typedef struct { | |
| DWORD cbSection; | |
| DWORD cProperties; | |
| } PROPERTYSECTIONHEADER; | |
| typedef struct { | |
| DWORD propid; | |
| DWORD dwOffset; | |
| } PROPERTYIDOFFSET; | |
| typedef struct { | |
| DWORD type; | |
| union { | |
| INT i4; | |
| SHORT i2; | |
| FILETIME ft; | |
| struct { | |
| DWORD len; | |
| BYTE str[1]; | |
| } str; | |
| } u; | |
| } PROPERTY_DATA; | |
| #include "poppack.h" | |
| typedef struct { | |
| BOOL unicode; | |
| union { | |
| LPSTR a; | |
| LPWSTR w; | |
| } str; | |
| } awstring; | |
| typedef struct tagMSISUMMARYINFO | |
| { | |
| MSIOBJECTHDR hdr; | |
| MSIDATABASE *db; | |
| DWORD update_count; | |
| PROPVARIANT property[MSI_MAX_PROPS]; | |
| } MSISUMMARYINFO; | |
| static const WCHAR szSumInfo[] = { 5 ,'S','u','m','m','a','r','y', | |
| 'I','n','f','o','r','m','a','t','i','o','n',0 }; | |
| static void MSI_CloseSummaryInfo( MSIOBJECTHDR *arg ) | |
| { | |
| MSISUMMARYINFO *si = (MSISUMMARYINFO *) arg; | |
| msiobj_release( &si->db->hdr ); | |
| } | |
| static UINT get_type( UINT uiProperty ) | |
| { | |
| switch( uiProperty ) | |
| { | |
| case PID_CODEPAGE: | |
| return VT_I2; | |
| case PID_SUBJECT: | |
| case PID_AUTHOR: | |
| case PID_KEYWORDS: | |
| case PID_COMMENTS: | |
| case PID_TEMPLATE: | |
| case PID_LASTAUTHOR: | |
| case PID_REVNUMBER: | |
| case PID_APPNAME: | |
| case PID_TITLE: | |
| return VT_LPSTR; | |
| case PID_LASTPRINTED: | |
| case PID_CREATE_DTM: | |
| case PID_LASTSAVE_DTM: | |
| return VT_FILETIME; | |
| case PID_WORDCOUNT: | |
| case PID_CHARCOUNT: | |
| case PID_SECURITY: | |
| case PID_PAGECOUNT: | |
| return VT_I4; | |
| } | |
| return VT_EMPTY; | |
| } | |
| static UINT get_property_count( PROPVARIANT *property ) | |
| { | |
| UINT i, n = 0; | |
| if( !property ) | |
| return n; | |
| for( i=0; i<MSI_MAX_PROPS; i++ ) | |
| if( property[i].vt != VT_EMPTY ) | |
| n++; | |
| return n; | |
| } | |
| /* FIXME: doesn't deal with endian conversion */ | |
| static void read_properties_from_data( PROPVARIANT *prop, | |
| PROPERTYIDOFFSET *idofs, DWORD count, LPBYTE data, DWORD sz ) | |
| { | |
| UINT type; | |
| DWORD i; | |
| int size; | |
| PROPERTY_DATA *propdata; | |
| PROPVARIANT *property; | |
| /* now set all the properties */ | |
| for( i = 0; i < count; i++ ) | |
| { | |
| type = get_type( idofs[i].propid ); | |
| if( type == VT_EMPTY ) | |
| { | |
| ERR("propid %ld has unknown type\n", idofs[i].propid); | |
| break; | |
| } | |
| propdata = (PROPERTY_DATA*) &data[idofs[i].dwOffset]; | |
| /* check the type is the same as we expect */ | |
| if( type != propdata->type ) | |
| { | |
| ERR("wrong type\n"); | |
| break; | |
| } | |
| /* check we don't run off the end of the data */ | |
| size = sz - idofs[i].dwOffset - sizeof(DWORD); | |
| if( sizeof(DWORD) > size || | |
| ( type == VT_FILETIME && sizeof(FILETIME) > size ) || | |
| ( type == VT_LPSTR && (propdata->u.str.len + sizeof(DWORD)) > size ) ) | |
| { | |
| ERR("not enough data\n"); | |
| break; | |
| } | |
| property = &prop[ idofs[i].propid ]; | |
| property->vt = type; | |
| if( type == VT_LPSTR ) | |
| { | |
| LPSTR str = HeapAlloc( GetProcessHeap(), 0, propdata->u.str.len ); | |
| memcpy( str, propdata->u.str.str, propdata->u.str.len ); | |
| str[ propdata->u.str.len - 1 ] = 0; | |
| property->u.pszVal = str; | |
| } | |
| else if( type == VT_FILETIME ) | |
| property->u.filetime = propdata->u.ft; | |
| else if( type == VT_I2 ) | |
| property->u.iVal = propdata->u.i2; | |
| else if( type == VT_I4 ) | |
| property->u.lVal = propdata->u.i4; | |
| } | |
| } | |
| static UINT load_summary_info( MSISUMMARYINFO *si, IStream *stm ) | |
| { | |
| UINT ret = ERROR_FUNCTION_FAILED; | |
| PROPERTYSETHEADER set_hdr; | |
| FORMATIDOFFSET format_hdr; | |
| PROPERTYSECTIONHEADER section_hdr; | |
| PROPERTYIDOFFSET idofs[MSI_MAX_PROPS]; | |
| LPBYTE data = NULL; | |
| LARGE_INTEGER ofs; | |
| ULONG count, sz; | |
| HRESULT r; | |
| TRACE("%p %p\n", si, stm); | |
| /* read the header */ | |
| sz = sizeof set_hdr; | |
| r = IStream_Read( stm, &set_hdr, sz, &count ); | |
| if( FAILED(r) || count != sz ) | |
| return ret; | |
| if( set_hdr.wByteOrder != 0xfffe ) | |
| { | |
| ERR("property set not big-endian %04X\n", set_hdr.wByteOrder); | |
| return ret; | |
| } | |
| sz = sizeof format_hdr; | |
| r = IStream_Read( stm, &format_hdr, sz, &count ); | |
| if( FAILED(r) || count != sz ) | |
| return ret; | |
| /* check the format id is correct */ | |
| if( !IsEqualGUID( &FMTID_SummaryInformation, &format_hdr.fmtid ) ) | |
| return ret; | |
| /* seek to the location of the section */ | |
| ofs.QuadPart = format_hdr.dwOffset; | |
| r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, NULL ); | |
| if( FAILED(r) ) | |
| return ret; | |
| /* read the section itself */ | |
| sz = sizeof section_hdr; | |
| r = IStream_Read( stm, §ion_hdr, sz, &count ); | |
| if( FAILED(r) || count != sz ) | |
| return ret; | |
| if( section_hdr.cProperties > MSI_MAX_PROPS ) | |
| { | |
| ERR("too many properties %ld\n", section_hdr.cProperties); | |
| return ret; | |
| } | |
| /* read the offsets */ | |
| sz = sizeof idofs[0] * section_hdr.cProperties; | |
| r = IStream_Read( stm, idofs, sz, &count ); | |
| if( FAILED(r) || count != sz ) | |
| return ret; | |
| /* read all the data in one go */ | |
| sz = section_hdr.cbSection; | |
| data = HeapAlloc( GetProcessHeap(), 0, sz ); | |
| if( !data ) | |
| return ret; | |
| r = IStream_Read( stm, data, sz, &count ); | |
| if( SUCCEEDED(r) && count == sz ) | |
| { | |
| read_properties_from_data( si->property, idofs, | |
| section_hdr.cProperties, data, sz ); | |
| } | |
| HeapFree( GetProcessHeap(), 0, data ); | |
| return ret; | |
| } | |
| static DWORD write_dword( LPBYTE data, DWORD ofs, DWORD val ) | |
| { | |
| if( data ) | |
| { | |
| data[ofs++] = val&0xff; | |
| data[ofs++] = (val>>8)&0xff; | |
| data[ofs++] = (val>>16)&0xff; | |
| data[ofs++] = (val>>24)&0xff; | |
| } | |
| return 4; | |
| } | |
| static DWORD write_filetime( LPBYTE data, DWORD ofs, LPFILETIME ft ) | |
| { | |
| write_dword( data, ofs, ft->dwLowDateTime ); | |
| write_dword( data, ofs + 4, ft->dwHighDateTime ); | |
| return 8; | |
| } | |
| static DWORD write_string( LPBYTE data, DWORD ofs, LPCSTR str ) | |
| { | |
| DWORD len = lstrlenA( str ) + 1; | |
| write_dword( data, ofs, len ); | |
| if( data ) | |
| lstrcpyA( &data[ofs + 4], str ); | |
| return (7 + len) & ~3; | |
| } | |
| static UINT write_property_to_data( PROPVARIANT *prop, LPBYTE data ) | |
| { | |
| DWORD sz = 0; | |
| if( prop->vt == VT_EMPTY ) | |
| return sz; | |
| /* add the type */ | |
| sz += write_dword( data, sz, prop->vt ); | |
| switch( prop->vt ) | |
| { | |
| case VT_I2: | |
| sz += write_dword( data, sz, prop->u.iVal ); | |
| break; | |
| case VT_I4: | |
| sz += write_dword( data, sz, prop->u.lVal ); | |
| break; | |
| case VT_FILETIME: | |
| sz += write_filetime( data, sz, &prop->u.filetime ); | |
| break; | |
| case VT_LPSTR: | |
| sz += write_string( data, sz, prop->u.pszVal ); | |
| break; | |
| } | |
| return sz; | |
| } | |
| static UINT save_summary_info( MSISUMMARYINFO * si, IStream *stm ) | |
| { | |
| UINT ret = ERROR_FUNCTION_FAILED; | |
| PROPERTYSETHEADER set_hdr; | |
| FORMATIDOFFSET format_hdr; | |
| PROPERTYSECTIONHEADER section_hdr; | |
| PROPERTYIDOFFSET idofs[MSI_MAX_PROPS]; | |
| LPBYTE data = NULL; | |
| ULONG count, sz; | |
| HRESULT r; | |
| int i, n; | |
| /* write the header */ | |
| sz = sizeof set_hdr; | |
| memset( &set_hdr, 0, sz ); | |
| set_hdr.wByteOrder = 0xfffe; | |
| set_hdr.wFormat = 0; | |
| set_hdr.dwOSVer = 0x00020005; /* build 5, platform id 2 */ | |
| /* set_hdr.clsID is {00000000-0000-0000-0000-000000000000} */ | |
| set_hdr.reserved = 1; | |
| r = IStream_Write( stm, &set_hdr, sz, &count ); | |
| if( FAILED(r) || count != sz ) | |
| return ret; | |
| /* write the format header */ | |
| sz = sizeof format_hdr; | |
| memcpy( &format_hdr.fmtid, &FMTID_SummaryInformation, sizeof (FMTID) ); | |
| format_hdr.dwOffset = sizeof format_hdr + sizeof set_hdr; | |
| r = IStream_Write( stm, &format_hdr, sz, &count ); | |
| if( FAILED(r) || count != sz ) | |
| return ret; | |
| /* add up how much space the data will take and calculate the offsets */ | |
| section_hdr.cbSection = sizeof section_hdr; | |
| section_hdr.cbSection += (get_property_count( si->property ) * sizeof idofs[0]); | |
| section_hdr.cProperties = 0; | |
| n = 0; | |
| for( i = 0; i < MSI_MAX_PROPS; i++ ) | |
| { | |
| sz = write_property_to_data( &si->property[i], NULL ); | |
| if( !sz ) | |
| continue; | |
| idofs[ section_hdr.cProperties ].propid = i; | |
| idofs[ section_hdr.cProperties ].dwOffset = section_hdr.cbSection; | |
| section_hdr.cProperties++; | |
| section_hdr.cbSection += sz; | |
| } | |
| data = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, section_hdr.cbSection ); | |
| sz = 0; | |
| memcpy( &data[sz], §ion_hdr, sizeof section_hdr ); | |
| sz += sizeof section_hdr; | |
| memcpy( &data[sz], idofs, section_hdr.cProperties * sizeof idofs[0] ); | |
| sz += section_hdr.cProperties * sizeof idofs[0]; | |
| /* write out the data */ | |
| for( i = 0; i < MSI_MAX_PROPS; i++ ) | |
| sz += write_property_to_data( &si->property[i], &data[sz] ); | |
| r = IStream_Write( stm, data, sz, &count ); | |
| HeapFree( GetProcessHeap(), 0, data ); | |
| if( FAILED(r) || count != sz ) | |
| return ret; | |
| return ERROR_SUCCESS; | |
| } | |
| UINT WINAPI MsiGetSummaryInformationW( MSIHANDLE hDatabase, | |
| LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle ) | |
| { | |
| UINT ret = ERROR_SUCCESS; | |
| IStream *stm = NULL; | |
| MSISUMMARYINFO *si; | |
| MSIHANDLE handle; | |
| MSIDATABASE *db; | |
| DWORD grfMode; | |
| HRESULT r; | |
| TRACE("%ld %s %d %p\n", hDatabase, debugstr_w(szDatabase), | |
| uiUpdateCount, pHandle); | |
| if( !pHandle ) | |
| return ERROR_INVALID_PARAMETER; | |
| if( szDatabase ) | |
| { | |
| UINT res; | |
| res = MSI_OpenDatabaseW(szDatabase, NULL, &db); | |
| if( res != ERROR_SUCCESS ) | |
| return res; | |
| } | |
| else | |
| { | |
| db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE); | |
| if( !db ) | |
| return ERROR_INVALID_PARAMETER; | |
| } | |
| si = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO, | |
| sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo ); | |
| if( !si ) | |
| { | |
| ret = ERROR_FUNCTION_FAILED; | |
| goto end; | |
| } | |
| msiobj_addref( &db->hdr ); | |
| si->db = db; | |
| memset( &si->property, 0, sizeof si->property ); | |
| si->update_count = uiUpdateCount; | |
| /* read the stream... if we fail, we'll start with an empty property set */ | |
| grfMode = STGM_READ | STGM_SHARE_EXCLUSIVE; | |
| r = IStorage_OpenStream( si->db->storage, szSumInfo, 0, grfMode, 0, &stm ); | |
| if( SUCCEEDED(r) ) | |
| { | |
| load_summary_info( si, stm ); | |
| IStream_Release( stm ); | |
| } | |
| handle = alloc_msihandle( &si->hdr ); | |
| if( handle ) | |
| *pHandle = handle; | |
| else | |
| ret = ERROR_FUNCTION_FAILED; | |
| msiobj_release( &si->hdr ); | |
| end: | |
| if( db ) | |
| msiobj_release(&db->hdr); | |
| return ret; | |
| } | |
| UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase, | |
| LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle) | |
| { | |
| LPWSTR szwDatabase = NULL; | |
| UINT ret; | |
| TRACE("%ld %s %d %p\n", hDatabase, debugstr_a(szDatabase), | |
| uiUpdateCount, pHandle); | |
| if( szDatabase ) | |
| { | |
| szwDatabase = strdupAtoW( szDatabase ); | |
| if( !szwDatabase ) | |
| return ERROR_FUNCTION_FAILED; | |
| } | |
| ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, pHandle); | |
| HeapFree( GetProcessHeap(), 0, szwDatabase ); | |
| return ret; | |
| } | |
| UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, UINT *pCount) | |
| { | |
| MSISUMMARYINFO *si; | |
| TRACE("%ld %p\n",hSummaryInfo, pCount); | |
| si = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO ); | |
| if( !si ) | |
| return ERROR_INVALID_HANDLE; | |
| if( pCount ) | |
| *pCount = get_property_count( si->property ); | |
| msiobj_release( &si->hdr ); | |
| return ERROR_SUCCESS; | |
| } | |
| static UINT get_prop( MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, | |
| INT *piValue, FILETIME *pftValue, awstring *str, DWORD *pcchValueBuf) | |
| { | |
| MSISUMMARYINFO *si; | |
| PROPVARIANT *prop; | |
| UINT type; | |
| TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType, | |
| piValue, pftValue, str, pcchValueBuf); | |
| type = get_type( uiProperty ); | |
| if( puiDataType ) | |
| *puiDataType = type; | |
| si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); | |
| if( !si ) | |
| return ERROR_INVALID_HANDLE; | |
| prop = &si->property[uiProperty]; | |
| if( prop->vt != type ) | |
| goto end; | |
| switch( type ) | |
| { | |
| case VT_I2: | |
| if( piValue ) | |
| *piValue = prop->u.iVal; | |
| break; | |
| case VT_I4: | |
| if( piValue ) | |
| *piValue = prop->u.lVal; | |
| break; | |
| case VT_LPSTR: | |
| if( pcchValueBuf ) | |
| { | |
| DWORD len = 0; | |
| if( str->unicode ) | |
| { | |
| len = MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1, | |
| str->str.w, *pcchValueBuf ); | |
| } | |
| else | |
| { | |
| len = lstrlenA( prop->u.pszVal ); | |
| if( str->str.a ) | |
| lstrcpynA(str->str.a, prop->u.pszVal, *pcchValueBuf ); | |
| } | |
| *pcchValueBuf = len; | |
| } | |
| break; | |
| case VT_FILETIME: | |
| if( pftValue ) | |
| memcpy(pftValue, &prop->u.filetime, sizeof (FILETIME) ); | |
| break; | |
| case VT_EMPTY: | |
| break; | |
| default: | |
| FIXME("Unknown property variant type\n"); | |
| break; | |
| } | |
| end: | |
| msiobj_release( &si->hdr ); | |
| return ERROR_SUCCESS; | |
| } | |
| UINT WINAPI MsiSummaryInfoGetPropertyA( | |
| MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue, | |
| FILETIME *pftValue, LPSTR szValueBuf, DWORD *pcchValueBuf) | |
| { | |
| awstring str; | |
| TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType, | |
| piValue, pftValue, szValueBuf, pcchValueBuf ); | |
| str.unicode = FALSE; | |
| str.str.a = szValueBuf; | |
| return get_prop( handle, uiProperty, puiDataType, piValue, | |
| pftValue, &str, pcchValueBuf ); | |
| } | |
| UINT WINAPI MsiSummaryInfoGetPropertyW( | |
| MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue, | |
| FILETIME *pftValue, LPWSTR szValueBuf, DWORD *pcchValueBuf) | |
| { | |
| awstring str; | |
| TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType, | |
| piValue, pftValue, szValueBuf, pcchValueBuf ); | |
| str.unicode = TRUE; | |
| str.str.w = szValueBuf; | |
| return get_prop( handle, uiProperty, puiDataType, piValue, | |
| pftValue, &str, pcchValueBuf ); | |
| } | |
| static UINT set_prop( MSIHANDLE handle, UINT uiProperty, UINT uiDataType, | |
| INT iValue, FILETIME* pftValue, awstring *str ) | |
| { | |
| MSISUMMARYINFO *si; | |
| PROPVARIANT *prop; | |
| UINT type, len, ret = ERROR_SUCCESS; | |
| TRACE("%ld %u %u %i %p %p\n", handle, uiProperty, uiDataType, | |
| iValue, pftValue, str ); | |
| type = get_type( uiProperty ); | |
| if( type == VT_EMPTY || type != uiDataType ) | |
| return ERROR_DATATYPE_MISMATCH; | |
| if( uiDataType == VT_LPSTR && !str->str.w ) | |
| return ERROR_INVALID_PARAMETER; | |
| if( uiDataType == VT_FILETIME && !pftValue ) | |
| return ERROR_INVALID_PARAMETER; | |
| si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); | |
| if( !si ) | |
| return ERROR_INVALID_HANDLE; | |
| prop = &si->property[uiProperty]; | |
| if( prop->vt == VT_EMPTY ) | |
| { | |
| if( !si->update_count ) | |
| { | |
| ret = ERROR_FUNCTION_FAILED; | |
| goto end; | |
| } | |
| si->update_count--; | |
| prop->vt = type; | |
| } | |
| else if( prop->vt != type ) | |
| goto end; | |
| switch( type ) | |
| { | |
| case VT_I4: | |
| prop->u.lVal = iValue; | |
| break; | |
| case VT_I2: | |
| prop->u.iVal = iValue; | |
| break; | |
| case VT_FILETIME: | |
| memcpy( &prop->u.filetime, pftValue, sizeof prop->u.filetime ); | |
| break; | |
| case VT_LPSTR: | |
| HeapFree( GetProcessHeap(), 0, prop->u.pszVal ); | |
| if( str->unicode ) | |
| { | |
| len = WideCharToMultiByte( CP_ACP, 0, str->str.w, -1, | |
| NULL, 0, NULL, NULL ); | |
| prop->u.pszVal = HeapAlloc( GetProcessHeap(), 0, len ); | |
| WideCharToMultiByte( CP_ACP, 0, str->str.w, -1, | |
| prop->u.pszVal, len, NULL, NULL ); | |
| } | |
| else | |
| { | |
| len = lstrlenA( str->str.a ) + 1; | |
| prop->u.pszVal = HeapAlloc( GetProcessHeap(), 0, len ); | |
| lstrcpyA( prop->u.pszVal, str->str.a ); | |
| } | |
| break; | |
| } | |
| end: | |
| msiobj_release( &si->hdr ); | |
| return ret; | |
| } | |
| UINT WINAPI MsiSummaryInfoSetPropertyW( MSIHANDLE handle, UINT uiProperty, | |
| UINT uiDataType, INT iValue, FILETIME* pftValue, LPWSTR szValue ) | |
| { | |
| awstring str; | |
| TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType, | |
| iValue, pftValue, debugstr_w(szValue) ); | |
| str.unicode = TRUE; | |
| str.str.w = szValue; | |
| return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str ); | |
| } | |
| UINT WINAPI MsiSummaryInfoSetPropertyA( MSIHANDLE handle, UINT uiProperty, | |
| UINT uiDataType, INT iValue, FILETIME* pftValue, LPSTR szValue ) | |
| { | |
| awstring str; | |
| TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType, | |
| iValue, pftValue, debugstr_a(szValue) ); | |
| str.unicode = FALSE; | |
| str.str.a = szValue; | |
| return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str ); | |
| } | |
| UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle ) | |
| { | |
| IStream *stm = NULL; | |
| MSISUMMARYINFO *si; | |
| DWORD grfMode; | |
| HRESULT r; | |
| UINT ret = ERROR_FUNCTION_FAILED; | |
| TRACE("%ld\n", handle ); | |
| si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); | |
| if( !si ) | |
| return ERROR_INVALID_HANDLE; | |
| grfMode = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE; | |
| r = IStorage_CreateStream( si->db->storage, szSumInfo, grfMode, 0, 0, &stm ); | |
| if( SUCCEEDED(r) ) | |
| { | |
| ret = save_summary_info( si, stm ); | |
| IStream_Release( stm ); | |
| } | |
| msiobj_release( &si->hdr ); | |
| return ret; | |
| } |