xref: /reactos/dll/win32/msi/record.c (revision da5f10af)
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 
337     if (iVal == MSI_NULL_INTEGER)
338     {
339         rec->fields[iField].type = MSIFIELD_NULL;
340         rec->fields[iField].u.szwVal = NULL;
341     }
342     else
343     {
344         rec->fields[iField].type = MSIFIELD_INT;
345         rec->fields[iField].u.iVal = iVal;
346     }
347 
348     return ERROR_SUCCESS;
349 }
350 
351 UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, UINT iField, int iVal )
352 {
353     MSIRECORD *rec;
354     UINT ret;
355 
356     TRACE("%d %u %d\n", handle, iField, iVal);
357 
358     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
359     if( !rec )
360         return ERROR_INVALID_HANDLE;
361 
362     msiobj_lock( &rec->hdr );
363     ret = MSI_RecordSetInteger( rec, iField, iVal );
364     msiobj_unlock( &rec->hdr );
365     msiobj_release( &rec->hdr );
366     return ret;
367 }
368 
369 BOOL MSI_RecordIsNull( MSIRECORD *rec, UINT iField )
370 {
371     BOOL r = TRUE;
372 
373     TRACE("%p %d\n", rec, iField );
374 
375     r = ( iField > rec->count ) ||
376         ( rec->fields[iField].type == MSIFIELD_NULL );
377 
378     return r;
379 }
380 
381 BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, UINT iField )
382 {
383     MSIRECORD *rec;
384     UINT ret;
385 
386     TRACE("%d %d\n", handle, iField );
387 
388     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
389     if( !rec )
390         return FALSE;
391     msiobj_lock( &rec->hdr );
392     ret = MSI_RecordIsNull( rec, iField );
393     msiobj_unlock( &rec->hdr );
394     msiobj_release( &rec->hdr );
395     return ret;
396 
397 }
398 
399 UINT MSI_RecordGetStringA(MSIRECORD *rec, UINT iField,
400                LPSTR szValue, LPDWORD pcchValue)
401 {
402     UINT len = 0, ret = ERROR_SUCCESS;
403     CHAR buffer[16];
404 
405     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
406 
407     if( iField > rec->count )
408     {
409         if ( szValue && *pcchValue > 0 )
410             szValue[0] = 0;
411 
412         *pcchValue = 0;
413         return ERROR_SUCCESS;
414     }
415 
416     switch( rec->fields[iField].type )
417     {
418     case MSIFIELD_INT:
419         wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
420         len = lstrlenA( buffer );
421         if (szValue)
422             lstrcpynA(szValue, buffer, *pcchValue);
423         break;
424     case MSIFIELD_WSTR:
425         len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal,
426                                    rec->fields[iField].len + 1, NULL, 0 , NULL, NULL );
427         if (szValue)
428             WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal,
429                                  rec->fields[iField].len + 1, szValue, *pcchValue, NULL, NULL );
430         if( szValue && *pcchValue && len>*pcchValue )
431             szValue[*pcchValue-1] = 0;
432         if( len )
433             len--;
434         break;
435     case MSIFIELD_NULL:
436         if( szValue && *pcchValue > 0 )
437             szValue[0] = 0;
438         break;
439     default:
440         ret = ERROR_INVALID_PARAMETER;
441         break;
442     }
443 
444     if( szValue && *pcchValue <= len )
445         ret = ERROR_MORE_DATA;
446     *pcchValue = len;
447 
448     return ret;
449 }
450 
451 UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, UINT iField,
452                LPSTR szValue, LPDWORD pcchValue)
453 {
454     MSIRECORD *rec;
455     UINT ret;
456 
457     TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
458 
459     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
460     if( !rec )
461         return ERROR_INVALID_HANDLE;
462     msiobj_lock( &rec->hdr );
463     ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
464     msiobj_unlock( &rec->hdr );
465     msiobj_release( &rec->hdr );
466     return ret;
467 }
468 
469 const WCHAR *msi_record_get_string( const MSIRECORD *rec, UINT field, int *len )
470 {
471     if (field > rec->count)
472         return NULL;
473 
474     if (rec->fields[field].type != MSIFIELD_WSTR)
475         return NULL;
476 
477     if (len) *len = rec->fields[field].len;
478 
479     return rec->fields[field].u.szwVal;
480 }
481 
482 const WCHAR *MSI_RecordGetString( const MSIRECORD *rec, UINT iField )
483 {
484     return msi_record_get_string( rec, iField, NULL );
485 }
486 
487 UINT MSI_RecordGetStringW(MSIRECORD *rec, UINT iField,
488                LPWSTR szValue, LPDWORD pcchValue)
489 {
490     static const WCHAR szFormat[] = {'%','d',0};
491     UINT len = 0, ret = ERROR_SUCCESS;
492     WCHAR buffer[16];
493 
494     TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
495 
496     if( iField > rec->count )
497     {
498         if ( szValue && *pcchValue > 0 )
499             szValue[0] = 0;
500 
501         *pcchValue = 0;
502         return ERROR_SUCCESS;
503     }
504 
505     switch( rec->fields[iField].type )
506     {
507     case MSIFIELD_INT:
508         wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
509         len = lstrlenW( buffer );
510         if (szValue)
511             lstrcpynW(szValue, buffer, *pcchValue);
512         break;
513     case MSIFIELD_WSTR:
514         len = rec->fields[iField].len;
515         if (szValue)
516             memcpy( szValue, rec->fields[iField].u.szwVal, min(len + 1, *pcchValue) * sizeof(WCHAR) );
517         break;
518     case MSIFIELD_NULL:
519         if( szValue && *pcchValue > 0 )
520             szValue[0] = 0;
521         break;
522     default:
523         break;
524     }
525 
526     if( szValue && *pcchValue <= len )
527         ret = ERROR_MORE_DATA;
528     *pcchValue = len;
529 
530     return ret;
531 }
532 
533 UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, UINT iField,
534                LPWSTR szValue, LPDWORD pcchValue)
535 {
536     MSIRECORD *rec;
537     UINT ret;
538 
539     TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
540 
541     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
542     if( !rec )
543         return ERROR_INVALID_HANDLE;
544 
545     msiobj_lock( &rec->hdr );
546     ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
547     msiobj_unlock( &rec->hdr );
548     msiobj_release( &rec->hdr );
549     return ret;
550 }
551 
552 static UINT msi_get_stream_size( IStream *stm )
553 {
554     STATSTG stat;
555     HRESULT r;
556 
557     r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
558     if( FAILED(r) )
559         return 0;
560     return stat.cbSize.QuadPart;
561 }
562 
563 static UINT MSI_RecordDataSize(MSIRECORD *rec, UINT iField)
564 {
565     TRACE("%p %d\n", rec, iField);
566 
567     if( iField > rec->count )
568         return 0;
569 
570     switch( rec->fields[iField].type )
571     {
572     case MSIFIELD_INT:
573         return sizeof (INT);
574     case MSIFIELD_WSTR:
575         return rec->fields[iField].len;
576     case MSIFIELD_NULL:
577         break;
578     case MSIFIELD_STREAM:
579         return msi_get_stream_size( rec->fields[iField].u.stream );
580     }
581     return 0;
582 }
583 
584 UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, UINT iField)
585 {
586     MSIRECORD *rec;
587     UINT ret;
588 
589     TRACE("%d %d\n", handle, iField);
590 
591     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
592     if( !rec )
593         return 0;
594     msiobj_lock( &rec->hdr );
595     ret = MSI_RecordDataSize( rec, iField);
596     msiobj_unlock( &rec->hdr );
597     msiobj_release( &rec->hdr );
598     return ret;
599 }
600 
601 UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, UINT iField, LPCSTR szValue )
602 {
603     WCHAR *valueW = NULL;
604     MSIRECORD *rec;
605     UINT ret;
606 
607     TRACE("%d %d %s\n", handle, iField, debugstr_a(szValue));
608 
609     if (szValue && !(valueW = strdupAtoW( szValue ))) return ERROR_OUTOFMEMORY;
610 
611     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
612     if( !rec )
613     {
614         msi_free( valueW );
615         return ERROR_INVALID_HANDLE;
616     }
617     msiobj_lock( &rec->hdr );
618     ret = MSI_RecordSetStringW( rec, iField, valueW );
619     msiobj_unlock( &rec->hdr );
620     msiobj_release( &rec->hdr );
621     msi_free( valueW );
622     return ret;
623 }
624 
625 UINT msi_record_set_string( MSIRECORD *rec, UINT field, const WCHAR *value, int len )
626 {
627     if (field > rec->count)
628         return ERROR_INVALID_FIELD;
629 
630     MSI_FreeField( &rec->fields[field] );
631 
632     if (value && len < 0) len = strlenW( value );
633 
634     if (value && len)
635     {
636         rec->fields[field].type = MSIFIELD_WSTR;
637         rec->fields[field].u.szwVal = msi_strdupW( value, len );
638         rec->fields[field].len = len;
639     }
640     else
641     {
642         rec->fields[field].type = MSIFIELD_NULL;
643         rec->fields[field].u.szwVal = NULL;
644         rec->fields[field].len = 0;
645     }
646     return 0;
647 }
648 
649 UINT MSI_RecordSetStringW( MSIRECORD *rec, UINT iField, LPCWSTR szValue )
650 {
651     TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
652 
653     return msi_record_set_string( rec, iField, szValue, -1 );
654 }
655 
656 UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, UINT iField, LPCWSTR szValue )
657 {
658     MSIRECORD *rec;
659     UINT ret;
660 
661     TRACE("%d %d %s\n", handle, iField, debugstr_w(szValue));
662 
663     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
664     if( !rec )
665         return ERROR_INVALID_HANDLE;
666 
667     msiobj_lock( &rec->hdr );
668     ret = MSI_RecordSetStringW( rec, iField, szValue );
669     msiobj_unlock( &rec->hdr );
670     msiobj_release( &rec->hdr );
671     return ret;
672 }
673 
674 /* read the data in a file into an IStream */
675 static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
676 {
677     DWORD sz, szHighWord = 0, read;
678     HANDLE handle;
679     HGLOBAL hGlob = 0;
680     HRESULT hr;
681     ULARGE_INTEGER ulSize;
682 
683     TRACE("reading %s\n", debugstr_w(szFile));
684 
685     /* read the file into memory */
686     handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
687     if( handle == INVALID_HANDLE_VALUE )
688         return GetLastError();
689     sz = GetFileSize(handle, &szHighWord);
690     if( sz != INVALID_FILE_SIZE && szHighWord == 0 )
691     {
692         hGlob = GlobalAlloc(GMEM_FIXED, sz);
693         if( hGlob )
694         {
695             BOOL r = ReadFile(handle, hGlob, sz, &read, NULL) && read == sz;
696             if( !r )
697             {
698                 GlobalFree(hGlob);
699                 hGlob = 0;
700             }
701         }
702     }
703     CloseHandle(handle);
704     if( !hGlob )
705         return ERROR_FUNCTION_FAILED;
706 
707     /* make a stream out of it, and set the correct file size */
708     hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
709     if( FAILED( hr ) )
710     {
711         GlobalFree(hGlob);
712         return ERROR_FUNCTION_FAILED;
713     }
714 
715     /* set the correct size - CreateStreamOnHGlobal screws it up */
716     ulSize.QuadPart = sz;
717     IStream_SetSize(*pstm, ulSize);
718 
719     TRACE("read %s, %d bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
720 
721     return ERROR_SUCCESS;
722 }
723 
724 UINT MSI_RecordSetStream(MSIRECORD *rec, UINT iField, IStream *stream)
725 {
726     if ( (iField == 0) || (iField > rec->count) )
727         return ERROR_INVALID_PARAMETER;
728 
729     MSI_FreeField( &rec->fields[iField] );
730     rec->fields[iField].type = MSIFIELD_STREAM;
731     rec->fields[iField].u.stream = stream;
732 
733     return ERROR_SUCCESS;
734 }
735 
736 UINT MSI_RecordSetStreamFromFileW(MSIRECORD *rec, UINT iField, LPCWSTR szFilename)
737 {
738     IStream *stm = NULL;
739     HRESULT hr;
740     UINT ret;
741 
742     if( (iField == 0) || (iField > rec->count) )
743         return ERROR_INVALID_PARAMETER;
744 
745     /* no filename means we should seek back to the start of the stream */
746     if( !szFilename )
747     {
748         LARGE_INTEGER ofs;
749         ULARGE_INTEGER cur;
750 
751         if( rec->fields[iField].type != MSIFIELD_STREAM )
752             return ERROR_INVALID_FIELD;
753 
754         stm = rec->fields[iField].u.stream;
755         if( !stm )
756             return ERROR_INVALID_FIELD;
757 
758         ofs.QuadPart = 0;
759         hr = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
760         if (FAILED( hr ))
761             return ERROR_FUNCTION_FAILED;
762     }
763     else
764     {
765         /* read the file into a stream and save the stream in the record */
766         ret = RECORD_StreamFromFile(szFilename, &stm);
767         if (ret != ERROR_SUCCESS)
768             return ret;
769 
770         /* if all's good, store it in the record */
771         MSI_RecordSetStream(rec, iField, stm);
772     }
773 
774     return ERROR_SUCCESS;
775 }
776 
777 UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, UINT iField, LPCSTR szFilename)
778 {
779     LPWSTR wstr = NULL;
780     UINT ret;
781 
782     TRACE("%d %d %s\n", hRecord, iField, debugstr_a(szFilename));
783 
784     if( szFilename )
785     {
786         wstr = strdupAtoW( szFilename );
787         if( !wstr )
788              return ERROR_OUTOFMEMORY;
789     }
790     ret = MsiRecordSetStreamW(hRecord, iField, wstr);
791     msi_free(wstr);
792 
793     return ret;
794 }
795 
796 UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, UINT iField, LPCWSTR szFilename)
797 {
798     MSIRECORD *rec;
799     UINT ret;
800 
801     TRACE("%d %d %s\n", handle, iField, debugstr_w(szFilename));
802 
803     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
804     if( !rec )
805         return ERROR_INVALID_HANDLE;
806 
807     msiobj_lock( &rec->hdr );
808     ret = MSI_RecordSetStreamFromFileW( rec, iField, szFilename );
809     msiobj_unlock( &rec->hdr );
810     msiobj_release( &rec->hdr );
811     return ret;
812 }
813 
814 UINT MSI_RecordReadStream(MSIRECORD *rec, UINT iField, char *buf, LPDWORD sz)
815 {
816     ULONG count;
817     HRESULT r;
818     IStream *stm;
819 
820     TRACE("%p %d %p %p\n", rec, iField, buf, sz);
821 
822     if( !sz )
823         return ERROR_INVALID_PARAMETER;
824 
825     if( iField > rec->count)
826         return ERROR_INVALID_PARAMETER;
827 
828     if ( rec->fields[iField].type == MSIFIELD_NULL )
829     {
830         *sz = 0;
831         return ERROR_INVALID_DATA;
832     }
833 
834     if( rec->fields[iField].type != MSIFIELD_STREAM )
835         return ERROR_INVALID_DATATYPE;
836 
837     stm = rec->fields[iField].u.stream;
838     if( !stm )
839         return ERROR_INVALID_PARAMETER;
840 
841     /* if there's no buffer pointer, calculate the length to the end */
842     if( !buf )
843     {
844         LARGE_INTEGER ofs;
845         ULARGE_INTEGER end, cur;
846 
847         ofs.QuadPart = cur.QuadPart = 0;
848         end.QuadPart = 0;
849         IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
850         IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
851         ofs.QuadPart = cur.QuadPart;
852         IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
853         *sz = end.QuadPart - cur.QuadPart;
854 
855         return ERROR_SUCCESS;
856     }
857 
858     /* read the data */
859     count = 0;
860     r = IStream_Read( stm, buf, *sz, &count );
861     if( FAILED( r ) )
862     {
863         *sz = 0;
864         return ERROR_FUNCTION_FAILED;
865     }
866 
867     *sz = count;
868 
869     return ERROR_SUCCESS;
870 }
871 
872 UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, UINT iField, char *buf, LPDWORD sz)
873 {
874     MSIRECORD *rec;
875     UINT ret;
876 
877     TRACE("%d %d %p %p\n", handle, iField, buf, sz);
878 
879     rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
880     if( !rec )
881         return ERROR_INVALID_HANDLE;
882     msiobj_lock( &rec->hdr );
883     ret = MSI_RecordReadStream( rec, iField, buf, sz );
884     msiobj_unlock( &rec->hdr );
885     msiobj_release( &rec->hdr );
886     return ret;
887 }
888 
889 UINT MSI_RecordSetIStream( MSIRECORD *rec, UINT iField, IStream *stm )
890 {
891     TRACE("%p %d %p\n", rec, iField, stm);
892 
893     if( iField > rec->count )
894         return ERROR_INVALID_FIELD;
895 
896     MSI_FreeField( &rec->fields[iField] );
897 
898     rec->fields[iField].type = MSIFIELD_STREAM;
899     rec->fields[iField].u.stream = stm;
900     IStream_AddRef( stm );
901 
902     return ERROR_SUCCESS;
903 }
904 
905 UINT MSI_RecordGetIStream( MSIRECORD *rec, UINT iField, IStream **pstm)
906 {
907     TRACE("%p %d %p\n", rec, iField, pstm);
908 
909     if( iField > rec->count )
910         return ERROR_INVALID_FIELD;
911 
912     if( rec->fields[iField].type != MSIFIELD_STREAM )
913         return ERROR_INVALID_FIELD;
914 
915     *pstm = rec->fields[iField].u.stream;
916     IStream_AddRef( *pstm );
917 
918     return ERROR_SUCCESS;
919 }
920 
921 static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name )
922 {
923     ULARGE_INTEGER size;
924     LARGE_INTEGER pos;
925     IStream *out;
926     DWORD stgm;
927     HRESULT r;
928 
929     stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE;
930     r = SHCreateStreamOnFileW( name, stgm, &out );
931     if( FAILED( r ) )
932         return ERROR_FUNCTION_FAILED;
933 
934     pos.QuadPart = 0;
935     r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size );
936     if( FAILED( r ) )
937         goto end;
938 
939     pos.QuadPart = 0;
940     r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
941     if( FAILED( r ) )
942         goto end;
943 
944     r = IStream_CopyTo( stm, out, size, NULL, NULL );
945 
946 end:
947     IStream_Release( out );
948     if( FAILED( r ) )
949         return ERROR_FUNCTION_FAILED;
950     return ERROR_SUCCESS;
951 }
952 
953 UINT MSI_RecordStreamToFile( MSIRECORD *rec, UINT iField, LPCWSTR name )
954 {
955     IStream *stm = NULL;
956     UINT r;
957 
958     TRACE("%p %u %s\n", rec, iField, debugstr_w(name));
959 
960     msiobj_lock( &rec->hdr );
961 
962     r = MSI_RecordGetIStream( rec, iField, &stm );
963     if( r == ERROR_SUCCESS )
964     {
965         r = msi_dump_stream_to_file( stm, name );
966         IStream_Release( stm );
967     }
968 
969     msiobj_unlock( &rec->hdr );
970 
971     return r;
972 }
973 
974 MSIRECORD *MSI_CloneRecord(MSIRECORD *rec)
975 {
976     MSIRECORD *clone;
977     UINT r, i, count;
978 
979     count = MSI_RecordGetFieldCount(rec);
980     clone = MSI_CreateRecord(count);
981     if (!clone)
982         return NULL;
983 
984     for (i = 0; i <= count; i++)
985     {
986         if (rec->fields[i].type == MSIFIELD_STREAM)
987         {
988             if (FAILED(IStream_Clone(rec->fields[i].u.stream,
989                                      &clone->fields[i].u.stream)))
990             {
991                 msiobj_release(&clone->hdr);
992                 return NULL;
993             }
994             clone->fields[i].type = MSIFIELD_STREAM;
995         }
996         else
997         {
998             r = MSI_RecordCopyField(rec, i, clone, i);
999             if (r != ERROR_SUCCESS)
1000             {
1001                 msiobj_release(&clone->hdr);
1002                 return NULL;
1003             }
1004         }
1005     }
1006 
1007     return clone;
1008 }
1009 
1010 BOOL MSI_RecordsAreFieldsEqual(MSIRECORD *a, MSIRECORD *b, UINT field)
1011 {
1012     if (a->fields[field].type != b->fields[field].type)
1013         return FALSE;
1014 
1015     switch (a->fields[field].type)
1016     {
1017         case MSIFIELD_NULL:
1018             break;
1019 
1020         case MSIFIELD_INT:
1021             if (a->fields[field].u.iVal != b->fields[field].u.iVal)
1022                 return FALSE;
1023             break;
1024 
1025         case MSIFIELD_WSTR:
1026             if (a->fields[field].len != b->fields[field].len) return FALSE;
1027             if (memcmp( a->fields[field].u.szwVal, b->fields[field].u.szwVal,
1028                         a->fields[field].len * sizeof(WCHAR) )) return FALSE;
1029             break;
1030 
1031         case MSIFIELD_STREAM:
1032         default:
1033             return FALSE;
1034     }
1035     return TRUE;
1036 }
1037 
1038 
1039 BOOL MSI_RecordsAreEqual(MSIRECORD *a, MSIRECORD *b)
1040 {
1041     UINT i;
1042 
1043     if (a->count != b->count)
1044         return FALSE;
1045 
1046     for (i = 0; i <= a->count; i++)
1047     {
1048         if (!MSI_RecordsAreFieldsEqual( a, b, i ))
1049             return FALSE;
1050     }
1051 
1052     return TRUE;
1053 }
1054 
1055 WCHAR *msi_dup_record_field( MSIRECORD *rec, INT field )
1056 {
1057     DWORD sz = 0;
1058     WCHAR *str;
1059     UINT r;
1060 
1061     if (MSI_RecordIsNull( rec, field )) return NULL;
1062 
1063     r = MSI_RecordGetStringW( rec, field, NULL, &sz );
1064     if (r != ERROR_SUCCESS)
1065         return NULL;
1066 
1067     sz++;
1068     str = msi_alloc( sz * sizeof(WCHAR) );
1069     if (!str) return NULL;
1070     str[0] = 0;
1071     r = MSI_RecordGetStringW( rec, field, str, &sz );
1072     if (r != ERROR_SUCCESS)
1073     {
1074         ERR("failed to get string!\n");
1075         msi_free( str );
1076         return NULL;
1077     }
1078     return str;
1079 }
1080 
1081 void dump_record(MSIRECORD *rec)
1082 {
1083     int i;
1084     if (!rec)
1085     {
1086         TRACE("(null)\n");
1087         return;
1088     }
1089 
1090     TRACE("[");
1091     for (i = 0; i <= rec->count; i++)
1092     {
1093         switch(rec->fields[i].type)
1094         {
1095         case MSIFIELD_NULL: TRACE("(null)"); break;
1096         case MSIFIELD_INT: TRACE("%d", rec->fields[i].u.iVal); break;
1097         case MSIFIELD_WSTR: TRACE("%s", debugstr_w(rec->fields[i].u.szwVal)); break;
1098         case MSIFIELD_INTPTR: TRACE("%ld", rec->fields[i].u.pVal); break;
1099         case MSIFIELD_STREAM: TRACE("%p", rec->fields[i].u.stream); break;
1100         }
1101         if (i < rec->count) TRACE(", ");
1102     }
1103     TRACE("]\n");
1104 }
1105