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