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