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"
40 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
42 DEFINE_GUID( CLSID_MsiDatabase
, 0x000c1084, 0x0000, 0x0000,
43 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
44 DEFINE_GUID( CLSID_MsiPatch
, 0x000c1086, 0x0000, 0x0000,
45 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
50 * An .msi file is a structured storage file.
51 * It contains a number of streams.
52 * A stream for each table in the database.
53 * Two streams for the string table in the database.
54 * Any binary data in a table is a reference to a stream.
57 static VOID
MSI_CloseDatabase( MSIOBJECTHDR
*arg
)
59 MSIDATABASE
*db
= (MSIDATABASE
*) arg
;
63 free_cached_tables( db
);
64 msi_free_transforms( db
);
65 msi_destroy_stringtable( db
->strings
);
66 r
= IStorage_Release( db
->storage
);
68 ERR("database reference count was not zero (%d)\n", r
);
71 DeleteFileW( db
->deletefile
);
72 msi_free( db
->deletefile
);
76 UINT
MSI_OpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIDATABASE
**pdb
)
80 MSIDATABASE
*db
= NULL
;
81 UINT ret
= ERROR_FUNCTION_FAILED
;
82 LPCWSTR szMode
, save_path
;
87 static const WCHAR backslash
[] = {'\\',0};
89 TRACE("%s %s\n",debugstr_w(szDBPath
),debugstr_w(szPersist
) );
92 return ERROR_INVALID_PARAMETER
;
96 if( HIWORD( szPersist
) )
98 if (!CopyFileW( szDBPath
, szPersist
, FALSE
))
99 return ERROR_OPEN_FAILED
;
101 szDBPath
= szPersist
;
102 szPersist
= MSIDBOPEN_TRANSACT
;
106 if( szPersist
== MSIDBOPEN_READONLY
)
108 r
= StgOpenStorage( szDBPath
, NULL
,
109 STGM_DIRECT
|STGM_READ
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
111 else if( szPersist
== MSIDBOPEN_CREATE
|| szPersist
== MSIDBOPEN_CREATEDIRECT
)
113 /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
115 r
= StgCreateDocfile( szDBPath
,
116 STGM_CREATE
|STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, 0, &stg
);
117 if( r
== ERROR_SUCCESS
)
119 IStorage_SetClass( stg
, &CLSID_MsiDatabase
);
120 r
= init_string_table( stg
);
124 else if( szPersist
== MSIDBOPEN_TRANSACT
)
126 /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
128 r
= StgOpenStorage( szDBPath
, NULL
,
129 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
131 else if( szPersist
== MSIDBOPEN_DIRECT
)
133 r
= StgOpenStorage( szDBPath
, NULL
,
134 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
138 ERR("unknown flag %p\n",szPersist
);
139 return ERROR_INVALID_PARAMETER
;
144 FIXME("open failed r = %08x!\n",r
);
145 return ERROR_FUNCTION_FAILED
;
148 r
= IStorage_Stat( stg
, &stat
, STATFLAG_NONAME
);
151 FIXME("Failed to stat storage\n");
155 if ( !IsEqualGUID( &stat
.clsid
, &CLSID_MsiDatabase
) &&
156 !IsEqualGUID( &stat
.clsid
, &CLSID_MsiPatch
) )
158 ERR("storage GUID is not a MSI database GUID %s\n",
159 debugstr_guid(&stat
.clsid
) );
163 db
= alloc_msiobject( MSIHANDLETYPE_DATABASE
, sizeof (MSIDATABASE
),
167 FIXME("Failed to allocate a handle\n");
171 if (!strchrW( save_path
, '\\' ))
173 GetCurrentDirectoryW( MAX_PATH
, path
);
174 lstrcatW( path
, backslash
);
175 lstrcatW( path
, save_path
);
178 lstrcpyW( path
, save_path
);
180 db
->path
= strdupW( path
);
182 if( TRACE_ON( msi
) )
183 enum_stream_names( stg
);
188 db
->deletefile
= strdupW( szDBPath
);
190 db
->deletefile
= NULL
;
191 list_init( &db
->tables
);
192 list_init( &db
->transforms
);
194 db
->strings
= load_string_table( stg
);
200 msiobj_addref( &db
->hdr
);
201 IStorage_AddRef( stg
);
206 msiobj_release( &db
->hdr
);
208 IStorage_Release( stg
);
213 UINT WINAPI
MsiOpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIHANDLE
*phDB
)
218 TRACE("%s %s %p\n",debugstr_w(szDBPath
),debugstr_w(szPersist
), phDB
);
220 ret
= MSI_OpenDatabaseW( szDBPath
, szPersist
, &db
);
221 if( ret
== ERROR_SUCCESS
)
223 *phDB
= alloc_msihandle( &db
->hdr
);
225 ret
= ERROR_NOT_ENOUGH_MEMORY
;
226 msiobj_release( &db
->hdr
);
232 UINT WINAPI
MsiOpenDatabaseA(LPCSTR szDBPath
, LPCSTR szPersist
, MSIHANDLE
*phDB
)
234 HRESULT r
= ERROR_FUNCTION_FAILED
;
235 LPWSTR szwDBPath
= NULL
, szwPersist
= NULL
;
237 TRACE("%s %s %p\n", debugstr_a(szDBPath
), debugstr_a(szPersist
), phDB
);
241 szwDBPath
= strdupAtoW( szDBPath
);
246 if( HIWORD(szPersist
) )
248 szwPersist
= strdupAtoW( szPersist
);
253 szwPersist
= (LPWSTR
)(DWORD_PTR
)szPersist
;
255 r
= MsiOpenDatabaseW( szwDBPath
, szwPersist
, phDB
);
258 if( HIWORD(szPersist
) )
259 msi_free( szwPersist
);
260 msi_free( szwDBPath
);
265 static LPWSTR
msi_read_text_archive(LPCWSTR path
)
270 DWORD read
, size
= 0;
272 file
= CreateFileW( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
273 if (file
== INVALID_HANDLE_VALUE
)
276 size
= GetFileSize( file
, NULL
);
277 data
= msi_alloc( size
+ 1 );
281 if (!ReadFile( file
, data
, size
, &read
, NULL
))
285 wdata
= strdupAtoW( data
);
293 static void msi_parse_line(LPWSTR
*line
, LPWSTR
**entries
, DWORD
*num_entries
)
295 LPWSTR ptr
= *line
, save
;
300 /* stay on this line */
301 while (*ptr
&& *ptr
!= '\n')
303 /* entries are separated by tabs */
310 *entries
= msi_alloc(count
* sizeof(LPWSTR
));
314 /* store pointers into the data */
315 for (i
= 0, ptr
= *line
; i
< count
; i
++)
319 while (*ptr
&& *ptr
!= '\t' && *ptr
!= '\n') ptr
++;
321 /* NULL-separate the data */
325 (*entries
)[i
] = save
;
328 /* move to the next line if there's more, else EOF */
332 *num_entries
= count
;
335 static LPWSTR
msi_build_createsql_prelude(LPWSTR table
)
340 static const WCHAR create_fmt
[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
342 size
= sizeof(create_fmt
) + lstrlenW(table
) - 2;
343 prelude
= msi_alloc(size
* sizeof(WCHAR
));
347 sprintfW(prelude
, create_fmt
, table
);
351 static LPWSTR
msi_build_createsql_columns(LPWSTR
*columns_data
, LPWSTR
*types
, DWORD num_columns
)
355 DWORD sql_size
= 1, i
, len
;
356 WCHAR expanded
[128], *ptr
;
357 WCHAR size
[10], comma
[2], extra
[10];
359 static const WCHAR column_fmt
[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
360 static const WCHAR size_fmt
[] = {'(','%','s',')',0};
361 static const WCHAR type_char
[] = {'C','H','A','R',0};
362 static const WCHAR type_int
[] = {'I','N','T',0};
363 static const WCHAR type_long
[] = {'L','O','N','G',0};
364 static const WCHAR type_notnull
[] = {' ','N','O','T',' ','N','U','L','L',0};
366 columns
= msi_alloc_zero(sql_size
* sizeof(WCHAR
));
370 for (i
= 0; i
< num_columns
; i
++)
373 comma
[1] = size
[0] = extra
[0] = '\0';
375 if (i
== num_columns
- 1)
386 lstrcpyW(extra
, type_notnull
);
389 sprintfW(size
, size_fmt
, ptr
);
399 sprintfW(expanded
, column_fmt
, columns_data
[i
], type
, size
, extra
, comma
);
400 sql_size
+= lstrlenW(expanded
);
402 columns
= msi_realloc(columns
, sql_size
* sizeof(WCHAR
));
406 lstrcatW(columns
, expanded
);
412 static LPWSTR
msi_build_createsql_postlude(LPWSTR primary_key
)
417 static const WCHAR postlude_fmt
[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','`','%','s','`',')',' ','H','O','L','D',0};
419 size
= sizeof(postlude_fmt
) + lstrlenW(primary_key
) - 2;
420 postlude
= msi_alloc(size
* sizeof(WCHAR
));
424 sprintfW(postlude
, postlude_fmt
, primary_key
);
428 static UINT
msi_add_table_to_db(MSIDATABASE
*db
, LPWSTR
*columns
, LPWSTR
*types
, LPWSTR
*labels
, DWORD num_columns
)
434 LPWSTR prelude
, columns_sql
, postlude
;
436 prelude
= msi_build_createsql_prelude(labels
[0]);
437 columns_sql
= msi_build_createsql_columns(columns
, types
, num_columns
);
438 postlude
= msi_build_createsql_postlude(labels
[1]);
440 if (!prelude
|| !columns_sql
|| !postlude
)
441 return ERROR_OUTOFMEMORY
;
443 size
= lstrlenW(prelude
) + lstrlenW(columns_sql
) + lstrlenW(postlude
) + 1;
444 create_sql
= msi_alloc(size
* sizeof(WCHAR
));
446 return ERROR_OUTOFMEMORY
;
448 lstrcpyW(create_sql
, prelude
);
449 lstrcatW(create_sql
, columns_sql
);
450 lstrcatW(create_sql
, postlude
);
453 msi_free(columns_sql
);
456 r
= MSI_DatabaseOpenViewW( db
, create_sql
, &view
);
457 msi_free(create_sql
);
459 if (r
!= ERROR_SUCCESS
)
462 r
= MSI_ViewExecute(view
, NULL
);
464 msiobj_release(&view
->hdr
);
469 static LPWSTR
msi_build_insertsql_prelude(LPWSTR table
)
474 static const WCHAR insert_fmt
[] = {'I','N','S','E','R','T',' ','I','N','T','O',' ','`','%','s','`',' ','(',' ',0};
476 size
= sizeof(insert_fmt
) + lstrlenW(table
) - 2;
477 prelude
= msi_alloc(size
* sizeof(WCHAR
));
481 sprintfW(prelude
, insert_fmt
, table
);
485 static LPWSTR
msi_build_insertsql_columns(LPWSTR
*columns_data
, LPWSTR
*types
, DWORD num_columns
)
488 DWORD sql_size
= 1, i
;
491 static const WCHAR column_fmt
[] = {'`','%','s','`',',',' ',0};
493 columns
= msi_alloc_zero(sql_size
* sizeof(WCHAR
));
497 for (i
= 0; i
< num_columns
; i
++)
499 sprintfW(expanded
, column_fmt
, columns_data
[i
]);
500 sql_size
+= lstrlenW(expanded
);
502 if (i
== num_columns
- 1)
505 expanded
[lstrlenW(expanded
) - 2] = '\0';
508 columns
= msi_realloc(columns
, sql_size
* sizeof(WCHAR
));
512 lstrcatW(columns
, expanded
);
518 static LPWSTR
msi_build_insertsql_data(LPWSTR
**records
, LPWSTR
*types
, DWORD num_columns
, DWORD irec
)
521 DWORD sql_size
= 1, i
;
524 static const WCHAR str_fmt
[] = {'\'','%','s','\'',',',' ',0};
525 static const WCHAR int_fmt
[] = {'%','s',',',' ',0};
526 static const WCHAR empty
[] = {'\'','\'',',',' ',0};
528 columns
= msi_alloc_zero(sql_size
* sizeof(WCHAR
));
532 for (i
= 0; i
< num_columns
; i
++)
536 case 'L': case 'l': case 'S': case 's':
537 sprintfW(expanded
, str_fmt
, records
[irec
][i
]);
541 sprintfW(expanded
, int_fmt
, records
[irec
][i
]);
543 lstrcpyW(expanded
, empty
);
549 if (i
== num_columns
- 1)
550 expanded
[lstrlenW(expanded
) - 2] = '\0';
552 sql_size
+= lstrlenW(expanded
);
553 columns
= msi_realloc(columns
, sql_size
* sizeof(WCHAR
));
557 lstrcatW(columns
, expanded
);
563 static UINT
msi_add_records_to_table(MSIDATABASE
*db
, LPWSTR
*columns
, LPWSTR
*types
,
564 LPWSTR
*labels
, LPWSTR
**records
,
565 int num_columns
, int num_records
)
570 UINT r
= ERROR_SUCCESS
;
572 static const WCHAR mid
[] = {' ',')',' ','V','A','L','U','E','S',' ','(',' ',0};
573 static const WCHAR end
[] = {' ',')',0};
575 LPWSTR prelude
= msi_build_insertsql_prelude(labels
[0]);
576 LPWSTR columns_sql
= msi_build_insertsql_columns(columns
, types
, num_columns
);
578 for (i
= 0; i
< num_records
; i
++)
580 LPWSTR data
= msi_build_insertsql_data(records
, types
, num_columns
, i
);
582 size
= lstrlenW(prelude
) + lstrlenW(columns_sql
) + sizeof(mid
) + lstrlenW(data
) + sizeof(end
) - 1;
583 insert_sql
= msi_alloc(size
* sizeof(WCHAR
));
585 return ERROR_OUTOFMEMORY
;
587 lstrcpyW(insert_sql
, prelude
);
588 lstrcatW(insert_sql
, columns_sql
);
589 lstrcatW(insert_sql
, mid
);
590 lstrcatW(insert_sql
, data
);
591 lstrcatW(insert_sql
, end
);
595 r
= MSI_DatabaseOpenViewW( db
, insert_sql
, &view
);
596 msi_free(insert_sql
);
598 if (r
!= ERROR_SUCCESS
)
601 r
= MSI_ViewExecute(view
, NULL
);
603 msiobj_release(&view
->hdr
);
608 msi_free(columns_sql
);
613 UINT
MSI_DatabaseImport(MSIDATABASE
*db
, LPCWSTR folder
, LPCWSTR file
)
617 DWORD num_columns
, num_records
= 0;
618 LPWSTR
*columns
, *types
, *labels
;
619 LPWSTR path
, ptr
, data
;
622 static const WCHAR backslash
[] = {'\\',0};
624 TRACE("%p %s %s\n", db
, debugstr_w(folder
), debugstr_w(file
) );
626 if( folder
== NULL
|| file
== NULL
)
627 return ERROR_INVALID_PARAMETER
;
629 len
= lstrlenW(folder
) + lstrlenW(backslash
) + lstrlenW(file
) + 1;
630 path
= msi_alloc( len
* sizeof(WCHAR
) );
632 return ERROR_OUTOFMEMORY
;
634 lstrcpyW( path
, folder
);
635 lstrcatW( path
, backslash
);
636 lstrcatW( path
, file
);
638 data
= msi_read_text_archive( path
);
641 msi_parse_line( &ptr
, &columns
, &num_columns
);
642 msi_parse_line( &ptr
, &types
, NULL
);
643 msi_parse_line( &ptr
, &labels
, NULL
);
645 records
= msi_alloc(sizeof(LPWSTR
*));
647 return ERROR_OUTOFMEMORY
;
649 /* read in the table records */
652 msi_parse_line( &ptr
, &records
[num_records
], NULL
);
655 records
= msi_realloc(records
, (num_records
+ 1) * sizeof(LPWSTR
*));
657 return ERROR_OUTOFMEMORY
;
660 r
= msi_add_table_to_db( db
, columns
, types
, labels
, num_columns
);
661 if (r
!= ERROR_SUCCESS
)
664 r
= msi_add_records_to_table( db
, columns
, types
, labels
, records
, num_columns
, num_records
);
673 for (i
= 0; i
< num_records
; i
++)
674 msi_free(records
[i
]);
681 UINT WINAPI
MsiDatabaseImportW(MSIHANDLE handle
, LPCWSTR szFolder
, LPCWSTR szFilename
)
686 TRACE("%lx %s %s\n",handle
,debugstr_w(szFolder
), debugstr_w(szFilename
));
688 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
690 return ERROR_INVALID_HANDLE
;
691 r
= MSI_DatabaseImport( db
, szFolder
, szFilename
);
692 msiobj_release( &db
->hdr
);
696 UINT WINAPI
MsiDatabaseImportA( MSIHANDLE handle
,
697 LPCSTR szFolder
, LPCSTR szFilename
)
699 LPWSTR path
= NULL
, file
= NULL
;
700 UINT r
= ERROR_OUTOFMEMORY
;
702 TRACE("%lx %s %s\n", handle
, debugstr_a(szFolder
), debugstr_a(szFilename
));
706 path
= strdupAtoW( szFolder
);
713 file
= strdupAtoW( szFilename
);
718 r
= MsiDatabaseImportW( handle
, path
, file
);
727 static UINT
msi_export_record( HANDLE handle
, MSIRECORD
*row
, UINT start
)
729 UINT i
, count
, len
, r
= ERROR_SUCCESS
;
735 buffer
= msi_alloc( len
);
737 return ERROR_OUTOFMEMORY
;
739 count
= MSI_RecordGetFieldCount( row
);
740 for ( i
=start
; i
<=count
; i
++ )
743 r
= MSI_RecordGetStringA( row
, i
, buffer
, &sz
);
744 if (r
== ERROR_MORE_DATA
)
746 char *p
= msi_realloc( buffer
, sz
+ 1 );
753 r
= MSI_RecordGetStringA( row
, i
, buffer
, &sz
);
754 if (r
!= ERROR_SUCCESS
)
757 if (!WriteFile( handle
, buffer
, sz
, &sz
, NULL
))
759 r
= ERROR_FUNCTION_FAILED
;
763 sep
= (i
< count
) ? "\t" : "\r\n";
764 if (!WriteFile( handle
, sep
, strlen(sep
), &sz
, NULL
))
766 r
= ERROR_FUNCTION_FAILED
;
774 static UINT
msi_export_row( MSIRECORD
*row
, void *arg
)
776 return msi_export_record( arg
, row
, 1 );
779 UINT
MSI_DatabaseExport( MSIDATABASE
*db
, LPCWSTR table
,
780 LPCWSTR folder
, LPCWSTR file
)
782 static const WCHAR query
[] = {
783 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
784 static const WCHAR szbs
[] = { '\\', 0 };
785 MSIRECORD
*rec
= NULL
;
786 MSIQUERY
*view
= NULL
;
791 TRACE("%p %s %s %s\n", db
, debugstr_w(table
),
792 debugstr_w(folder
), debugstr_w(file
) );
794 if( folder
== NULL
|| file
== NULL
)
795 return ERROR_INVALID_PARAMETER
;
797 len
= lstrlenW(folder
) + lstrlenW(file
) + 2;
798 filename
= msi_alloc(len
* sizeof (WCHAR
));
800 return ERROR_OUTOFMEMORY
;
802 lstrcpyW( filename
, folder
);
803 lstrcatW( filename
, szbs
);
804 lstrcatW( filename
, file
);
806 handle
= CreateFileW( filename
, GENERIC_READ
| GENERIC_WRITE
, 0,
807 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
808 msi_free( filename
);
809 if (handle
== INVALID_HANDLE_VALUE
)
810 return ERROR_FUNCTION_FAILED
;
812 r
= MSI_OpenQuery( db
, &view
, query
, table
);
813 if (r
== ERROR_SUCCESS
)
815 /* write out row 1, the column names */
816 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &rec
);
817 if (r
== ERROR_SUCCESS
)
819 msi_export_record( handle
, rec
, 1 );
820 msiobj_release( &rec
->hdr
);
823 /* write out row 2, the column types */
824 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_TYPES
, &rec
);
825 if (r
== ERROR_SUCCESS
)
827 msi_export_record( handle
, rec
, 1 );
828 msiobj_release( &rec
->hdr
);
831 /* write out row 3, the table name + keys */
832 r
= MSI_DatabaseGetPrimaryKeys( db
, table
, &rec
);
833 if (r
== ERROR_SUCCESS
)
835 MSI_RecordSetStringW( rec
, 0, table
);
836 msi_export_record( handle
, rec
, 0 );
837 msiobj_release( &rec
->hdr
);
840 /* write out row 4 onwards, the data */
841 r
= MSI_IterateRecords( view
, 0, msi_export_row
, handle
);
842 msiobj_release( &view
->hdr
);
845 CloseHandle( handle
);
850 /***********************************************************************
851 * MsiExportDatabaseW [MSI.@]
853 * Writes a file containing the table data as tab separated ASCII.
855 * The format is as follows:
857 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
858 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
859 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
861 * Followed by the data, starting at row 1 with one row per line
863 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
865 UINT WINAPI
MsiDatabaseExportW( MSIHANDLE handle
, LPCWSTR szTable
,
866 LPCWSTR szFolder
, LPCWSTR szFilename
)
871 TRACE("%lx %s %s %s\n", handle
, debugstr_w(szTable
),
872 debugstr_w(szFolder
), debugstr_w(szFilename
));
874 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
876 return ERROR_INVALID_HANDLE
;
877 r
= MSI_DatabaseExport( db
, szTable
, szFolder
, szFilename
);
878 msiobj_release( &db
->hdr
);
882 UINT WINAPI
MsiDatabaseExportA( MSIHANDLE handle
, LPCSTR szTable
,
883 LPCSTR szFolder
, LPCSTR szFilename
)
885 LPWSTR path
= NULL
, file
= NULL
, table
= NULL
;
886 UINT r
= ERROR_OUTOFMEMORY
;
888 TRACE("%lx %s %s %s\n", handle
, debugstr_a(szTable
),
889 debugstr_a(szFolder
), debugstr_a(szFilename
));
893 table
= strdupAtoW( szTable
);
900 path
= strdupAtoW( szFolder
);
907 file
= strdupAtoW( szFilename
);
912 r
= MsiDatabaseExportW( handle
, table
, path
, file
);
922 MSIDBSTATE WINAPI
MsiGetDatabaseState( MSIHANDLE handle
)
924 MSIDBSTATE ret
= MSIDBSTATE_READ
;
927 TRACE("%ld\n", handle
);
929 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
931 return MSIDBSTATE_ERROR
;
932 if (db
->mode
!= MSIDBOPEN_READONLY
)
933 ret
= MSIDBSTATE_WRITE
;
934 msiobj_release( &db
->hdr
);