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"
39 WINE_DEFAULT_DEBUG_CHANNEL(msi);
41 DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000,
42 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
43 DEFINE_GUID( CLSID_MsiPatch, 0x000c1086, 0x0000, 0x0000,
44 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
49 * An .msi file is a structured storage file.
50 * It contains a number of streams.
51 * A stream for each table in the database.
52 * Two streams for the string table in the database.
53 * Any binary data in a table is a reference to a stream.
56 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg )
58 MSIDATABASE *db = (MSIDATABASE *) arg;
61 free_cached_tables( db );
62 msi_free_transforms( db );
63 msi_destroy_stringtable( db->strings );
64 r = IStorage_Release( db->storage );
66 ERR("database reference count was not zero (%ld)\n", r);
69 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb)
73 MSIDATABASE *db = NULL;
74 UINT ret = ERROR_FUNCTION_FAILED;
78 TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) );
81 return ERROR_INVALID_PARAMETER;
84 if( HIWORD( szPersist ) )
86 /* UINT len = lstrlenW( szPerist ) + 1; */
87 FIXME("don't support persist files yet\b");
88 return ERROR_INVALID_PARAMETER;
89 /* szMode = msi_alloc( len * sizeof (DWORD) ); */
91 else if( szPersist == MSIDBOPEN_READONLY )
93 r = StgOpenStorage( szDBPath, NULL,
94 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
96 else if( szPersist == MSIDBOPEN_CREATE || szPersist == MSIDBOPEN_CREATEDIRECT )
98 /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
100 r = StgCreateDocfile( szDBPath,
101 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg);
102 if( r == ERROR_SUCCESS )
104 IStorage_SetClass( stg, &CLSID_MsiDatabase );
105 r = init_string_table( stg );
108 else if( szPersist == MSIDBOPEN_TRANSACT )
110 /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
112 r = StgOpenStorage( szDBPath, NULL,
113 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
115 else if( szPersist == MSIDBOPEN_DIRECT )
117 r = StgOpenStorage( szDBPath, NULL,
118 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg);
122 ERR("unknown flag %p\n",szPersist);
123 return ERROR_INVALID_PARAMETER;
128 FIXME("open failed r = %08lx!\n",r);
129 return ERROR_FUNCTION_FAILED;
132 r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
135 FIXME("Failed to stat storage\n");
139 if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) &&
140 !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) )
142 ERR("storage GUID is not a MSI database GUID %s\n",
143 debugstr_guid(&stat.clsid) );
147 db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE),
151 FIXME("Failed to allocate a handle\n");
155 if( TRACE_ON( msi ) )
156 enum_stream_names( stg );
160 list_init( &db->tables );
161 list_init( &db->transforms );
163 db->strings = load_string_table( stg );
169 msiobj_addref( &db->hdr );
170 IStorage_AddRef( stg );
175 msiobj_release( &db->hdr );
177 IStorage_Release( stg );
182 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
187 TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
189 ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db );
190 if( ret == ERROR_SUCCESS )
192 *phDB = alloc_msihandle( &db->hdr );
194 ret = ERROR_NOT_ENOUGH_MEMORY;
195 msiobj_release( &db->hdr );
201 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
203 HRESULT r = ERROR_FUNCTION_FAILED;
204 LPWSTR szwDBPath = NULL, szwPersist = NULL;
206 TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
210 szwDBPath = strdupAtoW( szDBPath );
215 if( HIWORD(szPersist) )
217 szwPersist = strdupAtoW( szPersist );
222 szwPersist = (LPWSTR)(DWORD_PTR)szPersist;
224 r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
227 if( HIWORD(szPersist) )
228 msi_free( szwPersist );
229 msi_free( szwDBPath );
234 UINT MSI_DatabaseImport( MSIDATABASE *db, LPCWSTR folder, LPCWSTR file )
236 FIXME("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) );
238 if( folder == NULL || file == NULL )
239 return ERROR_INVALID_PARAMETER;
241 return ERROR_CALL_NOT_IMPLEMENTED;
244 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename)
249 TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename));
251 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
253 return ERROR_INVALID_HANDLE;
254 r = MSI_DatabaseImport( db, szFolder, szFilename );
255 msiobj_release( &db->hdr );
259 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle,
260 LPCSTR szFolder, LPCSTR szFilename )
262 LPWSTR path = NULL, file = NULL;
263 UINT r = ERROR_OUTOFMEMORY;
265 TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename));
269 path = strdupAtoW( szFolder );
276 file = strdupAtoW( szFilename );
281 r = MsiDatabaseImportW( handle, path, file );
290 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start )
292 UINT i, count, len, r = ERROR_SUCCESS;
298 buffer = msi_alloc( len );
300 return ERROR_OUTOFMEMORY;
302 count = MSI_RecordGetFieldCount( row );
303 for ( i=start; i<=count; i++ )
306 r = MSI_RecordGetStringA( row, i, buffer, &sz );
307 if (r == ERROR_MORE_DATA)
309 char *p = msi_realloc( buffer, sz + 1 );
316 r = MSI_RecordGetStringA( row, i, buffer, &sz );
317 if (r != ERROR_SUCCESS)
320 if (!WriteFile( handle, buffer, sz, &sz, NULL ))
322 r = ERROR_FUNCTION_FAILED;
326 sep = (i < count) ? "\t" : "\r\n";
327 if (!WriteFile( handle, sep, strlen(sep), &sz, NULL ))
329 r = ERROR_FUNCTION_FAILED;
337 static UINT msi_export_row( MSIRECORD *row, void *arg )
339 return msi_export_record( arg, row, 1 );
342 UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table,
343 LPCWSTR folder, LPCWSTR file )
345 static const WCHAR query[] = {
346 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
347 static const WCHAR szbs[] = { '\\', 0 };
348 MSIRECORD *rec = NULL;
349 MSIQUERY *view = NULL;
354 TRACE("%p %s %s %s\n", db, debugstr_w(table),
355 debugstr_w(folder), debugstr_w(file) );
357 if( folder == NULL || file == NULL )
358 return ERROR_INVALID_PARAMETER;
360 len = lstrlenW(folder) + lstrlenW(file) + 2;
361 filename = msi_alloc(len * sizeof (WCHAR));
363 return ERROR_OUTOFMEMORY;
365 lstrcpyW( filename, folder );
366 lstrcatW( filename, szbs );
367 lstrcatW( filename, file );
369 handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0,
370 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
371 msi_free( filename );
372 if (handle == INVALID_HANDLE_VALUE)
373 return ERROR_FUNCTION_FAILED;
375 r = MSI_OpenQuery( db, &view, query, table );
376 if (r == ERROR_SUCCESS)
378 /* write out row 1, the column names */
379 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec);
380 if (r == ERROR_SUCCESS)
382 msi_export_record( handle, rec, 1 );
383 msiobj_release( &rec->hdr );
386 /* write out row 2, the column types */
387 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec);
388 if (r == ERROR_SUCCESS)
390 msi_export_record( handle, rec, 1 );
391 msiobj_release( &rec->hdr );
394 /* write out row 3, the table name + keys */
395 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
396 if (r == ERROR_SUCCESS)
398 MSI_RecordSetStringW( rec, 0, table );
399 msi_export_record( handle, rec, 0 );
400 msiobj_release( &rec->hdr );
403 /* write out row 4 onwards, the data */
404 r = MSI_IterateRecords( view, 0, msi_export_row, handle );
405 msiobj_release( &view->hdr );
408 CloseHandle( handle );
413 /***********************************************************************
414 * MsiExportDatabaseW [MSI.@]
416 * Writes a file containing the table data as tab separated ASCII.
418 * The format is as follows:
420 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
421 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
422 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
424 * Followed by the data, starting at row 1 with one row per line
426 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
428 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable,
429 LPCWSTR szFolder, LPCWSTR szFilename )
434 TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable),
435 debugstr_w(szFolder), debugstr_w(szFilename));
437 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
439 return ERROR_INVALID_HANDLE;
440 r = MSI_DatabaseExport( db, szTable, szFolder, szFilename );
441 msiobj_release( &db->hdr );
445 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable,
446 LPCSTR szFolder, LPCSTR szFilename )
448 LPWSTR path = NULL, file = NULL, table = NULL;
449 UINT r = ERROR_OUTOFMEMORY;
451 TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable),
452 debugstr_a(szFolder), debugstr_a(szFilename));
456 table = strdupAtoW( szTable );
463 path = strdupAtoW( szFolder );
470 file = strdupAtoW( szFilename );
475 r = MsiDatabaseExportW( handle, table, path, file );
485 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle )
487 MSIDBSTATE ret = MSIDBSTATE_READ;
490 TRACE("%ld\n", handle);
492 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
494 return MSIDBSTATE_ERROR;
495 if (db->mode != MSIDBOPEN_READONLY )
496 ret = MSIDBSTATE_WRITE;
497 msiobj_release( &db->hdr );