include: Change to LONG/ULONG in fci.h for Win64 compatibility.
[wine] / dlls / msi / record.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2002-2004 Mike McCormack for CodeWeavers
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <stdarg.h>
22
23 #define COBJMACROS
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winuser.h"
28 #include "winerror.h"
29 #include "wine/debug.h"
30 #include "msi.h"
31 #include "msiquery.h"
32 #include "msipriv.h"
33 #include "objidl.h"
34 #include "winnls.h"
35 #include "ole2.h"
36
37 #include "winreg.h"
38 #include "shlwapi.h"
39
40 #include "query.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
43
44 #define MSIFIELD_NULL   0
45 #define MSIFIELD_INT    1
46 #define MSIFIELD_WSTR   3
47 #define MSIFIELD_STREAM 4
48
49 static void MSI_FreeField( MSIFIELD *field )
50 {
51     switch( field->type )
52     {
53     case MSIFIELD_NULL:
54     case MSIFIELD_INT:
55         break;
56     case MSIFIELD_WSTR:
57         msi_free( field->u.szwVal);
58         break;
59     case MSIFIELD_STREAM:
60         IStream_Release( field->u.stream );
61         break;
62     default:
63         ERR("Invalid field type %d\n", field->type);
64     }
65 }
66
67 static void MSI_CloseRecord( MSIOBJECTHDR *arg )
68 {
69     MSIRECORD *rec = (MSIRECORD *) arg;
70     UINT i;
71
72     for( i=0; i<=rec->count; i++ )
73         MSI_FreeField( &rec->fields[i] );
74 }
75
76 MSIRECORD *MSI_CreateRecord( UINT cParams )
77 {
78     MSIRECORD *rec;
79     UINT len;
80
81     TRACE("%d\n", cParams);
82
83     if( cParams>65535 )
84         return NULL;
85
86     len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams;
87     rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord );
88     if( rec )
89         rec->count = cParams;
90     return rec;
91 }
92
93 MSIHANDLE WINAPI MsiCreateRecord( UINT cParams )
94 {
95     MSIRECORD *rec;
96     MSIHANDLE ret = 0;
97
98     TRACE("%d\n", cParams);
99
100     rec = MSI_CreateRecord( cParams );
101     if( rec )
102     {
103         ret = alloc_msihandle( &rec->hdr );
104         msiobj_release( &rec->hdr );
105     }
106     return ret;
107 }
108
109 UINT MSI_RecordGetFieldCount( const MSIRECORD *rec )
110 {
111     return rec->count;
112 }
113
114 UINT WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
115 {
116     MSIRECORD *rec;
117     UINT ret;
118
119     TRACE("%d\n", handle );
120
121     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
122     if( !rec )
123         return -1;
124
125     msiobj_lock( &rec->hdr );
126     ret = MSI_RecordGetFieldCount( rec );
127     msiobj_unlock( &rec->hdr );
128     msiobj_release( &rec->hdr );
129
130     return ret;
131 }
132
133 static BOOL string2intW( LPCWSTR str, int *out )
134 {
135     int x = 0;
136     LPCWSTR p = str;
137
138     if( *p == '-' ) /* skip the minus sign */
139         p++;
140     while ( *p )
141     {
142         if( (*p < '0') || (*p > '9') )
143             return FALSE;
144         x *= 10;
145         x += (*p - '0');
146         p++;
147     }
148
149     if( str[0] == '-' ) /* check if it's negative */
150         x = -x;
151     *out = x; 
152
153     return TRUE;
154 }
155
156 UINT MSI_RecordCopyField( MSIRECORD *in_rec, UINT in_n,
157                           MSIRECORD *out_rec, UINT out_n )
158 {
159     UINT r = ERROR_SUCCESS;
160
161     msiobj_lock( &in_rec->hdr );
162
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 )
166     {
167         LPWSTR str;
168         MSIFIELD *in, *out;
169
170         in = &in_rec->fields[in_n];
171         out = &out_rec->fields[out_n];
172
173         switch ( in->type )
174         {
175         case MSIFIELD_NULL:
176             break;
177         case MSIFIELD_INT:
178             out->u.iVal = in->u.iVal;
179             break;
180         case MSIFIELD_WSTR:
181             str = strdupW( in->u.szwVal );
182             if ( !str )
183                 r = ERROR_OUTOFMEMORY;
184             else
185                 out->u.szwVal = str;
186             break;
187         case MSIFIELD_STREAM:
188             IStream_AddRef( in->u.stream );
189             out->u.stream = in->u.stream;
190             break;
191         default:
192             ERR("invalid field type %d\n", in->type);
193         }
194         if (r == ERROR_SUCCESS)
195             out->type = in->type;
196     }
197
198     msiobj_unlock( &in_rec->hdr );
199
200     return r;
201 }
202
203 int MSI_RecordGetInteger( MSIRECORD *rec, UINT iField)
204 {
205     int ret = 0;
206
207     TRACE("%p %d\n", rec, iField );
208
209     if( iField > rec->count )
210         return MSI_NULL_INTEGER;
211
212     switch( rec->fields[iField].type )
213     {
214     case MSIFIELD_INT:
215         return rec->fields[iField].u.iVal;
216     case MSIFIELD_WSTR:
217         if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
218             return ret;
219         return MSI_NULL_INTEGER;
220     default:
221         break;
222     }
223
224     return MSI_NULL_INTEGER;
225 }
226
227 int WINAPI MsiRecordGetInteger( MSIHANDLE handle, UINT iField)
228 {
229     MSIRECORD *rec;
230     UINT ret;
231
232     TRACE("%d %d\n", handle, iField );
233
234     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
235     if( !rec )
236         return MSI_NULL_INTEGER;
237
238     msiobj_lock( &rec->hdr );
239     ret = MSI_RecordGetInteger( rec, iField );
240     msiobj_unlock( &rec->hdr );
241     msiobj_release( &rec->hdr );
242
243     return ret;
244 }
245
246 UINT WINAPI MsiRecordClearData( MSIHANDLE handle )
247 {
248     MSIRECORD *rec;
249     UINT i;
250
251     TRACE("%d\n", handle );
252
253     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
254     if( !rec )
255         return ERROR_INVALID_HANDLE;
256
257     msiobj_lock( &rec->hdr );
258     for( i=0; i<=rec->count; i++)
259     {
260         MSI_FreeField( &rec->fields[i] );
261         rec->fields[i].type = MSIFIELD_NULL;
262         rec->fields[i].u.iVal = 0;
263     }
264     msiobj_unlock( &rec->hdr );
265     msiobj_release( &rec->hdr );
266
267     return ERROR_SUCCESS;
268 }
269
270 UINT MSI_RecordSetInteger( MSIRECORD *rec, UINT iField, int iVal )
271 {
272     TRACE("%p %u %d\n", rec, iField, iVal);
273
274     if( iField > rec->count )
275         return ERROR_INVALID_PARAMETER;
276
277     MSI_FreeField( &rec->fields[iField] );
278     rec->fields[iField].type = MSIFIELD_INT;
279     rec->fields[iField].u.iVal = iVal;
280
281     return ERROR_SUCCESS;
282 }
283
284 UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, UINT iField, int iVal )
285 {
286     MSIRECORD *rec;
287     UINT ret;
288
289     TRACE("%d %u %d\n", handle, iField, iVal);
290
291     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
292     if( !rec )
293         return ERROR_INVALID_HANDLE;
294
295     msiobj_lock( &rec->hdr );
296     ret = MSI_RecordSetInteger( rec, iField, iVal );
297     msiobj_unlock( &rec->hdr );
298     msiobj_release( &rec->hdr );
299     return ret;
300 }
301
302 BOOL MSI_RecordIsNull( MSIRECORD *rec, UINT iField )
303 {
304     BOOL r = TRUE;
305
306     TRACE("%p %d\n", rec, iField );
307
308     r = ( iField > rec->count ) ||
309         ( rec->fields[iField].type == MSIFIELD_NULL );
310
311     return r;
312 }
313
314 BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, UINT iField )
315 {
316     MSIRECORD *rec;
317     UINT ret;
318
319     TRACE("%d %d\n", handle, iField );
320
321     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
322     if( !rec )
323         return 0;
324     msiobj_lock( &rec->hdr );
325     ret = MSI_RecordIsNull( rec, iField );
326     msiobj_unlock( &rec->hdr );
327     msiobj_release( &rec->hdr );
328     return ret;
329
330 }
331
332 UINT MSI_RecordGetStringA(MSIRECORD *rec, UINT iField,
333                LPSTR szValue, LPDWORD pcchValue)
334 {
335     UINT len=0, ret;
336     CHAR buffer[16];
337
338     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
339
340     if( iField > rec->count )
341     {
342         if ( szValue && *pcchValue > 0 )
343             szValue[0] = 0;
344
345         *pcchValue = 0;
346         return ERROR_SUCCESS;
347     }
348
349     ret = ERROR_SUCCESS;
350     switch( rec->fields[iField].type )
351     {
352     case MSIFIELD_INT:
353         wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
354         len = lstrlenA( buffer );
355         if (szValue)
356             lstrcpynA(szValue, buffer, *pcchValue);
357         break;
358     case MSIFIELD_WSTR:
359         len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
360                              NULL, 0 , NULL, NULL);
361         if (szValue)
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;
366         if( len )
367             len--;
368         break;
369     case MSIFIELD_NULL:
370         if( szValue && *pcchValue > 0 )
371             szValue[0] = 0;
372         break;
373     default:
374         ret = ERROR_INVALID_PARAMETER;
375         break;
376     }
377
378     if( szValue && *pcchValue <= len )
379         ret = ERROR_MORE_DATA;
380     *pcchValue = len;
381
382     return ret;
383 }
384
385 UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, UINT iField,
386                LPSTR szValue, LPDWORD pcchValue)
387 {
388     MSIRECORD *rec;
389     UINT ret;
390
391     TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
392
393     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
394     if( !rec )
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 );
400     return ret;
401 }
402
403 const WCHAR *MSI_RecordGetString( const MSIRECORD *rec, UINT iField )
404 {
405     if( iField > rec->count )
406         return NULL;
407
408     if( rec->fields[iField].type != MSIFIELD_WSTR )
409         return NULL;
410
411     return rec->fields[iField].u.szwVal;
412 }
413
414 UINT MSI_RecordGetStringW(MSIRECORD *rec, UINT iField,
415                LPWSTR szValue, LPDWORD pcchValue)
416 {
417     UINT len=0, ret;
418     WCHAR buffer[16];
419     static const WCHAR szFormat[] = { '%','d',0 };
420
421     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
422
423     if( iField > rec->count )
424     {
425         if ( szValue && *pcchValue > 0 )
426             szValue[0] = 0;
427
428         *pcchValue = 0;
429         return ERROR_SUCCESS;
430     }
431
432     ret = ERROR_SUCCESS;
433     switch( rec->fields[iField].type )
434     {
435     case MSIFIELD_INT:
436         wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
437         len = lstrlenW( buffer );
438         if (szValue)
439             lstrcpynW(szValue, buffer, *pcchValue);
440         break;
441     case MSIFIELD_WSTR:
442         len = lstrlenW( rec->fields[iField].u.szwVal );
443         if (szValue)
444             lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue);
445         break;
446     case MSIFIELD_NULL:
447         len = 1;
448         if( szValue && *pcchValue > 0 )
449             szValue[0] = 0;
450     default:
451         break;
452     }
453
454     if( szValue && *pcchValue <= len )
455         ret = ERROR_MORE_DATA;
456     *pcchValue = len;
457
458     return ret;
459 }
460
461 UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, UINT iField,
462                LPWSTR szValue, LPDWORD pcchValue)
463 {
464     MSIRECORD *rec;
465     UINT ret;
466
467     TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
468
469     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
470     if( !rec )
471         return ERROR_INVALID_HANDLE;
472
473     msiobj_lock( &rec->hdr );
474     ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
475     msiobj_unlock( &rec->hdr );
476     msiobj_release( &rec->hdr );
477     return ret;
478 }
479
480 static UINT msi_get_stream_size( IStream *stm )
481 {
482     STATSTG stat;
483     HRESULT r;
484
485     r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
486     if( FAILED(r) )
487         return 0;
488     return stat.cbSize.QuadPart;
489 }
490
491 static UINT MSI_RecordDataSize(MSIRECORD *rec, UINT iField)
492 {
493     TRACE("%p %d\n", rec, iField);
494
495     if( iField > rec->count )
496         return 0;
497
498     switch( rec->fields[iField].type )
499     {
500     case MSIFIELD_INT:
501         return sizeof (INT);
502     case MSIFIELD_WSTR:
503         return lstrlenW( rec->fields[iField].u.szwVal );
504     case MSIFIELD_NULL:
505         break;
506     case MSIFIELD_STREAM:
507         return msi_get_stream_size( rec->fields[iField].u.stream );
508     }
509     return 0;
510 }
511
512 UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, UINT iField)
513 {
514     MSIRECORD *rec;
515     UINT ret;
516
517     TRACE("%d %d\n", handle, iField);
518
519     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
520     if( !rec )
521         return 0;
522     msiobj_lock( &rec->hdr );
523     ret = MSI_RecordDataSize( rec, iField);
524     msiobj_unlock( &rec->hdr );
525     msiobj_release( &rec->hdr );
526     return ret;
527 }
528
529 static UINT MSI_RecordSetStringA( MSIRECORD *rec, UINT iField, LPCSTR szValue )
530 {
531     LPWSTR str;
532
533     TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
534
535     if( iField > rec->count )
536         return ERROR_INVALID_FIELD;
537
538     MSI_FreeField( &rec->fields[iField] );
539     if( szValue && szValue[0] )
540     {
541         str = strdupAtoW( szValue );
542         rec->fields[iField].type = MSIFIELD_WSTR;
543         rec->fields[iField].u.szwVal = str;
544     }
545     else
546     {
547         rec->fields[iField].type = MSIFIELD_NULL;
548         rec->fields[iField].u.szwVal = NULL;
549     }
550
551     return 0;
552 }
553
554 UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, UINT iField, LPCSTR szValue )
555 {
556     MSIRECORD *rec;
557     UINT ret;
558
559     TRACE("%d %d %s\n", handle, iField, debugstr_a(szValue));
560
561     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
562     if( !rec )
563         return ERROR_INVALID_HANDLE;
564     msiobj_lock( &rec->hdr );
565     ret = MSI_RecordSetStringA( rec, iField, szValue );
566     msiobj_unlock( &rec->hdr );
567     msiobj_release( &rec->hdr );
568     return ret;
569 }
570
571 UINT MSI_RecordSetStringW( MSIRECORD *rec, UINT iField, LPCWSTR szValue )
572 {
573     LPWSTR str;
574
575     TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
576
577     if( iField > rec->count )
578         return ERROR_INVALID_FIELD;
579
580     MSI_FreeField( &rec->fields[iField] );
581
582     if( szValue && szValue[0] )
583     {
584         str = strdupW( szValue );
585         rec->fields[iField].type = MSIFIELD_WSTR;
586         rec->fields[iField].u.szwVal = str;
587     }
588     else
589     {
590         rec->fields[iField].type = MSIFIELD_NULL;
591         rec->fields[iField].u.szwVal = NULL;
592     }
593
594     return 0;
595 }
596
597 UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, UINT iField, LPCWSTR szValue )
598 {
599     MSIRECORD *rec;
600     UINT ret;
601
602     TRACE("%d %d %s\n", handle, iField, debugstr_w(szValue));
603
604     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
605     if( !rec )
606         return ERROR_INVALID_HANDLE;
607
608     msiobj_lock( &rec->hdr );
609     ret = MSI_RecordSetStringW( rec, iField, szValue );
610     msiobj_unlock( &rec->hdr );
611     msiobj_release( &rec->hdr );
612     return ret;
613 }
614
615 /* read the data in a file into an IStream */
616 static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
617 {
618     DWORD sz, szHighWord = 0, read;
619     HANDLE handle;
620     HGLOBAL hGlob = 0;
621     HRESULT hr;
622     ULARGE_INTEGER ulSize;
623
624     TRACE("reading %s\n", debugstr_w(szFile));
625
626     /* read the file into memory */
627     handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
628     if( handle == INVALID_HANDLE_VALUE )
629         return GetLastError();
630     sz = GetFileSize(handle, &szHighWord);
631     if( sz != INVALID_FILE_SIZE && szHighWord == 0 )
632     {
633         hGlob = GlobalAlloc(GMEM_FIXED, sz);
634         if( hGlob )
635         {
636             BOOL r = ReadFile(handle, hGlob, sz, &read, NULL);
637             if( !r )
638             {
639                 GlobalFree(hGlob);
640                 hGlob = 0;
641             }
642         }
643     }
644     CloseHandle(handle);
645     if( !hGlob )
646         return ERROR_FUNCTION_FAILED;
647
648     /* make a stream out of it, and set the correct file size */
649     hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
650     if( FAILED( hr ) )
651     {
652         GlobalFree(hGlob);
653         return ERROR_FUNCTION_FAILED;
654     }
655
656     /* set the correct size - CreateStreamOnHGlobal screws it up */
657     ulSize.QuadPart = sz;
658     IStream_SetSize(*pstm, ulSize);
659
660     TRACE("read %s, %d bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
661
662     return ERROR_SUCCESS;
663 }
664
665 static UINT MSI_RecordSetStream(MSIRECORD *rec, UINT iField, IStream *stream)
666 {
667     if ( (iField == 0) || (iField > rec->count) )
668         return ERROR_INVALID_PARAMETER;
669
670     MSI_FreeField( &rec->fields[iField] );
671     rec->fields[iField].type = MSIFIELD_STREAM;
672     rec->fields[iField].u.stream = stream;
673
674     return ERROR_SUCCESS;
675 }
676
677 static UINT MSI_RecordSetStreamFromFileW(MSIRECORD *rec, UINT iField, LPCWSTR szFilename)
678 {
679     IStream *stm = NULL;
680     HRESULT r;
681
682     if( (iField == 0) || (iField > rec->count) )
683         return ERROR_INVALID_PARAMETER;
684
685     /* no filename means we should seek back to the start of the stream */
686     if( !szFilename )
687     {
688         LARGE_INTEGER ofs;
689         ULARGE_INTEGER cur;
690
691         if( rec->fields[iField].type != MSIFIELD_STREAM )
692             return ERROR_INVALID_FIELD;
693
694         stm = rec->fields[iField].u.stream;
695         if( !stm )
696             return ERROR_INVALID_FIELD;
697
698         ofs.QuadPart = 0;
699         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
700         if( FAILED( r ) )
701             return ERROR_FUNCTION_FAILED;
702     }
703     else
704     {
705         /* read the file into a stream and save the stream in the record */
706         r = RECORD_StreamFromFile(szFilename, &stm);
707         if( r != ERROR_SUCCESS )
708             return r;
709
710         /* if all's good, store it in the record */
711         MSI_RecordSetStream(rec, iField, stm);
712     }
713
714     return ERROR_SUCCESS;
715 }
716
717 UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, UINT iField, LPCSTR szFilename)
718 {
719     LPWSTR wstr = NULL;
720     UINT ret;
721
722     TRACE("%d %d %s\n", hRecord, iField, debugstr_a(szFilename));
723
724     if( szFilename )
725     {
726         wstr = strdupAtoW( szFilename );
727         if( !wstr )
728              return ERROR_OUTOFMEMORY;
729     }
730     ret = MsiRecordSetStreamW(hRecord, iField, wstr);
731     msi_free(wstr);
732
733     return ret;
734 }
735
736 UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, UINT iField, LPCWSTR szFilename)
737 {
738     MSIRECORD *rec;
739     UINT ret;
740
741     TRACE("%d %d %s\n", handle, iField, debugstr_w(szFilename));
742
743     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
744     if( !rec )
745         return ERROR_INVALID_HANDLE;
746
747     msiobj_lock( &rec->hdr );
748     ret = MSI_RecordSetStreamFromFileW( rec, iField, szFilename );
749     msiobj_unlock( &rec->hdr );
750     msiobj_release( &rec->hdr );
751     return ret;
752 }
753
754 UINT MSI_RecordReadStream(MSIRECORD *rec, UINT iField, char *buf, LPDWORD sz)
755 {
756     ULONG count;
757     HRESULT r;
758     IStream *stm;
759
760     TRACE("%p %d %p %p\n", rec, iField, buf, sz);
761
762     if( !sz )
763         return ERROR_INVALID_PARAMETER;
764
765     if( iField > rec->count)
766         return ERROR_INVALID_PARAMETER;
767
768     if( rec->fields[iField].type != MSIFIELD_STREAM )
769         return ERROR_INVALID_DATATYPE;
770
771     stm = rec->fields[iField].u.stream;
772     if( !stm )
773         return ERROR_INVALID_PARAMETER;
774
775     /* if there's no buffer pointer, calculate the length to the end */
776     if( !buf )
777     {
778         LARGE_INTEGER ofs;
779         ULARGE_INTEGER end, cur;
780
781         ofs.QuadPart = cur.QuadPart = 0;
782         end.QuadPart = 0;
783         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
784         IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
785         ofs.QuadPart = cur.QuadPart;
786         IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
787         *sz = end.QuadPart - cur.QuadPart;
788
789         return ERROR_SUCCESS;
790     }
791
792     /* read the data */
793     count = 0;
794     r = IStream_Read( stm, buf, *sz, &count );
795     if( FAILED( r ) )
796     {
797         *sz = 0;
798         return ERROR_FUNCTION_FAILED;
799     }
800
801     *sz = count;
802
803     return ERROR_SUCCESS;
804 }
805
806 UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, UINT iField, char *buf, LPDWORD sz)
807 {
808     MSIRECORD *rec;
809     UINT ret;
810
811     TRACE("%d %d %p %p\n", handle, iField, buf, sz);
812
813     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
814     if( !rec )
815         return ERROR_INVALID_HANDLE;
816     msiobj_lock( &rec->hdr );
817     ret = MSI_RecordReadStream( rec, iField, buf, sz );
818     msiobj_unlock( &rec->hdr );
819     msiobj_release( &rec->hdr );
820     return ret;
821 }
822
823 UINT MSI_RecordSetIStream( MSIRECORD *rec, UINT iField, IStream *stm )
824 {
825     TRACE("%p %d %p\n", rec, iField, stm);
826
827     if( iField > rec->count )
828         return ERROR_INVALID_FIELD;
829
830     MSI_FreeField( &rec->fields[iField] );
831
832     rec->fields[iField].type = MSIFIELD_STREAM;
833     rec->fields[iField].u.stream = stm;
834     IStream_AddRef( stm );
835
836     return ERROR_SUCCESS;
837 }
838
839 UINT MSI_RecordGetIStream( MSIRECORD *rec, UINT iField, IStream **pstm)
840 {
841     TRACE("%p %d %p\n", rec, iField, pstm);
842
843     if( iField > rec->count )
844         return ERROR_INVALID_FIELD;
845
846     if( rec->fields[iField].type != MSIFIELD_STREAM )
847         return ERROR_INVALID_FIELD;
848
849     *pstm = rec->fields[iField].u.stream;
850     IStream_AddRef( *pstm );
851
852     return ERROR_SUCCESS;
853 }
854
855 static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name )
856 {
857     ULARGE_INTEGER size;
858     LARGE_INTEGER pos;
859     IStream *out;
860     DWORD stgm;
861     HRESULT r;
862
863     stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE;
864     r = SHCreateStreamOnFileW( name, stgm, &out );
865     if( FAILED( r ) )
866         return ERROR_FUNCTION_FAILED;
867
868     pos.QuadPart = 0;
869     r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size );
870     if( FAILED( r ) )
871         goto end;
872
873     pos.QuadPart = 0;
874     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
875     if( FAILED( r ) )
876         goto end;
877
878     r = IStream_CopyTo( stm, out, size, NULL, NULL );
879
880 end:
881     IStream_Release( out );
882     if( FAILED( r ) )
883         return ERROR_FUNCTION_FAILED;
884     return ERROR_SUCCESS;
885 }
886
887 UINT MSI_RecordStreamToFile( MSIRECORD *rec, UINT iField, LPCWSTR name )
888 {
889     IStream *stm = NULL;
890     UINT r;
891
892     TRACE("%p %u %s\n", rec, iField, debugstr_w(name));
893
894     msiobj_lock( &rec->hdr );
895
896     r = MSI_RecordGetIStream( rec, iField, &stm );
897     if( r == ERROR_SUCCESS )
898     {
899         r = msi_dump_stream_to_file( stm, name );
900         IStream_Release( stm );
901     }
902
903     msiobj_unlock( &rec->hdr );
904
905     return r;
906 }
907
908 MSIRECORD *MSI_CloneRecord(MSIRECORD *rec)
909 {
910     MSIRECORD *clone;
911     UINT r, i, count;
912
913     count = MSI_RecordGetFieldCount(rec);
914     clone = MSI_CreateRecord(count);
915     if (!clone)
916         return NULL;
917
918     for (i = 0; i <= count; i++)
919     {
920         if (rec->fields[i].type == MSIFIELD_STREAM)
921         {
922             if (FAILED(IStream_Clone(rec->fields[i].u.stream,
923                                      &clone->fields[i].u.stream)))
924             {
925                 msiobj_release(&clone->hdr);
926                 return NULL;
927             }
928         }
929         else
930         {
931             r = MSI_RecordCopyField(rec, i, clone, i);
932             if (r != ERROR_SUCCESS)
933             {
934                 msiobj_release(&clone->hdr);
935                 return NULL;
936             }
937         }
938     }
939
940     return clone;
941 }
942
943 BOOL MSI_RecordsAreEqual(MSIRECORD *a, MSIRECORD *b)
944 {
945     UINT i;
946
947     if (a->count != b->count)
948         return FALSE;
949
950     for (i = 0; i <= a->count; i++)
951     {
952         if (a->fields[i].type != b->fields[i].type)
953             return FALSE;
954
955         switch (a->fields[i].type)
956         {
957             case MSIFIELD_NULL:
958                 break;
959
960             case MSIFIELD_INT:
961                 if (a->fields[i].u.iVal != b->fields[i].u.iVal)
962                     return FALSE;
963                 break;
964
965             case MSIFIELD_WSTR:
966                 if (lstrcmpW(a->fields[i].u.szwVal, b->fields[i].u.szwVal))
967                     return FALSE;
968                 break;
969
970             case MSIFIELD_STREAM:
971             default:
972                 return FALSE;
973         }
974     }
975
976     return TRUE;
977 }