msi: Improve the SetODBCFolders action stub.
[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 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         if( szValue && *pcchValue > 0 )
448             szValue[0] = 0;
449     default:
450         break;
451     }
452
453     if( szValue && *pcchValue <= len )
454         ret = ERROR_MORE_DATA;
455     *pcchValue = len;
456
457     return ret;
458 }
459
460 UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, UINT iField,
461                LPWSTR szValue, LPDWORD pcchValue)
462 {
463     MSIRECORD *rec;
464     UINT ret;
465
466     TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
467
468     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
469     if( !rec )
470         return ERROR_INVALID_HANDLE;
471
472     msiobj_lock( &rec->hdr );
473     ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
474     msiobj_unlock( &rec->hdr );
475     msiobj_release( &rec->hdr );
476     return ret;
477 }
478
479 static UINT msi_get_stream_size( IStream *stm )
480 {
481     STATSTG stat;
482     HRESULT r;
483
484     r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
485     if( FAILED(r) )
486         return 0;
487     return stat.cbSize.QuadPart;
488 }
489
490 static UINT MSI_RecordDataSize(MSIRECORD *rec, UINT iField)
491 {
492     TRACE("%p %d\n", rec, iField);
493
494     if( iField > rec->count )
495         return 0;
496
497     switch( rec->fields[iField].type )
498     {
499     case MSIFIELD_INT:
500         return sizeof (INT);
501     case MSIFIELD_WSTR:
502         return lstrlenW( rec->fields[iField].u.szwVal );
503     case MSIFIELD_NULL:
504         break;
505     case MSIFIELD_STREAM:
506         return msi_get_stream_size( rec->fields[iField].u.stream );
507     }
508     return 0;
509 }
510
511 UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, UINT iField)
512 {
513     MSIRECORD *rec;
514     UINT ret;
515
516     TRACE("%d %d\n", handle, iField);
517
518     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
519     if( !rec )
520         return 0;
521     msiobj_lock( &rec->hdr );
522     ret = MSI_RecordDataSize( rec, iField);
523     msiobj_unlock( &rec->hdr );
524     msiobj_release( &rec->hdr );
525     return ret;
526 }
527
528 static UINT MSI_RecordSetStringA( MSIRECORD *rec, UINT iField, LPCSTR szValue )
529 {
530     LPWSTR str;
531
532     TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
533
534     if( iField > rec->count )
535         return ERROR_INVALID_FIELD;
536
537     MSI_FreeField( &rec->fields[iField] );
538     if( szValue && szValue[0] )
539     {
540         str = strdupAtoW( szValue );
541         rec->fields[iField].type = MSIFIELD_WSTR;
542         rec->fields[iField].u.szwVal = str;
543     }
544     else
545     {
546         rec->fields[iField].type = MSIFIELD_NULL;
547         rec->fields[iField].u.szwVal = NULL;
548     }
549
550     return 0;
551 }
552
553 UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, UINT iField, LPCSTR szValue )
554 {
555     MSIRECORD *rec;
556     UINT ret;
557
558     TRACE("%d %d %s\n", handle, iField, debugstr_a(szValue));
559
560     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
561     if( !rec )
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 );
567     return ret;
568 }
569
570 UINT MSI_RecordSetStringW( MSIRECORD *rec, UINT iField, LPCWSTR szValue )
571 {
572     LPWSTR str;
573
574     TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
575
576     if( iField > rec->count )
577         return ERROR_INVALID_FIELD;
578
579     MSI_FreeField( &rec->fields[iField] );
580
581     if( szValue && szValue[0] )
582     {
583         str = strdupW( szValue );
584         rec->fields[iField].type = MSIFIELD_WSTR;
585         rec->fields[iField].u.szwVal = str;
586     }
587     else
588     {
589         rec->fields[iField].type = MSIFIELD_NULL;
590         rec->fields[iField].u.szwVal = NULL;
591     }
592
593     return 0;
594 }
595
596 UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, UINT iField, LPCWSTR szValue )
597 {
598     MSIRECORD *rec;
599     UINT ret;
600
601     TRACE("%d %d %s\n", handle, iField, debugstr_w(szValue));
602
603     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
604     if( !rec )
605         return ERROR_INVALID_HANDLE;
606
607     msiobj_lock( &rec->hdr );
608     ret = MSI_RecordSetStringW( rec, iField, szValue );
609     msiobj_unlock( &rec->hdr );
610     msiobj_release( &rec->hdr );
611     return ret;
612 }
613
614 /* read the data in a file into an IStream */
615 static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
616 {
617     DWORD sz, szHighWord = 0, read;
618     HANDLE handle;
619     HGLOBAL hGlob = 0;
620     HRESULT hr;
621     ULARGE_INTEGER ulSize;
622
623     TRACE("reading %s\n", debugstr_w(szFile));
624
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 )
631     {
632         hGlob = GlobalAlloc(GMEM_FIXED, sz);
633         if( hGlob )
634         {
635             BOOL r = ReadFile(handle, hGlob, sz, &read, NULL);
636             if( !r )
637             {
638                 GlobalFree(hGlob);
639                 hGlob = 0;
640             }
641         }
642     }
643     CloseHandle(handle);
644     if( !hGlob )
645         return ERROR_FUNCTION_FAILED;
646
647     /* make a stream out of it, and set the correct file size */
648     hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
649     if( FAILED( hr ) )
650     {
651         GlobalFree(hGlob);
652         return ERROR_FUNCTION_FAILED;
653     }
654
655     /* set the correct size - CreateStreamOnHGlobal screws it up */
656     ulSize.QuadPart = sz;
657     IStream_SetSize(*pstm, ulSize);
658
659     TRACE("read %s, %d bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
660
661     return ERROR_SUCCESS;
662 }
663
664 UINT MSI_RecordSetStream(MSIRECORD *rec, UINT iField, IStream *stream)
665 {
666     if ( (iField == 0) || (iField > rec->count) )
667         return ERROR_INVALID_PARAMETER;
668
669     MSI_FreeField( &rec->fields[iField] );
670     rec->fields[iField].type = MSIFIELD_STREAM;
671     rec->fields[iField].u.stream = stream;
672
673     return ERROR_SUCCESS;
674 }
675
676 UINT MSI_RecordSetStreamFromFileW(MSIRECORD *rec, UINT iField, LPCWSTR szFilename)
677 {
678     IStream *stm = NULL;
679     HRESULT r;
680
681     if( (iField == 0) || (iField > rec->count) )
682         return ERROR_INVALID_PARAMETER;
683
684     /* no filename means we should seek back to the start of the stream */
685     if( !szFilename )
686     {
687         LARGE_INTEGER ofs;
688         ULARGE_INTEGER cur;
689
690         if( rec->fields[iField].type != MSIFIELD_STREAM )
691             return ERROR_INVALID_FIELD;
692
693         stm = rec->fields[iField].u.stream;
694         if( !stm )
695             return ERROR_INVALID_FIELD;
696
697         ofs.QuadPart = 0;
698         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
699         if( FAILED( r ) )
700             return ERROR_FUNCTION_FAILED;
701     }
702     else
703     {
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 )
707             return r;
708
709         /* if all's good, store it in the record */
710         MSI_RecordSetStream(rec, iField, stm);
711     }
712
713     return ERROR_SUCCESS;
714 }
715
716 UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, UINT iField, LPCSTR szFilename)
717 {
718     LPWSTR wstr = NULL;
719     UINT ret;
720
721     TRACE("%d %d %s\n", hRecord, iField, debugstr_a(szFilename));
722
723     if( szFilename )
724     {
725         wstr = strdupAtoW( szFilename );
726         if( !wstr )
727              return ERROR_OUTOFMEMORY;
728     }
729     ret = MsiRecordSetStreamW(hRecord, iField, wstr);
730     msi_free(wstr);
731
732     return ret;
733 }
734
735 UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, UINT iField, LPCWSTR szFilename)
736 {
737     MSIRECORD *rec;
738     UINT ret;
739
740     TRACE("%d %d %s\n", handle, iField, debugstr_w(szFilename));
741
742     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
743     if( !rec )
744         return ERROR_INVALID_HANDLE;
745
746     msiobj_lock( &rec->hdr );
747     ret = MSI_RecordSetStreamFromFileW( rec, iField, szFilename );
748     msiobj_unlock( &rec->hdr );
749     msiobj_release( &rec->hdr );
750     return ret;
751 }
752
753 UINT MSI_RecordReadStream(MSIRECORD *rec, UINT iField, char *buf, LPDWORD sz)
754 {
755     ULONG count;
756     HRESULT r;
757     IStream *stm;
758
759     TRACE("%p %d %p %p\n", rec, iField, buf, sz);
760
761     if( !sz )
762         return ERROR_INVALID_PARAMETER;
763
764     if( iField > rec->count)
765         return ERROR_INVALID_PARAMETER;
766
767     if ( rec->fields[iField].type == MSIFIELD_NULL )
768     {
769         *sz = 0;
770         return ERROR_INVALID_DATA;
771     }
772
773     if( rec->fields[iField].type != MSIFIELD_STREAM )
774         return ERROR_INVALID_DATATYPE;
775
776     stm = rec->fields[iField].u.stream;
777     if( !stm )
778         return ERROR_INVALID_PARAMETER;
779
780     /* if there's no buffer pointer, calculate the length to the end */
781     if( !buf )
782     {
783         LARGE_INTEGER ofs;
784         ULARGE_INTEGER end, cur;
785
786         ofs.QuadPart = cur.QuadPart = 0;
787         end.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;
793
794         return ERROR_SUCCESS;
795     }
796
797     /* read the data */
798     count = 0;
799     r = IStream_Read( stm, buf, *sz, &count );
800     if( FAILED( r ) )
801     {
802         *sz = 0;
803         return ERROR_FUNCTION_FAILED;
804     }
805
806     *sz = count;
807
808     return ERROR_SUCCESS;
809 }
810
811 UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, UINT iField, char *buf, LPDWORD sz)
812 {
813     MSIRECORD *rec;
814     UINT ret;
815
816     TRACE("%d %d %p %p\n", handle, iField, buf, sz);
817
818     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
819     if( !rec )
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 );
825     return ret;
826 }
827
828 UINT MSI_RecordSetIStream( MSIRECORD *rec, UINT iField, IStream *stm )
829 {
830     TRACE("%p %d %p\n", rec, iField, stm);
831
832     if( iField > rec->count )
833         return ERROR_INVALID_FIELD;
834
835     MSI_FreeField( &rec->fields[iField] );
836
837     rec->fields[iField].type = MSIFIELD_STREAM;
838     rec->fields[iField].u.stream = stm;
839     IStream_AddRef( stm );
840
841     return ERROR_SUCCESS;
842 }
843
844 UINT MSI_RecordGetIStream( MSIRECORD *rec, UINT iField, IStream **pstm)
845 {
846     TRACE("%p %d %p\n", rec, iField, pstm);
847
848     if( iField > rec->count )
849         return ERROR_INVALID_FIELD;
850
851     if( rec->fields[iField].type != MSIFIELD_STREAM )
852         return ERROR_INVALID_FIELD;
853
854     *pstm = rec->fields[iField].u.stream;
855     IStream_AddRef( *pstm );
856
857     return ERROR_SUCCESS;
858 }
859
860 static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name )
861 {
862     ULARGE_INTEGER size;
863     LARGE_INTEGER pos;
864     IStream *out;
865     DWORD stgm;
866     HRESULT r;
867
868     stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE;
869     r = SHCreateStreamOnFileW( name, stgm, &out );
870     if( FAILED( r ) )
871         return ERROR_FUNCTION_FAILED;
872
873     pos.QuadPart = 0;
874     r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size );
875     if( FAILED( r ) )
876         goto end;
877
878     pos.QuadPart = 0;
879     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
880     if( FAILED( r ) )
881         goto end;
882
883     r = IStream_CopyTo( stm, out, size, NULL, NULL );
884
885 end:
886     IStream_Release( out );
887     if( FAILED( r ) )
888         return ERROR_FUNCTION_FAILED;
889     return ERROR_SUCCESS;
890 }
891
892 UINT MSI_RecordStreamToFile( MSIRECORD *rec, UINT iField, LPCWSTR name )
893 {
894     IStream *stm = NULL;
895     UINT r;
896
897     TRACE("%p %u %s\n", rec, iField, debugstr_w(name));
898
899     msiobj_lock( &rec->hdr );
900
901     r = MSI_RecordGetIStream( rec, iField, &stm );
902     if( r == ERROR_SUCCESS )
903     {
904         r = msi_dump_stream_to_file( stm, name );
905         IStream_Release( stm );
906     }
907
908     msiobj_unlock( &rec->hdr );
909
910     return r;
911 }
912
913 MSIRECORD *MSI_CloneRecord(MSIRECORD *rec)
914 {
915     MSIRECORD *clone;
916     UINT r, i, count;
917
918     count = MSI_RecordGetFieldCount(rec);
919     clone = MSI_CreateRecord(count);
920     if (!clone)
921         return NULL;
922
923     for (i = 0; i <= count; i++)
924     {
925         if (rec->fields[i].type == MSIFIELD_STREAM)
926         {
927             if (FAILED(IStream_Clone(rec->fields[i].u.stream,
928                                      &clone->fields[i].u.stream)))
929             {
930                 msiobj_release(&clone->hdr);
931                 return NULL;
932             }
933             clone->fields[i].type = MSIFIELD_STREAM;
934         }
935         else
936         {
937             r = MSI_RecordCopyField(rec, i, clone, i);
938             if (r != ERROR_SUCCESS)
939             {
940                 msiobj_release(&clone->hdr);
941                 return NULL;
942             }
943         }
944     }
945
946     return clone;
947 }
948
949 BOOL MSI_RecordsAreEqual(MSIRECORD *a, MSIRECORD *b)
950 {
951     UINT i;
952
953     if (a->count != b->count)
954         return FALSE;
955
956     for (i = 0; i <= a->count; i++)
957     {
958         if (a->fields[i].type != b->fields[i].type)
959             return FALSE;
960
961         switch (a->fields[i].type)
962         {
963             case MSIFIELD_NULL:
964                 break;
965
966             case MSIFIELD_INT:
967                 if (a->fields[i].u.iVal != b->fields[i].u.iVal)
968                     return FALSE;
969                 break;
970
971             case MSIFIELD_WSTR:
972                 if (lstrcmpW(a->fields[i].u.szwVal, b->fields[i].u.szwVal))
973                     return FALSE;
974                 break;
975
976             case MSIFIELD_STREAM:
977             default:
978                 return FALSE;
979         }
980     }
981
982     return TRUE;
983 }