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