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_STR 2
47 #define MSIFIELD_WSTR 3
48 #define MSIFIELD_STREAM 4
50 static void MSI_FreeField( MSIFIELD
*field
)
58 msi_free( field
->u
.szwVal
);
61 IStream_Release( field
->u
.stream
);
64 ERR("Invalid field type %d\n", field
->type
);
68 static void MSI_CloseRecord( MSIOBJECTHDR
*arg
)
70 MSIRECORD
*rec
= (MSIRECORD
*) arg
;
73 for( i
=0; i
<=rec
->count
; i
++ )
74 MSI_FreeField( &rec
->fields
[i
] );
77 MSIRECORD
*MSI_CreateRecord( unsigned int cParams
)
82 TRACE("%d\n", cParams
);
87 len
= sizeof (MSIRECORD
) + sizeof (MSIFIELD
)*cParams
;
88 rec
= alloc_msiobject( MSIHANDLETYPE_RECORD
, len
, MSI_CloseRecord
);
94 MSIHANDLE WINAPI
MsiCreateRecord( unsigned int cParams
)
99 TRACE("%d\n", cParams
);
101 rec
= MSI_CreateRecord( cParams
);
104 ret
= alloc_msihandle( &rec
->hdr
);
105 msiobj_release( &rec
->hdr
);
110 unsigned int MSI_RecordGetFieldCount( MSIRECORD
*rec
)
115 unsigned int WINAPI
MsiRecordGetFieldCount( MSIHANDLE handle
)
120 TRACE("%ld\n", handle
);
122 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
126 msiobj_lock( &rec
->hdr
);
127 ret
= MSI_RecordGetFieldCount( rec
);
128 msiobj_unlock( &rec
->hdr
);
129 msiobj_release( &rec
->hdr
);
134 static BOOL
string2intW( LPCWSTR str
, int *out
)
139 if( *p
== '-' ) /* skip the minus sign */
143 if( (*p
< '0') || (*p
> '9') )
150 if( str
[0] == '-' ) /* check if it's negative */
157 int MSI_RecordGetInteger( MSIRECORD
*rec
, unsigned int iField
)
161 TRACE("%p %d\n", rec
, iField
);
163 if( iField
> rec
->count
)
164 return MSI_NULL_INTEGER
;
166 switch( rec
->fields
[iField
].type
)
169 return rec
->fields
[iField
].u
.iVal
;
171 if( string2intW( rec
->fields
[iField
].u
.szwVal
, &ret
) )
173 return MSI_NULL_INTEGER
;
178 return MSI_NULL_INTEGER
;
181 int WINAPI
MsiRecordGetInteger( MSIHANDLE handle
, unsigned int iField
)
186 TRACE("%ld %d\n", handle
, iField
);
188 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
190 return MSI_NULL_INTEGER
;
192 msiobj_lock( &rec
->hdr
);
193 ret
= MSI_RecordGetInteger( rec
, iField
);
194 msiobj_unlock( &rec
->hdr
);
195 msiobj_release( &rec
->hdr
);
200 UINT WINAPI
MsiRecordClearData( MSIHANDLE handle
)
205 TRACE("%ld\n", handle
);
207 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
209 return ERROR_INVALID_HANDLE
;
211 msiobj_lock( &rec
->hdr
);
212 for( i
=0; i
<=rec
->count
; i
++)
214 MSI_FreeField( &rec
->fields
[i
] );
215 rec
->fields
[i
].type
= MSIFIELD_NULL
;
216 rec
->fields
[i
].u
.iVal
= 0;
218 msiobj_unlock( &rec
->hdr
);
219 msiobj_release( &rec
->hdr
);
221 return ERROR_SUCCESS
;
224 UINT
MSI_RecordSetInteger( MSIRECORD
*rec
, unsigned int iField
, int iVal
)
226 TRACE("%p %u %d\n", rec
, iField
, iVal
);
228 if( iField
> rec
->count
)
229 return ERROR_INVALID_PARAMETER
;
231 MSI_FreeField( &rec
->fields
[iField
] );
232 rec
->fields
[iField
].type
= MSIFIELD_INT
;
233 rec
->fields
[iField
].u
.iVal
= iVal
;
235 return ERROR_SUCCESS
;
238 UINT WINAPI
MsiRecordSetInteger( MSIHANDLE handle
, unsigned int iField
, int iVal
)
243 TRACE("%ld %u %d\n", handle
, iField
, iVal
);
245 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
247 return ERROR_INVALID_HANDLE
;
249 msiobj_lock( &rec
->hdr
);
250 ret
= MSI_RecordSetInteger( rec
, iField
, iVal
);
251 msiobj_unlock( &rec
->hdr
);
252 msiobj_release( &rec
->hdr
);
256 BOOL
MSI_RecordIsNull( MSIRECORD
*rec
, unsigned int iField
)
260 TRACE("%p %d\n", rec
, iField
);
262 r
= ( iField
> rec
->count
) ||
263 ( rec
->fields
[iField
].type
== MSIFIELD_NULL
);
268 BOOL WINAPI
MsiRecordIsNull( MSIHANDLE handle
, unsigned int iField
)
273 TRACE("%ld %d\n", handle
, iField
);
275 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
278 msiobj_lock( &rec
->hdr
);
279 ret
= MSI_RecordIsNull( rec
, iField
);
280 msiobj_unlock( &rec
->hdr
);
281 msiobj_release( &rec
->hdr
);
286 UINT
MSI_RecordGetStringA(MSIRECORD
*rec
, unsigned int iField
,
287 LPSTR szValue
, DWORD
*pcchValue
)
292 TRACE("%p %d %p %p\n", rec
, iField
, szValue
, pcchValue
);
294 if( iField
> rec
->count
)
295 return ERROR_INVALID_PARAMETER
;
298 switch( rec
->fields
[iField
].type
)
301 wsprintfA(buffer
, "%d", rec
->fields
[iField
].u
.iVal
);
302 len
= lstrlenA( buffer
);
303 lstrcpynA(szValue
, buffer
, *pcchValue
);
306 len
= WideCharToMultiByte( CP_ACP
, 0, rec
->fields
[iField
].u
.szwVal
, -1,
307 NULL
, 0 , NULL
, NULL
);
308 WideCharToMultiByte( CP_ACP
, 0, rec
->fields
[iField
].u
.szwVal
, -1,
309 szValue
, *pcchValue
, NULL
, NULL
);
310 if( *pcchValue
&& len
>*pcchValue
)
311 szValue
[*pcchValue
-1] = 0;
320 ret
= ERROR_INVALID_PARAMETER
;
324 if( *pcchValue
< len
)
325 ret
= ERROR_MORE_DATA
;
331 UINT WINAPI
MsiRecordGetStringA(MSIHANDLE handle
, unsigned int iField
,
332 LPSTR szValue
, DWORD
*pcchValue
)
337 TRACE("%ld %d %p %p\n", handle
, iField
, szValue
, pcchValue
);
339 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
341 return ERROR_INVALID_HANDLE
;
342 msiobj_lock( &rec
->hdr
);
343 ret
= MSI_RecordGetStringA( rec
, iField
, szValue
, pcchValue
);
344 msiobj_unlock( &rec
->hdr
);
345 msiobj_release( &rec
->hdr
);
349 const WCHAR
*MSI_RecordGetString( MSIRECORD
*rec
, unsigned int iField
)
351 if( iField
> rec
->count
)
354 if( rec
->fields
[iField
].type
!= MSIFIELD_WSTR
)
357 return rec
->fields
[iField
].u
.szwVal
;
360 UINT
MSI_RecordGetStringW(MSIRECORD
*rec
, unsigned int iField
,
361 LPWSTR szValue
, DWORD
*pcchValue
)
365 static const WCHAR szFormat
[] = { '%','d',0 };
367 TRACE("%p %d %p %p\n", rec
, iField
, szValue
, pcchValue
);
369 if( iField
> rec
->count
)
370 return ERROR_INVALID_PARAMETER
;
373 switch( rec
->fields
[iField
].type
)
376 wsprintfW(buffer
, szFormat
, rec
->fields
[iField
].u
.iVal
);
377 len
= lstrlenW( buffer
);
378 lstrcpynW(szValue
, buffer
, *pcchValue
);
381 len
= lstrlenW( rec
->fields
[iField
].u
.szwVal
);
382 lstrcpynW(szValue
, rec
->fields
[iField
].u
.szwVal
, *pcchValue
);
392 if( *pcchValue
< len
)
393 ret
= ERROR_MORE_DATA
;
399 UINT WINAPI
MsiRecordGetStringW(MSIHANDLE handle
, unsigned int iField
,
400 LPWSTR szValue
, DWORD
*pcchValue
)
405 TRACE("%ld %d %p %p\n", handle
, iField
, szValue
, pcchValue
);
407 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
409 return ERROR_INVALID_HANDLE
;
411 msiobj_lock( &rec
->hdr
);
412 ret
= MSI_RecordGetStringW( rec
, iField
, szValue
, pcchValue
);
413 msiobj_unlock( &rec
->hdr
);
414 msiobj_release( &rec
->hdr
);
418 static UINT
msi_get_stream_size( IStream
*stm
)
423 r
= IStream_Stat( stm
, &stat
, STATFLAG_NONAME
);
426 return stat
.cbSize
.QuadPart
;
429 UINT
MSI_RecordDataSize(MSIRECORD
*rec
, unsigned int iField
)
431 TRACE("%p %d\n", rec
, iField
);
433 if( iField
> rec
->count
)
436 switch( rec
->fields
[iField
].type
)
441 return lstrlenW( rec
->fields
[iField
].u
.szwVal
);
444 case MSIFIELD_STREAM
:
445 return msi_get_stream_size( rec
->fields
[iField
].u
.stream
);
450 UINT WINAPI
MsiRecordDataSize(MSIHANDLE handle
, unsigned int iField
)
455 TRACE("%ld %d\n", handle
, iField
);
457 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
460 msiobj_lock( &rec
->hdr
);
461 ret
= MSI_RecordDataSize( rec
, iField
);
462 msiobj_unlock( &rec
->hdr
);
463 msiobj_release( &rec
->hdr
);
467 UINT
MSI_RecordSetStringA( MSIRECORD
*rec
, unsigned int iField
, LPCSTR szValue
)
471 TRACE("%p %d %s\n", rec
, iField
, debugstr_a(szValue
));
473 if( iField
> rec
->count
)
474 return ERROR_INVALID_FIELD
;
476 MSI_FreeField( &rec
->fields
[iField
] );
477 if( szValue
&& szValue
[0] )
479 str
= strdupAtoW( szValue
);
480 rec
->fields
[iField
].type
= MSIFIELD_WSTR
;
481 rec
->fields
[iField
].u
.szwVal
= str
;
485 rec
->fields
[iField
].type
= MSIFIELD_NULL
;
486 rec
->fields
[iField
].u
.szwVal
= NULL
;
492 UINT WINAPI
MsiRecordSetStringA( MSIHANDLE handle
, unsigned int iField
, LPCSTR szValue
)
497 TRACE("%ld %d %s\n", handle
, iField
, debugstr_a(szValue
));
499 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
501 return ERROR_INVALID_HANDLE
;
502 msiobj_lock( &rec
->hdr
);
503 ret
= MSI_RecordSetStringA( rec
, iField
, szValue
);
504 msiobj_unlock( &rec
->hdr
);
505 msiobj_release( &rec
->hdr
);
509 UINT
MSI_RecordSetStringW( MSIRECORD
*rec
, unsigned int iField
, LPCWSTR szValue
)
513 TRACE("%p %d %s\n", rec
, iField
, debugstr_w(szValue
));
515 if( iField
> rec
->count
)
516 return ERROR_INVALID_FIELD
;
518 MSI_FreeField( &rec
->fields
[iField
] );
520 if( szValue
&& szValue
[0] )
522 str
= strdupW( szValue
);
523 rec
->fields
[iField
].type
= MSIFIELD_WSTR
;
524 rec
->fields
[iField
].u
.szwVal
= str
;
528 rec
->fields
[iField
].type
= MSIFIELD_NULL
;
529 rec
->fields
[iField
].u
.szwVal
= NULL
;
535 UINT WINAPI
MsiRecordSetStringW( MSIHANDLE handle
, unsigned int iField
, LPCWSTR szValue
)
540 TRACE("%ld %d %s\n", handle
, iField
, debugstr_w(szValue
));
542 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
544 return ERROR_INVALID_HANDLE
;
546 msiobj_lock( &rec
->hdr
);
547 ret
= MSI_RecordSetStringW( rec
, iField
, szValue
);
548 msiobj_unlock( &rec
->hdr
);
549 msiobj_release( &rec
->hdr
);
553 /* read the data in a file into an IStream */
554 static UINT
RECORD_StreamFromFile(LPCWSTR szFile
, IStream
**pstm
)
556 DWORD sz
, szHighWord
= 0, read
;
560 ULARGE_INTEGER ulSize
;
562 TRACE("reading %s\n", debugstr_w(szFile
));
564 /* read the file into memory */
565 handle
= CreateFileW(szFile
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
566 if( handle
== INVALID_HANDLE_VALUE
)
567 return GetLastError();
568 sz
= GetFileSize(handle
, &szHighWord
);
569 if( sz
!= INVALID_FILE_SIZE
&& szHighWord
== 0 )
571 hGlob
= GlobalAlloc(GMEM_FIXED
, sz
);
574 BOOL r
= ReadFile(handle
, hGlob
, sz
, &read
, NULL
);
584 return ERROR_FUNCTION_FAILED
;
586 /* make a stream out of it, and set the correct file size */
587 hr
= CreateStreamOnHGlobal(hGlob
, TRUE
, pstm
);
591 return ERROR_FUNCTION_FAILED
;
594 /* set the correct size - CreateStreamOnHGlobal screws it up */
595 ulSize
.QuadPart
= sz
;
596 IStream_SetSize(*pstm
, ulSize
);
598 TRACE("read %s, %ld bytes into IStream %p\n", debugstr_w(szFile
), sz
, *pstm
);
600 return ERROR_SUCCESS
;
603 UINT
MSI_RecordSetStreamW(MSIRECORD
*rec
, unsigned int iField
, LPCWSTR szFilename
)
608 if( (iField
== 0) || (iField
> rec
->count
) )
609 return ERROR_INVALID_PARAMETER
;
611 /* no filename means we should seek back to the start of the stream */
617 if( rec
->fields
[iField
].type
!= MSIFIELD_STREAM
)
618 return ERROR_INVALID_FIELD
;
620 stm
= rec
->fields
[iField
].u
.stream
;
622 return ERROR_INVALID_FIELD
;
625 r
= IStream_Seek( stm
, ofs
, STREAM_SEEK_SET
, &cur
);
627 return ERROR_FUNCTION_FAILED
;
631 /* read the file into a stream and save the stream in the record */
632 r
= RECORD_StreamFromFile(szFilename
, &stm
);
633 if( r
!= ERROR_SUCCESS
)
636 /* if all's good, store it in the record */
637 MSI_FreeField( &rec
->fields
[iField
] );
638 rec
->fields
[iField
].type
= MSIFIELD_STREAM
;
639 rec
->fields
[iField
].u
.stream
= stm
;
642 return ERROR_SUCCESS
;
645 UINT WINAPI
MsiRecordSetStreamA(MSIHANDLE hRecord
, unsigned int iField
, LPCSTR szFilename
)
650 TRACE("%ld %d %s\n", hRecord
, iField
, debugstr_a(szFilename
));
654 wstr
= strdupAtoW( szFilename
);
656 return ERROR_OUTOFMEMORY
;
658 ret
= MsiRecordSetStreamW(hRecord
, iField
, wstr
);
664 UINT WINAPI
MsiRecordSetStreamW(MSIHANDLE handle
, unsigned int iField
, LPCWSTR szFilename
)
669 TRACE("%ld %d %s\n", handle
, iField
, debugstr_w(szFilename
));
671 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
673 return ERROR_INVALID_HANDLE
;
675 msiobj_lock( &rec
->hdr
);
676 ret
= MSI_RecordSetStreamW( rec
, iField
, szFilename
);
677 msiobj_unlock( &rec
->hdr
);
678 msiobj_release( &rec
->hdr
);
682 UINT
MSI_RecordReadStream(MSIRECORD
*rec
, unsigned int iField
, char *buf
, DWORD
*sz
)
688 TRACE("%p %d %p %p\n", rec
, iField
, buf
, sz
);
691 return ERROR_INVALID_PARAMETER
;
693 if( iField
> rec
->count
)
694 return ERROR_INVALID_PARAMETER
;
696 if( rec
->fields
[iField
].type
!= MSIFIELD_STREAM
)
697 return ERROR_INVALID_DATATYPE
;
699 stm
= rec
->fields
[iField
].u
.stream
;
701 return ERROR_INVALID_PARAMETER
;
703 /* if there's no buffer pointer, calculate the length to the end */
707 ULARGE_INTEGER end
, cur
;
709 ofs
.QuadPart
= cur
.QuadPart
= 0;
711 r
= IStream_Seek( stm
, ofs
, STREAM_SEEK_SET
, &cur
);
712 IStream_Seek( stm
, ofs
, STREAM_SEEK_END
, &end
);
713 ofs
.QuadPart
= cur
.QuadPart
;
714 IStream_Seek( stm
, ofs
, STREAM_SEEK_SET
, &cur
);
715 *sz
= end
.QuadPart
- cur
.QuadPart
;
717 return ERROR_SUCCESS
;
722 r
= IStream_Read( stm
, buf
, *sz
, &count
);
726 return ERROR_FUNCTION_FAILED
;
731 return ERROR_SUCCESS
;
734 UINT WINAPI
MsiRecordReadStream(MSIHANDLE handle
, unsigned int iField
, char *buf
, DWORD
*sz
)
739 TRACE("%ld %d %p %p\n", handle
, iField
, buf
, sz
);
741 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
743 return ERROR_INVALID_HANDLE
;
744 msiobj_lock( &rec
->hdr
);
745 ret
= MSI_RecordReadStream( rec
, iField
, buf
, sz
);
746 msiobj_unlock( &rec
->hdr
);
747 msiobj_release( &rec
->hdr
);
751 UINT
MSI_RecordSetIStream( MSIRECORD
*rec
, unsigned int iField
, IStream
*stm
)
753 TRACE("%p %d %p\n", rec
, iField
, stm
);
755 if( iField
> rec
->count
)
756 return ERROR_INVALID_FIELD
;
758 MSI_FreeField( &rec
->fields
[iField
] );
760 rec
->fields
[iField
].type
= MSIFIELD_STREAM
;
761 rec
->fields
[iField
].u
.stream
= stm
;
762 IStream_AddRef( stm
);
764 return ERROR_SUCCESS
;
767 UINT
MSI_RecordGetIStream( MSIRECORD
*rec
, unsigned int iField
, IStream
**pstm
)
769 TRACE("%p %d %p\n", rec
, iField
, pstm
);
771 if( iField
> rec
->count
)
772 return ERROR_INVALID_FIELD
;
774 if( rec
->fields
[iField
].type
!= MSIFIELD_STREAM
)
775 return ERROR_INVALID_FIELD
;
777 *pstm
= rec
->fields
[iField
].u
.stream
;
778 IStream_AddRef( *pstm
);
780 return ERROR_SUCCESS
;
783 static UINT
msi_dump_stream_to_file( IStream
*stm
, LPCWSTR name
)
791 stgm
= STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
| STGM_FAILIFTHERE
;
792 r
= SHCreateStreamOnFileW( name
, stgm
, &out
);
794 return ERROR_FUNCTION_FAILED
;
797 r
= IStream_Seek( stm
, pos
, STREAM_SEEK_END
, &size
);
802 r
= IStream_Seek( stm
, pos
, STREAM_SEEK_SET
, NULL
);
806 r
= IStream_CopyTo( stm
, out
, size
, NULL
, NULL
);
809 IStream_Release( out
);
811 return ERROR_FUNCTION_FAILED
;
812 return ERROR_SUCCESS
;
815 UINT
MSI_RecordStreamToFile( MSIRECORD
*rec
, unsigned int iField
, LPCWSTR name
)
820 TRACE("%p %u %s\n", rec
, iField
, debugstr_w(name
));
822 msiobj_lock( &rec
->hdr
);
824 r
= MSI_RecordGetIStream( rec
, iField
, &stm
);
825 if( r
== ERROR_SUCCESS
)
827 r
= msi_dump_stream_to_file( stm
, name
);
828 IStream_Release( stm
);
831 msiobj_unlock( &rec
->hdr
);