msi: Properly delete rows from the table, instead of zeroing out the row.
[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( unsigned int 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( unsigned int 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 unsigned int MSI_RecordGetFieldCount( const MSIRECORD *rec )
110 {
111     return rec->count;
112 }
113
114 unsigned int WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
115 {
116     MSIRECORD *rec;
117     UINT ret;
118
119     TRACE("%ld\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, unsigned int in_n,
157                           MSIRECORD *out_rec, unsigned int 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, unsigned int 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, unsigned int iField)
228 {
229     MSIRECORD *rec;
230     UINT ret;
231
232     TRACE("%ld %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("%ld\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, unsigned int 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, unsigned int iField, int iVal )
285 {
286     MSIRECORD *rec;
287     UINT ret;
288
289     TRACE("%ld %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, unsigned int 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, unsigned int iField )
315 {
316     MSIRECORD *rec;
317     UINT ret;
318
319     TRACE("%ld %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, unsigned int iField, 
333                LPSTR szValue, DWORD *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         return ERROR_INVALID_PARAMETER;
342
343     ret = ERROR_SUCCESS;
344     switch( rec->fields[iField].type )
345     {
346     case MSIFIELD_INT:
347         wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
348         len = lstrlenA( buffer );
349         if (szValue)
350             lstrcpynA(szValue, buffer, *pcchValue);
351         break;
352     case MSIFIELD_WSTR:
353         len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
354                              NULL, 0 , NULL, NULL);
355         WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
356                              szValue, *pcchValue, NULL, NULL);
357         if( szValue && *pcchValue && len>*pcchValue )
358             szValue[*pcchValue-1] = 0;
359         if( len )
360             len--;
361         break;
362     case MSIFIELD_NULL:
363         if( *pcchValue > 0 )
364             szValue[0] = 0;
365         break;
366     default:
367         ret = ERROR_INVALID_PARAMETER;
368         break;
369     }
370
371     if( szValue && *pcchValue <= len )
372         ret = ERROR_MORE_DATA;
373     *pcchValue = len;
374
375     return ret;
376 }
377
378 UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, unsigned int iField, 
379                LPSTR szValue, DWORD *pcchValue)
380 {
381     MSIRECORD *rec;
382     UINT ret;
383
384     TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
385
386     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
387     if( !rec )
388         return ERROR_INVALID_HANDLE;
389     msiobj_lock( &rec->hdr );
390     ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
391     msiobj_unlock( &rec->hdr );
392     msiobj_release( &rec->hdr );
393     return ret;
394 }
395
396 const WCHAR *MSI_RecordGetString( const MSIRECORD *rec, unsigned int iField )
397 {
398     if( iField > rec->count )
399         return NULL;
400
401     if( rec->fields[iField].type != MSIFIELD_WSTR )
402         return NULL;
403
404     return rec->fields[iField].u.szwVal;
405 }
406
407 UINT MSI_RecordGetStringW(MSIRECORD *rec, unsigned int iField,
408                LPWSTR szValue, DWORD *pcchValue)
409 {
410     UINT len=0, ret;
411     WCHAR buffer[16];
412     static const WCHAR szFormat[] = { '%','d',0 };
413
414     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
415
416     if( iField > rec->count )
417         return ERROR_INVALID_PARAMETER;
418
419     ret = ERROR_SUCCESS;
420     switch( rec->fields[iField].type )
421     {
422     case MSIFIELD_INT:
423         wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
424         len = lstrlenW( buffer );
425         if (szValue)
426             lstrcpynW(szValue, buffer, *pcchValue);
427         break;
428     case MSIFIELD_WSTR:
429         len = lstrlenW( rec->fields[iField].u.szwVal );
430         if (szValue)
431             lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue);
432         break;
433     case MSIFIELD_NULL:
434         len = 1;
435         if( szValue && *pcchValue > 0 )
436             szValue[0] = 0;
437     default:
438         break;
439     }
440
441     if( szValue && *pcchValue <= len )
442         ret = ERROR_MORE_DATA;
443     *pcchValue = len;
444
445     return ret;
446 }
447
448 UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, unsigned int iField,
449                LPWSTR szValue, DWORD *pcchValue)
450 {
451     MSIRECORD *rec;
452     UINT ret;
453
454     TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
455
456     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
457     if( !rec )
458         return ERROR_INVALID_HANDLE;
459
460     msiobj_lock( &rec->hdr );
461     ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
462     msiobj_unlock( &rec->hdr );
463     msiobj_release( &rec->hdr );
464     return ret;
465 }
466
467 static UINT msi_get_stream_size( IStream *stm )
468 {
469     STATSTG stat;
470     HRESULT r;
471
472     r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
473     if( FAILED(r) )
474         return 0;
475     return stat.cbSize.QuadPart;
476 }
477
478 UINT MSI_RecordDataSize(MSIRECORD *rec, unsigned int iField)
479 {
480     TRACE("%p %d\n", rec, iField);
481
482     if( iField > rec->count )
483         return 0;
484
485     switch( rec->fields[iField].type )
486     {
487     case MSIFIELD_INT:
488         return sizeof (INT);
489     case MSIFIELD_WSTR:
490         return lstrlenW( rec->fields[iField].u.szwVal );
491     case MSIFIELD_NULL:
492         break;
493     case MSIFIELD_STREAM:
494         return msi_get_stream_size( rec->fields[iField].u.stream );
495     }
496     return 0;
497 }
498
499 UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, unsigned int iField)
500 {
501     MSIRECORD *rec;
502     UINT ret;
503
504     TRACE("%ld %d\n", handle, iField);
505
506     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
507     if( !rec )
508         return 0;
509     msiobj_lock( &rec->hdr );
510     ret = MSI_RecordDataSize( rec, iField);
511     msiobj_unlock( &rec->hdr );
512     msiobj_release( &rec->hdr );
513     return ret;
514 }
515
516 UINT MSI_RecordSetStringA( MSIRECORD *rec, unsigned int iField, LPCSTR szValue )
517 {
518     LPWSTR str;
519
520     TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
521
522     if( iField > rec->count )
523         return ERROR_INVALID_FIELD;
524
525     MSI_FreeField( &rec->fields[iField] );
526     if( szValue && szValue[0] )
527     {
528         str = strdupAtoW( szValue );
529         rec->fields[iField].type = MSIFIELD_WSTR;
530         rec->fields[iField].u.szwVal = str;
531     }
532     else
533     {
534         rec->fields[iField].type = MSIFIELD_NULL;
535         rec->fields[iField].u.szwVal = NULL;
536     }
537
538     return 0;
539 }
540
541 UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, unsigned int iField, LPCSTR szValue )
542 {
543     MSIRECORD *rec;
544     UINT ret;
545
546     TRACE("%ld %d %s\n", handle, iField, debugstr_a(szValue));
547
548     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
549     if( !rec )
550         return ERROR_INVALID_HANDLE;
551     msiobj_lock( &rec->hdr );
552     ret = MSI_RecordSetStringA( rec, iField, szValue );
553     msiobj_unlock( &rec->hdr );
554     msiobj_release( &rec->hdr );
555     return ret;
556 }
557
558 UINT MSI_RecordSetStringW( MSIRECORD *rec, unsigned int iField, LPCWSTR szValue )
559 {
560     LPWSTR str;
561
562     TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
563
564     if( iField > rec->count )
565         return ERROR_INVALID_FIELD;
566
567     MSI_FreeField( &rec->fields[iField] );
568
569     if( szValue && szValue[0] )
570     {
571         str = strdupW( szValue );
572         rec->fields[iField].type = MSIFIELD_WSTR;
573         rec->fields[iField].u.szwVal = str;
574     }
575     else
576     {
577         rec->fields[iField].type = MSIFIELD_NULL;
578         rec->fields[iField].u.szwVal = NULL;
579     }
580
581     return 0;
582 }
583
584 UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, unsigned int iField, LPCWSTR szValue )
585 {
586     MSIRECORD *rec;
587     UINT ret;
588
589     TRACE("%ld %d %s\n", handle, iField, debugstr_w(szValue));
590
591     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
592     if( !rec )
593         return ERROR_INVALID_HANDLE;
594
595     msiobj_lock( &rec->hdr );
596     ret = MSI_RecordSetStringW( rec, iField, szValue );
597     msiobj_unlock( &rec->hdr );
598     msiobj_release( &rec->hdr );
599     return ret;
600 }
601
602 /* read the data in a file into an IStream */
603 static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
604 {
605     DWORD sz, szHighWord = 0, read;
606     HANDLE handle;
607     HGLOBAL hGlob = 0;
608     HRESULT hr;
609     ULARGE_INTEGER ulSize;
610
611     TRACE("reading %s\n", debugstr_w(szFile));
612
613     /* read the file into memory */
614     handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
615     if( handle == INVALID_HANDLE_VALUE )
616         return GetLastError();
617     sz = GetFileSize(handle, &szHighWord);
618     if( sz != INVALID_FILE_SIZE && szHighWord == 0 )
619     {
620         hGlob = GlobalAlloc(GMEM_FIXED, sz);
621         if( hGlob )
622         {
623             BOOL r = ReadFile(handle, hGlob, sz, &read, NULL);
624             if( !r )
625             {
626                 GlobalFree(hGlob);
627                 hGlob = 0;
628             }
629         }
630     }
631     CloseHandle(handle);
632     if( !hGlob )
633         return ERROR_FUNCTION_FAILED;
634
635     /* make a stream out of it, and set the correct file size */
636     hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
637     if( FAILED( hr ) )
638     {
639         GlobalFree(hGlob);
640         return ERROR_FUNCTION_FAILED;
641     }
642
643     /* set the correct size - CreateStreamOnHGlobal screws it up */
644     ulSize.QuadPart = sz;
645     IStream_SetSize(*pstm, ulSize);
646
647     TRACE("read %s, %d bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
648
649     return ERROR_SUCCESS;
650 }
651
652 UINT MSI_RecordSetStream(MSIRECORD *rec, unsigned int iField, IStream *stream)
653 {
654     if ( (iField == 0) || (iField > rec->count) )
655         return ERROR_INVALID_PARAMETER;
656
657     MSI_FreeField( &rec->fields[iField] );
658     rec->fields[iField].type = MSIFIELD_STREAM;
659     rec->fields[iField].u.stream = stream;
660
661     return ERROR_SUCCESS;
662 }
663
664 UINT MSI_RecordSetStreamFromFileW(MSIRECORD *rec, unsigned int iField, LPCWSTR szFilename)
665 {
666     IStream *stm = NULL;
667     HRESULT r;
668
669     if( (iField == 0) || (iField > rec->count) )
670         return ERROR_INVALID_PARAMETER;
671
672     /* no filename means we should seek back to the start of the stream */
673     if( !szFilename )
674     {
675         LARGE_INTEGER ofs;
676         ULARGE_INTEGER cur;
677
678         if( rec->fields[iField].type != MSIFIELD_STREAM )
679             return ERROR_INVALID_FIELD;
680
681         stm = rec->fields[iField].u.stream;
682         if( !stm )
683             return ERROR_INVALID_FIELD;
684
685         ofs.QuadPart = 0;
686         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
687         if( FAILED( r ) )
688             return ERROR_FUNCTION_FAILED;
689     }
690     else
691     {
692         /* read the file into a stream and save the stream in the record */
693         r = RECORD_StreamFromFile(szFilename, &stm);
694         if( r != ERROR_SUCCESS )
695             return r;
696
697         /* if all's good, store it in the record */
698         MSI_RecordSetStream(rec, iField, stm);
699     }
700
701     return ERROR_SUCCESS;
702 }
703
704 UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, unsigned int iField, LPCSTR szFilename)
705 {
706     LPWSTR wstr = NULL;
707     UINT ret;
708
709     TRACE("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename));
710
711     if( szFilename )
712     {
713         wstr = strdupAtoW( szFilename );
714         if( !wstr )
715              return ERROR_OUTOFMEMORY;
716     }
717     ret = MsiRecordSetStreamW(hRecord, iField, wstr);
718     msi_free(wstr);
719
720     return ret;
721 }
722
723 UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, unsigned int iField, LPCWSTR szFilename)
724 {
725     MSIRECORD *rec;
726     UINT ret;
727
728     TRACE("%ld %d %s\n", handle, iField, debugstr_w(szFilename));
729
730     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
731     if( !rec )
732         return ERROR_INVALID_HANDLE;
733
734     msiobj_lock( &rec->hdr );
735     ret = MSI_RecordSetStreamFromFileW( rec, iField, szFilename );
736     msiobj_unlock( &rec->hdr );
737     msiobj_release( &rec->hdr );
738     return ret;
739 }
740
741 UINT MSI_RecordReadStream(MSIRECORD *rec, unsigned int iField, char *buf, DWORD *sz)
742 {
743     ULONG count;
744     HRESULT r;
745     IStream *stm;
746
747     TRACE("%p %d %p %p\n", rec, iField, buf, sz);
748
749     if( !sz )
750         return ERROR_INVALID_PARAMETER;
751
752     if( iField > rec->count)
753         return ERROR_INVALID_PARAMETER;
754
755     if( rec->fields[iField].type != MSIFIELD_STREAM )
756         return ERROR_INVALID_DATATYPE;
757
758     stm = rec->fields[iField].u.stream;
759     if( !stm )
760         return ERROR_INVALID_PARAMETER;
761
762     /* if there's no buffer pointer, calculate the length to the end */
763     if( !buf )
764     {
765         LARGE_INTEGER ofs;
766         ULARGE_INTEGER end, cur;
767
768         ofs.QuadPart = cur.QuadPart = 0;
769         end.QuadPart = 0;
770         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
771         IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
772         ofs.QuadPart = cur.QuadPart;
773         IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
774         *sz = end.QuadPart - cur.QuadPart;
775
776         return ERROR_SUCCESS;
777     }
778
779     /* read the data */
780     count = 0;
781     r = IStream_Read( stm, buf, *sz, &count );
782     if( FAILED( r ) )
783     {
784         *sz = 0;
785         return ERROR_FUNCTION_FAILED;
786     }
787
788     *sz = count;
789
790     return ERROR_SUCCESS;
791 }
792
793 UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, unsigned int iField, char *buf, DWORD *sz)
794 {
795     MSIRECORD *rec;
796     UINT ret;
797
798     TRACE("%ld %d %p %p\n", handle, iField, buf, sz);
799
800     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
801     if( !rec )
802         return ERROR_INVALID_HANDLE;
803     msiobj_lock( &rec->hdr );
804     ret = MSI_RecordReadStream( rec, iField, buf, sz );
805     msiobj_unlock( &rec->hdr );
806     msiobj_release( &rec->hdr );
807     return ret;
808 }
809
810 UINT MSI_RecordSetIStream( MSIRECORD *rec, unsigned int iField, IStream *stm )
811 {
812     TRACE("%p %d %p\n", rec, iField, stm);
813
814     if( iField > rec->count )
815         return ERROR_INVALID_FIELD;
816
817     MSI_FreeField( &rec->fields[iField] );
818
819     rec->fields[iField].type = MSIFIELD_STREAM;
820     rec->fields[iField].u.stream = stm;
821     IStream_AddRef( stm );
822
823     return ERROR_SUCCESS;
824 }
825
826 UINT MSI_RecordGetIStream( MSIRECORD *rec, unsigned int iField, IStream **pstm)
827 {
828     TRACE("%p %d %p\n", rec, iField, pstm);
829
830     if( iField > rec->count )
831         return ERROR_INVALID_FIELD;
832
833     if( rec->fields[iField].type != MSIFIELD_STREAM )
834         return ERROR_INVALID_FIELD;
835
836     *pstm = rec->fields[iField].u.stream;
837     IStream_AddRef( *pstm );
838
839     return ERROR_SUCCESS;
840 }
841
842 static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name )
843 {
844     ULARGE_INTEGER size;
845     LARGE_INTEGER pos;
846     IStream *out;
847     DWORD stgm;
848     HRESULT r;
849
850     stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE;
851     r = SHCreateStreamOnFileW( name, stgm, &out );
852     if( FAILED( r ) )
853         return ERROR_FUNCTION_FAILED;
854
855     pos.QuadPart = 0;
856     r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size );
857     if( FAILED( r ) )
858         goto end;
859
860     pos.QuadPart = 0;
861     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
862     if( FAILED( r ) )
863         goto end;
864
865     r = IStream_CopyTo( stm, out, size, NULL, NULL );
866
867 end:
868     IStream_Release( out );
869     if( FAILED( r ) )
870         return ERROR_FUNCTION_FAILED;
871     return ERROR_SUCCESS;
872 }
873
874 UINT MSI_RecordStreamToFile( MSIRECORD *rec, unsigned int iField, LPCWSTR name )
875 {
876     IStream *stm = NULL;
877     UINT r;
878
879     TRACE("%p %u %s\n", rec, iField, debugstr_w(name));
880
881     msiobj_lock( &rec->hdr );
882
883     r = MSI_RecordGetIStream( rec, iField, &stm );
884     if( r == ERROR_SUCCESS )
885     {
886         r = msi_dump_stream_to_file( stm, name );
887         IStream_Release( stm );
888     }
889
890     msiobj_unlock( &rec->hdr );
891
892     return r;
893 }