2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers
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.
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.
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
24 #define NONAMELESSUNION
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
37 #include "msiserver.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
47 * An .msi file is a structured storage file.
48 * It contains a number of streams.
49 * A stream for each table in the database.
50 * Two streams for the string table in the database.
51 * Any binary data in a table is a reference to a stream.
54 #define IS_INTMSIDBOPEN(x) (((ULONG_PTR)(x) >> 16) == 0)
56 typedef struct tagMSITRANSFORM {
61 typedef struct tagMSISTREAM {
66 static UINT find_open_stream( MSIDATABASE *db, LPCWSTR name, IStream **stm )
70 LIST_FOR_EACH_ENTRY( stream, &db->streams, MSISTREAM, entry )
75 r = IStream_Stat( stream->stm, &stat, 0 );
78 WARN("failed to stat stream r = %08x!\n", r);
82 if( !lstrcmpW( name, stat.pwcsName ) )
84 TRACE("found %s\n", debugstr_w(name));
86 CoTaskMemFree( stat.pwcsName );
90 CoTaskMemFree( stat.pwcsName );
93 return ERROR_FUNCTION_FAILED;
96 static UINT clone_open_stream( MSIDATABASE *db, LPCWSTR name, IStream **stm )
100 if (find_open_stream( db, name, &stream ) == ERROR_SUCCESS)
105 r = IStream_Clone( stream, stm );
108 WARN("failed to clone stream r = %08x!\n", r);
109 return ERROR_FUNCTION_FAILED;
113 r = IStream_Seek( *stm, pos, STREAM_SEEK_SET, NULL );
116 IStream_Release( *stm );
117 return ERROR_FUNCTION_FAILED;
120 return ERROR_SUCCESS;
123 return ERROR_FUNCTION_FAILED;
126 UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm )
130 TRACE("%s\n", debugstr_w(stname));
132 if (clone_open_stream( db, stname, stm ) == ERROR_SUCCESS)
133 return ERROR_SUCCESS;
135 r = IStorage_OpenStream( db->storage, stname, NULL,
136 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
139 MSITRANSFORM *transform;
141 LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry )
143 TRACE("looking for %s in transform storage\n", debugstr_w(stname) );
144 r = IStorage_OpenStream( transform->stg, stname, NULL,
145 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm );
155 stream = msi_alloc( sizeof(MSISTREAM) );
157 return ERROR_NOT_ENOUGH_MEMORY;
160 IStream_AddRef( *stm );
161 list_add_tail( &db->streams, &stream->entry );
164 return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED;
167 static void free_transforms( MSIDATABASE *db )
169 while( !list_empty( &db->transforms ) )
171 MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ),
172 MSITRANSFORM, entry );
173 list_remove( &t->entry );
174 IStorage_Release( t->stg );
179 void db_destroy_stream( MSIDATABASE *db, LPCWSTR stname )
181 MSISTREAM *stream, *stream2;
183 LIST_FOR_EACH_ENTRY_SAFE( stream, stream2, &db->streams, MSISTREAM, entry )
188 r = IStream_Stat( stream->stm, &stat, 0 );
191 WARN("failed to stat stream r = %08x\n", r);
195 if (!strcmpW( stname, stat.pwcsName ))
197 TRACE("destroying %s\n", debugstr_w(stname));
199 list_remove( &stream->entry );
200 IStream_Release( stream->stm );
202 IStorage_DestroyElement( db->storage, stname );
203 CoTaskMemFree( stat.pwcsName );
206 CoTaskMemFree( stat.pwcsName );
210 static void free_streams( MSIDATABASE *db )
212 while( !list_empty( &db->streams ) )
214 MSISTREAM *s = LIST_ENTRY( list_head( &db->streams ),
216 list_remove( &s->entry );
217 IStream_Release( s->stm );
222 void append_storage_to_db( MSIDATABASE *db, IStorage *stg )
226 t = msi_alloc( sizeof *t );
228 IStorage_AddRef( stg );
229 list_add_tail( &db->transforms, &t->entry );
231 /* the transform may add or replace streams */
235 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
237 MSIDATABASE *db = (MSIDATABASE *) arg;
240 free_cached_tables( db );
242 free_transforms( db );
243 msi_destroy_stringtable( db->strings );
244 IStorage_Release( db->storage );
247 DeleteFileW( db->deletefile );
248 msi_free( db->deletefile );
252 DeleteFileW( db->localfile );
253 msi_free( db->localfile );
257 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
259 IStorage *stg = NULL;
261 MSIDATABASE *db = NULL;
262 UINT ret = ERROR_FUNCTION_FAILED;
263 LPCWSTR szMode, save_path;
265 BOOL created = FALSE, patch = FALSE;
266 WCHAR path[MAX_PATH];
268 static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
270 TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
273 return ERROR_INVALID_PARAMETER;
275 if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY &&
276 szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT)
278 TRACE("Database is a patch\n");
279 szPersist -= MSIDBOPEN_PATCHFILE;
283 save_path = szDBPath;
285 if( !IS_INTMSIDBOPEN(szPersist) )
287 if (!CopyFileW( szDBPath, szPersist, FALSE ))
288 return ERROR_OPEN_FAILED;
290 szDBPath = szPersist;
291 szPersist = MSIDBOPEN_TRANSACT;
295 if( szPersist == MSIDBOPEN_READONLY )
297 r = StgOpenStorage( szDBPath, NULL,
298 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
300 else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
302 /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
304 r = StgCreateDocfile( szDBPath,
305 STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
306 if( r == ERROR_SUCCESS )
308 IStorage_SetClass( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase );
309 /* create the _Tables stream */
310 r = write_stream_data(stg, szTables, NULL, 0, TRUE);
312 r = msi_init_string_table( stg );
316 else if( szPersist == MSIDBOPEN_TRANSACT )
318 /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
320 r = StgOpenStorage( szDBPath, NULL,
321 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
323 else if( szPersist == MSIDBOPEN_DIRECT )
325 r = StgOpenStorage( szDBPath, NULL,
326 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
330 ERR("unknown flag %p\n",szPersist);
331 return ERROR_INVALID_PARAMETER;
334 if( FAILED( r ) || !stg )
336 FIXME("open failed r = %08x for %s\n", r, debugstr_w(szDBPath));
337 return ERROR_FUNCTION_FAILED;
340 r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
343 FIXME("Failed to stat storage\n");
347 if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
348 !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) &&
349 !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
351 ERR("storage GUID is not a MSI database GUID %s\n",
352 debugstr_guid(&stat.clsid) );
356 if ( patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
358 ERR("storage GUID is not the MSI patch GUID %s\n",
359 debugstr_guid(&stat.clsid) );
360 ret = ERROR_OPEN_FAILED;
364 db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
368 FIXME("Failed to allocate a handle\n");
372 if (!strchrW( save_path, '\\' ))
374 GetCurrentDirectoryW( MAX_PATH, path );
375 lstrcatW( path, szBackSlash );
376 lstrcatW( path, save_path );
379 lstrcpyW( path, save_path );
381 db->path = strdupW( path );
383 if( TRACE_ON( msi ) )
384 enum_stream_names( stg );
389 db->deletefile = strdupW( szDBPath );
390 list_init( &db->tables );
391 list_init( &db->transforms );
392 list_init( &db->streams );
394 db->strings = msi_load_string_table( stg, &db->bytes_per_strref );
400 msiobj_addref( &db->hdr );
401 IStorage_AddRef( stg );
406 msiobj_release( &db->hdr );
408 IStorage_Release( stg );
413 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
418 TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
420 ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
421 if( ret == ERROR_SUCCESS )
423 *phDB = alloc_msihandle( &db->hdr );
425 ret = ERROR_NOT_ENOUGH_MEMORY;
426 msiobj_release( &db->hdr );
432 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
434 HRESULT r = ERROR_FUNCTION_FAILED;
435 LPWSTR szwDBPath = NULL, szwPersist = NULL;
437 TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
441 szwDBPath = strdupAtoW( szDBPath );
446 if( !IS_INTMSIDBOPEN(szPersist) )
448 szwPersist = strdupAtoW( szPersist );
453 szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
455 r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
458 if( !IS_INTMSIDBOPEN(szPersist) )
459 msi_free( szwPersist );
460 msi_free( szwDBPath );
465 static LPWSTR msi_read_text_archive(LPCWSTR path)
470 DWORD read, size = 0;
472 file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
473 if (file == INVALID_HANDLE_VALUE)
476 size = GetFileSize( file, NULL );
477 data = msi_alloc( size + 1 );
481 if (!ReadFile( file, data, size, &read, NULL ))
485 wdata = strdupAtoW( data );
493 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries)
495 LPWSTR ptr = *line, save;
500 /* stay on this line */
501 while (*ptr && *ptr != '\n')
503 /* entries are separated by tabs */
510 *entries = msi_alloc(count * sizeof(LPWSTR));
514 /* store pointers into the data */
515 for (i = 0, ptr = *line; i < count; i++)
517 while (*ptr && *ptr == '\r') ptr++;
520 while (*ptr && *ptr != '\t' && *ptr != '\n' && *ptr != '\r') ptr++;
522 /* NULL-separate the data */
523 if (*ptr == '\n' || *ptr == '\r')
525 while (*ptr == '\n' || *ptr == '\r')
531 (*entries)[i] = save;
534 /* move to the next line if there's more, else EOF */
538 *num_entries = count;
541 static LPWSTR msi_build_createsql_prelude(LPWSTR table)
546 static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
548 size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2;
549 prelude = msi_alloc(size * sizeof(WCHAR));
553 sprintfW(prelude, create_fmt, table);
557 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns)
561 DWORD sql_size = 1, i, len;
562 WCHAR expanded[128], *ptr;
563 WCHAR size[10], comma[2], extra[30];
565 static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
566 static const WCHAR size_fmt[] = {'(','%','s',')',0};
567 static const WCHAR type_char[] = {'C','H','A','R',0};
568 static const WCHAR type_int[] = {'I','N','T',0};
569 static const WCHAR type_long[] = {'L','O','N','G',0};
570 static const WCHAR type_object[] = {'O','B','J','E','C','T',0};
571 static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0};
572 static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
574 columns = msi_alloc_zero(sql_size * sizeof(WCHAR));
578 for (i = 0; i < num_columns; i++)
581 comma[1] = size[0] = extra[0] = '\0';
583 if (i == num_columns - 1)
595 lstrcpyW(extra, type_notnull);
597 lstrcatW(extra, localizable);
599 sprintfW(size, size_fmt, ptr);
602 lstrcpyW(extra, type_notnull);
605 sprintfW(size, size_fmt, ptr);
608 lstrcpyW(extra, type_notnull);
616 WARN("invalid int width %u\n", len);
622 lstrcpyW(extra, type_notnull);
627 ERR("Unknown type: %c\n", types[i][0]);
632 sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma);
633 sql_size += lstrlenW(expanded);
635 p = msi_realloc(columns, sql_size * sizeof(WCHAR));
643 lstrcatW(columns, expanded);
649 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys)
651 LPWSTR postlude, keys, ptr;
652 DWORD size, key_size, i;
654 static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0};
655 static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
657 for (i = 0, size = 1; i < num_keys; i++)
658 size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2;
660 keys = msi_alloc(size * sizeof(WCHAR));
664 for (i = 0, ptr = keys; i < num_keys; i++)
666 key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2;
667 sprintfW(ptr, key_fmt, primary_keys[i]);
671 /* remove final ', ' */
674 size = lstrlenW(postlude_fmt) + size - 1;
675 postlude = msi_alloc(size * sizeof(WCHAR));
679 sprintfW(postlude, postlude_fmt, keys);
686 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns)
688 UINT r = ERROR_OUTOFMEMORY;
691 LPWSTR create_sql = NULL;
692 LPWSTR prelude, columns_sql, postlude;
694 prelude = msi_build_createsql_prelude(labels[0]);
695 columns_sql = msi_build_createsql_columns(columns, types, num_columns);
696 postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */
698 if (!prelude || !columns_sql || !postlude)
701 size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1;
702 create_sql = msi_alloc(size * sizeof(WCHAR));
706 lstrcpyW(create_sql, prelude);
707 lstrcatW(create_sql, columns_sql);
708 lstrcatW(create_sql, postlude);
710 r = MSI_DatabaseOpenViewW( db, create_sql, &view );
711 if (r != ERROR_SUCCESS)
714 r = MSI_ViewExecute(view, NULL);
716 msiobj_release(&view->hdr);
720 msi_free(columns_sql);
722 msi_free(create_sql);
726 static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name)
729 LPWSTR fullname, ptr;
731 len = lstrlenW(path) + lstrlenW(name) + 1;
732 fullname = msi_alloc(len*sizeof(WCHAR));
736 lstrcpyW( fullname, path );
738 /* chop off extension from path */
739 ptr = strrchrW(fullname, '.');
746 lstrcpyW( ptr, name );
750 static UINT construct_record(DWORD num_columns, LPWSTR *types,
751 LPWSTR *data, LPWSTR path, MSIRECORD **rec)
755 *rec = MSI_CreateRecord(num_columns);
757 return ERROR_OUTOFMEMORY;
759 for (i = 0; i < num_columns; i++)
763 case 'L': case 'l': case 'S': case 's':
764 MSI_RecordSetStringW(*rec, i + 1, data[i]);
768 MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i]));
774 LPWSTR file = msi_import_stream_filename(path, data[i]);
776 return ERROR_FUNCTION_FAILED;
778 r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file);
780 if (r != ERROR_SUCCESS)
781 return ERROR_FUNCTION_FAILED;
785 ERR("Unhandled column type: %c\n", types[i][0]);
786 msiobj_release(&(*rec)->hdr);
787 return ERROR_FUNCTION_FAILED;
791 return ERROR_SUCCESS;
794 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types,
795 LPWSTR *labels, LPWSTR **records,
796 int num_columns, int num_records,
804 static const WCHAR select[] = {
805 'S','E','L','E','C','T',' ','*',' ',
806 'F','R','O','M',' ','`','%','s','`',0
809 r = MSI_OpenQuery(db, &view, select, labels[0]);
810 if (r != ERROR_SUCCESS)
813 while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS)
815 r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec);
816 msiobj_release(&rec->hdr);
817 if (r != ERROR_SUCCESS)
821 for (i = 0; i < num_records; i++)
823 r = construct_record(num_columns, types, records[i], path, &rec);
824 if (r != ERROR_SUCCESS)
827 r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec);
828 if (r != ERROR_SUCCESS)
830 msiobj_release(&rec->hdr);
834 msiobj_release(&rec->hdr);
838 msiobj_release(&view->hdr);
842 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file)
846 DWORD num_labels, num_types;
847 DWORD num_columns, num_records = 0;
848 LPWSTR *columns, *types, *labels;
849 LPWSTR path, ptr, data;
850 LPWSTR **records = NULL;
851 LPWSTR **temp_records;
853 static const WCHAR suminfo[] =
854 {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0};
856 TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
858 if( folder == NULL || file == NULL )
859 return ERROR_INVALID_PARAMETER;
861 len = lstrlenW(folder) + lstrlenW(szBackSlash) + lstrlenW(file) + 1;
862 path = msi_alloc( len * sizeof(WCHAR) );
864 return ERROR_OUTOFMEMORY;
866 lstrcpyW( path, folder );
867 lstrcatW( path, szBackSlash );
868 lstrcatW( path, file );
870 data = msi_read_text_archive( path );
873 msi_parse_line( &ptr, &columns, &num_columns );
874 msi_parse_line( &ptr, &types, &num_types );
875 msi_parse_line( &ptr, &labels, &num_labels );
877 if (num_columns != num_types)
879 r = ERROR_FUNCTION_FAILED;
883 records = msi_alloc(sizeof(LPWSTR *));
886 r = ERROR_OUTOFMEMORY;
890 /* read in the table records */
893 msi_parse_line( &ptr, &records[num_records], NULL );
896 temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *));
899 r = ERROR_OUTOFMEMORY;
902 records = temp_records;
905 if (!strcmpW(labels[0], suminfo))
907 r = msi_add_suminfo( db, records, num_records, num_columns );
908 if (r != ERROR_SUCCESS)
910 r = ERROR_FUNCTION_FAILED;
916 if (!TABLE_Exists(db, labels[0]))
918 r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns );
919 if (r != ERROR_SUCCESS)
921 r = ERROR_FUNCTION_FAILED;
926 r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path );
936 for (i = 0; i < num_records; i++)
937 msi_free(records[i]);
944 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
949 TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
951 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
954 IWineMsiRemoteDatabase *remote_database;
956 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
957 if ( !remote_database )
958 return ERROR_INVALID_HANDLE;
960 IWineMsiRemoteDatabase_Release( remote_database );
961 WARN("MsiDatabaseImport not allowed during a custom action!\n");
963 return ERROR_SUCCESS;
966 r = MSI_DatabaseImport( db, szFolder, szFilename );
967 msiobj_release( &db->hdr );
971 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
972 LPCSTR szFolder, LPCSTR szFilename )
974 LPWSTR path = NULL, file = NULL;
975 UINT r = ERROR_OUTOFMEMORY;
977 TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
981 path = strdupAtoW( szFolder );
988 file = strdupAtoW( szFilename );
993 r = MsiDatabaseImportW( handle, path, file );
1002 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
1004 UINT i, count, len, r = ERROR_SUCCESS;
1010 buffer = msi_alloc( len );
1012 return ERROR_OUTOFMEMORY;
1014 count = MSI_RecordGetFieldCount( row );
1015 for ( i=start; i<=count; i++ )
1018 r = MSI_RecordGetStringA( row, i, buffer, &sz );
1019 if (r == ERROR_MORE_DATA)
1021 char *p = msi_realloc( buffer, sz + 1 );
1028 r = MSI_RecordGetStringA( row, i, buffer, &sz );
1029 if (r != ERROR_SUCCESS)
1032 if (!WriteFile( handle, buffer, sz, &sz, NULL ))
1034 r = ERROR_FUNCTION_FAILED;
1038 sep = (i < count) ? "\t" : "\r\n";
1039 if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
1041 r = ERROR_FUNCTION_FAILED;
1049 static UINT msi_export_row( MSIRECORD *row, void *arg )
1051 return msi_export_record( arg, row, 1 );
1054 static UINT msi_export_forcecodepage( HANDLE handle )
1058 static const char data[] = "\r\n\r\n0\t_ForceCodepage\r\n";
1060 FIXME("Read the codepage from the strings table!\n");
1062 sz = lstrlenA(data) + 1;
1063 if (!WriteFile(handle, data, sz, &sz, NULL))
1064 return ERROR_FUNCTION_FAILED;
1066 return ERROR_SUCCESS;
1069 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
1070 LPCWSTR folder, LPCWSTR file )
1072 static const WCHAR query[] = {
1073 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
1074 static const WCHAR forcecodepage[] = {
1075 '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
1076 MSIRECORD *rec = NULL;
1077 MSIQUERY *view = NULL;
1082 TRACE("%p %s %s %s\n", db, debugstr_w(table),
1083 debugstr_w(folder), debugstr_w(file) );
1085 if( folder == NULL || file == NULL )
1086 return ERROR_INVALID_PARAMETER;
1088 len = lstrlenW(folder) + lstrlenW(file) + 2;
1089 filename = msi_alloc(len * sizeof (WCHAR));
1091 return ERROR_OUTOFMEMORY;
1093 lstrcpyW( filename, folder );
1094 lstrcatW( filename, szBackSlash );
1095 lstrcatW( filename, file );
1097 handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
1098 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
1099 msi_free( filename );
1100 if (handle == INVALID_HANDLE_VALUE)
1101 return ERROR_FUNCTION_FAILED;
1103 if (!lstrcmpW( table, forcecodepage ))
1105 r = msi_export_forcecodepage( handle );
1109 r = MSI_OpenQuery( db, &view, query, table );
1110 if (r == ERROR_SUCCESS)
1112 /* write out row 1, the column names */
1113 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
1114 if (r == ERROR_SUCCESS)
1116 msi_export_record( handle, rec, 1 );
1117 msiobj_release( &rec->hdr );
1120 /* write out row 2, the column types */
1121 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
1122 if (r == ERROR_SUCCESS)
1124 msi_export_record( handle, rec, 1 );
1125 msiobj_release( &rec->hdr );
1128 /* write out row 3, the table name + keys */
1129 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
1130 if (r == ERROR_SUCCESS)
1132 MSI_RecordSetStringW( rec, 0, table );
1133 msi_export_record( handle, rec, 0 );
1134 msiobj_release( &rec->hdr );
1137 /* write out row 4 onwards, the data */
1138 r = MSI_IterateRecords( view, 0, msi_export_row, handle );
1139 msiobj_release( &view->hdr );
1143 CloseHandle( handle );
1147 /***********************************************************************
1148 * MsiExportDatabaseW [MSI.@]
1150 * Writes a file containing the table data as tab separated ASCII.
1152 * The format is as follows:
1154 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
1155 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
1156 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
1158 * Followed by the data, starting at row 1 with one row per line
1160 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
1162 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
1163 LPCWSTR szFolder, LPCWSTR szFilename )
1168 TRACE("%x %s %s %s\n", handle, debugstr_w(szTable),
1169 debugstr_w(szFolder), debugstr_w(szFilename));
1171 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1174 IWineMsiRemoteDatabase *remote_database;
1176 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1177 if ( !remote_database )
1178 return ERROR_INVALID_HANDLE;
1180 IWineMsiRemoteDatabase_Release( remote_database );
1181 WARN("MsiDatabaseExport not allowed during a custom action!\n");
1183 return ERROR_SUCCESS;
1186 r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
1187 msiobj_release( &db->hdr );
1191 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
1192 LPCSTR szFolder, LPCSTR szFilename )
1194 LPWSTR path = NULL, file = NULL, table = NULL;
1195 UINT r = ERROR_OUTOFMEMORY;
1197 TRACE("%x %s %s %s\n", handle, debugstr_a(szTable),
1198 debugstr_a(szFolder), debugstr_a(szFilename));
1202 table = strdupAtoW( szTable );
1209 path = strdupAtoW( szFolder );
1216 file = strdupAtoW( szFilename );
1221 r = MsiDatabaseExportW( handle, table, path, file );
1231 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1237 TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1238 debugstr_a(szTableName));
1240 table = strdupAtoW(szTableName);
1241 r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table);
1247 typedef struct _tagMERGETABLE
1261 typedef struct _tagMERGEROW
1267 typedef struct _tagMERGEDATA
1271 MERGETABLE *curtable;
1273 struct list *tabledata;
1276 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2)
1278 if (((type1[0] == 'l') || (type1[0] == 's')) &&
1279 ((type2[0] == 'l') || (type2[0] == 's')))
1282 if (((type1[0] == 'L') || (type1[0] == 'S')) &&
1283 ((type2[0] == 'L') || (type2[0] == 'S')))
1286 return !lstrcmpW(type1, type2);
1289 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview)
1291 MSIRECORD *dbrec, *mergerec;
1294 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec);
1295 if (r != ERROR_SUCCESS)
1298 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec);
1299 if (r != ERROR_SUCCESS)
1302 count = MSI_RecordGetFieldCount(dbrec);
1303 for (i = 1; i <= count; i++)
1305 if (!MSI_RecordGetString(mergerec, i))
1308 if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1309 MSI_RecordGetString(mergerec, i)))
1311 r = ERROR_DATATYPE_MISMATCH;
1316 msiobj_release(&dbrec->hdr);
1317 msiobj_release(&mergerec->hdr);
1318 dbrec = mergerec = NULL;
1320 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec);
1321 if (r != ERROR_SUCCESS)
1324 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec);
1325 if (r != ERROR_SUCCESS)
1328 count = MSI_RecordGetFieldCount(dbrec);
1329 for (i = 1; i <= count; i++)
1331 if (!MSI_RecordGetString(mergerec, i))
1334 if (!merge_type_match(MSI_RecordGetString(dbrec, i),
1335 MSI_RecordGetString(mergerec, i)))
1337 r = ERROR_DATATYPE_MISMATCH;
1343 msiobj_release(&dbrec->hdr);
1344 msiobj_release(&mergerec->hdr);
1349 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb,
1352 MSIRECORD *dbrec, *mergerec = NULL;
1355 r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec);
1356 if (r != ERROR_SUCCESS)
1359 r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec);
1360 if (r != ERROR_SUCCESS)
1363 count = MSI_RecordGetFieldCount(dbrec);
1364 if (count != MSI_RecordGetFieldCount(mergerec))
1366 r = ERROR_DATATYPE_MISMATCH;
1370 for (i = 1; i <= count; i++)
1372 if (lstrcmpW(MSI_RecordGetString(dbrec, i),
1373 MSI_RecordGetString(mergerec, i)))
1375 r = ERROR_DATATYPE_MISMATCH;
1381 msiobj_release(&dbrec->hdr);
1382 msiobj_release(&mergerec->hdr);
1387 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec)
1389 MSIRECORD *colnames;
1391 UINT r, i = 0, sz = 0;
1394 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames);
1395 if (r != ERROR_SUCCESS)
1400 str = msi_dup_record_field(colnames, ++i);
1401 cmp = lstrcmpW(key, str);
1405 msiobj_release(&colnames->hdr);
1407 r = MSI_RecordGetStringW(rec, i, NULL, &sz);
1408 if (r != ERROR_SUCCESS)
1412 if (MSI_RecordGetString(rec, i)) /* check record field is a string */
1414 /* quote string record fields */
1415 const WCHAR szQuote[] = {'\'', 0};
1417 val = msi_alloc(sz*sizeof(WCHAR));
1421 lstrcpyW(val, szQuote);
1422 r = MSI_RecordGetStringW(rec, i, val+1, &sz);
1423 lstrcpyW(val+1+sz, szQuote);
1427 /* do not quote integer record fields */
1428 val = msi_alloc(sz*sizeof(WCHAR));
1432 r = MSI_RecordGetStringW(rec, i, val, &sz);
1435 if (r != ERROR_SUCCESS)
1437 ERR("failed to get string!\n");
1445 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view,
1446 LPWSTR table, MSIRECORD *rec)
1448 LPWSTR query = NULL, clause = NULL;
1449 LPWSTR ptr = NULL, val;
1451 DWORD size = 1, oldsize;
1456 static const WCHAR keyset[] = {
1457 '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0};
1458 static const WCHAR lastkeyset[] = {
1459 '`','%','s','`',' ','=',' ','%','s',' ',0};
1460 static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ',
1461 'F','R','O','M',' ','`','%','s','`',' ',
1462 'W','H','E','R','E',' ','%','s',0};
1464 r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys);
1465 if (r != ERROR_SUCCESS)
1468 clause = msi_alloc_zero(size * sizeof(WCHAR));
1473 count = MSI_RecordGetFieldCount(keys);
1474 for (i = 1; i <= count; i++)
1476 key = MSI_RecordGetString(keys, i);
1477 val = get_key_value(view, key, rec);
1480 setptr = lastkeyset;
1485 size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4;
1486 clause = msi_realloc(clause, size * sizeof (WCHAR));
1493 ptr = clause + oldsize - 1;
1494 sprintfW(ptr, setptr, key, val);
1498 size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1;
1499 query = msi_alloc(size * sizeof(WCHAR));
1503 sprintfW(query, fmt, table, clause);
1507 msiobj_release(&keys->hdr);
1511 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param)
1513 MERGEDATA *data = param;
1514 MERGETABLE *table = data->curtable;
1516 MSIQUERY *dbview = NULL;
1517 MSIRECORD *row = NULL;
1518 LPWSTR query = NULL;
1519 UINT r = ERROR_SUCCESS;
1521 if (TABLE_Exists(data->db, table->name))
1523 query = create_diff_row_query(data->merge, data->curview, table->name, rec);
1525 return ERROR_OUTOFMEMORY;
1527 r = MSI_DatabaseOpenViewW(data->db, query, &dbview);
1528 if (r != ERROR_SUCCESS)
1531 r = MSI_ViewExecute(dbview, NULL);
1532 if (r != ERROR_SUCCESS)
1535 r = MSI_ViewFetch(dbview, &row);
1536 if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row))
1538 table->numconflicts++;
1541 else if (r != ERROR_NO_MORE_ITEMS)
1547 mergerow = msi_alloc(sizeof(MERGEROW));
1550 r = ERROR_OUTOFMEMORY;
1554 mergerow->data = MSI_CloneRecord(rec);
1555 if (!mergerow->data)
1557 r = ERROR_OUTOFMEMORY;
1562 list_add_tail(&table->rows, &mergerow->entry);
1566 msiobj_release(&row->hdr);
1567 msiobj_release(&dbview->hdr);
1571 static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels)
1574 MSIRECORD *prec = NULL;
1576 r = MSI_DatabaseGetPrimaryKeys(db, table, &prec);
1577 if (r != ERROR_SUCCESS)
1580 count = MSI_RecordGetFieldCount(prec);
1581 *numlabels = count + 1;
1582 *labels = msi_alloc((*numlabels)*sizeof(LPWSTR));
1585 r = ERROR_OUTOFMEMORY;
1589 (*labels)[0] = strdupW(table);
1590 for (i=1; i<=count; i++ )
1592 (*labels)[i] = strdupW(MSI_RecordGetString(prec, i));
1596 msiobj_release( &prec->hdr );
1600 static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns)
1603 MSIRECORD *prec = NULL;
1605 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec);
1606 if (r != ERROR_SUCCESS)
1609 count = MSI_RecordGetFieldCount(prec);
1610 *columns = msi_alloc(count*sizeof(LPWSTR));
1613 r = ERROR_OUTOFMEMORY;
1617 for (i=1; i<=count; i++ )
1619 (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1622 *numcolumns = count;
1625 msiobj_release( &prec->hdr );
1629 static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes)
1632 MSIRECORD *prec = NULL;
1634 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec);
1635 if (r != ERROR_SUCCESS)
1638 count = MSI_RecordGetFieldCount(prec);
1639 *types = msi_alloc(count*sizeof(LPWSTR));
1642 r = ERROR_OUTOFMEMORY;
1647 for (i=1; i<=count; i++ )
1649 (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i));
1653 msiobj_release( &prec->hdr );
1657 static void merge_free_rows(MERGETABLE *table)
1659 struct list *item, *cursor;
1661 LIST_FOR_EACH_SAFE(item, cursor, &table->rows)
1663 MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry);
1665 list_remove(&row->entry);
1666 msiobj_release(&row->data->hdr);
1671 static void free_merge_table(MERGETABLE *table)
1675 if (table->labels != NULL)
1677 for (i = 0; i < table->numlabels; i++)
1678 msi_free(table->labels[i]);
1680 msi_free(table->labels);
1683 if (table->columns != NULL)
1685 for (i = 0; i < table->numcolumns; i++)
1686 msi_free(table->columns[i]);
1688 msi_free(table->columns);
1691 if (table->types != NULL)
1693 for (i = 0; i < table->numtypes; i++)
1694 msi_free(table->types[i]);
1696 msi_free(table->types);
1699 msi_free(table->name);
1700 merge_free_rows(table);
1705 static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable)
1709 MSIQUERY *mergeview = NULL;
1711 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1712 'F','R','O','M',' ','`','%','s','`',0};
1714 table = msi_alloc_zero(sizeof(MERGETABLE));
1718 return ERROR_OUTOFMEMORY;
1721 r = msi_get_table_labels(db, name, &table->labels, &table->numlabels);
1722 if (r != ERROR_SUCCESS)
1725 r = MSI_OpenQuery(db, &mergeview, query, name);
1726 if (r != ERROR_SUCCESS)
1729 r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns);
1730 if (r != ERROR_SUCCESS)
1733 r = msi_get_query_types(mergeview, &table->types, &table->numtypes);
1734 if (r != ERROR_SUCCESS)
1737 list_init(&table->rows);
1739 table->name = strdupW(name);
1740 table->numconflicts = 0;
1742 msiobj_release(&mergeview->hdr);
1744 return ERROR_SUCCESS;
1747 msiobj_release(&mergeview->hdr);
1748 free_merge_table(table);
1753 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param)
1755 MERGEDATA *data = param;
1757 MSIQUERY *dbview = NULL;
1758 MSIQUERY *mergeview = NULL;
1762 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1763 'F','R','O','M',' ','`','%','s','`',0};
1765 name = MSI_RecordGetString(rec, 1);
1767 r = MSI_OpenQuery(data->merge, &mergeview, query, name);
1768 if (r != ERROR_SUCCESS)
1771 if (TABLE_Exists(data->db, name))
1773 r = MSI_OpenQuery(data->db, &dbview, query, name);
1774 if (r != ERROR_SUCCESS)
1777 r = merge_verify_colnames(dbview, mergeview);
1778 if (r != ERROR_SUCCESS)
1781 r = merge_verify_primary_keys(data->db, data->merge, name);
1782 if (r != ERROR_SUCCESS)
1786 r = msi_get_merge_table(data->merge, name, &table);
1787 if (r != ERROR_SUCCESS)
1790 data->curtable = table;
1791 data->curview = mergeview;
1792 r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data);
1793 if (r != ERROR_SUCCESS)
1795 free_merge_table(table);
1799 list_add_tail(data->tabledata, &table->entry);
1802 msiobj_release(&dbview->hdr);
1803 msiobj_release(&mergeview->hdr);
1807 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge,
1808 struct list *tabledata)
1814 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ',
1815 'F','R','O','M',' ','`','_','T','a','b','l','e','s','`',0};
1817 r = MSI_DatabaseOpenViewW(merge, query, &view);
1818 if (r != ERROR_SUCCESS)
1823 data.tabledata = tabledata;
1824 r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data);
1826 msiobj_release(&view->hdr);
1830 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table)
1836 if (!TABLE_Exists(db, table->name))
1838 r = msi_add_table_to_db(db, table->columns, table->types,
1839 table->labels, table->numlabels, table->numcolumns);
1840 if (r != ERROR_SUCCESS)
1841 return ERROR_FUNCTION_FAILED;
1844 LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry)
1846 r = TABLE_CreateView(db, table->name, &tv);
1847 if (r != ERROR_SUCCESS)
1850 r = tv->ops->insert_row(tv, row->data, -1, FALSE);
1851 tv->ops->delete(tv);
1853 if (r != ERROR_SUCCESS)
1857 return ERROR_SUCCESS;
1860 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error,
1861 LPWSTR table, DWORD numconflicts)
1866 static const WCHAR create[] = {
1867 'C','R','E','A','T','E',' ','T','A','B','L','E',' ',
1868 '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ',
1869 'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ',
1870 'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e',
1871 'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ',
1872 'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ',
1873 'K','E','Y',' ','`','T','a','b','l','e','`',')',0};
1874 static const WCHAR insert[] = {
1875 'I','N','S','E','R','T',' ','I','N','T','O',' ',
1876 '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ',
1877 '`','N','u','m','R','o','w','M','e','r','g','e',
1878 'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S',
1879 ' ','(','\'','%','s','\'',',',' ','%','d',')',0};
1881 if (!TABLE_Exists(db, error))
1883 r = MSI_OpenQuery(db, &view, create, error);
1884 if (r != ERROR_SUCCESS)
1887 r = MSI_ViewExecute(view, NULL);
1888 msiobj_release(&view->hdr);
1889 if (r != ERROR_SUCCESS)
1893 r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts);
1894 if (r != ERROR_SUCCESS)
1897 r = MSI_ViewExecute(view, NULL);
1898 msiobj_release(&view->hdr);
1902 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge,
1903 LPCWSTR szTableName)
1905 struct list tabledata = LIST_INIT(tabledata);
1906 struct list *item, *cursor;
1907 MSIDATABASE *db, *merge;
1912 TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge,
1913 debugstr_w(szTableName));
1915 if (szTableName && !*szTableName)
1916 return ERROR_INVALID_TABLE;
1918 db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE);
1919 merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE);
1922 r = ERROR_INVALID_HANDLE;
1926 r = gather_merge_data(db, merge, &tabledata);
1927 if (r != ERROR_SUCCESS)
1931 LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry)
1933 if (table->numconflicts)
1937 r = update_merge_errors(db, szTableName, table->name,
1938 table->numconflicts);
1939 if (r != ERROR_SUCCESS)
1944 r = merge_table(db, table);
1945 if (r != ERROR_SUCCESS)
1950 LIST_FOR_EACH_SAFE(item, cursor, &tabledata)
1952 MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry);
1953 list_remove(&table->entry);
1954 free_merge_table(table);
1958 r = ERROR_FUNCTION_FAILED;
1961 msiobj_release(&db->hdr);
1962 msiobj_release(&merge->hdr);
1966 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
1968 MSIDBSTATE ret = MSIDBSTATE_READ;
1971 TRACE("%d\n", handle);
1973 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
1976 IWineMsiRemoteDatabase *remote_database;
1978 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle );
1979 if ( !remote_database )
1980 return MSIDBSTATE_ERROR;
1982 IWineMsiRemoteDatabase_Release( remote_database );
1983 WARN("MsiGetDatabaseState not allowed during a custom action!\n");
1985 return MSIDBSTATE_READ;
1988 if (db->mode != MSIDBOPEN_READONLY )
1989 ret = MSIDBSTATE_WRITE;
1990 msiobj_release( &db->hdr );
1995 typedef struct _msi_remote_database_impl {
1996 const IWineMsiRemoteDatabaseVtbl *lpVtbl;
1999 } msi_remote_database_impl;
2001 static inline msi_remote_database_impl* mrd_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase* iface )
2003 return (msi_remote_database_impl *)iface;
2006 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface,
2007 REFIID riid,LPVOID *ppobj)
2009 if( IsEqualCLSID( riid, &IID_IUnknown ) ||
2010 IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) )
2012 IUnknown_AddRef( iface );
2017 return E_NOINTERFACE;
2020 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface )
2022 msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2024 return InterlockedIncrement( &This->refs );
2027 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface )
2029 msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2032 r = InterlockedDecrement( &This->refs );
2035 MsiCloseHandle( This->database );
2041 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface,
2042 LPCWSTR table, MSICONDITION *persistent )
2044 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2045 *persistent = MsiDatabaseIsTablePersistentW(This->database, table);
2049 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface,
2050 LPCWSTR table, MSIHANDLE *keys )
2052 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2053 UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys);
2054 return HRESULT_FROM_WIN32(r);
2057 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface,
2058 UINT updatecount, MSIHANDLE *suminfo )
2060 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2061 UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo);
2062 return HRESULT_FROM_WIN32(r);
2065 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface,
2066 LPCWSTR query, MSIHANDLE *view )
2068 msi_remote_database_impl *This = mrd_from_IWineMsiRemoteDatabase( iface );
2069 UINT r = MsiDatabaseOpenViewW(This->database, query, view);
2070 return HRESULT_FROM_WIN32(r);
2073 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle )
2075 msi_remote_database_impl* This = mrd_from_IWineMsiRemoteDatabase( iface );
2076 This->database = handle;
2080 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl =
2085 mrd_IsTablePersistent,
2087 mrd_GetSummaryInformation,
2092 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj )
2094 msi_remote_database_impl *This;
2096 This = msi_alloc( sizeof *This );
2098 return E_OUTOFMEMORY;
2100 This->lpVtbl = &msi_remote_database_vtbl;