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"
41 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
43 DEFINE_GUID( CLSID_MsiDatabase
, 0x000c1084, 0x0000, 0x0000,
44 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
45 DEFINE_GUID( CLSID_MsiPatch
, 0x000c1086, 0x0000, 0x0000,
46 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
51 * An .msi file is a structured storage file.
52 * It contains a number of streams.
53 * A stream for each table in the database.
54 * Two streams for the string table in the database.
55 * Any binary data in a table is a reference to a stream.
58 static VOID
MSI_CloseDatabase( MSIOBJECTHDR
*arg
)
60 MSIDATABASE
*db
= (MSIDATABASE
*) arg
;
63 free_cached_tables( db
);
64 msi_free_transforms( db
);
65 msi_destroy_stringtable( db
->strings
);
66 IStorage_Release( db
->storage
);
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
;
80 LPCWSTR szMode
, save_path
;
85 static const WCHAR backslash
[] = {'\\',0};
86 static const WCHAR szTables
[] = { '_','T','a','b','l','e','s',0 };
88 TRACE("%s %s\n",debugstr_w(szDBPath
),debugstr_w(szPersist
) );
91 return ERROR_INVALID_PARAMETER
;
93 if (szPersist
- MSIDBOPEN_PATCHFILE
>= MSIDBOPEN_READONLY
&&
94 szPersist
- MSIDBOPEN_PATCHFILE
<= MSIDBOPEN_CREATEDIRECT
)
96 TRACE("Database is a patch\n");
97 szPersist
-= MSIDBOPEN_PATCHFILE
;
100 save_path
= szDBPath
;
102 if( HIWORD( szPersist
) )
104 if (!CopyFileW( szDBPath
, szPersist
, FALSE
))
105 return ERROR_OPEN_FAILED
;
107 szDBPath
= szPersist
;
108 szPersist
= MSIDBOPEN_TRANSACT
;
112 if( szPersist
== MSIDBOPEN_READONLY
)
114 r
= StgOpenStorage( szDBPath
, NULL
,
115 STGM_DIRECT
|STGM_READ
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
117 else if( szPersist
== MSIDBOPEN_CREATE
|| szPersist
== MSIDBOPEN_CREATEDIRECT
)
119 /* FIXME: MSIDBOPEN_CREATE should case STGM_TRANSACTED flag to be
121 r
= StgCreateDocfile( szDBPath
,
122 STGM_CREATE
|STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, 0, &stg
);
123 if( r
== ERROR_SUCCESS
)
125 IStorage_SetClass( stg
, &CLSID_MsiDatabase
);
126 /* create the _Tables stream */
127 r
= write_stream_data(stg
, szTables
, NULL
, 0, TRUE
);
129 r
= msi_init_string_table( stg
);
133 else if( szPersist
== MSIDBOPEN_TRANSACT
)
135 /* FIXME: MSIDBOPEN_TRANSACT should case STGM_TRANSACTED flag to be
137 r
= StgOpenStorage( szDBPath
, NULL
,
138 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
140 else if( szPersist
== MSIDBOPEN_DIRECT
)
142 r
= StgOpenStorage( szDBPath
, NULL
,
143 STGM_DIRECT
|STGM_READWRITE
|STGM_SHARE_EXCLUSIVE
, NULL
, 0, &stg
);
147 ERR("unknown flag %p\n",szPersist
);
148 return ERROR_INVALID_PARAMETER
;
151 if( FAILED( r
) || !stg
)
153 FIXME("open failed r = %08x for %s\n", r
, debugstr_w(szDBPath
));
154 return ERROR_FUNCTION_FAILED
;
157 r
= IStorage_Stat( stg
, &stat
, STATFLAG_NONAME
);
160 FIXME("Failed to stat storage\n");
164 if ( !IsEqualGUID( &stat
.clsid
, &CLSID_MsiDatabase
) &&
165 !IsEqualGUID( &stat
.clsid
, &CLSID_MsiPatch
) )
167 ERR("storage GUID is not a MSI database GUID %s\n",
168 debugstr_guid(&stat
.clsid
) );
172 db
= alloc_msiobject( MSIHANDLETYPE_DATABASE
, sizeof (MSIDATABASE
),
176 FIXME("Failed to allocate a handle\n");
180 if (!strchrW( save_path
, '\\' ))
182 GetCurrentDirectoryW( MAX_PATH
, path
);
183 lstrcatW( path
, backslash
);
184 lstrcatW( path
, save_path
);
187 lstrcpyW( path
, save_path
);
189 db
->path
= strdupW( path
);
191 if( TRACE_ON( msi
) )
192 enum_stream_names( stg
);
197 db
->deletefile
= strdupW( szDBPath
);
199 db
->deletefile
= NULL
;
200 list_init( &db
->tables
);
201 list_init( &db
->transforms
);
203 db
->strings
= msi_load_string_table( stg
, &db
->bytes_per_strref
);
209 msiobj_addref( &db
->hdr
);
210 IStorage_AddRef( stg
);
215 msiobj_release( &db
->hdr
);
217 IStorage_Release( stg
);
222 UINT WINAPI
MsiOpenDatabaseW(LPCWSTR szDBPath
, LPCWSTR szPersist
, MSIHANDLE
*phDB
)
227 TRACE("%s %s %p\n",debugstr_w(szDBPath
),debugstr_w(szPersist
), phDB
);
229 ret
= MSI_OpenDatabaseW( szDBPath
, szPersist
, &db
);
230 if( ret
== ERROR_SUCCESS
)
232 *phDB
= alloc_msihandle( &db
->hdr
);
234 ret
= ERROR_NOT_ENOUGH_MEMORY
;
235 msiobj_release( &db
->hdr
);
241 UINT WINAPI
MsiOpenDatabaseA(LPCSTR szDBPath
, LPCSTR szPersist
, MSIHANDLE
*phDB
)
243 HRESULT r
= ERROR_FUNCTION_FAILED
;
244 LPWSTR szwDBPath
= NULL
, szwPersist
= NULL
;
246 TRACE("%s %s %p\n", debugstr_a(szDBPath
), debugstr_a(szPersist
), phDB
);
250 szwDBPath
= strdupAtoW( szDBPath
);
255 if( HIWORD(szPersist
) )
257 szwPersist
= strdupAtoW( szPersist
);
262 szwPersist
= (LPWSTR
)(DWORD_PTR
)szPersist
;
264 r
= MsiOpenDatabaseW( szwDBPath
, szwPersist
, phDB
);
267 if( HIWORD(szPersist
) )
268 msi_free( szwPersist
);
269 msi_free( szwDBPath
);
274 static LPWSTR
msi_read_text_archive(LPCWSTR path
)
279 DWORD read
, size
= 0;
281 file
= CreateFileW( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
282 if (file
== INVALID_HANDLE_VALUE
)
285 size
= GetFileSize( file
, NULL
);
286 data
= msi_alloc( size
+ 1 );
290 if (!ReadFile( file
, data
, size
, &read
, NULL
))
294 wdata
= strdupAtoW( data
);
302 static void msi_parse_line(LPWSTR
*line
, LPWSTR
**entries
, DWORD
*num_entries
)
304 LPWSTR ptr
= *line
, save
;
309 /* stay on this line */
310 while (*ptr
&& *ptr
!= '\n')
312 /* entries are separated by tabs */
319 *entries
= msi_alloc(count
* sizeof(LPWSTR
));
323 /* store pointers into the data */
324 for (i
= 0, ptr
= *line
; i
< count
; i
++)
326 while (*ptr
&& *ptr
== '\r') ptr
++;
329 while (*ptr
&& *ptr
!= '\t' && *ptr
!= '\n' && *ptr
!= '\r') ptr
++;
331 /* NULL-separate the data */
332 if (*ptr
== '\n' || *ptr
== '\r')
334 while (*ptr
== '\n' || *ptr
== '\r')
340 (*entries
)[i
] = save
;
343 /* move to the next line if there's more, else EOF */
347 *num_entries
= count
;
350 static LPWSTR
msi_build_createsql_prelude(LPWSTR table
)
355 static const WCHAR create_fmt
[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0};
357 size
= sizeof(create_fmt
)/sizeof(create_fmt
[0]) + lstrlenW(table
) - 2;
358 prelude
= msi_alloc(size
* sizeof(WCHAR
));
362 sprintfW(prelude
, create_fmt
, table
);
366 static LPWSTR
msi_build_createsql_columns(LPWSTR
*columns_data
, LPWSTR
*types
, DWORD num_columns
)
370 DWORD sql_size
= 1, i
, len
;
371 WCHAR expanded
[128], *ptr
;
372 WCHAR size
[10], comma
[2], extra
[30];
374 static const WCHAR column_fmt
[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0};
375 static const WCHAR size_fmt
[] = {'(','%','s',')',0};
376 static const WCHAR type_char
[] = {'C','H','A','R',0};
377 static const WCHAR type_int
[] = {'I','N','T',0};
378 static const WCHAR type_long
[] = {'L','O','N','G',0};
379 static const WCHAR type_notnull
[] = {' ','N','O','T',' ','N','U','L','L',0};
380 static const WCHAR localizable
[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0};
382 columns
= msi_alloc_zero(sql_size
* sizeof(WCHAR
));
386 for (i
= 0; i
< num_columns
; i
++)
389 comma
[1] = size
[0] = extra
[0] = '\0';
391 if (i
== num_columns
- 1)
403 lstrcpyW(extra
, type_notnull
);
405 lstrcatW(extra
, localizable
);
407 sprintfW(size
, size_fmt
, ptr
);
410 lstrcpyW(extra
, type_notnull
);
413 sprintfW(size
, size_fmt
, ptr
);
416 lstrcpyW(extra
, type_notnull
);
424 ERR("Unknown type: %c\n", types
[i
][0]);
429 sprintfW(expanded
, column_fmt
, columns_data
[i
], type
, size
, extra
, comma
);
430 sql_size
+= lstrlenW(expanded
);
432 p
= msi_realloc(columns
, sql_size
* sizeof(WCHAR
));
440 lstrcatW(columns
, expanded
);
446 static LPWSTR
msi_build_createsql_postlude(LPWSTR
*primary_keys
, DWORD num_keys
)
448 LPWSTR postlude
, keys
, ptr
;
449 DWORD size
, key_size
, i
;
451 static const WCHAR key_fmt
[] = {'`','%','s','`',',',' ',0};
452 static const WCHAR postlude_fmt
[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0};
454 for (i
= 0, size
= 1; i
< num_keys
; i
++)
455 size
+= lstrlenW(key_fmt
) + lstrlenW(primary_keys
[i
]) - 2;
457 keys
= msi_alloc(size
* sizeof(WCHAR
));
461 for (i
= 0, ptr
= keys
; i
< num_keys
; i
++)
463 key_size
= lstrlenW(key_fmt
) + lstrlenW(primary_keys
[i
]) -2;
464 sprintfW(ptr
, key_fmt
, primary_keys
[i
]);
468 /* remove final ', ' */
471 size
= lstrlenW(postlude_fmt
) + size
- 1;
472 postlude
= msi_alloc(size
* sizeof(WCHAR
));
476 sprintfW(postlude
, postlude_fmt
, keys
);
483 static UINT
msi_add_table_to_db(MSIDATABASE
*db
, LPWSTR
*columns
, LPWSTR
*types
, LPWSTR
*labels
, DWORD num_labels
, DWORD num_columns
)
489 LPWSTR prelude
, columns_sql
, postlude
;
491 prelude
= msi_build_createsql_prelude(labels
[0]);
492 columns_sql
= msi_build_createsql_columns(columns
, types
, num_columns
);
493 postlude
= msi_build_createsql_postlude(labels
+ 1, num_labels
- 1); /* skip over table name */
495 if (!prelude
|| !columns_sql
|| !postlude
)
496 return ERROR_OUTOFMEMORY
;
498 size
= lstrlenW(prelude
) + lstrlenW(columns_sql
) + lstrlenW(postlude
) + 1;
499 create_sql
= msi_alloc(size
* sizeof(WCHAR
));
501 return ERROR_OUTOFMEMORY
;
503 lstrcpyW(create_sql
, prelude
);
504 lstrcatW(create_sql
, columns_sql
);
505 lstrcatW(create_sql
, postlude
);
508 msi_free(columns_sql
);
511 r
= MSI_DatabaseOpenViewW( db
, create_sql
, &view
);
512 msi_free(create_sql
);
514 if (r
!= ERROR_SUCCESS
)
517 r
= MSI_ViewExecute(view
, NULL
);
519 msiobj_release(&view
->hdr
);
524 static UINT
construct_record(DWORD num_columns
, LPWSTR
*types
,
525 LPWSTR
*data
, MSIRECORD
**rec
)
529 *rec
= MSI_CreateRecord(num_columns
);
531 return ERROR_OUTOFMEMORY
;
533 for (i
= 0; i
< num_columns
; i
++)
537 case 'L': case 'l': case 'S': case 's':
538 MSI_RecordSetStringW(*rec
, i
+ 1, data
[i
]);
542 MSI_RecordSetInteger(*rec
, i
+ 1, atoiW(data
[i
]));
545 ERR("Unhandled column type: %c\n", types
[i
][0]);
546 msiobj_release(&(*rec
)->hdr
);
547 return ERROR_FUNCTION_FAILED
;
551 return ERROR_SUCCESS
;
554 static UINT
msi_add_records_to_table(MSIDATABASE
*db
, LPWSTR
*columns
, LPWSTR
*types
,
555 LPWSTR
*labels
, LPWSTR
**records
,
556 int num_columns
, int num_records
)
564 static const WCHAR select
[] = {
565 'S','E','L','E','C','T',' ','*',' ',
566 'F','R','O','M',' ','`','%','s','`',0
569 size
= lstrlenW(select
) + lstrlenW(labels
[0]) - 1;
570 query
= msi_alloc(size
* sizeof(WCHAR
));
572 return ERROR_OUTOFMEMORY
;
574 sprintfW(query
, select
, labels
[0]);
576 r
= MSI_DatabaseOpenViewW(db
, query
, &view
);
578 if (r
!= ERROR_SUCCESS
)
581 while (MSI_ViewFetch(view
, &rec
) != ERROR_NO_MORE_ITEMS
)
583 r
= MSI_ViewModify(view
, MSIMODIFY_DELETE
, rec
);
584 if (r
!= ERROR_SUCCESS
)
588 for (i
= 0; i
< num_records
; i
++)
590 r
= construct_record(num_columns
, types
, records
[i
], &rec
);
591 if (r
!= ERROR_SUCCESS
)
594 r
= MSI_ViewModify(view
, MSIMODIFY_INSERT
, rec
);
595 if (r
!= ERROR_SUCCESS
)
597 msiobj_release(&rec
->hdr
);
601 msiobj_release(&rec
->hdr
);
605 msiobj_release(&view
->hdr
);
609 UINT
MSI_DatabaseImport(MSIDATABASE
*db
, LPCWSTR folder
, LPCWSTR file
)
613 DWORD num_labels
, num_types
;
614 DWORD num_columns
, num_records
= 0;
615 LPWSTR
*columns
, *types
, *labels
;
616 LPWSTR path
, ptr
, data
;
617 LPWSTR
**records
= NULL
;
618 LPWSTR
**temp_records
;
620 static const WCHAR backslash
[] = {'\\',0};
622 TRACE("%p %s %s\n", db
, debugstr_w(folder
), debugstr_w(file
) );
624 if( folder
== NULL
|| file
== NULL
)
625 return ERROR_INVALID_PARAMETER
;
627 len
= lstrlenW(folder
) + lstrlenW(backslash
) + lstrlenW(file
) + 1;
628 path
= msi_alloc( len
* sizeof(WCHAR
) );
630 return ERROR_OUTOFMEMORY
;
632 lstrcpyW( path
, folder
);
633 lstrcatW( path
, backslash
);
634 lstrcatW( path
, file
);
636 data
= msi_read_text_archive( path
);
639 msi_parse_line( &ptr
, &columns
, &num_columns
);
640 msi_parse_line( &ptr
, &types
, &num_types
);
641 msi_parse_line( &ptr
, &labels
, &num_labels
);
643 if (num_columns
!= num_types
)
645 r
= ERROR_FUNCTION_FAILED
;
649 records
= msi_alloc(sizeof(LPWSTR
*));
652 r
= ERROR_OUTOFMEMORY
;
656 /* read in the table records */
659 msi_parse_line( &ptr
, &records
[num_records
], NULL
);
662 temp_records
= msi_realloc(records
, (num_records
+ 1) * sizeof(LPWSTR
*));
665 r
= ERROR_OUTOFMEMORY
;
668 records
= temp_records
;
671 if (!TABLE_Exists(db
, labels
[0]))
673 r
= msi_add_table_to_db( db
, columns
, types
, labels
, num_labels
, num_columns
);
674 if (r
!= ERROR_SUCCESS
)
676 r
= ERROR_FUNCTION_FAILED
;
681 r
= msi_add_records_to_table( db
, columns
, types
, labels
, records
, num_columns
, num_records
);
690 for (i
= 0; i
< num_records
; i
++)
691 msi_free(records
[i
]);
698 UINT WINAPI
MsiDatabaseImportW(MSIHANDLE handle
, LPCWSTR szFolder
, LPCWSTR szFilename
)
703 TRACE("%lx %s %s\n",handle
,debugstr_w(szFolder
), debugstr_w(szFilename
));
705 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
708 IWineMsiRemoteDatabase
*remote_database
;
710 remote_database
= (IWineMsiRemoteDatabase
*)msi_get_remote( handle
);
711 if ( !remote_database
)
712 return ERROR_INVALID_HANDLE
;
714 IWineMsiRemoteDatabase_Release( remote_database
);
715 WARN("MsiDatabaseImport not allowed during a custom action!\n");
717 return ERROR_SUCCESS
;
720 r
= MSI_DatabaseImport( db
, szFolder
, szFilename
);
721 msiobj_release( &db
->hdr
);
725 UINT WINAPI
MsiDatabaseImportA( MSIHANDLE handle
,
726 LPCSTR szFolder
, LPCSTR szFilename
)
728 LPWSTR path
= NULL
, file
= NULL
;
729 UINT r
= ERROR_OUTOFMEMORY
;
731 TRACE("%lx %s %s\n", handle
, debugstr_a(szFolder
), debugstr_a(szFilename
));
735 path
= strdupAtoW( szFolder
);
742 file
= strdupAtoW( szFilename
);
747 r
= MsiDatabaseImportW( handle
, path
, file
);
756 static UINT
msi_export_record( HANDLE handle
, MSIRECORD
*row
, UINT start
)
758 UINT i
, count
, len
, r
= ERROR_SUCCESS
;
764 buffer
= msi_alloc( len
);
766 return ERROR_OUTOFMEMORY
;
768 count
= MSI_RecordGetFieldCount( row
);
769 for ( i
=start
; i
<=count
; i
++ )
772 r
= MSI_RecordGetStringA( row
, i
, buffer
, &sz
);
773 if (r
== ERROR_MORE_DATA
)
775 char *p
= msi_realloc( buffer
, sz
+ 1 );
782 r
= MSI_RecordGetStringA( row
, i
, buffer
, &sz
);
783 if (r
!= ERROR_SUCCESS
)
786 if (!WriteFile( handle
, buffer
, sz
, &sz
, NULL
))
788 r
= ERROR_FUNCTION_FAILED
;
792 sep
= (i
< count
) ? "\t" : "\r\n";
793 if (!WriteFile( handle
, sep
, strlen(sep
), &sz
, NULL
))
795 r
= ERROR_FUNCTION_FAILED
;
803 static UINT
msi_export_row( MSIRECORD
*row
, void *arg
)
805 return msi_export_record( arg
, row
, 1 );
808 static UINT
msi_export_forcecodepage( HANDLE handle
)
812 static const char data
[] = "\r\n\r\n0\t_ForceCodepage\r\n";
814 FIXME("Read the codepage from the strings table!\n");
816 sz
= lstrlenA(data
) + 1;
817 if (!WriteFile(handle
, data
, sz
, &sz
, NULL
))
818 return ERROR_FUNCTION_FAILED
;
820 return ERROR_SUCCESS
;
823 UINT
MSI_DatabaseExport( MSIDATABASE
*db
, LPCWSTR table
,
824 LPCWSTR folder
, LPCWSTR file
)
826 static const WCHAR query
[] = {
827 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 };
828 static const WCHAR szbs
[] = { '\\', 0 };
829 static const WCHAR forcecodepage
[] = {
830 '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 };
831 MSIRECORD
*rec
= NULL
;
832 MSIQUERY
*view
= NULL
;
837 TRACE("%p %s %s %s\n", db
, debugstr_w(table
),
838 debugstr_w(folder
), debugstr_w(file
) );
840 if( folder
== NULL
|| file
== NULL
)
841 return ERROR_INVALID_PARAMETER
;
843 len
= lstrlenW(folder
) + lstrlenW(file
) + 2;
844 filename
= msi_alloc(len
* sizeof (WCHAR
));
846 return ERROR_OUTOFMEMORY
;
848 lstrcpyW( filename
, folder
);
849 lstrcatW( filename
, szbs
);
850 lstrcatW( filename
, file
);
852 handle
= CreateFileW( filename
, GENERIC_READ
| GENERIC_WRITE
, 0,
853 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
854 msi_free( filename
);
855 if (handle
== INVALID_HANDLE_VALUE
)
856 return ERROR_FUNCTION_FAILED
;
858 if (!lstrcmpW( table
, forcecodepage
))
860 r
= msi_export_forcecodepage( handle
);
864 r
= MSI_OpenQuery( db
, &view
, query
, table
);
865 if (r
== ERROR_SUCCESS
)
867 /* write out row 1, the column names */
868 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &rec
);
869 if (r
== ERROR_SUCCESS
)
871 msi_export_record( handle
, rec
, 1 );
872 msiobj_release( &rec
->hdr
);
875 /* write out row 2, the column types */
876 r
= MSI_ViewGetColumnInfo(view
, MSICOLINFO_TYPES
, &rec
);
877 if (r
== ERROR_SUCCESS
)
879 msi_export_record( handle
, rec
, 1 );
880 msiobj_release( &rec
->hdr
);
883 /* write out row 3, the table name + keys */
884 r
= MSI_DatabaseGetPrimaryKeys( db
, table
, &rec
);
885 if (r
== ERROR_SUCCESS
)
887 MSI_RecordSetStringW( rec
, 0, table
);
888 msi_export_record( handle
, rec
, 0 );
889 msiobj_release( &rec
->hdr
);
892 /* write out row 4 onwards, the data */
893 r
= MSI_IterateRecords( view
, 0, msi_export_row
, handle
);
894 msiobj_release( &view
->hdr
);
898 CloseHandle( handle
);
902 /***********************************************************************
903 * MsiExportDatabaseW [MSI.@]
905 * Writes a file containing the table data as tab separated ASCII.
907 * The format is as follows:
909 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf>
910 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf>
911 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf>
913 * Followed by the data, starting at row 1 with one row per line
915 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf>
917 UINT WINAPI
MsiDatabaseExportW( MSIHANDLE handle
, LPCWSTR szTable
,
918 LPCWSTR szFolder
, LPCWSTR szFilename
)
923 TRACE("%lx %s %s %s\n", handle
, debugstr_w(szTable
),
924 debugstr_w(szFolder
), debugstr_w(szFilename
));
926 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
929 IWineMsiRemoteDatabase
*remote_database
;
931 remote_database
= (IWineMsiRemoteDatabase
*)msi_get_remote( handle
);
932 if ( !remote_database
)
933 return ERROR_INVALID_HANDLE
;
935 IWineMsiRemoteDatabase_Release( remote_database
);
936 WARN("MsiDatabaseExport not allowed during a custom action!\n");
938 return ERROR_SUCCESS
;
941 r
= MSI_DatabaseExport( db
, szTable
, szFolder
, szFilename
);
942 msiobj_release( &db
->hdr
);
946 UINT WINAPI
MsiDatabaseExportA( MSIHANDLE handle
, LPCSTR szTable
,
947 LPCSTR szFolder
, LPCSTR szFilename
)
949 LPWSTR path
= NULL
, file
= NULL
, table
= NULL
;
950 UINT r
= ERROR_OUTOFMEMORY
;
952 TRACE("%lx %s %s %s\n", handle
, debugstr_a(szTable
),
953 debugstr_a(szFolder
), debugstr_a(szFilename
));
957 table
= strdupAtoW( szTable
);
964 path
= strdupAtoW( szFolder
);
971 file
= strdupAtoW( szFilename
);
976 r
= MsiDatabaseExportW( handle
, table
, path
, file
);
986 MSIDBSTATE WINAPI
MsiGetDatabaseState( MSIHANDLE handle
)
988 MSIDBSTATE ret
= MSIDBSTATE_READ
;
991 TRACE("%ld\n", handle
);
993 db
= msihandle2msiinfo( handle
, MSIHANDLETYPE_DATABASE
);
996 IWineMsiRemoteDatabase
*remote_database
;
998 remote_database
= (IWineMsiRemoteDatabase
*)msi_get_remote( handle
);
999 if ( !remote_database
)
1000 return MSIDBSTATE_ERROR
;
1002 IWineMsiRemoteDatabase_Release( remote_database
);
1003 WARN("MsiGetDatabaseState not allowed during a custom action!\n");
1005 return MSIDBSTATE_READ
;
1008 if (db
->mode
!= MSIDBOPEN_READONLY
)
1009 ret
= MSIDBSTATE_WRITE
;
1010 msiobj_release( &db
->hdr
);
1015 typedef struct _msi_remote_database_impl
{
1016 const IWineMsiRemoteDatabaseVtbl
*lpVtbl
;
1019 } msi_remote_database_impl
;
1021 static inline msi_remote_database_impl
* mrd_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase
* iface
)
1023 return (msi_remote_database_impl
*)iface
;
1026 static HRESULT WINAPI
mrd_QueryInterface( IWineMsiRemoteDatabase
*iface
,
1027 REFIID riid
,LPVOID
*ppobj
)
1029 if( IsEqualCLSID( riid
, &IID_IUnknown
) ||
1030 IsEqualCLSID( riid
, &IID_IWineMsiRemoteDatabase
) )
1032 IUnknown_AddRef( iface
);
1037 return E_NOINTERFACE
;
1040 static ULONG WINAPI
mrd_AddRef( IWineMsiRemoteDatabase
*iface
)
1042 msi_remote_database_impl
* This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1044 return InterlockedIncrement( &This
->refs
);
1047 static ULONG WINAPI
mrd_Release( IWineMsiRemoteDatabase
*iface
)
1049 msi_remote_database_impl
* This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1052 r
= InterlockedDecrement( &This
->refs
);
1055 MsiCloseHandle( This
->database
);
1061 static HRESULT WINAPI
mrd_IsTablePersistent( IWineMsiRemoteDatabase
*iface
,
1062 BSTR table
, MSICONDITION
*persistent
)
1064 msi_remote_database_impl
*This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1065 *persistent
= MsiDatabaseIsTablePersistentW(This
->database
, (LPWSTR
)table
);
1069 static HRESULT WINAPI
mrd_GetPrimaryKeys( IWineMsiRemoteDatabase
*iface
,
1070 BSTR table
, MSIHANDLE
*keys
)
1072 msi_remote_database_impl
*This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1073 UINT r
= MsiDatabaseGetPrimaryKeysW(This
->database
, (LPWSTR
)table
, keys
);
1074 return HRESULT_FROM_WIN32(r
);
1077 static HRESULT WINAPI
mrd_GetSummaryInformation( IWineMsiRemoteDatabase
*iface
,
1078 UINT updatecount
, MSIHANDLE
*suminfo
)
1080 msi_remote_database_impl
*This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1081 UINT r
= MsiGetSummaryInformationW(This
->database
, NULL
, updatecount
, suminfo
);
1082 return HRESULT_FROM_WIN32(r
);
1085 static HRESULT WINAPI
mrd_OpenView( IWineMsiRemoteDatabase
*iface
,
1086 BSTR query
, MSIHANDLE
*view
)
1088 msi_remote_database_impl
*This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1089 UINT r
= MsiDatabaseOpenViewW(This
->database
, (LPWSTR
)query
, view
);
1090 return HRESULT_FROM_WIN32(r
);
1093 static HRESULT WINAPI
mrd_SetMsiHandle( IWineMsiRemoteDatabase
*iface
, MSIHANDLE handle
)
1095 msi_remote_database_impl
* This
= mrd_from_IWineMsiRemoteDatabase( iface
);
1096 This
->database
= handle
;
1100 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl
=
1105 mrd_IsTablePersistent
,
1107 mrd_GetSummaryInformation
,
1112 HRESULT
create_msi_remote_database( IUnknown
*pOuter
, LPVOID
*ppObj
)
1114 msi_remote_database_impl
*This
;
1116 This
= msi_alloc( sizeof *This
);
1118 return E_OUTOFMEMORY
;
1120 This
->lpVtbl
= &msi_remote_database_vtbl
;