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
49 static void MSI_FreeField( MSIFIELD
*field
)
57 msi_free( field
->u
.szwVal
);
60 IStream_Release( field
->u
.stream
);
63 ERR("Invalid field type %d\n", field
->type
);
67 static void MSI_CloseRecord( MSIOBJECTHDR
*arg
)
69 MSIRECORD
*rec
= (MSIRECORD
*) arg
;
72 for( i
=0; i
<=rec
->count
; i
++ )
73 MSI_FreeField( &rec
->fields
[i
] );
76 MSIRECORD
*MSI_CreateRecord( UINT cParams
)
81 TRACE("%d\n", cParams
);
86 len
= sizeof (MSIRECORD
) + sizeof (MSIFIELD
)*cParams
;
87 rec
= alloc_msiobject( MSIHANDLETYPE_RECORD
, len
, MSI_CloseRecord
);
93 MSIHANDLE WINAPI
MsiCreateRecord( UINT cParams
)
98 TRACE("%d\n", cParams
);
100 rec
= MSI_CreateRecord( cParams
);
103 ret
= alloc_msihandle( &rec
->hdr
);
104 msiobj_release( &rec
->hdr
);
109 UINT
MSI_RecordGetFieldCount( const MSIRECORD
*rec
)
114 UINT WINAPI
MsiRecordGetFieldCount( MSIHANDLE handle
)
119 TRACE("%d\n", handle
);
121 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
125 msiobj_lock( &rec
->hdr
);
126 ret
= MSI_RecordGetFieldCount( rec
);
127 msiobj_unlock( &rec
->hdr
);
128 msiobj_release( &rec
->hdr
);
133 static BOOL
string2intW( LPCWSTR str
, int *out
)
138 if( *p
== '-' ) /* skip the minus sign */
142 if( (*p
< '0') || (*p
> '9') )
149 if( str
[0] == '-' ) /* check if it's negative */
156 UINT
MSI_RecordCopyField( MSIRECORD
*in_rec
, UINT in_n
,
157 MSIRECORD
*out_rec
, UINT out_n
)
159 UINT r
= ERROR_SUCCESS
;
161 msiobj_lock( &in_rec
->hdr
);
163 if ( in_n
> in_rec
->count
|| out_n
> out_rec
->count
)
164 r
= ERROR_FUNCTION_FAILED
;
165 else if ( in_rec
!= out_rec
|| in_n
!= out_n
)
170 in
= &in_rec
->fields
[in_n
];
171 out
= &out_rec
->fields
[out_n
];
178 out
->u
.iVal
= in
->u
.iVal
;
181 str
= strdupW( in
->u
.szwVal
);
183 r
= ERROR_OUTOFMEMORY
;
187 case MSIFIELD_STREAM
:
188 IStream_AddRef( in
->u
.stream
);
189 out
->u
.stream
= in
->u
.stream
;
192 ERR("invalid field type %d\n", in
->type
);
194 if (r
== ERROR_SUCCESS
)
195 out
->type
= in
->type
;
198 msiobj_unlock( &in_rec
->hdr
);
203 int MSI_RecordGetInteger( MSIRECORD
*rec
, UINT iField
)
207 TRACE("%p %d\n", rec
, iField
);
209 if( iField
> rec
->count
)
210 return MSI_NULL_INTEGER
;
212 switch( rec
->fields
[iField
].type
)
215 return rec
->fields
[iField
].u
.iVal
;
217 if( string2intW( rec
->fields
[iField
].u
.szwVal
, &ret
) )
219 return MSI_NULL_INTEGER
;
224 return MSI_NULL_INTEGER
;
227 int WINAPI
MsiRecordGetInteger( MSIHANDLE handle
, UINT iField
)
232 TRACE("%d %d\n", handle
, iField
);
234 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
236 return MSI_NULL_INTEGER
;
238 msiobj_lock( &rec
->hdr
);
239 ret
= MSI_RecordGetInteger( rec
, iField
);
240 msiobj_unlock( &rec
->hdr
);
241 msiobj_release( &rec
->hdr
);
246 UINT WINAPI
MsiRecordClearData( MSIHANDLE handle
)
251 TRACE("%d\n", handle
);
253 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
255 return ERROR_INVALID_HANDLE
;
257 msiobj_lock( &rec
->hdr
);
258 for( i
=0; i
<=rec
->count
; i
++)
260 MSI_FreeField( &rec
->fields
[i
] );
261 rec
->fields
[i
].type
= MSIFIELD_NULL
;
262 rec
->fields
[i
].u
.iVal
= 0;
264 msiobj_unlock( &rec
->hdr
);
265 msiobj_release( &rec
->hdr
);
267 return ERROR_SUCCESS
;
270 UINT
MSI_RecordSetInteger( MSIRECORD
*rec
, UINT iField
, int iVal
)
272 TRACE("%p %u %d\n", rec
, iField
, iVal
);
274 if( iField
> rec
->count
)
275 return ERROR_INVALID_PARAMETER
;
277 MSI_FreeField( &rec
->fields
[iField
] );
278 rec
->fields
[iField
].type
= MSIFIELD_INT
;
279 rec
->fields
[iField
].u
.iVal
= iVal
;
281 return ERROR_SUCCESS
;
284 UINT WINAPI
MsiRecordSetInteger( MSIHANDLE handle
, UINT iField
, int iVal
)
289 TRACE("%d %u %d\n", handle
, iField
, iVal
);
291 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
293 return ERROR_INVALID_HANDLE
;
295 msiobj_lock( &rec
->hdr
);
296 ret
= MSI_RecordSetInteger( rec
, iField
, iVal
);
297 msiobj_unlock( &rec
->hdr
);
298 msiobj_release( &rec
->hdr
);
302 BOOL
MSI_RecordIsNull( MSIRECORD
*rec
, UINT iField
)
306 TRACE("%p %d\n", rec
, iField
);
308 r
= ( iField
> rec
->count
) ||
309 ( rec
->fields
[iField
].type
== MSIFIELD_NULL
);
314 BOOL WINAPI
MsiRecordIsNull( MSIHANDLE handle
, UINT iField
)
319 TRACE("%d %d\n", handle
, iField
);
321 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
324 msiobj_lock( &rec
->hdr
);
325 ret
= MSI_RecordIsNull( rec
, iField
);
326 msiobj_unlock( &rec
->hdr
);
327 msiobj_release( &rec
->hdr
);
332 UINT
MSI_RecordGetStringA(MSIRECORD
*rec
, UINT iField
,
333 LPSTR szValue
, LPDWORD pcchValue
)
338 TRACE("%p %d %p %p\n", rec
, iField
, szValue
, pcchValue
);
340 if( iField
> rec
->count
)
342 if ( szValue
&& *pcchValue
> 0 )
346 return ERROR_SUCCESS
;
350 switch( rec
->fields
[iField
].type
)
353 wsprintfA(buffer
, "%d", rec
->fields
[iField
].u
.iVal
);
354 len
= lstrlenA( buffer
);
356 lstrcpynA(szValue
, buffer
, *pcchValue
);
359 len
= WideCharToMultiByte( CP_ACP
, 0, rec
->fields
[iField
].u
.szwVal
, -1,
360 NULL
, 0 , NULL
, NULL
);
362 WideCharToMultiByte( CP_ACP
, 0, rec
->fields
[iField
].u
.szwVal
, -1,
363 szValue
, *pcchValue
, NULL
, NULL
);
364 if( szValue
&& *pcchValue
&& len
>*pcchValue
)
365 szValue
[*pcchValue
-1] = 0;
370 if( szValue
&& *pcchValue
> 0 )
374 ret
= ERROR_INVALID_PARAMETER
;
378 if( szValue
&& *pcchValue
<= len
)
379 ret
= ERROR_MORE_DATA
;
385 UINT WINAPI
MsiRecordGetStringA(MSIHANDLE handle
, UINT iField
,
386 LPSTR szValue
, LPDWORD pcchValue
)
391 TRACE("%d %d %p %p\n", handle
, iField
, szValue
, pcchValue
);
393 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
395 return ERROR_INVALID_HANDLE
;
396 msiobj_lock( &rec
->hdr
);
397 ret
= MSI_RecordGetStringA( rec
, iField
, szValue
, pcchValue
);
398 msiobj_unlock( &rec
->hdr
);
399 msiobj_release( &rec
->hdr
);
403 const WCHAR
*MSI_RecordGetString( const MSIRECORD
*rec
, UINT iField
)
405 if( iField
> rec
->count
)
408 if( rec
->fields
[iField
].type
!= MSIFIELD_WSTR
)
411 return rec
->fields
[iField
].u
.szwVal
;
414 UINT
MSI_RecordGetStringW(MSIRECORD
*rec
, UINT iField
,
415 LPWSTR szValue
, LPDWORD pcchValue
)
419 static const WCHAR szFormat
[] = { '%','d',0 };
421 TRACE("%p %d %p %p\n", rec
, iField
, szValue
, pcchValue
);
423 if( iField
> rec
->count
)
425 if ( szValue
&& *pcchValue
> 0 )
429 return ERROR_SUCCESS
;
433 switch( rec
->fields
[iField
].type
)
436 wsprintfW(buffer
, szFormat
, rec
->fields
[iField
].u
.iVal
);
437 len
= lstrlenW( buffer
);
439 lstrcpynW(szValue
, buffer
, *pcchValue
);
442 len
= lstrlenW( rec
->fields
[iField
].u
.szwVal
);
444 lstrcpynW(szValue
, rec
->fields
[iField
].u
.szwVal
, *pcchValue
);
447 if( szValue
&& *pcchValue
> 0 )
453 if( szValue
&& *pcchValue
<= len
)
454 ret
= ERROR_MORE_DATA
;
460 UINT WINAPI
MsiRecordGetStringW(MSIHANDLE handle
, UINT iField
,
461 LPWSTR szValue
, LPDWORD pcchValue
)
466 TRACE("%d %d %p %p\n", handle
, iField
, szValue
, pcchValue
);
468 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
470 return ERROR_INVALID_HANDLE
;
472 msiobj_lock( &rec
->hdr
);
473 ret
= MSI_RecordGetStringW( rec
, iField
, szValue
, pcchValue
);
474 msiobj_unlock( &rec
->hdr
);
475 msiobj_release( &rec
->hdr
);
479 static UINT
msi_get_stream_size( IStream
*stm
)
484 r
= IStream_Stat( stm
, &stat
, STATFLAG_NONAME
);
487 return stat
.cbSize
.QuadPart
;
490 static UINT
MSI_RecordDataSize(MSIRECORD
*rec
, UINT iField
)
492 TRACE("%p %d\n", rec
, iField
);
494 if( iField
> rec
->count
)
497 switch( rec
->fields
[iField
].type
)
502 return lstrlenW( rec
->fields
[iField
].u
.szwVal
);
505 case MSIFIELD_STREAM
:
506 return msi_get_stream_size( rec
->fields
[iField
].u
.stream
);
511 UINT WINAPI
MsiRecordDataSize(MSIHANDLE handle
, UINT iField
)
516 TRACE("%d %d\n", handle
, iField
);
518 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
521 msiobj_lock( &rec
->hdr
);
522 ret
= MSI_RecordDataSize( rec
, iField
);
523 msiobj_unlock( &rec
->hdr
);
524 msiobj_release( &rec
->hdr
);
528 static UINT
MSI_RecordSetStringA( MSIRECORD
*rec
, UINT iField
, LPCSTR szValue
)
532 TRACE("%p %d %s\n", rec
, iField
, debugstr_a(szValue
));
534 if( iField
> rec
->count
)
535 return ERROR_INVALID_FIELD
;
537 MSI_FreeField( &rec
->fields
[iField
] );
538 if( szValue
&& szValue
[0] )
540 str
= strdupAtoW( szValue
);
541 rec
->fields
[iField
].type
= MSIFIELD_WSTR
;
542 rec
->fields
[iField
].u
.szwVal
= str
;
546 rec
->fields
[iField
].type
= MSIFIELD_NULL
;
547 rec
->fields
[iField
].u
.szwVal
= NULL
;
553 UINT WINAPI
MsiRecordSetStringA( MSIHANDLE handle
, UINT iField
, LPCSTR szValue
)
558 TRACE("%d %d %s\n", handle
, iField
, debugstr_a(szValue
));
560 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
562 return ERROR_INVALID_HANDLE
;
563 msiobj_lock( &rec
->hdr
);
564 ret
= MSI_RecordSetStringA( rec
, iField
, szValue
);
565 msiobj_unlock( &rec
->hdr
);
566 msiobj_release( &rec
->hdr
);
570 UINT
MSI_RecordSetStringW( MSIRECORD
*rec
, UINT iField
, LPCWSTR szValue
)
574 TRACE("%p %d %s\n", rec
, iField
, debugstr_w(szValue
));
576 if( iField
> rec
->count
)
577 return ERROR_INVALID_FIELD
;
579 MSI_FreeField( &rec
->fields
[iField
] );
581 if( szValue
&& szValue
[0] )
583 str
= strdupW( szValue
);
584 rec
->fields
[iField
].type
= MSIFIELD_WSTR
;
585 rec
->fields
[iField
].u
.szwVal
= str
;
589 rec
->fields
[iField
].type
= MSIFIELD_NULL
;
590 rec
->fields
[iField
].u
.szwVal
= NULL
;
596 UINT WINAPI
MsiRecordSetStringW( MSIHANDLE handle
, UINT iField
, LPCWSTR szValue
)
601 TRACE("%d %d %s\n", handle
, iField
, debugstr_w(szValue
));
603 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
605 return ERROR_INVALID_HANDLE
;
607 msiobj_lock( &rec
->hdr
);
608 ret
= MSI_RecordSetStringW( rec
, iField
, szValue
);
609 msiobj_unlock( &rec
->hdr
);
610 msiobj_release( &rec
->hdr
);
614 /* read the data in a file into an IStream */
615 static UINT
RECORD_StreamFromFile(LPCWSTR szFile
, IStream
**pstm
)
617 DWORD sz
, szHighWord
= 0, read
;
621 ULARGE_INTEGER ulSize
;
623 TRACE("reading %s\n", debugstr_w(szFile
));
625 /* read the file into memory */
626 handle
= CreateFileW(szFile
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
627 if( handle
== INVALID_HANDLE_VALUE
)
628 return GetLastError();
629 sz
= GetFileSize(handle
, &szHighWord
);
630 if( sz
!= INVALID_FILE_SIZE
&& szHighWord
== 0 )
632 hGlob
= GlobalAlloc(GMEM_FIXED
, sz
);
635 BOOL r
= ReadFile(handle
, hGlob
, sz
, &read
, NULL
);
645 return ERROR_FUNCTION_FAILED
;
647 /* make a stream out of it, and set the correct file size */
648 hr
= CreateStreamOnHGlobal(hGlob
, TRUE
, pstm
);
652 return ERROR_FUNCTION_FAILED
;
655 /* set the correct size - CreateStreamOnHGlobal screws it up */
656 ulSize
.QuadPart
= sz
;
657 IStream_SetSize(*pstm
, ulSize
);
659 TRACE("read %s, %d bytes into IStream %p\n", debugstr_w(szFile
), sz
, *pstm
);
661 return ERROR_SUCCESS
;
664 UINT
MSI_RecordSetStream(MSIRECORD
*rec
, UINT iField
, IStream
*stream
)
666 if ( (iField
== 0) || (iField
> rec
->count
) )
667 return ERROR_INVALID_PARAMETER
;
669 MSI_FreeField( &rec
->fields
[iField
] );
670 rec
->fields
[iField
].type
= MSIFIELD_STREAM
;
671 rec
->fields
[iField
].u
.stream
= stream
;
673 return ERROR_SUCCESS
;
676 UINT
MSI_RecordSetStreamFromFileW(MSIRECORD
*rec
, UINT iField
, LPCWSTR szFilename
)
681 if( (iField
== 0) || (iField
> rec
->count
) )
682 return ERROR_INVALID_PARAMETER
;
684 /* no filename means we should seek back to the start of the stream */
690 if( rec
->fields
[iField
].type
!= MSIFIELD_STREAM
)
691 return ERROR_INVALID_FIELD
;
693 stm
= rec
->fields
[iField
].u
.stream
;
695 return ERROR_INVALID_FIELD
;
698 r
= IStream_Seek( stm
, ofs
, STREAM_SEEK_SET
, &cur
);
700 return ERROR_FUNCTION_FAILED
;
704 /* read the file into a stream and save the stream in the record */
705 r
= RECORD_StreamFromFile(szFilename
, &stm
);
706 if( r
!= ERROR_SUCCESS
)
709 /* if all's good, store it in the record */
710 MSI_RecordSetStream(rec
, iField
, stm
);
713 return ERROR_SUCCESS
;
716 UINT WINAPI
MsiRecordSetStreamA(MSIHANDLE hRecord
, UINT iField
, LPCSTR szFilename
)
721 TRACE("%d %d %s\n", hRecord
, iField
, debugstr_a(szFilename
));
725 wstr
= strdupAtoW( szFilename
);
727 return ERROR_OUTOFMEMORY
;
729 ret
= MsiRecordSetStreamW(hRecord
, iField
, wstr
);
735 UINT WINAPI
MsiRecordSetStreamW(MSIHANDLE handle
, UINT iField
, LPCWSTR szFilename
)
740 TRACE("%d %d %s\n", handle
, iField
, debugstr_w(szFilename
));
742 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
744 return ERROR_INVALID_HANDLE
;
746 msiobj_lock( &rec
->hdr
);
747 ret
= MSI_RecordSetStreamFromFileW( rec
, iField
, szFilename
);
748 msiobj_unlock( &rec
->hdr
);
749 msiobj_release( &rec
->hdr
);
753 UINT
MSI_RecordReadStream(MSIRECORD
*rec
, UINT iField
, char *buf
, LPDWORD sz
)
759 TRACE("%p %d %p %p\n", rec
, iField
, buf
, sz
);
762 return ERROR_INVALID_PARAMETER
;
764 if( iField
> rec
->count
)
765 return ERROR_INVALID_PARAMETER
;
767 if ( rec
->fields
[iField
].type
== MSIFIELD_NULL
)
770 return ERROR_INVALID_DATA
;
773 if( rec
->fields
[iField
].type
!= MSIFIELD_STREAM
)
774 return ERROR_INVALID_DATATYPE
;
776 stm
= rec
->fields
[iField
].u
.stream
;
778 return ERROR_INVALID_PARAMETER
;
780 /* if there's no buffer pointer, calculate the length to the end */
784 ULARGE_INTEGER end
, cur
;
786 ofs
.QuadPart
= cur
.QuadPart
= 0;
788 r
= IStream_Seek( stm
, ofs
, STREAM_SEEK_SET
, &cur
);
789 IStream_Seek( stm
, ofs
, STREAM_SEEK_END
, &end
);
790 ofs
.QuadPart
= cur
.QuadPart
;
791 IStream_Seek( stm
, ofs
, STREAM_SEEK_SET
, &cur
);
792 *sz
= end
.QuadPart
- cur
.QuadPart
;
794 return ERROR_SUCCESS
;
799 r
= IStream_Read( stm
, buf
, *sz
, &count
);
803 return ERROR_FUNCTION_FAILED
;
808 return ERROR_SUCCESS
;
811 UINT WINAPI
MsiRecordReadStream(MSIHANDLE handle
, UINT iField
, char *buf
, LPDWORD sz
)
816 TRACE("%d %d %p %p\n", handle
, iField
, buf
, sz
);
818 rec
= msihandle2msiinfo( handle
, MSIHANDLETYPE_RECORD
);
820 return ERROR_INVALID_HANDLE
;
821 msiobj_lock( &rec
->hdr
);
822 ret
= MSI_RecordReadStream( rec
, iField
, buf
, sz
);
823 msiobj_unlock( &rec
->hdr
);
824 msiobj_release( &rec
->hdr
);
828 UINT
MSI_RecordSetIStream( MSIRECORD
*rec
, UINT iField
, IStream
*stm
)
830 TRACE("%p %d %p\n", rec
, iField
, stm
);
832 if( iField
> rec
->count
)
833 return ERROR_INVALID_FIELD
;
835 MSI_FreeField( &rec
->fields
[iField
] );
837 rec
->fields
[iField
].type
= MSIFIELD_STREAM
;
838 rec
->fields
[iField
].u
.stream
= stm
;
839 IStream_AddRef( stm
);
841 return ERROR_SUCCESS
;
844 UINT
MSI_RecordGetIStream( MSIRECORD
*rec
, UINT iField
, IStream
**pstm
)
846 TRACE("%p %d %p\n", rec
, iField
, pstm
);
848 if( iField
> rec
->count
)
849 return ERROR_INVALID_FIELD
;
851 if( rec
->fields
[iField
].type
!= MSIFIELD_STREAM
)
852 return ERROR_INVALID_FIELD
;
854 *pstm
= rec
->fields
[iField
].u
.stream
;
855 IStream_AddRef( *pstm
);
857 return ERROR_SUCCESS
;
860 static UINT
msi_dump_stream_to_file( IStream
*stm
, LPCWSTR name
)
868 stgm
= STGM_READWRITE
| STGM_SHARE_EXCLUSIVE
| STGM_FAILIFTHERE
;
869 r
= SHCreateStreamOnFileW( name
, stgm
, &out
);
871 return ERROR_FUNCTION_FAILED
;
874 r
= IStream_Seek( stm
, pos
, STREAM_SEEK_END
, &size
);
879 r
= IStream_Seek( stm
, pos
, STREAM_SEEK_SET
, NULL
);
883 r
= IStream_CopyTo( stm
, out
, size
, NULL
, NULL
);
886 IStream_Release( out
);
888 return ERROR_FUNCTION_FAILED
;
889 return ERROR_SUCCESS
;
892 UINT
MSI_RecordStreamToFile( MSIRECORD
*rec
, UINT iField
, LPCWSTR name
)
897 TRACE("%p %u %s\n", rec
, iField
, debugstr_w(name
));
899 msiobj_lock( &rec
->hdr
);
901 r
= MSI_RecordGetIStream( rec
, iField
, &stm
);
902 if( r
== ERROR_SUCCESS
)
904 r
= msi_dump_stream_to_file( stm
, name
);
905 IStream_Release( stm
);
908 msiobj_unlock( &rec
->hdr
);
913 MSIRECORD
*MSI_CloneRecord(MSIRECORD
*rec
)
918 count
= MSI_RecordGetFieldCount(rec
);
919 clone
= MSI_CreateRecord(count
);
923 for (i
= 0; i
<= count
; i
++)
925 if (rec
->fields
[i
].type
== MSIFIELD_STREAM
)
927 if (FAILED(IStream_Clone(rec
->fields
[i
].u
.stream
,
928 &clone
->fields
[i
].u
.stream
)))
930 msiobj_release(&clone
->hdr
);
933 clone
->fields
[i
].type
= MSIFIELD_STREAM
;
937 r
= MSI_RecordCopyField(rec
, i
, clone
, i
);
938 if (r
!= ERROR_SUCCESS
)
940 msiobj_release(&clone
->hdr
);
949 BOOL
MSI_RecordsAreEqual(MSIRECORD
*a
, MSIRECORD
*b
)
953 if (a
->count
!= b
->count
)
956 for (i
= 0; i
<= a
->count
; i
++)
958 if (a
->fields
[i
].type
!= b
->fields
[i
].type
)
961 switch (a
->fields
[i
].type
)
967 if (a
->fields
[i
].u
.iVal
!= b
->fields
[i
].u
.iVal
)
972 if (lstrcmpW(a
->fields
[i
].u
.szwVal
, b
->fields
[i
].u
.szwVal
))
976 case MSIFIELD_STREAM
: