2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2002-2004 Mike McCormack for CodeWeavers
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.
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.
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
29 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
44 #define MSIFIELD_NULL 0
45 #define MSIFIELD_INT 1
46 #define MSIFIELD_WSTR 3
47 #define MSIFIELD_STREAM 4
48 #define MSIFIELD_INTPTR 5
50 static void MSI_FreeField( MSIFIELD *field )
59 msi_free( field->u.szwVal);
62 IStream_Release( field->u.stream );
65 ERR("Invalid field type %d\n", field->type);
69 void MSI_CloseRecord( MSIOBJECTHDR *arg )
71 MSIRECORD *rec = (MSIRECORD *) arg;
74 for( i=0; i<=rec->count; i++ )
75 MSI_FreeField( &rec->fields[i] );
78 MSIRECORD *MSI_CreateRecord( UINT cParams )
83 TRACE("%d\n", cParams);
88 len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams;
89 rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord );
95 MSIHANDLE WINAPI MsiCreateRecord( UINT cParams )
100 TRACE("%d\n", cParams);
102 rec = MSI_CreateRecord( cParams );
105 ret = alloc_msihandle( &rec->hdr );
106 msiobj_release( &rec->hdr );
111 UINT MSI_RecordGetFieldCount( const MSIRECORD *rec )
116 UINT WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
121 TRACE("%d\n", handle );
123 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
127 msiobj_lock( &rec->hdr );
128 ret = MSI_RecordGetFieldCount( rec );
129 msiobj_unlock( &rec->hdr );
130 msiobj_release( &rec->hdr );
135 static BOOL string2intW( LPCWSTR str, int *out )
140 if( *p == '-' ) /* skip the minus sign */
144 if( (*p < '0') || (*p > '9') )
151 if( str[0] == '-' ) /* check if it's negative */
158 UINT MSI_RecordCopyField( MSIRECORD *in_rec, UINT in_n,
159 MSIRECORD *out_rec, UINT out_n )
161 UINT r = ERROR_SUCCESS;
163 msiobj_lock( &in_rec->hdr );
165 if ( in_n > in_rec->count || out_n > out_rec->count )
166 r = ERROR_FUNCTION_FAILED;
167 else if ( in_rec != out_rec || in_n != out_n )
172 in = &in_rec->fields[in_n];
173 out = &out_rec->fields[out_n];
180 out->u.iVal = in->u.iVal;
182 case MSIFIELD_INTPTR:
183 out->u.pVal = in->u.pVal;
186 str = strdupW( in->u.szwVal );
188 r = ERROR_OUTOFMEMORY;
192 case MSIFIELD_STREAM:
193 IStream_AddRef( in->u.stream );
194 out->u.stream = in->u.stream;
197 ERR("invalid field type %d\n", in->type);
199 if (r == ERROR_SUCCESS)
200 out->type = in->type;
203 msiobj_unlock( &in_rec->hdr );
208 INT_PTR MSI_RecordGetIntPtr( MSIRECORD *rec, UINT iField )
212 TRACE( "%p %d\n", rec, iField );
214 if( iField > rec->count )
217 switch( rec->fields[iField].type )
220 return rec->fields[iField].u.iVal;
221 case MSIFIELD_INTPTR:
222 return rec->fields[iField].u.pVal;
224 if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
234 int MSI_RecordGetInteger( MSIRECORD *rec, UINT iField)
238 TRACE("%p %d\n", rec, iField );
240 if( iField > rec->count )
241 return MSI_NULL_INTEGER;
243 switch( rec->fields[iField].type )
246 return rec->fields[iField].u.iVal;
247 case MSIFIELD_INTPTR:
248 return rec->fields[iField].u.pVal;
250 if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
252 return MSI_NULL_INTEGER;
257 return MSI_NULL_INTEGER;
260 int WINAPI MsiRecordGetInteger( MSIHANDLE handle, UINT iField)
265 TRACE("%d %d\n", handle, iField );
267 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
269 return MSI_NULL_INTEGER;
271 msiobj_lock( &rec->hdr );
272 ret = MSI_RecordGetInteger( rec, iField );
273 msiobj_unlock( &rec->hdr );
274 msiobj_release( &rec->hdr );
279 UINT WINAPI MsiRecordClearData( MSIHANDLE handle )
284 TRACE("%d\n", handle );
286 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
288 return ERROR_INVALID_HANDLE;
290 msiobj_lock( &rec->hdr );
291 for( i=0; i<=rec->count; i++)
293 MSI_FreeField( &rec->fields[i] );
294 rec->fields[i].type = MSIFIELD_NULL;
295 rec->fields[i].u.iVal = 0;
297 msiobj_unlock( &rec->hdr );
298 msiobj_release( &rec->hdr );
300 return ERROR_SUCCESS;
303 UINT MSI_RecordSetIntPtr( MSIRECORD *rec, UINT iField, INT_PTR pVal )
305 TRACE("%p %u %ld\n", rec, iField, pVal);
307 if( iField > rec->count )
308 return ERROR_INVALID_PARAMETER;
310 MSI_FreeField( &rec->fields[iField] );
311 rec->fields[iField].type = MSIFIELD_INTPTR;
312 rec->fields[iField].u.pVal = pVal;
314 return ERROR_SUCCESS;
317 UINT MSI_RecordSetInteger( MSIRECORD *rec, UINT iField, int iVal )
319 TRACE("%p %u %d\n", rec, iField, iVal);
321 if( iField > rec->count )
322 return ERROR_INVALID_PARAMETER;
324 MSI_FreeField( &rec->fields[iField] );
325 rec->fields[iField].type = MSIFIELD_INT;
326 rec->fields[iField].u.iVal = iVal;
328 return ERROR_SUCCESS;
331 UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, UINT iField, int iVal )
336 TRACE("%d %u %d\n", handle, iField, iVal);
338 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
340 return ERROR_INVALID_HANDLE;
342 msiobj_lock( &rec->hdr );
343 ret = MSI_RecordSetInteger( rec, iField, iVal );
344 msiobj_unlock( &rec->hdr );
345 msiobj_release( &rec->hdr );
349 BOOL MSI_RecordIsNull( MSIRECORD *rec, UINT iField )
353 TRACE("%p %d\n", rec, iField );
355 r = ( iField > rec->count ) ||
356 ( rec->fields[iField].type == MSIFIELD_NULL );
361 BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, UINT iField )
366 TRACE("%d %d\n", handle, iField );
368 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
371 msiobj_lock( &rec->hdr );
372 ret = MSI_RecordIsNull( rec, iField );
373 msiobj_unlock( &rec->hdr );
374 msiobj_release( &rec->hdr );
379 UINT MSI_RecordGetStringA(MSIRECORD *rec, UINT iField,
380 LPSTR szValue, LPDWORD pcchValue)
385 TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
387 if( iField > rec->count )
389 if ( szValue && *pcchValue > 0 )
393 return ERROR_SUCCESS;
397 switch( rec->fields[iField].type )
400 wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
401 len = lstrlenA( buffer );
403 lstrcpynA(szValue, buffer, *pcchValue);
406 len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
407 NULL, 0 , NULL, NULL);
409 WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
410 szValue, *pcchValue, NULL, NULL);
411 if( szValue && *pcchValue && len>*pcchValue )
412 szValue[*pcchValue-1] = 0;
417 if( szValue && *pcchValue > 0 )
421 ret = ERROR_INVALID_PARAMETER;
425 if( szValue && *pcchValue <= len )
426 ret = ERROR_MORE_DATA;
432 UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, UINT iField,
433 LPSTR szValue, LPDWORD pcchValue)
438 TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
440 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
442 return ERROR_INVALID_HANDLE;
443 msiobj_lock( &rec->hdr );
444 ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
445 msiobj_unlock( &rec->hdr );
446 msiobj_release( &rec->hdr );
450 const WCHAR *MSI_RecordGetString( const MSIRECORD *rec, UINT iField )
452 if( iField > rec->count )
455 if( rec->fields[iField].type != MSIFIELD_WSTR )
458 return rec->fields[iField].u.szwVal;
461 UINT MSI_RecordGetStringW(MSIRECORD *rec, UINT iField,
462 LPWSTR szValue, LPDWORD pcchValue)
466 static const WCHAR szFormat[] = { '%','d',0 };
468 TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
470 if( iField > rec->count )
472 if ( szValue && *pcchValue > 0 )
476 return ERROR_SUCCESS;
480 switch( rec->fields[iField].type )
483 wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
484 len = lstrlenW( buffer );
486 lstrcpynW(szValue, buffer, *pcchValue);
489 len = lstrlenW( rec->fields[iField].u.szwVal );
491 lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue);
494 if( szValue && *pcchValue > 0 )
500 if( szValue && *pcchValue <= len )
501 ret = ERROR_MORE_DATA;
507 UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, UINT iField,
508 LPWSTR szValue, LPDWORD pcchValue)
513 TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
515 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
517 return ERROR_INVALID_HANDLE;
519 msiobj_lock( &rec->hdr );
520 ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
521 msiobj_unlock( &rec->hdr );
522 msiobj_release( &rec->hdr );
526 static UINT msi_get_stream_size( IStream *stm )
531 r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
534 return stat.cbSize.QuadPart;
537 static UINT MSI_RecordDataSize(MSIRECORD *rec, UINT iField)
539 TRACE("%p %d\n", rec, iField);
541 if( iField > rec->count )
544 switch( rec->fields[iField].type )
549 return lstrlenW( rec->fields[iField].u.szwVal );
552 case MSIFIELD_STREAM:
553 return msi_get_stream_size( rec->fields[iField].u.stream );
558 UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, UINT iField)
563 TRACE("%d %d\n", handle, iField);
565 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
568 msiobj_lock( &rec->hdr );
569 ret = MSI_RecordDataSize( rec, iField);
570 msiobj_unlock( &rec->hdr );
571 msiobj_release( &rec->hdr );
575 static UINT MSI_RecordSetStringA( MSIRECORD *rec, UINT iField, LPCSTR szValue )
579 TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
581 if( iField > rec->count )
582 return ERROR_INVALID_FIELD;
584 MSI_FreeField( &rec->fields[iField] );
585 if( szValue && szValue[0] )
587 str = strdupAtoW( szValue );
588 rec->fields[iField].type = MSIFIELD_WSTR;
589 rec->fields[iField].u.szwVal = str;
593 rec->fields[iField].type = MSIFIELD_NULL;
594 rec->fields[iField].u.szwVal = NULL;
600 UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, UINT iField, LPCSTR szValue )
605 TRACE("%d %d %s\n", handle, iField, debugstr_a(szValue));
607 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
609 return ERROR_INVALID_HANDLE;
610 msiobj_lock( &rec->hdr );
611 ret = MSI_RecordSetStringA( rec, iField, szValue );
612 msiobj_unlock( &rec->hdr );
613 msiobj_release( &rec->hdr );
617 UINT MSI_RecordSetStringW( MSIRECORD *rec, UINT iField, LPCWSTR szValue )
621 TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
623 if( iField > rec->count )
624 return ERROR_INVALID_FIELD;
626 MSI_FreeField( &rec->fields[iField] );
628 if( szValue && szValue[0] )
630 str = strdupW( szValue );
631 rec->fields[iField].type = MSIFIELD_WSTR;
632 rec->fields[iField].u.szwVal = str;
636 rec->fields[iField].type = MSIFIELD_NULL;
637 rec->fields[iField].u.szwVal = NULL;
643 UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, UINT iField, LPCWSTR szValue )
648 TRACE("%d %d %s\n", handle, iField, debugstr_w(szValue));
650 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
652 return ERROR_INVALID_HANDLE;
654 msiobj_lock( &rec->hdr );
655 ret = MSI_RecordSetStringW( rec, iField, szValue );
656 msiobj_unlock( &rec->hdr );
657 msiobj_release( &rec->hdr );
661 /* read the data in a file into an IStream */
662 static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
664 DWORD sz, szHighWord = 0, read;
668 ULARGE_INTEGER ulSize;
670 TRACE("reading %s\n", debugstr_w(szFile));
672 /* read the file into memory */
673 handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
674 if( handle == INVALID_HANDLE_VALUE )
675 return GetLastError();
676 sz = GetFileSize(handle, &szHighWord);
677 if( sz != INVALID_FILE_SIZE && szHighWord == 0 )
679 hGlob = GlobalAlloc(GMEM_FIXED, sz);
682 BOOL r = ReadFile(handle, hGlob, sz, &read, NULL);
692 return ERROR_FUNCTION_FAILED;
694 /* make a stream out of it, and set the correct file size */
695 hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
699 return ERROR_FUNCTION_FAILED;
702 /* set the correct size - CreateStreamOnHGlobal screws it up */
703 ulSize.QuadPart = sz;
704 IStream_SetSize(*pstm, ulSize);
706 TRACE("read %s, %d bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
708 return ERROR_SUCCESS;
711 UINT MSI_RecordSetStream(MSIRECORD *rec, UINT iField, IStream *stream)
713 if ( (iField == 0) || (iField > rec->count) )
714 return ERROR_INVALID_PARAMETER;
716 MSI_FreeField( &rec->fields[iField] );
717 rec->fields[iField].type = MSIFIELD_STREAM;
718 rec->fields[iField].u.stream = stream;
720 return ERROR_SUCCESS;
723 UINT MSI_RecordSetStreamFromFileW(MSIRECORD *rec, UINT iField, LPCWSTR szFilename)
728 if( (iField == 0) || (iField > rec->count) )
729 return ERROR_INVALID_PARAMETER;
731 /* no filename means we should seek back to the start of the stream */
737 if( rec->fields[iField].type != MSIFIELD_STREAM )
738 return ERROR_INVALID_FIELD;
740 stm = rec->fields[iField].u.stream;
742 return ERROR_INVALID_FIELD;
745 r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
747 return ERROR_FUNCTION_FAILED;
751 /* read the file into a stream and save the stream in the record */
752 r = RECORD_StreamFromFile(szFilename, &stm);
753 if( r != ERROR_SUCCESS )
756 /* if all's good, store it in the record */
757 MSI_RecordSetStream(rec, iField, stm);
760 return ERROR_SUCCESS;
763 UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, UINT iField, LPCSTR szFilename)
768 TRACE("%d %d %s\n", hRecord, iField, debugstr_a(szFilename));
772 wstr = strdupAtoW( szFilename );
774 return ERROR_OUTOFMEMORY;
776 ret = MsiRecordSetStreamW(hRecord, iField, wstr);
782 UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, UINT iField, LPCWSTR szFilename)
787 TRACE("%d %d %s\n", handle, iField, debugstr_w(szFilename));
789 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
791 return ERROR_INVALID_HANDLE;
793 msiobj_lock( &rec->hdr );
794 ret = MSI_RecordSetStreamFromFileW( rec, iField, szFilename );
795 msiobj_unlock( &rec->hdr );
796 msiobj_release( &rec->hdr );
800 UINT MSI_RecordReadStream(MSIRECORD *rec, UINT iField, char *buf, LPDWORD sz)
806 TRACE("%p %d %p %p\n", rec, iField, buf, sz);
809 return ERROR_INVALID_PARAMETER;
811 if( iField > rec->count)
812 return ERROR_INVALID_PARAMETER;
814 if ( rec->fields[iField].type == MSIFIELD_NULL )
817 return ERROR_INVALID_DATA;
820 if( rec->fields[iField].type != MSIFIELD_STREAM )
821 return ERROR_INVALID_DATATYPE;
823 stm = rec->fields[iField].u.stream;
825 return ERROR_INVALID_PARAMETER;
827 /* if there's no buffer pointer, calculate the length to the end */
831 ULARGE_INTEGER end, cur;
833 ofs.QuadPart = cur.QuadPart = 0;
835 r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
836 IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
837 ofs.QuadPart = cur.QuadPart;
838 IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
839 *sz = end.QuadPart - cur.QuadPart;
841 return ERROR_SUCCESS;
846 r = IStream_Read( stm, buf, *sz, &count );
850 return ERROR_FUNCTION_FAILED;
855 return ERROR_SUCCESS;
858 UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, UINT iField, char *buf, LPDWORD sz)
863 TRACE("%d %d %p %p\n", handle, iField, buf, sz);
865 rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
867 return ERROR_INVALID_HANDLE;
868 msiobj_lock( &rec->hdr );
869 ret = MSI_RecordReadStream( rec, iField, buf, sz );
870 msiobj_unlock( &rec->hdr );
871 msiobj_release( &rec->hdr );
875 UINT MSI_RecordSetIStream( MSIRECORD *rec, UINT iField, IStream *stm )
877 TRACE("%p %d %p\n", rec, iField, stm);
879 if( iField > rec->count )
880 return ERROR_INVALID_FIELD;
882 MSI_FreeField( &rec->fields[iField] );
884 rec->fields[iField].type = MSIFIELD_STREAM;
885 rec->fields[iField].u.stream = stm;
886 IStream_AddRef( stm );
888 return ERROR_SUCCESS;
891 UINT MSI_RecordGetIStream( MSIRECORD *rec, UINT iField, IStream **pstm)
893 TRACE("%p %d %p\n", rec, iField, pstm);
895 if( iField > rec->count )
896 return ERROR_INVALID_FIELD;
898 if( rec->fields[iField].type != MSIFIELD_STREAM )
899 return ERROR_INVALID_FIELD;
901 *pstm = rec->fields[iField].u.stream;
902 IStream_AddRef( *pstm );
904 return ERROR_SUCCESS;
907 static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name )
915 stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE;
916 r = SHCreateStreamOnFileW( name, stgm, &out );
918 return ERROR_FUNCTION_FAILED;
921 r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size );
926 r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
930 r = IStream_CopyTo( stm, out, size, NULL, NULL );
933 IStream_Release( out );
935 return ERROR_FUNCTION_FAILED;
936 return ERROR_SUCCESS;
939 UINT MSI_RecordStreamToFile( MSIRECORD *rec, UINT iField, LPCWSTR name )
944 TRACE("%p %u %s\n", rec, iField, debugstr_w(name));
946 msiobj_lock( &rec->hdr );
948 r = MSI_RecordGetIStream( rec, iField, &stm );
949 if( r == ERROR_SUCCESS )
951 r = msi_dump_stream_to_file( stm, name );
952 IStream_Release( stm );
955 msiobj_unlock( &rec->hdr );
960 MSIRECORD *MSI_CloneRecord(MSIRECORD *rec)
965 count = MSI_RecordGetFieldCount(rec);
966 clone = MSI_CreateRecord(count);
970 for (i = 0; i <= count; i++)
972 if (rec->fields[i].type == MSIFIELD_STREAM)
974 if (FAILED(IStream_Clone(rec->fields[i].u.stream,
975 &clone->fields[i].u.stream)))
977 msiobj_release(&clone->hdr);
980 clone->fields[i].type = MSIFIELD_STREAM;
984 r = MSI_RecordCopyField(rec, i, clone, i);
985 if (r != ERROR_SUCCESS)
987 msiobj_release(&clone->hdr);
996 BOOL MSI_RecordsAreEqual(MSIRECORD *a, MSIRECORD *b)
1000 if (a->count != b->count)
1003 for (i = 0; i <= a->count; i++)
1005 if (a->fields[i].type != b->fields[i].type)
1008 switch (a->fields[i].type)
1014 if (a->fields[i].u.iVal != b->fields[i].u.iVal)
1019 if (lstrcmpW(a->fields[i].u.szwVal, b->fields[i].u.szwVal))
1023 case MSIFIELD_STREAM: