Extending upon Mike McCormack's cleanup to use MSI_RecordGetString.
[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 static 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 static 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 static UINT msi_get_stream_size( IStream *stm )
414 {
415     STATSTG stat;
416     HRESULT r;
417
418     r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
419     if( FAILED(r) )
420         return 0;
421     return stat.cbSize.QuadPart;
422 }
423
424 UINT MSI_RecordDataSize(MSIRECORD *rec, unsigned int iField)
425 {
426     TRACE("%p %d\n", rec, iField);
427
428     if( iField > rec->count )
429         return 0;
430
431     switch( rec->fields[iField].type )
432     {
433     case MSIFIELD_INT:
434         return sizeof (INT);
435     case MSIFIELD_WSTR:
436         return lstrlenW( rec->fields[iField].u.szwVal );
437     case MSIFIELD_NULL:
438         break;
439     case MSIFIELD_STREAM:
440         return msi_get_stream_size( rec->fields[iField].u.stream );
441     }
442     return 0;
443 }
444
445 UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, unsigned int iField)
446 {
447     MSIRECORD *rec;
448     UINT ret;
449
450     TRACE("%ld %d\n", handle, iField);
451
452     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
453     if( !rec )
454         return 0;
455     msiobj_lock( &rec->hdr );
456     ret = MSI_RecordDataSize( rec, iField);
457     msiobj_unlock( &rec->hdr );
458     msiobj_release( &rec->hdr );
459     return ret;
460 }
461
462 UINT MSI_RecordSetStringA( MSIRECORD *rec, unsigned int iField, LPCSTR szValue )
463 {
464     LPWSTR str;
465
466     TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
467
468     if( iField > rec->count )
469         return ERROR_INVALID_FIELD;
470
471     MSI_FreeField( &rec->fields[iField] );
472     if( szValue && szValue[0] )
473     {
474         str = strdupAtoW( szValue );
475         rec->fields[iField].type = MSIFIELD_WSTR;
476         rec->fields[iField].u.szwVal = str;
477     }
478     else
479     {
480         rec->fields[iField].type = MSIFIELD_NULL;
481         rec->fields[iField].u.szwVal = NULL;
482     }
483
484     return 0;
485 }
486
487 UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, unsigned int iField, LPCSTR szValue )
488 {
489     MSIRECORD *rec;
490     UINT ret;
491
492     TRACE("%ld %d %s\n", handle, iField, debugstr_a(szValue));
493
494     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
495     if( !rec )
496         return ERROR_INVALID_HANDLE;
497     msiobj_lock( &rec->hdr );
498     ret = MSI_RecordSetStringA( rec, iField, szValue );
499     msiobj_unlock( &rec->hdr );
500     msiobj_release( &rec->hdr );
501     return ret;
502 }
503
504 UINT MSI_RecordSetStringW( MSIRECORD *rec, unsigned int iField, LPCWSTR szValue )
505 {
506     LPWSTR str;
507
508     TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
509
510     if( iField > rec->count )
511         return ERROR_INVALID_FIELD;
512
513     MSI_FreeField( &rec->fields[iField] );
514
515     if( szValue && szValue[0] )
516     {
517         str = strdupW( szValue );
518         rec->fields[iField].type = MSIFIELD_WSTR;
519         rec->fields[iField].u.szwVal = str;
520     }
521     else
522     {
523         rec->fields[iField].type = MSIFIELD_NULL;
524         rec->fields[iField].u.szwVal = NULL;
525     }
526
527     return 0;
528 }
529
530 UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, unsigned int iField, LPCWSTR szValue )
531 {
532     MSIRECORD *rec;
533     UINT ret;
534
535     TRACE("%ld %d %s\n", handle, iField, debugstr_w(szValue));
536
537     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
538     if( !rec )
539         return ERROR_INVALID_HANDLE;
540
541     msiobj_lock( &rec->hdr );
542     ret = MSI_RecordSetStringW( rec, iField, szValue );
543     msiobj_unlock( &rec->hdr );
544     msiobj_release( &rec->hdr );
545     return ret;
546 }
547
548 /* read the data in a file into an IStream */
549 static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
550 {
551     DWORD sz, szHighWord = 0, read;
552     HANDLE handle;
553     HGLOBAL hGlob = 0;
554     HRESULT hr;
555     ULARGE_INTEGER ulSize;
556
557     TRACE("reading %s\n", debugstr_w(szFile));
558
559     /* read the file into memory */
560     handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
561     if( handle == INVALID_HANDLE_VALUE )
562         return GetLastError();
563     sz = GetFileSize(handle, &szHighWord);
564     if( sz != INVALID_FILE_SIZE && szHighWord == 0 )
565     {
566         hGlob = GlobalAlloc(GMEM_FIXED, sz);
567         if( hGlob )
568         {
569             BOOL r = ReadFile(handle, hGlob, sz, &read, NULL);
570             if( !r )
571             {
572                 GlobalFree(hGlob);
573                 hGlob = 0;
574             }
575         }
576     }
577     CloseHandle(handle);
578     if( !hGlob )
579         return ERROR_FUNCTION_FAILED;
580
581     /* make a stream out of it, and set the correct file size */
582     hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
583     if( FAILED( hr ) )
584     {
585         GlobalFree(hGlob);
586         return ERROR_FUNCTION_FAILED;
587     }
588
589     /* set the correct size - CreateStreamOnHGlobal screws it up */
590     ulSize.QuadPart = sz;
591     IStream_SetSize(*pstm, ulSize);
592
593     TRACE("read %s, %ld bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
594
595     return ERROR_SUCCESS;
596 }
597
598 UINT MSI_RecordSetStreamW(MSIRECORD *rec, unsigned int iField, LPCWSTR szFilename)
599 {
600     IStream *stm = NULL;
601     HRESULT r;
602
603     if( (iField == 0) || (iField > rec->count) )
604         return ERROR_INVALID_PARAMETER;
605
606     /* no filename means we should seek back to the start of the stream */
607     if( !szFilename )
608     {
609         LARGE_INTEGER ofs;
610         ULARGE_INTEGER cur;
611
612         if( rec->fields[iField].type != MSIFIELD_STREAM )
613             return ERROR_INVALID_FIELD;
614
615         stm = rec->fields[iField].u.stream;
616         if( !stm )
617             return ERROR_INVALID_FIELD;
618
619         ofs.QuadPart = 0;
620         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
621         if( FAILED( r ) )
622             return ERROR_FUNCTION_FAILED;
623     }
624     else
625     {
626         /* read the file into a stream and save the stream in the record */
627         r = RECORD_StreamFromFile(szFilename, &stm);
628         if( r != ERROR_SUCCESS )
629             return r;
630
631         /* if all's good, store it in the record */
632         MSI_FreeField( &rec->fields[iField] );
633         rec->fields[iField].type = MSIFIELD_STREAM;
634         rec->fields[iField].u.stream = stm;
635     }
636
637     return ERROR_SUCCESS;
638 }
639
640 UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, unsigned int iField, LPCSTR szFilename)
641 {
642     LPWSTR wstr = NULL;
643     UINT ret;
644
645     TRACE("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename));
646
647     if( szFilename )
648     {
649         wstr = strdupAtoW( szFilename );
650         if( !wstr )
651              return ERROR_OUTOFMEMORY;
652     }
653     ret = MsiRecordSetStreamW(hRecord, iField, wstr);
654     HeapFree(GetProcessHeap(),0,wstr);
655
656     return ret;
657 }
658
659 UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, unsigned int iField, LPCWSTR szFilename)
660 {
661     MSIRECORD *rec;
662     UINT ret;
663
664     TRACE("%ld %d %s\n", handle, iField, debugstr_w(szFilename));
665
666     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
667     if( !rec )
668         return ERROR_INVALID_HANDLE;
669
670     msiobj_lock( &rec->hdr );
671     ret = MSI_RecordSetStreamW( rec, iField, szFilename );
672     msiobj_unlock( &rec->hdr );
673     msiobj_release( &rec->hdr );
674     return ret;
675 }
676
677 UINT MSI_RecordReadStream(MSIRECORD *rec, unsigned int iField, char *buf, DWORD *sz)
678 {
679     ULONG count;
680     HRESULT r;
681     IStream *stm;
682
683     TRACE("%p %d %p %p\n", rec, iField, buf, sz);
684
685     if( !sz )
686         return ERROR_INVALID_PARAMETER;
687
688     if( iField > rec->count)
689         return ERROR_INVALID_PARAMETER;
690
691     if( rec->fields[iField].type != MSIFIELD_STREAM )
692         return ERROR_INVALID_DATATYPE;
693
694     stm = rec->fields[iField].u.stream;
695     if( !stm )
696         return ERROR_INVALID_PARAMETER;
697
698     /* if there's no buffer pointer, calculate the length to the end */
699     if( !buf )
700     {
701         LARGE_INTEGER ofs;
702         ULARGE_INTEGER end, cur;
703
704         ofs.QuadPart = cur.QuadPart = 0;
705         end.QuadPart = 0;
706         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
707         IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
708         ofs.QuadPart = cur.QuadPart;
709         IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
710         *sz = end.QuadPart - cur.QuadPart;
711
712         return ERROR_SUCCESS;
713     }
714
715     /* read the data */
716     count = 0;
717     r = IStream_Read( stm, buf, *sz, &count );
718     if( FAILED( r ) )
719     {
720         *sz = 0;
721         return ERROR_FUNCTION_FAILED;
722     }
723
724     *sz = count;
725
726     return ERROR_SUCCESS;
727 }
728
729 UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, unsigned int iField, char *buf, DWORD *sz)
730 {
731     MSIRECORD *rec;
732     UINT ret;
733
734     TRACE("%ld %d %p %p\n", handle, iField, buf, sz);
735
736     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
737     if( !rec )
738         return ERROR_INVALID_HANDLE;
739     msiobj_lock( &rec->hdr );
740     ret = MSI_RecordReadStream( rec, iField, buf, sz );
741     msiobj_unlock( &rec->hdr );
742     msiobj_release( &rec->hdr );
743     return ret;
744 }
745
746 UINT MSI_RecordSetIStream( MSIRECORD *rec, unsigned int iField, IStream *stm )
747 {
748     TRACE("%p %d %p\n", rec, iField, stm);
749
750     if( iField > rec->count )
751         return ERROR_INVALID_FIELD;
752
753     MSI_FreeField( &rec->fields[iField] );
754
755     rec->fields[iField].type = MSIFIELD_STREAM;
756     rec->fields[iField].u.stream = stm;
757     IStream_AddRef( stm );
758
759     return ERROR_SUCCESS;
760 }
761
762 UINT MSI_RecordGetIStream( MSIRECORD *rec, unsigned int iField, IStream **pstm)
763 {
764     TRACE("%p %d %p\n", rec, iField, pstm);
765
766     if( iField > rec->count )
767         return ERROR_INVALID_FIELD;
768
769     if( rec->fields[iField].type != MSIFIELD_STREAM )
770         return ERROR_INVALID_FIELD;
771
772     *pstm = rec->fields[iField].u.stream;
773     IStream_AddRef( *pstm );
774
775     return ERROR_SUCCESS;
776 }