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
: