diff --git a/dlls/msi/database.c b/dlls/msi/database.c index f302610771e..4537ed5836e 100644 --- a/dlls/msi/database.c +++ b/dlls/msi/database.c @@ -961,8 +961,68 @@ static UINT msi_export_field( HANDLE handle, MSIRECORD *row, UINT field ) return ret ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED; } -static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start ) +static UINT msi_export_stream( const WCHAR *folder, const WCHAR *table, MSIRECORD *row, UINT field, UINT start ) { + static const WCHAR fmt[] = {'%','s','\\','%','s',0}; + WCHAR stream[MAX_STREAM_NAME_LEN + 1], *path; + DWORD sz, read_size, write_size; + char buffer[1024]; + HANDLE file; + UINT len, r; + + sz = ARRAY_SIZE( stream ); + r = MSI_RecordGetStringW( row, start, stream, &sz ); + if (r != ERROR_SUCCESS) + return r; + + len = (sz + strlenW( folder ) + strlenW( table ) + ARRAY_SIZE( fmt ) + 1) * sizeof(WCHAR); + if (!(path = msi_alloc( len ))) + return ERROR_OUTOFMEMORY; + + len = sprintfW( path, fmt, folder, table ); + if (!CreateDirectoryW( path, NULL ) && GetLastError() != ERROR_ALREADY_EXISTS) + { + msi_free( path ); + return ERROR_FUNCTION_FAILED; + } + + path[len++] = '\\'; + strcpyW( path + len, stream ); + file = CreateFileW( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); + msi_free( path ); + if (file == INVALID_HANDLE_VALUE) + return ERROR_FUNCTION_FAILED; + + read_size = sizeof(buffer); + while (read_size == sizeof(buffer)) + { + r = MSI_RecordReadStream( row, field, buffer, &read_size ); + if (r != ERROR_SUCCESS) + { + CloseHandle( file ); + return r; + } + if (!WriteFile( file, buffer, read_size, &write_size, NULL ) || read_size != write_size) + { + CloseHandle( file ); + return ERROR_WRITE_FAULT; + } + } + CloseHandle( file ); + return r; +} + +struct row_export_info +{ + HANDLE handle; + const WCHAR *folder; + const WCHAR *table; +}; + +static UINT msi_export_record( struct row_export_info *row_export_info, MSIRECORD *row, UINT start ) +{ + HANDLE handle = row_export_info->handle; UINT i, count, r = ERROR_SUCCESS; const char *sep; DWORD sz; @@ -971,7 +1031,18 @@ static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start ) for (i = start; i <= count; i++) { r = msi_export_field( handle, row, i ); - if (r != ERROR_SUCCESS) + if (r == ERROR_INVALID_PARAMETER) + { + r = msi_export_stream( row_export_info->folder, row_export_info->table, row, i, start ); + if (r != ERROR_SUCCESS) + return r; + + /* exporting a binary stream, repeat the "Name" field */ + r = msi_export_field( handle, row, start ); + if (r != ERROR_SUCCESS) + return r; + } + else if (r != ERROR_SUCCESS) return r; sep = (i < count) ? "\t" : "\r\n"; @@ -1062,11 +1133,13 @@ static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table, LPCWSTR folder, r = MSI_OpenQuery( db, &view, query, table ); if (r == ERROR_SUCCESS) { + struct row_export_info row_export_info = { handle, folder, table }; + /* write out row 1, the column names */ r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); if (r == ERROR_SUCCESS) { - msi_export_record( handle, rec, 1 ); + msi_export_record( &row_export_info, rec, 1 ); msiobj_release( &rec->hdr ); } @@ -1074,7 +1147,7 @@ static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table, LPCWSTR folder, r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); if (r == ERROR_SUCCESS) { - msi_export_record( handle, rec, 1 ); + msi_export_record( &row_export_info, rec, 1 ); msiobj_release( &rec->hdr ); } @@ -1083,12 +1156,12 @@ static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table, LPCWSTR folder, if (r == ERROR_SUCCESS) { MSI_RecordSetStringW( rec, 0, table ); - msi_export_record( handle, rec, 0 ); + msi_export_record( &row_export_info, rec, 0 ); msiobj_release( &rec->hdr ); } /* write out row 4 onwards, the data */ - r = MSI_IterateRecords( view, 0, msi_export_row, handle ); + r = MSI_IterateRecords( view, 0, msi_export_row, &row_export_info ); msiobj_release( &view->hdr ); }