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