d3d10core: Implement d3d10_device_SOSetTargets().
[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 "wine/unicode.h"
31 #include "msi.h"
32 #include "msiquery.h"
33 #include "msipriv.h"
34 #include "objidl.h"
35 #include "winnls.h"
36 #include "ole2.h"
37
38 #include "winreg.h"
39 #include "shlwapi.h"
40
41 #include "query.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(msidb);
44
45 #define MSIFIELD_NULL   0
46 #define MSIFIELD_INT    1
47 #define MSIFIELD_WSTR   3
48 #define MSIFIELD_STREAM 4
49 #define MSIFIELD_INTPTR 5
50
51 static void MSI_FreeField( MSIFIELD *field )
52 {
53     switch( field->type )
54     {
55     case MSIFIELD_NULL:
56     case MSIFIELD_INT:
57     case MSIFIELD_INTPTR:
58         break;
59     case MSIFIELD_WSTR:
60         msi_free( field->u.szwVal);
61         break;
62     case MSIFIELD_STREAM:
63         IStream_Release( field->u.stream );
64         break;
65     default:
66         ERR("Invalid field type %d\n", field->type);
67     }
68 }
69
70 void MSI_CloseRecord( MSIOBJECTHDR *arg )
71 {
72     MSIRECORD *rec = (MSIRECORD *) arg;
73     UINT i;
74
75     for( i=0; i<=rec->count; i++ )
76         MSI_FreeField( &rec->fields[i] );
77 }
78
79 MSIRECORD *MSI_CreateRecord( UINT cParams )
80 {
81     MSIRECORD *rec;
82     UINT len;
83
84     TRACE("%d\n", cParams);
85
86     if( cParams>65535 )
87         return NULL;
88
89     len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams;
90     rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord );
91     if( rec )
92         rec->count = cParams;
93     return rec;
94 }
95
96 MSIHANDLE WINAPI MsiCreateRecord( UINT cParams )
97 {
98     MSIRECORD *rec;
99     MSIHANDLE ret = 0;
100
101     TRACE("%d\n", cParams);
102
103     rec = MSI_CreateRecord( cParams );
104     if( rec )
105     {
106         ret = alloc_msihandle( &rec->hdr );
107         msiobj_release( &rec->hdr );
108     }
109     return ret;
110 }
111
112 UINT MSI_RecordGetFieldCount( const MSIRECORD *rec )
113 {
114     return rec->count;
115 }
116
117 UINT WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
118 {
119     MSIRECORD *rec;
120     UINT ret;
121
122     TRACE("%d\n", handle );
123
124     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
125     if( !rec )
126         return -1;
127
128     msiobj_lock( &rec->hdr );
129     ret = MSI_RecordGetFieldCount( rec );
130     msiobj_unlock( &rec->hdr );
131     msiobj_release( &rec->hdr );
132
133     return ret;
134 }
135
136 static BOOL string2intW( LPCWSTR str, int *out )
137 {
138     int x = 0;
139     LPCWSTR p = str;
140
141     if( *p == '-' ) /* skip the minus sign */
142         p++;
143     while ( *p )
144     {
145         if( (*p < '0') || (*p > '9') )
146             return FALSE;
147         x *= 10;
148         x += (*p - '0');
149         p++;
150     }
151
152     if( str[0] == '-' ) /* check if it's negative */
153         x = -x;
154     *out = x; 
155
156     return TRUE;
157 }
158
159 WCHAR *msi_strdupW( const WCHAR *value, int len )
160 {
161     WCHAR *ret;
162
163     if (!value) return NULL;
164     if (!(ret = msi_alloc( (len + 1) * sizeof(WCHAR) ))) return NULL;
165     memcpy( ret, value, len * sizeof(WCHAR) );
166     ret[len] = 0;
167     return ret;
168 }
169
170 UINT MSI_RecordCopyField( MSIRECORD *in_rec, UINT in_n,
171                           MSIRECORD *out_rec, UINT out_n )
172 {
173     UINT r = ERROR_SUCCESS;
174
175     msiobj_lock( &in_rec->hdr );
176
177     if ( in_n > in_rec->count || out_n > out_rec->count )
178         r = ERROR_FUNCTION_FAILED;
179     else if ( in_rec != out_rec || in_n != out_n )
180     {
181         LPWSTR str;
182         MSIFIELD *in, *out;
183
184         in = &in_rec->fields[in_n];
185         out = &out_rec->fields[out_n];
186
187         switch ( in->type )
188         {
189         case MSIFIELD_NULL:
190             break;
191         case MSIFIELD_INT:
192             out->u.iVal = in->u.iVal;
193             break;
194         case MSIFIELD_INTPTR:
195             out->u.pVal = in->u.pVal;
196             break;
197         case MSIFIELD_WSTR:
198             if ((str = msi_strdupW( in->u.szwVal, in->len )))
199             {
200                 out->u.szwVal = str;
201                 out->len = in->len;
202             }
203             else r = ERROR_OUTOFMEMORY;
204             break;
205         case MSIFIELD_STREAM:
206             IStream_AddRef( in->u.stream );
207             out->u.stream = in->u.stream;
208             break;
209         default:
210             ERR("invalid field type %d\n", in->type);
211         }
212         if (r == ERROR_SUCCESS)
213             out->type = in->type;
214     }
215
216     msiobj_unlock( &in_rec->hdr );
217     return r;
218 }
219
220 INT_PTR MSI_RecordGetIntPtr( MSIRECORD *rec, UINT iField )
221 {
222     int ret;
223
224     TRACE( "%p %d\n", rec, iField );
225
226     if( iField > rec->count )
227         return MININT_PTR;
228
229     switch( rec->fields[iField].type )
230     {
231     case MSIFIELD_INT:
232         return rec->fields[iField].u.iVal;
233     case MSIFIELD_INTPTR:
234         return rec->fields[iField].u.pVal;
235     case MSIFIELD_WSTR:
236         if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
237             return ret;
238         return MININT_PTR;
239     default:
240         break;
241     }
242
243     return MININT_PTR;
244 }
245
246 int MSI_RecordGetInteger( MSIRECORD *rec, UINT iField)
247 {
248     int ret = 0;
249
250     TRACE("%p %d\n", rec, iField );
251
252     if( iField > rec->count )
253         return MSI_NULL_INTEGER;
254
255     switch( rec->fields[iField].type )
256     {
257     case MSIFIELD_INT:
258         return rec->fields[iField].u.iVal;
259     case MSIFIELD_INTPTR:
260         return rec->fields[iField].u.pVal;
261     case MSIFIELD_WSTR:
262         if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
263             return ret;
264         return MSI_NULL_INTEGER;
265     default:
266         break;
267     }
268
269     return MSI_NULL_INTEGER;
270 }
271
272 int WINAPI MsiRecordGetInteger( MSIHANDLE handle, UINT iField)
273 {
274     MSIRECORD *rec;
275     UINT ret;
276
277     TRACE("%d %d\n", handle, iField );
278
279     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
280     if( !rec )
281         return MSI_NULL_INTEGER;
282
283     msiobj_lock( &rec->hdr );
284     ret = MSI_RecordGetInteger( rec, iField );
285     msiobj_unlock( &rec->hdr );
286     msiobj_release( &rec->hdr );
287
288     return ret;
289 }
290
291 UINT WINAPI MsiRecordClearData( MSIHANDLE handle )
292 {
293     MSIRECORD *rec;
294     UINT i;
295
296     TRACE("%d\n", handle );
297
298     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
299     if( !rec )
300         return ERROR_INVALID_HANDLE;
301
302     msiobj_lock( &rec->hdr );
303     for( i=0; i<=rec->count; i++)
304     {
305         MSI_FreeField( &rec->fields[i] );
306         rec->fields[i].type = MSIFIELD_NULL;
307         rec->fields[i].u.iVal = 0;
308     }
309     msiobj_unlock( &rec->hdr );
310     msiobj_release( &rec->hdr );
311
312     return ERROR_SUCCESS;
313 }
314
315 UINT MSI_RecordSetIntPtr( MSIRECORD *rec, UINT iField, INT_PTR pVal )
316 {
317     TRACE("%p %u %ld\n", rec, iField, pVal);
318
319     if( iField > rec->count )
320         return ERROR_INVALID_PARAMETER;
321
322     MSI_FreeField( &rec->fields[iField] );
323     rec->fields[iField].type = MSIFIELD_INTPTR;
324     rec->fields[iField].u.pVal = pVal;
325
326     return ERROR_SUCCESS;
327 }
328
329 UINT MSI_RecordSetInteger( MSIRECORD *rec, UINT iField, int iVal )
330 {
331     TRACE("%p %u %d\n", rec, iField, iVal);
332
333     if( iField > rec->count )
334         return ERROR_INVALID_PARAMETER;
335
336     MSI_FreeField( &rec->fields[iField] );
337     rec->fields[iField].type = MSIFIELD_INT;
338     rec->fields[iField].u.iVal = iVal;
339
340     return ERROR_SUCCESS;
341 }
342
343 UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, UINT iField, int iVal )
344 {
345     MSIRECORD *rec;
346     UINT ret;
347
348     TRACE("%d %u %d\n", handle, iField, iVal);
349
350     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
351     if( !rec )
352         return ERROR_INVALID_HANDLE;
353
354     msiobj_lock( &rec->hdr );
355     ret = MSI_RecordSetInteger( rec, iField, iVal );
356     msiobj_unlock( &rec->hdr );
357     msiobj_release( &rec->hdr );
358     return ret;
359 }
360
361 BOOL MSI_RecordIsNull( MSIRECORD *rec, UINT iField )
362 {
363     BOOL r = TRUE;
364
365     TRACE("%p %d\n", rec, iField );
366
367     r = ( iField > rec->count ) ||
368         ( rec->fields[iField].type == MSIFIELD_NULL );
369
370     return r;
371 }
372
373 BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, UINT iField )
374 {
375     MSIRECORD *rec;
376     UINT ret;
377
378     TRACE("%d %d\n", handle, iField );
379
380     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
381     if( !rec )
382         return 0;
383     msiobj_lock( &rec->hdr );
384     ret = MSI_RecordIsNull( rec, iField );
385     msiobj_unlock( &rec->hdr );
386     msiobj_release( &rec->hdr );
387     return ret;
388
389 }
390
391 UINT MSI_RecordGetStringA(MSIRECORD *rec, UINT iField,
392                LPSTR szValue, LPDWORD pcchValue)
393 {
394     UINT len = 0, ret = ERROR_SUCCESS;
395     CHAR buffer[16];
396
397     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
398
399     if( iField > rec->count )
400     {
401         if ( szValue && *pcchValue > 0 )
402             szValue[0] = 0;
403
404         *pcchValue = 0;
405         return ERROR_SUCCESS;
406     }
407
408     switch( rec->fields[iField].type )
409     {
410     case MSIFIELD_INT:
411         wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
412         len = lstrlenA( buffer );
413         if (szValue)
414             lstrcpynA(szValue, buffer, *pcchValue);
415         break;
416     case MSIFIELD_WSTR:
417         len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal,
418                                    rec->fields[iField].len + 1, NULL, 0 , NULL, NULL );
419         if (szValue)
420             WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal,
421                                  rec->fields[iField].len + 1, szValue, *pcchValue, NULL, NULL );
422         if( szValue && *pcchValue && len>*pcchValue )
423             szValue[*pcchValue-1] = 0;
424         if( len )
425             len--;
426         break;
427     case MSIFIELD_NULL:
428         if( szValue && *pcchValue > 0 )
429             szValue[0] = 0;
430         break;
431     default:
432         ret = ERROR_INVALID_PARAMETER;
433         break;
434     }
435
436     if( szValue && *pcchValue <= len )
437         ret = ERROR_MORE_DATA;
438     *pcchValue = len;
439
440     return ret;
441 }
442
443 UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, UINT iField,
444                LPSTR szValue, LPDWORD pcchValue)
445 {
446     MSIRECORD *rec;
447     UINT ret;
448
449     TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
450
451     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
452     if( !rec )
453         return ERROR_INVALID_HANDLE;
454     msiobj_lock( &rec->hdr );
455     ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
456     msiobj_unlock( &rec->hdr );
457     msiobj_release( &rec->hdr );
458     return ret;
459 }
460
461 const WCHAR *msi_record_get_string( const MSIRECORD *rec, UINT field, int *len )
462 {
463     if (field > rec->count)
464         return NULL;
465
466     if (rec->fields[field].type != MSIFIELD_WSTR)
467         return NULL;
468
469     if (len) *len = rec->fields[field].len;
470
471     return rec->fields[field].u.szwVal;
472 }
473
474 const WCHAR *MSI_RecordGetString( const MSIRECORD *rec, UINT iField )
475 {
476     return msi_record_get_string( rec, iField, NULL );
477 }
478
479 UINT MSI_RecordGetStringW(MSIRECORD *rec, UINT iField,
480                LPWSTR szValue, LPDWORD pcchValue)
481 {
482     static const WCHAR szFormat[] = {'%','d',0};
483     UINT len = 0, ret = ERROR_SUCCESS;
484     WCHAR buffer[16];
485
486     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
487
488     if( iField > rec->count )
489     {
490         if ( szValue && *pcchValue > 0 )
491             szValue[0] = 0;
492
493         *pcchValue = 0;
494         return ERROR_SUCCESS;
495     }
496
497     switch( rec->fields[iField].type )
498     {
499     case MSIFIELD_INT:
500         wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
501         len = lstrlenW( buffer );
502         if (szValue)
503             lstrcpynW(szValue, buffer, *pcchValue);
504         break;
505     case MSIFIELD_WSTR:
506         len = rec->fields[iField].len;
507         if (szValue)
508             memcpy( szValue, rec->fields[iField].u.szwVal, min(len + 1, *pcchValue) * sizeof(WCHAR) );
509         break;
510     case MSIFIELD_NULL:
511         if( szValue && *pcchValue > 0 )
512             szValue[0] = 0;
513         break;
514     default:
515         break;
516     }
517
518     if( szValue && *pcchValue <= len )
519         ret = ERROR_MORE_DATA;
520     *pcchValue = len;
521
522     return ret;
523 }
524
525 UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, UINT iField,
526                LPWSTR szValue, LPDWORD pcchValue)
527 {
528     MSIRECORD *rec;
529     UINT ret;
530
531     TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
532
533     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
534     if( !rec )
535         return ERROR_INVALID_HANDLE;
536
537     msiobj_lock( &rec->hdr );
538     ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
539     msiobj_unlock( &rec->hdr );
540     msiobj_release( &rec->hdr );
541     return ret;
542 }
543
544 static UINT msi_get_stream_size( IStream *stm )
545 {
546     STATSTG stat;
547     HRESULT r;
548
549     r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
550     if( FAILED(r) )
551         return 0;
552     return stat.cbSize.QuadPart;
553 }
554
555 static UINT MSI_RecordDataSize(MSIRECORD *rec, UINT iField)
556 {
557     TRACE("%p %d\n", rec, iField);
558
559     if( iField > rec->count )
560         return 0;
561
562     switch( rec->fields[iField].type )
563     {
564     case MSIFIELD_INT:
565         return sizeof (INT);
566     case MSIFIELD_WSTR:
567         return rec->fields[iField].len;
568     case MSIFIELD_NULL:
569         break;
570     case MSIFIELD_STREAM:
571         return msi_get_stream_size( rec->fields[iField].u.stream );
572     }
573     return 0;
574 }
575
576 UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, UINT iField)
577 {
578     MSIRECORD *rec;
579     UINT ret;
580
581     TRACE("%d %d\n", handle, iField);
582
583     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
584     if( !rec )
585         return 0;
586     msiobj_lock( &rec->hdr );
587     ret = MSI_RecordDataSize( rec, iField);
588     msiobj_unlock( &rec->hdr );
589     msiobj_release( &rec->hdr );
590     return ret;
591 }
592
593 UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, UINT iField, LPCSTR szValue )
594 {
595     WCHAR *valueW = NULL;
596     MSIRECORD *rec;
597     UINT ret;
598
599     TRACE("%d %d %s\n", handle, iField, debugstr_a(szValue));
600
601     if (szValue && !(valueW = strdupAtoW( szValue ))) return ERROR_OUTOFMEMORY;
602
603     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
604     if( !rec )
605     {
606         msi_free( valueW );
607         return ERROR_INVALID_HANDLE;
608     }
609     msiobj_lock( &rec->hdr );
610     ret = MSI_RecordSetStringW( rec, iField, valueW );
611     msiobj_unlock( &rec->hdr );
612     msiobj_release( &rec->hdr );
613     msi_free( valueW );
614     return ret;
615 }
616
617 UINT msi_record_set_string( MSIRECORD *rec, UINT field, const WCHAR *value, int len )
618 {
619     if (field > rec->count)
620         return ERROR_INVALID_FIELD;
621
622     MSI_FreeField( &rec->fields[field] );
623
624     if (value && len < 0) len = strlenW( value );
625
626     if (value && len)
627     {
628         rec->fields[field].type = MSIFIELD_WSTR;
629         rec->fields[field].u.szwVal = msi_strdupW( value, len );
630         rec->fields[field].len = len;
631     }
632     else
633     {
634         rec->fields[field].type = MSIFIELD_NULL;
635         rec->fields[field].u.szwVal = NULL;
636         rec->fields[field].len = 0;
637     }
638     return 0;
639 }
640
641 UINT MSI_RecordSetStringW( MSIRECORD *rec, UINT iField, LPCWSTR szValue )
642 {
643     TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
644
645     return msi_record_set_string( rec, iField, szValue, -1 );
646 }
647
648 UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, UINT iField, LPCWSTR szValue )
649 {
650     MSIRECORD *rec;
651     UINT ret;
652
653     TRACE("%d %d %s\n", handle, iField, debugstr_w(szValue));
654
655     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
656     if( !rec )
657         return ERROR_INVALID_HANDLE;
658
659     msiobj_lock( &rec->hdr );
660     ret = MSI_RecordSetStringW( rec, iField, szValue );
661     msiobj_unlock( &rec->hdr );
662     msiobj_release( &rec->hdr );
663     return ret;
664 }
665
666 /* read the data in a file into an IStream */
667 static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
668 {
669     DWORD sz, szHighWord = 0, read;
670     HANDLE handle;
671     HGLOBAL hGlob = 0;
672     HRESULT hr;
673     ULARGE_INTEGER ulSize;
674
675     TRACE("reading %s\n", debugstr_w(szFile));
676
677     /* read the file into memory */
678     handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
679     if( handle == INVALID_HANDLE_VALUE )
680         return GetLastError();
681     sz = GetFileSize(handle, &szHighWord);
682     if( sz != INVALID_FILE_SIZE && szHighWord == 0 )
683     {
684         hGlob = GlobalAlloc(GMEM_FIXED, sz);
685         if( hGlob )
686         {
687             BOOL r = ReadFile(handle, hGlob, sz, &read, NULL);
688             if( !r )
689             {
690                 GlobalFree(hGlob);
691                 hGlob = 0;
692             }
693         }
694     }
695     CloseHandle(handle);
696     if( !hGlob )
697         return ERROR_FUNCTION_FAILED;
698
699     /* make a stream out of it, and set the correct file size */
700     hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
701     if( FAILED( hr ) )
702     {
703         GlobalFree(hGlob);
704         return ERROR_FUNCTION_FAILED;
705     }
706
707     /* set the correct size - CreateStreamOnHGlobal screws it up */
708     ulSize.QuadPart = sz;
709     IStream_SetSize(*pstm, ulSize);
710
711     TRACE("read %s, %d bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
712
713     return ERROR_SUCCESS;
714 }
715
716 UINT MSI_RecordSetStream(MSIRECORD *rec, UINT iField, IStream *stream)
717 {
718     if ( (iField == 0) || (iField > rec->count) )
719         return ERROR_INVALID_PARAMETER;
720
721     MSI_FreeField( &rec->fields[iField] );
722     rec->fields[iField].type = MSIFIELD_STREAM;
723     rec->fields[iField].u.stream = stream;
724
725     return ERROR_SUCCESS;
726 }
727
728 UINT MSI_RecordSetStreamFromFileW(MSIRECORD *rec, UINT iField, LPCWSTR szFilename)
729 {
730     IStream *stm = NULL;
731     HRESULT r;
732
733     if( (iField == 0) || (iField > rec->count) )
734         return ERROR_INVALID_PARAMETER;
735
736     /* no filename means we should seek back to the start of the stream */
737     if( !szFilename )
738     {
739         LARGE_INTEGER ofs;
740         ULARGE_INTEGER cur;
741
742         if( rec->fields[iField].type != MSIFIELD_STREAM )
743             return ERROR_INVALID_FIELD;
744
745         stm = rec->fields[iField].u.stream;
746         if( !stm )
747             return ERROR_INVALID_FIELD;
748
749         ofs.QuadPart = 0;
750         r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
751         if( FAILED( r ) )
752             return ERROR_FUNCTION_FAILED;
753     }
754     else
755     {
756         /* read the file into a stream and save the stream in the record */
757         r = RECORD_StreamFromFile(szFilename, &stm);
758         if( r != ERROR_SUCCESS )
759             return r;
760
761         /* if all's good, store it in the record */
762         MSI_RecordSetStream(rec, iField, stm);
763     }
764
765     return ERROR_SUCCESS;
766 }
767
768 UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, UINT iField, LPCSTR szFilename)
769 {
770     LPWSTR wstr = NULL;
771     UINT ret;
772
773     TRACE("%d %d %s\n", hRecord, iField, debugstr_a(szFilename));
774
775     if( szFilename )
776     {
777         wstr = strdupAtoW( szFilename );
778         if( !wstr )
779              return ERROR_OUTOFMEMORY;
780     }
781     ret = MsiRecordSetStreamW(hRecord, iField, wstr);
782     msi_free(wstr);
783
784     return ret;
785 }
786
787 UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, UINT iField, LPCWSTR szFilename)
788 {
789     MSIRECORD *rec;
790     UINT ret;
791
792     TRACE("%d %d %s\n", handle, iField, debugstr_w(szFilename));
793
794     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
795     if( !rec )
796         return ERROR_INVALID_HANDLE;
797
798     msiobj_lock( &rec->hdr );
799     ret = MSI_RecordSetStreamFromFileW( rec, iField, szFilename );
800     msiobj_unlock( &rec->hdr );
801     msiobj_release( &rec->hdr );
802     return ret;
803 }
804
805 UINT MSI_RecordReadStream(MSIRECORD *rec, UINT iField, char *buf, LPDWORD sz)
806 {
807     ULONG count;
808     HRESULT r;
809     IStream *stm;
810
811     TRACE("%p %d %p %p\n", rec, iField, buf, sz);
812
813     if( !sz )
814         return ERROR_INVALID_PARAMETER;
815
816     if( iField > rec->count)
817         return ERROR_INVALID_PARAMETER;
818
819     if ( rec->fields[iField].type == MSIFIELD_NULL )
820     {
821         *sz = 0;
822         return ERROR_INVALID_DATA;
823     }
824
825     if( rec->fields[iField].type != MSIFIELD_STREAM )
826         return ERROR_INVALID_DATATYPE;
827
828     stm = rec->fields[iField].u.stream;
829     if( !stm )
830         return ERROR_INVALID_PARAMETER;
831
832     /* if there's no buffer pointer, calculate the length to the end */
833     if( !buf )
834     {
835         LARGE_INTEGER ofs;
836         ULARGE_INTEGER end, cur;
837
838         ofs.QuadPart = cur.QuadPart = 0;
839         end.QuadPart = 0;
840         IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
841         IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
842         ofs.QuadPart = cur.QuadPart;
843         IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
844         *sz = end.QuadPart - cur.QuadPart;
845
846         return ERROR_SUCCESS;
847     }
848
849     /* read the data */
850     count = 0;
851     r = IStream_Read( stm, buf, *sz, &count );
852     if( FAILED( r ) )
853     {
854         *sz = 0;
855         return ERROR_FUNCTION_FAILED;
856     }
857
858     *sz = count;
859
860     return ERROR_SUCCESS;
861 }
862
863 UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, UINT iField, char *buf, LPDWORD sz)
864 {
865     MSIRECORD *rec;
866     UINT ret;
867
868     TRACE("%d %d %p %p\n", handle, iField, buf, sz);
869
870     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
871     if( !rec )
872         return ERROR_INVALID_HANDLE;
873     msiobj_lock( &rec->hdr );
874     ret = MSI_RecordReadStream( rec, iField, buf, sz );
875     msiobj_unlock( &rec->hdr );
876     msiobj_release( &rec->hdr );
877     return ret;
878 }
879
880 UINT MSI_RecordSetIStream( MSIRECORD *rec, UINT iField, IStream *stm )
881 {
882     TRACE("%p %d %p\n", rec, iField, stm);
883
884     if( iField > rec->count )
885         return ERROR_INVALID_FIELD;
886
887     MSI_FreeField( &rec->fields[iField] );
888
889     rec->fields[iField].type = MSIFIELD_STREAM;
890     rec->fields[iField].u.stream = stm;
891     IStream_AddRef( stm );
892
893     return ERROR_SUCCESS;
894 }
895
896 UINT MSI_RecordGetIStream( MSIRECORD *rec, UINT iField, IStream **pstm)
897 {
898     TRACE("%p %d %p\n", rec, iField, pstm);
899
900     if( iField > rec->count )
901         return ERROR_INVALID_FIELD;
902
903     if( rec->fields[iField].type != MSIFIELD_STREAM )
904         return ERROR_INVALID_FIELD;
905
906     *pstm = rec->fields[iField].u.stream;
907     IStream_AddRef( *pstm );
908
909     return ERROR_SUCCESS;
910 }
911
912 static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name )
913 {
914     ULARGE_INTEGER size;
915     LARGE_INTEGER pos;
916     IStream *out;
917     DWORD stgm;
918     HRESULT r;
919
920     stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE;
921     r = SHCreateStreamOnFileW( name, stgm, &out );
922     if( FAILED( r ) )
923         return ERROR_FUNCTION_FAILED;
924
925     pos.QuadPart = 0;
926     r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size );
927     if( FAILED( r ) )
928         goto end;
929
930     pos.QuadPart = 0;
931     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
932     if( FAILED( r ) )
933         goto end;
934
935     r = IStream_CopyTo( stm, out, size, NULL, NULL );
936
937 end:
938     IStream_Release( out );
939     if( FAILED( r ) )
940         return ERROR_FUNCTION_FAILED;
941     return ERROR_SUCCESS;
942 }
943
944 UINT MSI_RecordStreamToFile( MSIRECORD *rec, UINT iField, LPCWSTR name )
945 {
946     IStream *stm = NULL;
947     UINT r;
948
949     TRACE("%p %u %s\n", rec, iField, debugstr_w(name));
950
951     msiobj_lock( &rec->hdr );
952
953     r = MSI_RecordGetIStream( rec, iField, &stm );
954     if( r == ERROR_SUCCESS )
955     {
956         r = msi_dump_stream_to_file( stm, name );
957         IStream_Release( stm );
958     }
959
960     msiobj_unlock( &rec->hdr );
961
962     return r;
963 }
964
965 MSIRECORD *MSI_CloneRecord(MSIRECORD *rec)
966 {
967     MSIRECORD *clone;
968     UINT r, i, count;
969
970     count = MSI_RecordGetFieldCount(rec);
971     clone = MSI_CreateRecord(count);
972     if (!clone)
973         return NULL;
974
975     for (i = 0; i <= count; i++)
976     {
977         if (rec->fields[i].type == MSIFIELD_STREAM)
978         {
979             if (FAILED(IStream_Clone(rec->fields[i].u.stream,
980                                      &clone->fields[i].u.stream)))
981             {
982                 msiobj_release(&clone->hdr);
983                 return NULL;
984             }
985             clone->fields[i].type = MSIFIELD_STREAM;
986         }
987         else
988         {
989             r = MSI_RecordCopyField(rec, i, clone, i);
990             if (r != ERROR_SUCCESS)
991             {
992                 msiobj_release(&clone->hdr);
993                 return NULL;
994             }
995         }
996     }
997
998     return clone;
999 }
1000
1001 BOOL MSI_RecordsAreFieldsEqual(MSIRECORD *a, MSIRECORD *b, UINT field)
1002 {
1003     if (a->fields[field].type != b->fields[field].type)
1004         return FALSE;
1005
1006     switch (a->fields[field].type)
1007     {
1008         case MSIFIELD_NULL:
1009             break;
1010
1011         case MSIFIELD_INT:
1012             if (a->fields[field].u.iVal != b->fields[field].u.iVal)
1013                 return FALSE;
1014             break;
1015
1016         case MSIFIELD_WSTR:
1017             if (a->fields[field].len != b->fields[field].len) return FALSE;
1018             if (memcmp( a->fields[field].u.szwVal, b->fields[field].u.szwVal,
1019                         a->fields[field].len * sizeof(WCHAR) )) return FALSE;
1020             break;
1021
1022         case MSIFIELD_STREAM:
1023         default:
1024             return FALSE;
1025     }
1026     return TRUE;
1027 }
1028
1029
1030 BOOL MSI_RecordsAreEqual(MSIRECORD *a, MSIRECORD *b)
1031 {
1032     UINT i;
1033
1034     if (a->count != b->count)
1035         return FALSE;
1036
1037     for (i = 0; i <= a->count; i++)
1038     {
1039         if (!MSI_RecordsAreFieldsEqual( a, b, i ))
1040             return FALSE;
1041     }
1042
1043     return TRUE;
1044 }
1045
1046 WCHAR *msi_dup_record_field( MSIRECORD *rec, INT field )
1047 {
1048     DWORD sz = 0;
1049     WCHAR *str;
1050     UINT r;
1051
1052     if (MSI_RecordIsNull( rec, field )) return NULL;
1053
1054     r = MSI_RecordGetStringW( rec, field, NULL, &sz );
1055     if (r != ERROR_SUCCESS)
1056         return NULL;
1057
1058     sz++;
1059     str = msi_alloc( sz * sizeof(WCHAR) );
1060     if (!str) return NULL;
1061     str[0] = 0;
1062     r = MSI_RecordGetStringW( rec, field, str, &sz );
1063     if (r != ERROR_SUCCESS)
1064     {
1065         ERR("failed to get string!\n");
1066         msi_free( str );
1067         return NULL;
1068     }
1069     return str;
1070 }