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