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