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