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