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