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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 #include "wine/debug.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
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 HeapFree( GetProcessHeap(), 0, 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
);
103 ret
= alloc_msihandle( &rec
->hdr
);
104 msiobj_release( &rec
->hdr
);
108 unsigned int MSI_RecordGetFieldCount( MSIRECORD
*rec
)
113 unsigned int WINAPI
MsiRecordGetFieldCount( MSIHANDLE handle
)
118 TRACE("%ld\n", handle
);
120 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
124 msiobj_lock( &rec
->hdr
);
125 ret
= MSI_RecordGetFieldCount( rec
);
126 msiobj_unlock( &rec
->hdr
);
127 msiobj_release( &rec
->hdr
);
132 static BOOL
string2intW( LPCWSTR str
, int *out
)
137 if( *p
== '-' ) /* skip the minus sign */
141 if( (*p
< '0') || (*p
> '9') )
148 if( str
[0] == '-' ) /* check if it's negative */
155 int MSI_RecordGetInteger( MSIRECORD
*rec
, unsigned int iField
)
159 TRACE("%p %d\n", rec
, iField
);
161 if( iField
> rec
->count
)
162 return MSI_NULL_INTEGER
;
164 switch( rec
->fields
[iField
].type
)
167 return rec
->fields
[iField
].u
.iVal
;
169 if( string2intW( rec
->fields
[iField
].u
.szwVal
, &ret
) )
171 return MSI_NULL_INTEGER
;
176 return MSI_NULL_INTEGER
;
179 int WINAPI
MsiRecordGetInteger( MSIHANDLE handle
, unsigned int iField
)
184 TRACE("%ld %d\n", handle
, iField
);
186 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
188 return MSI_NULL_INTEGER
;
190 msiobj_lock( &rec
->hdr
);
191 ret
= MSI_RecordGetInteger( rec
, iField
);
192 msiobj_unlock( &rec
->hdr
);
193 msiobj_release( &rec
->hdr
);
198 UINT WINAPI
MsiRecordClearData( MSIHANDLE handle
)
203 TRACE("%ld\n", handle
);
205 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
207 return ERROR_INVALID_HANDLE
;
209 msiobj_lock( &rec
->hdr
);
210 for( i
=0; i
<=rec
->count
; i
++)
212 MSI_FreeField( &rec
->fields
[i
] );
213 rec
->fields
[i
].type
= MSIFIELD_NULL
;
214 rec
->fields
[i
].u
.iVal
= 0;
216 msiobj_unlock( &rec
->hdr
);
217 msiobj_release( &rec
->hdr
);
219 return ERROR_SUCCESS
;
222 UINT
MSI_RecordSetInteger( MSIRECORD
*rec
, unsigned int iField
, int iVal
)
224 TRACE("%p %u %d\n", rec
, iField
, iVal
);
226 if( iField
> rec
->count
)
227 return ERROR_INVALID_PARAMETER
;
229 MSI_FreeField( &rec
->fields
[iField
] );
230 rec
->fields
[iField
].type
= MSIFIELD_INT
;
231 rec
->fields
[iField
].u
.iVal
= iVal
;
233 return ERROR_SUCCESS
;
236 UINT WINAPI
MsiRecordSetInteger( MSIHANDLE handle
, unsigned int iField
, int iVal
)
241 TRACE("%ld %u %d\n", handle
, iField
, iVal
);
243 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
245 return ERROR_INVALID_HANDLE
;
247 msiobj_lock( &rec
->hdr
);
248 ret
= MSI_RecordSetInteger( rec
, iField
, iVal
);
249 msiobj_unlock( &rec
->hdr
);
250 msiobj_release( &rec
->hdr
);
254 BOOL
MSI_RecordIsNull( MSIRECORD
*rec
, unsigned int iField
)
258 TRACE("%p %d\n", rec
, iField
);
260 r
= ( iField
> rec
->count
) ||
261 ( rec
->fields
[iField
].type
== MSIFIELD_NULL
);
266 BOOL WINAPI
MsiRecordIsNull( MSIHANDLE handle
, unsigned int iField
)
271 TRACE("%ld %d\n", handle
, iField
);
273 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
276 msiobj_lock( &rec
->hdr
);
277 ret
= MSI_RecordIsNull( rec
, iField
);
278 msiobj_unlock( &rec
->hdr
);
279 msiobj_release( &rec
->hdr
);
284 UINT
MSI_RecordGetStringA(MSIRECORD
*rec
, unsigned int iField
,
285 LPSTR szValue
, DWORD
*pcchValue
)
290 TRACE("%p %d %p %p\n", rec
, iField
, szValue
, pcchValue
);
292 if( iField
> rec
->count
)
293 return ERROR_INVALID_PARAMETER
;
296 switch( rec
->fields
[iField
].type
)
299 wsprintfA(buffer
, "%d", rec
->fields
[iField
].u
.iVal
);
300 len
= lstrlenA( buffer
);
301 lstrcpynA(szValue
, buffer
, *pcchValue
);
304 len
= WideCharToMultiByte( CP_ACP
, 0, rec
->fields
[iField
].u
.szwVal
, -1,
305 NULL
, 0 , NULL
, NULL
);
306 WideCharToMultiByte( CP_ACP
, 0, rec
->fields
[iField
].u
.szwVal
, -1,
307 szValue
, *pcchValue
, NULL
, NULL
);
308 if( *pcchValue
&& len
>*pcchValue
)
309 szValue
[*pcchValue
-1] = 0;
318 ret
= ERROR_INVALID_PARAMETER
;
322 if( *pcchValue
< len
)
323 ret
= ERROR_MORE_DATA
;
329 UINT WINAPI
MsiRecordGetStringA(MSIHANDLE handle
, unsigned int iField
,
330 LPSTR szValue
, DWORD
*pcchValue
)
335 TRACE("%ld %d %p %p\n", handle
, iField
, szValue
, pcchValue
);
337 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
339 return ERROR_INVALID_HANDLE
;
340 msiobj_lock( &rec
->hdr
);
341 ret
= MSI_RecordGetStringA( rec
, iField
, szValue
, pcchValue
);
342 msiobj_unlock( &rec
->hdr
);
343 msiobj_release( &rec
->hdr
);
347 const WCHAR
*MSI_RecordGetString( MSIRECORD
*rec
, unsigned int iField
)
349 if( iField
> rec
->count
)
352 if( rec
->fields
[iField
].type
!= MSIFIELD_WSTR
)
355 return rec
->fields
[iField
].u
.szwVal
;
358 UINT
MSI_RecordGetStringW(MSIRECORD
*rec
, unsigned int iField
,
359 LPWSTR szValue
, DWORD
*pcchValue
)
363 static const WCHAR szFormat
[] = { '%','d',0 };
365 TRACE("%p %d %p %p\n", rec
, iField
, szValue
, pcchValue
);
367 if( iField
> rec
->count
)
368 return ERROR_INVALID_PARAMETER
;
371 switch( rec
->fields
[iField
].type
)
374 wsprintfW(buffer
, szFormat
, rec
->fields
[iField
].u
.iVal
);
375 len
= lstrlenW( buffer
);
376 lstrcpynW(szValue
, buffer
, *pcchValue
);
379 len
= lstrlenW( rec
->fields
[iField
].u
.szwVal
);
380 lstrcpynW(szValue
, rec
->fields
[iField
].u
.szwVal
, *pcchValue
);
390 if( *pcchValue
< len
)
391 ret
= ERROR_MORE_DATA
;
397 UINT WINAPI
MsiRecordGetStringW(MSIHANDLE handle
, unsigned int iField
,
398 LPWSTR szValue
, DWORD
*pcchValue
)
403 TRACE("%ld %d %p %p\n", handle
, iField
, szValue
, pcchValue
);
405 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
407 return ERROR_INVALID_HANDLE
;
409 msiobj_lock( &rec
->hdr
);
410 ret
= MSI_RecordGetStringW( rec
, iField
, szValue
, pcchValue
);
411 msiobj_unlock( &rec
->hdr
);
412 msiobj_release( &rec
->hdr
);
416 static UINT
msi_get_stream_size( IStream
*stm
)
421 r
= IStream_Stat( stm
, &stat
, STATFLAG_NONAME
);
424 return stat
.cbSize
.QuadPart
;
427 UINT
MSI_RecordDataSize(MSIRECORD
*rec
, unsigned int iField
)
429 TRACE("%p %d\n", rec
, iField
);
431 if( iField
> rec
->count
)
434 switch( rec
->fields
[iField
].type
)
439 return lstrlenW( rec
->fields
[iField
].u
.szwVal
);
442 case MSIFIELD_STREAM
:
443 return msi_get_stream_size( rec
->fields
[iField
].u
.stream
);
448 UINT WINAPI
MsiRecordDataSize(MSIHANDLE handle
, unsigned int iField
)
453 TRACE("%ld %d\n", handle
, iField
);
455 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
458 msiobj_lock( &rec
->hdr
);
459 ret
= MSI_RecordDataSize( rec
, iField
);
460 msiobj_unlock( &rec
->hdr
);
461 msiobj_release( &rec
->hdr
);
465 UINT
MSI_RecordSetStringA( MSIRECORD
*rec
, unsigned int iField
, LPCSTR szValue
)
469 TRACE("%p %d %s\n", rec
, iField
, debugstr_a(szValue
));
471 if( iField
> rec
->count
)
472 return ERROR_INVALID_FIELD
;
474 MSI_FreeField( &rec
->fields
[iField
] );
475 if( szValue
&& szValue
[0] )
477 str
= strdupAtoW( szValue
);
478 rec
->fields
[iField
].type
= MSIFIELD_WSTR
;
479 rec
->fields
[iField
].u
.szwVal
= str
;
483 rec
->fields
[iField
].type
= MSIFIELD_NULL
;
484 rec
->fields
[iField
].u
.szwVal
= NULL
;
490 UINT WINAPI
MsiRecordSetStringA( MSIHANDLE handle
, unsigned int iField
, LPCSTR szValue
)
495 TRACE("%ld %d %s\n", handle
, iField
, debugstr_a(szValue
));
497 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
499 return ERROR_INVALID_HANDLE
;
500 msiobj_lock( &rec
->hdr
);
501 ret
= MSI_RecordSetStringA( rec
, iField
, szValue
);
502 msiobj_unlock( &rec
->hdr
);
503 msiobj_release( &rec
->hdr
);
507 UINT
MSI_RecordSetStringW( MSIRECORD
*rec
, unsigned int iField
, LPCWSTR szValue
)
511 TRACE("%p %d %s\n", rec
, iField
, debugstr_w(szValue
));
513 if( iField
> rec
->count
)
514 return ERROR_INVALID_FIELD
;
516 MSI_FreeField( &rec
->fields
[iField
] );
518 if( szValue
&& szValue
[0] )
520 str
= strdupW( szValue
);
521 rec
->fields
[iField
].type
= MSIFIELD_WSTR
;
522 rec
->fields
[iField
].u
.szwVal
= str
;
526 rec
->fields
[iField
].type
= MSIFIELD_NULL
;
527 rec
->fields
[iField
].u
.szwVal
= NULL
;
533 UINT WINAPI
MsiRecordSetStringW( MSIHANDLE handle
, unsigned int iField
, LPCWSTR szValue
)
538 TRACE("%ld %d %s\n", handle
, iField
, debugstr_w(szValue
));
540 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
542 return ERROR_INVALID_HANDLE
;
544 msiobj_lock( &rec
->hdr
);
545 ret
= MSI_RecordSetStringW( rec
, iField
, szValue
);
546 msiobj_unlock( &rec
->hdr
);
547 msiobj_release( &rec
->hdr
);
551 /* read the data in a file into an IStream */
552 static UINT
RECORD_StreamFromFile(LPCWSTR szFile
, IStream
**pstm
)
554 DWORD sz
, szHighWord
= 0, read
;
558 ULARGE_INTEGER ulSize
;
560 TRACE("reading %s\n", debugstr_w(szFile
));
562 /* read the file into memory */
563 handle
= CreateFileW(szFile
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
564 if( handle
== INVALID_HANDLE_VALUE
)
565 return GetLastError();
566 sz
= GetFileSize(handle
, &szHighWord
);
567 if( sz
!= INVALID_FILE_SIZE
&& szHighWord
== 0 )
569 hGlob
= GlobalAlloc(GMEM_FIXED
, sz
);
572 BOOL r
= ReadFile(handle
, hGlob
, sz
, &read
, NULL
);
582 return ERROR_FUNCTION_FAILED
;
584 /* make a stream out of it, and set the correct file size */
585 hr
= CreateStreamOnHGlobal(hGlob
, TRUE
, pstm
);
589 return ERROR_FUNCTION_FAILED
;
592 /* set the correct size - CreateStreamOnHGlobal screws it up */
593 ulSize
.QuadPart
= sz
;
594 IStream_SetSize(*pstm
, ulSize
);
596 TRACE("read %s, %ld bytes into IStream %p\n", debugstr_w(szFile
), sz
, *pstm
);
598 return ERROR_SUCCESS
;
601 UINT
MSI_RecordSetStreamW(MSIRECORD
*rec
, unsigned int iField
, LPCWSTR szFilename
)
606 if( (iField
== 0) || (iField
> rec
->count
) )
607 return ERROR_INVALID_PARAMETER
;
609 /* no filename means we should seek back to the start of the stream */
615 if( rec
->fields
[iField
].type
!= MSIFIELD_STREAM
)
616 return ERROR_INVALID_FIELD
;
618 stm
= rec
->fields
[iField
].u
.stream
;
620 return ERROR_INVALID_FIELD
;
623 r
= IStream_Seek( stm
, ofs
, STREAM_SEEK_SET
, &cur
);
625 return ERROR_FUNCTION_FAILED
;
629 /* read the file into a stream and save the stream in the record */
630 r
= RECORD_StreamFromFile(szFilename
, &stm
);
631 if( r
!= ERROR_SUCCESS
)
634 /* if all's good, store it in the record */
635 MSI_FreeField( &rec
->fields
[iField
] );
636 rec
->fields
[iField
].type
= MSIFIELD_STREAM
;
637 rec
->fields
[iField
].u
.stream
= stm
;
640 return ERROR_SUCCESS
;
643 UINT WINAPI
MsiRecordSetStreamA(MSIHANDLE hRecord
, unsigned int iField
, LPCSTR szFilename
)
648 TRACE("%ld %d %s\n", hRecord
, iField
, debugstr_a(szFilename
));
652 wstr
= strdupAtoW( szFilename
);
654 return ERROR_OUTOFMEMORY
;
656 ret
= MsiRecordSetStreamW(hRecord
, iField
, wstr
);
657 HeapFree(GetProcessHeap(),0,wstr
);
662 UINT WINAPI
MsiRecordSetStreamW(MSIHANDLE handle
, unsigned int iField
, LPCWSTR szFilename
)
667 TRACE("%ld %d %s\n", handle
, iField
, debugstr_w(szFilename
));
669 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
671 return ERROR_INVALID_HANDLE
;
673 msiobj_lock( &rec
->hdr
);
674 ret
= MSI_RecordSetStreamW( rec
, iField
, szFilename
);
675 msiobj_unlock( &rec
->hdr
);
676 msiobj_release( &rec
->hdr
);
680 UINT
MSI_RecordReadStream(MSIRECORD
*rec
, unsigned int iField
, char *buf
, DWORD
*sz
)
686 TRACE("%p %d %p %p\n", rec
, iField
, buf
, sz
);
689 return ERROR_INVALID_PARAMETER
;
691 if( iField
> rec
->count
)
692 return ERROR_INVALID_PARAMETER
;
694 if( rec
->fields
[iField
].type
!= MSIFIELD_STREAM
)
695 return ERROR_INVALID_DATATYPE
;
697 stm
= rec
->fields
[iField
].u
.stream
;
699 return ERROR_INVALID_PARAMETER
;
701 /* if there's no buffer pointer, calculate the length to the end */
705 ULARGE_INTEGER end
, cur
;
707 ofs
.QuadPart
= cur
.QuadPart
= 0;
709 r
= IStream_Seek( stm
, ofs
, STREAM_SEEK_SET
, &cur
);
710 IStream_Seek( stm
, ofs
, STREAM_SEEK_END
, &end
);
711 ofs
.QuadPart
= cur
.QuadPart
;
712 IStream_Seek( stm
, ofs
, STREAM_SEEK_SET
, &cur
);
713 *sz
= end
.QuadPart
- cur
.QuadPart
;
715 return ERROR_SUCCESS
;
720 r
= IStream_Read( stm
, buf
, *sz
, &count
);
724 return ERROR_FUNCTION_FAILED
;
729 return ERROR_SUCCESS
;
732 UINT WINAPI
MsiRecordReadStream(MSIHANDLE handle
, unsigned int iField
, char *buf
, DWORD
*sz
)
737 TRACE("%ld %d %p %p\n", handle
, iField
, buf
, sz
);
739 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
741 return ERROR_INVALID_HANDLE
;
742 msiobj_lock( &rec
->hdr
);
743 ret
= MSI_RecordReadStream( rec
, iField
, buf
, sz
);
744 msiobj_unlock( &rec
->hdr
);
745 msiobj_release( &rec
->hdr
);
749 UINT
MSI_RecordSetIStream( MSIRECORD
*rec
, unsigned int iField
, IStream
*stm
)
751 TRACE("%p %d %p\n", rec
, iField
, stm
);
753 if( iField
> rec
->count
)
754 return ERROR_INVALID_FIELD
;
756 MSI_FreeField( &rec
->fields
[iField
] );
758 rec
->fields
[iField
].type
= MSIFIELD_STREAM
;
759 rec
->fields
[iField
].u
.stream
= stm
;
760 IStream_AddRef( stm
);
762 return ERROR_SUCCESS
;
765 UINT
MSI_RecordGetIStream( MSIRECORD
*rec
, unsigned int iField
, IStream
**pstm
)
767 TRACE("%p %d %p\n", rec
, iField
, pstm
);
769 if( iField
> rec
->count
)
770 return ERROR_INVALID_FIELD
;
772 if( rec
->fields
[iField
].type
!= MSIFIELD_STREAM
)
773 return ERROR_INVALID_FIELD
;
775 *pstm
= rec
->fields
[iField
].u
.stream
;
776 IStream_AddRef( *pstm
);
778 return ERROR_SUCCESS
;
781 static UINT
msi_dump_stream_to_file( IStream
*stm
, LPCWSTR name
)
789 stgm
= STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
| STGM_FAILIFTHERE
;
790 r
= SHCreateStreamOnFileW( name
, stgm
, &out
);
792 return ERROR_FUNCTION_FAILED
;
795 r
= IStream_Seek( stm
, pos
, STREAM_SEEK_END
, &size
);
800 r
= IStream_Seek( stm
, pos
, STREAM_SEEK_SET
, NULL
);
804 r
= IStream_CopyTo( stm
, out
, size
, NULL
, NULL
);
807 IStream_Release( out
);
809 return ERROR_FUNCTION_FAILED
;
810 return ERROR_SUCCESS
;
813 UINT
MSI_RecordStreamToFile( MSIRECORD
*rec
, unsigned int iField
, LPCWSTR name
)
818 TRACE("%p %u %s\n", rec
, iField
, debugstr_w(name
));
820 msiobj_lock( &rec
->hdr
);
822 r
= MSI_RecordGetIStream( rec
, iField
, &stm
);
823 if( r
== ERROR_SUCCESS
)
825 r
= msi_dump_stream_to_file( stm
, name
);
826 IStream_Release( stm
);
829 msiobj_unlock( &rec
->hdr
);