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