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