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 DeleteFileW( db
->deletefile
);
70 msi_free( db
->deletefile
);
74 UINT
MSI_OpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIDATABASE
**pdb
)
78 MSIDATABASE
*db
= NULL
;
79 UINT ret
= ERROR_FUNCTION_FAILED
;
84 TRACE("%s %s\n",debugstr_w(szDBPath
),debugstr_w(szPersist
) );
87 return ERROR_INVALID_PARAMETER
;
90 if( HIWORD( szPersist
) )
92 if (!CopyFileW( szDBPath
, szPersist
, FALSE
))
93 return ERROR_OPEN_FAILED
;
96 szPersist
= MSIDBOPEN_TRANSACT
;
100 if( szPersist
== MSIDBOPEN_READONLY
)
102 r
= StgOpenStorage( szDBPath
, NULL
,
103 STGM_DIRECT
|STGM_READ
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
105 else if( szPersist
== MSIDBOPEN_CREATE
|| szPersist
== MSIDBOPEN_CREATEDIRECT
)
107 /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
109 r
= StgCreateDocfile( szDBPath
,
110 STGM_CREATE
|STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, 0, &stg
);
111 if( r
== ERROR_SUCCESS
)
113 IStorage_SetClass( stg
, &CLSID_MsiDatabase
);
114 r
= init_string_table( stg
);
118 else if( szPersist
== MSIDBOPEN_TRANSACT
)
120 /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
122 r
= StgOpenStorage( szDBPath
, NULL
,
123 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
125 else if( szPersist
== MSIDBOPEN_DIRECT
)
127 r
= StgOpenStorage( szDBPath
, NULL
,
128 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
132 ERR("unknown flag %p\n",szPersist
);
133 return ERROR_INVALID_PARAMETER
;
138 FIXME("open failed r = %08lx!\n",r
);
139 return ERROR_FUNCTION_FAILED
;
142 r
= IStorage_Stat( stg
, &stat
, STATFLAG_NONAME
);
145 FIXME("Failed to stat storage\n");
149 if ( !IsEqualGUID( &stat
.clsid
, &CLSID_MsiDatabase
) &&
150 !IsEqualGUID( &stat
.clsid
, &CLSID_MsiPatch
) )
152 ERR("storage GUID is not a MSI database GUID %s\n",
153 debugstr_guid(&stat
.clsid
) );
157 db
= alloc_msiobject( MSIHANDLETYPE_DATABASE
, sizeof (MSIDATABASE
),
161 FIXME("Failed to allocate a handle\n");
165 if( TRACE_ON( msi
) )
166 enum_stream_names( stg
);
171 db
->deletefile
= strdupW( szDBPath
);
173 db
->deletefile
= NULL
;
174 list_init( &db
->tables
);
175 list_init( &db
->transforms
);
177 db
->strings
= load_string_table( stg
);
183 msiobj_addref( &db
->hdr
);
184 IStorage_AddRef( stg
);
189 msiobj_release( &db
->hdr
);
191 IStorage_Release( stg
);
196 UINT WINAPI
MsiOpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIHANDLE
*phDB
)
201 TRACE("%s %s %p\n",debugstr_w(szDBPath
),debugstr_w(szPersist
), phDB
);
203 ret
= MSI_OpenDatabaseW( szDBPath
, szPersist
, &db
);
204 if( ret
== ERROR_SUCCESS
)
206 *phDB
= alloc_msihandle( &db
->hdr
);
208 ret
= ERROR_NOT_ENOUGH_MEMORY
;
209 msiobj_release( &db
->hdr
);
215 UINT WINAPI
MsiOpenDatabaseA(LPCSTR szDBPath
, LPCSTR szPersist
, MSIHANDLE
*phDB
)
217 HRESULT r
= ERROR_FUNCTION_FAILED
;
218 LPWSTR szwDBPath
= NULL
, szwPersist
= NULL
;
220 TRACE("%s %s %p\n", debugstr_a(szDBPath
), debugstr_a(szPersist
), phDB
);
224 szwDBPath
= strdupAtoW( szDBPath
);
229 if( HIWORD(szPersist
) )
231 szwPersist
= strdupAtoW( szPersist
);
236 szwPersist
= (LPWSTR
)(DWORD_PTR
)szPersist
;
238 r
= MsiOpenDatabaseW( szwDBPath
, szwPersist
, phDB
);
241 if( HIWORD(szPersist
) )
242 msi_free( szwPersist
);
243 msi_free( szwDBPath
);
248 UINT
MSI_DatabaseImport( MSIDATABASE
*db
, LPCWSTR folder
, LPCWSTR file
)
250 FIXME("%p %s %s\n", db
, debugstr_w(folder
), debugstr_w(file
) );
252 if( folder
== NULL
|| file
== NULL
)
253 return ERROR_INVALID_PARAMETER
;
255 return ERROR_CALL_NOT_IMPLEMENTED
;
258 UINT WINAPI
MsiDatabaseImportW(MSIHANDLE handle
, LPCWSTR szFolder
, LPCWSTR szFilename
)
263 TRACE("%lx %s %s\n",handle
,debugstr_w(szFolder
), debugstr_w(szFilename
));
265 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
267 return ERROR_INVALID_HANDLE
;
268 r
= MSI_DatabaseImport( db
, szFolder
, szFilename
);
269 msiobj_release( &db
->hdr
);
273 UINT WINAPI
MsiDatabaseImportA( MSIHANDLE handle
,
274 LPCSTR szFolder
, LPCSTR szFilename
)
276 LPWSTR path
= NULL
, file
= NULL
;
277 UINT r
= ERROR_OUTOFMEMORY
;
279 TRACE("%lx %s %s\n", handle
, debugstr_a(szFolder
), debugstr_a(szFilename
));
283 path
= strdupAtoW( szFolder
);
290 file
= strdupAtoW( szFilename
);
295 r
= MsiDatabaseImportW( handle
, path
, file
);
304 static UINT
msi_export_record( HANDLE handle
, MSIRECORD
*row
, UINT start
)
306 UINT i
, count
, len
, r
= ERROR_SUCCESS
;
312 buffer
= msi_alloc( len
);
314 return ERROR_OUTOFMEMORY
;
316 count
= MSI_RecordGetFieldCount( row
);
317 for ( i
=start
; i
<=count
; i
++ )
320 r
= MSI_RecordGetStringA( row
, i
, buffer
, &sz
);
321 if (r
== ERROR_MORE_DATA
)
323 char *p
= msi_realloc( buffer
, sz
+ 1 );
330 r
= MSI_RecordGetStringA( row
, i
, buffer
, &sz
);
331 if (r
!= ERROR_SUCCESS
)
334 if (!WriteFile( handle
, buffer
, sz
, &sz
, NULL
))
336 r
= ERROR_FUNCTION_FAILED
;
340 sep
= (i
< count
) ? "\t" : "\r\n";
341 if (!WriteFile( handle
, sep
, strlen(sep
), &sz
, NULL
))
343 r
= ERROR_FUNCTION_FAILED
;
351 static UINT
msi_export_row( MSIRECORD
*row
, void *arg
)
353 return msi_export_record( arg
, row
, 1 );
356 UINT
MSI_DatabaseExport( MSIDATABASE
*db
, LPCWSTR table
,
357 LPCWSTR folder
, LPCWSTR file
)
359 static const WCHAR query
[] = {
360 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
361 static const WCHAR szbs
[] = { '\\', 0 };
362 MSIRECORD
*rec
= NULL
;
363 MSIQUERY
*view
= NULL
;
368 TRACE("%p %s %s %s\n", db
, debugstr_w(table
),
369 debugstr_w(folder
), debugstr_w(file
) );
371 if( folder
== NULL
|| file
== NULL
)
372 return ERROR_INVALID_PARAMETER
;
374 len
= lstrlenW(folder
) + lstrlenW(file
) + 2;
375 filename
= msi_alloc(len
* sizeof (WCHAR
));
377 return ERROR_OUTOFMEMORY
;
379 lstrcpyW( filename
, folder
);
380 lstrcatW( filename
, szbs
);
381 lstrcatW( filename
, file
);
383 handle
= CreateFileW( filename
, GENERIC_READ
| GENERIC_WRITE
, 0,
384 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
385 msi_free( filename
);
386 if (handle
== INVALID_HANDLE_VALUE
)
387 return ERROR_FUNCTION_FAILED
;
389 r
= MSI_OpenQuery( db
, &view
, query
, table
);
390 if (r
== ERROR_SUCCESS
)
392 /* write out row 1, the column names */
393 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &rec
);
394 if (r
== ERROR_SUCCESS
)
396 msi_export_record( handle
, rec
, 1 );
397 msiobj_release( &rec
->hdr
);
400 /* write out row 2, the column types */
401 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_TYPES
, &rec
);
402 if (r
== ERROR_SUCCESS
)
404 msi_export_record( handle
, rec
, 1 );
405 msiobj_release( &rec
->hdr
);
408 /* write out row 3, the table name + keys */
409 r
= MSI_DatabaseGetPrimaryKeys( db
, table
, &rec
);
410 if (r
== ERROR_SUCCESS
)
412 MSI_RecordSetStringW( rec
, 0, table
);
413 msi_export_record( handle
, rec
, 0 );
414 msiobj_release( &rec
->hdr
);
417 /* write out row 4 onwards, the data */
418 r
= MSI_IterateRecords( view
, 0, msi_export_row
, handle
);
419 msiobj_release( &view
->hdr
);
422 CloseHandle( handle
);
427 /***********************************************************************
428 * MsiExportDatabaseW [MSI.@]
430 * Writes a file containing the table data as tab separated ASCII.
432 * The format is as follows:
434 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
435 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
436 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
438 * Followed by the data, starting at row 1 with one row per line
440 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
442 UINT WINAPI
MsiDatabaseExportW( MSIHANDLE handle
, LPCWSTR szTable
,
443 LPCWSTR szFolder
, LPCWSTR szFilename
)
448 TRACE("%lx %s %s %s\n", handle
, debugstr_w(szTable
),
449 debugstr_w(szFolder
), debugstr_w(szFilename
));
451 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
453 return ERROR_INVALID_HANDLE
;
454 r
= MSI_DatabaseExport( db
, szTable
, szFolder
, szFilename
);
455 msiobj_release( &db
->hdr
);
459 UINT WINAPI
MsiDatabaseExportA( MSIHANDLE handle
, LPCSTR szTable
,
460 LPCSTR szFolder
, LPCSTR szFilename
)
462 LPWSTR path
= NULL
, file
= NULL
, table
= NULL
;
463 UINT r
= ERROR_OUTOFMEMORY
;
465 TRACE("%lx %s %s %s\n", handle
, debugstr_a(szTable
),
466 debugstr_a(szFolder
), debugstr_a(szFilename
));
470 table
= strdupAtoW( szTable
);
477 path
= strdupAtoW( szFolder
);
484 file
= strdupAtoW( szFilename
);
489 r
= MsiDatabaseExportW( handle
, table
, path
, file
);
499 MSIDBSTATE WINAPI
MsiGetDatabaseState( MSIHANDLE handle
)
501 MSIDBSTATE ret
= MSIDBSTATE_READ
;
504 TRACE("%ld\n", handle
);
506 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
508 return MSIDBSTATE_ERROR
;
509 if (db
->mode
!= MSIDBOPEN_READONLY
)
510 ret
= MSIDBSTATE_WRITE
;
511 msiobj_release( &db
->hdr
);