2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2002-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
28 #include "wine/debug.h"
29 #include "wine/unicode.h"
38 #include "msiserver.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(msi);
44 static void MSI_CloseView( MSIOBJECTHDR *arg )
46 MSIQUERY *query = (MSIQUERY*) arg;
49 if( query->view && query->view->ops->delete )
50 query->view->ops->delete( query->view );
51 msiobj_release( &query->db->hdr );
53 LIST_FOR_EACH_SAFE( ptr, t, &query->mem )
59 UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, UINT *n )
64 r = table->ops->get_dimensions( table, NULL, &count );
65 if( r != ERROR_SUCCESS )
68 for( i=1; i<=count; i++ )
73 r = table->ops->get_column_info( table, i, &col_name, NULL, NULL );
74 if( r != ERROR_SUCCESS )
76 x = lstrcmpW( name, col_name );
85 return ERROR_INVALID_PARAMETER;
88 UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb,
89 LPCSTR szQuery, MSIHANDLE *phView)
94 TRACE("%d %s %p\n", hdb, debugstr_a(szQuery), phView);
98 szwQuery = strdupAtoW( szQuery );
100 return ERROR_FUNCTION_FAILED;
105 r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
107 msi_free( szwQuery );
111 UINT MSI_DatabaseOpenViewW(MSIDATABASE *db,
112 LPCWSTR szQuery, MSIQUERY **pView)
117 TRACE("%s %p\n", debugstr_w(szQuery), pView);
120 return ERROR_INVALID_PARAMETER;
122 /* pre allocate a handle to hold a pointer to the view */
123 query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),
126 return ERROR_FUNCTION_FAILED;
128 msiobj_addref( &db->hdr );
130 list_init( &query->mem );
132 r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem );
133 if( r == ERROR_SUCCESS )
135 msiobj_addref( &query->hdr );
139 msiobj_release( &query->hdr );
143 UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )
149 /* construct the string */
153 query = msi_alloc( size*sizeof(WCHAR) );
155 res = vsnprintfW(query, size, fmt, va);
157 if (res == -1) size *= 2;
158 else if (res >= size) size = res + 1;
162 /* perform the query */
163 r = MSI_DatabaseOpenViewW(db, query, view);
168 UINT MSI_IterateRecords( MSIQUERY *view, LPDWORD count,
169 record_func func, LPVOID param )
171 MSIRECORD *rec = NULL;
172 UINT r, n = 0, max = 0;
174 r = MSI_ViewExecute( view, NULL );
175 if( r != ERROR_SUCCESS )
181 /* iterate a query */
182 for( n = 0; (max == 0) || (n < max); n++ )
184 r = MSI_ViewFetch( view, &rec );
185 if( r != ERROR_SUCCESS )
188 r = func( rec, param );
189 msiobj_release( &rec->hdr );
190 if( r != ERROR_SUCCESS )
194 MSI_ViewClose( view );
199 if( r == ERROR_NO_MORE_ITEMS )
205 /* return a single record from a query */
206 MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... )
208 MSIRECORD *rec = NULL;
209 MSIQUERY *view = NULL;
214 /* construct the string */
218 query = msi_alloc( size*sizeof(WCHAR) );
220 res = vsnprintfW(query, size, fmt, va);
222 if (res == -1) size *= 2;
223 else if (res >= size) size = res + 1;
227 /* perform the query */
228 r = MSI_DatabaseOpenViewW(db, query, &view);
231 if( r == ERROR_SUCCESS )
233 MSI_ViewExecute( view, NULL );
234 MSI_ViewFetch( view, &rec );
235 MSI_ViewClose( view );
236 msiobj_release( &view->hdr );
241 UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
242 LPCWSTR szQuery, MSIHANDLE *phView)
245 MSIQUERY *query = NULL;
248 TRACE("%s %p\n", debugstr_w(szQuery), phView);
250 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
254 IWineMsiRemoteDatabase *remote_database;
256 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
257 if ( !remote_database )
258 return ERROR_INVALID_HANDLE;
260 hr = IWineMsiRemoteDatabase_OpenView( remote_database, (BSTR)szQuery, phView );
261 IWineMsiRemoteDatabase_Release( remote_database );
265 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
266 return HRESULT_CODE(hr);
268 return ERROR_FUNCTION_FAILED;
271 return ERROR_SUCCESS;
274 ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
275 if( ret == ERROR_SUCCESS )
277 *phView = alloc_msihandle( &query->hdr );
279 ret = ERROR_NOT_ENOUGH_MEMORY;
280 msiobj_release( &query->hdr );
282 msiobj_release( &db->hdr );
287 UINT msi_view_get_row(MSIDATABASE *db, MSIVIEW *view, UINT row, MSIRECORD **rec)
289 UINT row_count = 0, col_count = 0, i, ival, ret, type;
291 TRACE("%p %p %d %p\n", db, view, row, rec);
293 ret = view->ops->get_dimensions(view, &row_count, &col_count);
298 return ERROR_INVALID_PARAMETER;
300 if (row >= row_count)
301 return ERROR_NO_MORE_ITEMS;
303 *rec = MSI_CreateRecord(col_count);
305 return ERROR_FUNCTION_FAILED;
307 for (i = 1; i <= col_count; i++)
309 ret = view->ops->get_column_info(view, i, NULL, &type, NULL);
312 ERR("Error getting column type for %d\n", i);
316 if (MSITYPE_IS_BINARY(type))
320 ret = view->ops->fetch_stream(view, row, i, &stm);
321 if ((ret == ERROR_SUCCESS) && stm)
323 MSI_RecordSetIStream(*rec, i, stm);
324 IStream_Release(stm);
327 WARN("failed to get stream\n");
332 ret = view->ops->fetch_int(view, row, i, &ival);
335 ERR("Error fetching data for %d\n", i);
339 if (! (type & MSITYPE_VALID))
340 ERR("Invalid type!\n");
342 /* check if it's nul (0) - if so, don't set anything */
346 if (type & MSITYPE_STRING)
350 sval = msi_string_lookup_id(db->strings, ival);
351 MSI_RecordSetStringW(*rec, i, sval);
355 if ((type & MSI_DATASIZEMASK) == 2)
356 MSI_RecordSetInteger(*rec, i, ival - (1<<15));
358 MSI_RecordSetInteger(*rec, i, ival - (1<<31));
362 return ERROR_SUCCESS;
365 UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
370 TRACE("%p %p\n", query, prec );
374 return ERROR_FUNCTION_FAILED;
376 r = msi_view_get_row(query->db, view, query->row, prec);
377 if (r == ERROR_SUCCESS)
380 MSI_RecordSetInteger(*prec, 0, (int)query);
386 UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
389 MSIRECORD *rec = NULL;
392 TRACE("%d %p\n", hView, record);
395 return ERROR_INVALID_PARAMETER;
398 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
400 return ERROR_INVALID_HANDLE;
401 ret = MSI_ViewFetch( query, &rec );
402 if( ret == ERROR_SUCCESS )
404 *record = alloc_msihandle( &rec->hdr );
406 ret = ERROR_NOT_ENOUGH_MEMORY;
407 msiobj_release( &rec->hdr );
409 msiobj_release( &query->hdr );
413 UINT MSI_ViewClose(MSIQUERY *query)
417 TRACE("%p\n", query );
421 return ERROR_FUNCTION_FAILED;
422 if( !view->ops->close )
423 return ERROR_FUNCTION_FAILED;
425 return view->ops->close( view );
428 UINT WINAPI MsiViewClose(MSIHANDLE hView)
433 TRACE("%d\n", hView );
435 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
437 return ERROR_INVALID_HANDLE;
439 ret = MSI_ViewClose( query );
440 msiobj_release( &query->hdr );
444 UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
448 TRACE("%p %p\n", query, rec);
452 return ERROR_FUNCTION_FAILED;
453 if( !view->ops->execute )
454 return ERROR_FUNCTION_FAILED;
457 return view->ops->execute( view, rec );
460 UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
463 MSIRECORD *rec = NULL;
466 TRACE("%d %d\n", hView, hRec);
468 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
470 return ERROR_INVALID_HANDLE;
474 rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
477 ret = ERROR_INVALID_HANDLE;
482 msiobj_lock( &rec->hdr );
483 ret = MSI_ViewExecute( query, rec );
484 msiobj_unlock( &rec->hdr );
487 msiobj_release( &query->hdr );
489 msiobj_release( &rec->hdr );
494 static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field,
495 UINT type, BOOL temporary )
497 static const WCHAR fmt[] = { '%','d',0 };
500 if (MSITYPE_IS_BINARY(type))
502 else if (type & MSITYPE_LOCALIZABLE)
504 else if (type & MSITYPE_STRING)
519 if (type & MSITYPE_NULLABLE)
522 sprintfW( &szType[1], fmt, (type&0xff) );
524 TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
526 return MSI_RecordSetStringW( rec, field, szType );
529 UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec )
531 UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
533 MSIVIEW *view = query->view;
538 return ERROR_FUNCTION_FAILED;
540 if( !view->ops->get_dimensions )
541 return ERROR_FUNCTION_FAILED;
543 r = view->ops->get_dimensions( view, NULL, &count );
544 if( r != ERROR_SUCCESS )
547 return ERROR_INVALID_PARAMETER;
549 rec = MSI_CreateRecord( count );
551 return ERROR_FUNCTION_FAILED;
553 for( i=0; i<count; i++ )
556 r = view->ops->get_column_info( view, i+1, &name, &type, &temporary );
557 if( r != ERROR_SUCCESS )
559 if (info == MSICOLINFO_NAMES)
560 MSI_RecordSetStringW( rec, i+1, name );
562 msi_set_record_type_string( rec, i+1, type, temporary );
567 return ERROR_SUCCESS;
570 UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
572 MSIQUERY *query = NULL;
573 MSIRECORD *rec = NULL;
576 TRACE("%d %d %p\n", hView, info, hRec);
579 return ERROR_INVALID_PARAMETER;
581 if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
582 return ERROR_INVALID_PARAMETER;
584 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
586 return ERROR_INVALID_HANDLE;
588 r = MSI_ViewGetColumnInfo( query, info, &rec );
589 if ( r == ERROR_SUCCESS )
591 *hRec = alloc_msihandle( &rec->hdr );
593 r = ERROR_NOT_ENOUGH_MEMORY;
594 msiobj_release( &rec->hdr );
597 msiobj_release( &query->hdr );
602 UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec )
604 MSIVIEW *view = NULL;
607 if ( !query || !rec )
608 return ERROR_INVALID_HANDLE;
611 if ( !view || !view->ops->modify)
612 return ERROR_FUNCTION_FAILED;
614 if ( mode == MSIMODIFY_UPDATE && MSI_RecordGetInteger( rec, 0 ) != (int)query )
615 return ERROR_FUNCTION_FAILED;
617 r = view->ops->modify( view, mode, rec, query->row );
618 if (mode == MSIMODIFY_DELETE && r == ERROR_SUCCESS)
624 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
627 MSIQUERY *query = NULL;
628 MSIRECORD *rec = NULL;
629 UINT r = ERROR_FUNCTION_FAILED;
631 TRACE("%d %x %d\n", hView, eModifyMode, hRecord);
633 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
635 return ERROR_INVALID_HANDLE;
637 rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
638 r = MSI_ViewModify( query, eModifyMode, rec );
640 msiobj_release( &query->hdr );
642 msiobj_release( &rec->hdr );
647 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
650 MSIQUERY *query = NULL;
651 static const WCHAR szError[] = { 0 };
652 MSIDBERROR r = MSIDBERROR_NOERROR;
655 FIXME("%d %p %p - returns empty error string\n",
656 handle, szColumnNameBuffer, pcchBuf );
659 return MSIDBERROR_INVALIDARG;
661 query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
663 return MSIDBERROR_INVALIDARG;
665 len = strlenW( szError );
666 if( szColumnNameBuffer )
669 lstrcpyW( szColumnNameBuffer, szError );
671 r = MSIDBERROR_MOREDATA;
675 msiobj_release( &query->hdr );
679 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
682 static const CHAR szError[] = { 0 };
683 MSIQUERY *query = NULL;
684 MSIDBERROR r = MSIDBERROR_NOERROR;
687 FIXME("%d %p %p - returns empty error string\n",
688 handle, szColumnNameBuffer, pcchBuf );
691 return MSIDBERROR_INVALIDARG;
693 query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
695 return MSIDBERROR_INVALIDARG;
697 len = strlen( szError );
698 if( szColumnNameBuffer )
701 lstrcpyA( szColumnNameBuffer, szError );
703 r = MSIDBERROR_MOREDATA;
707 msiobj_release( &query->hdr );
711 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
717 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
718 LPCWSTR szTransformFile, int iErrorCond )
721 UINT ret = ERROR_FUNCTION_FAILED;
722 IStorage *stg = NULL;
725 TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
727 r = StgOpenStorage( szTransformFile, NULL,
728 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
732 r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
736 if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
739 if( TRACE_ON( msi ) )
740 enum_stream_names( stg );
742 ret = msi_table_apply_transform( db, stg );
745 IStorage_Release( stg );
750 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
751 LPCWSTR szTransformFile, int iErrorCond)
756 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
759 IWineMsiRemoteDatabase *remote_database;
761 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
762 if ( !remote_database )
763 return ERROR_INVALID_HANDLE;
765 IWineMsiRemoteDatabase_Release( remote_database );
766 WARN("MsiDatabaseApplyTransform not allowed during a custom action!\n");
768 return ERROR_SUCCESS;
771 r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
772 msiobj_release( &db->hdr );
776 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb,
777 LPCSTR szTransformFile, int iErrorCond)
782 TRACE("%d %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
784 wstr = strdupAtoW( szTransformFile );
785 if( szTransformFile && !wstr )
786 return ERROR_NOT_ENOUGH_MEMORY;
788 ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
795 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
796 LPCSTR szTransformFile, int iReserved1, int iReserved2 )
798 FIXME("%d %d %s %d %d\n", hdb, hdbref,
799 debugstr_a(szTransformFile), iReserved1, iReserved2);
800 return ERROR_CALL_NOT_IMPLEMENTED;
803 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
804 LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
806 FIXME("%d %d %s %d %d\n", hdb, hdbref,
807 debugstr_w(szTransformFile), iReserved1, iReserved2);
808 return ERROR_CALL_NOT_IMPLEMENTED;
811 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
818 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
821 IWineMsiRemoteDatabase *remote_database;
823 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
824 if ( !remote_database )
825 return ERROR_INVALID_HANDLE;
827 IWineMsiRemoteDatabase_Release( remote_database );
828 WARN("MsiDatabaseCommit not allowed during a custom action!\n");
830 return ERROR_SUCCESS;
833 /* FIXME: lock the database */
835 r = MSI_CommitTables( db );
836 if (r != ERROR_SUCCESS) ERR("Failed to commit tables!\n");
838 /* FIXME: unlock the database */
840 msiobj_release( &db->hdr );
842 if (r == ERROR_SUCCESS)
844 msi_free( db->deletefile );
845 db->deletefile = NULL;
851 struct msi_primary_key_record_info
857 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
859 struct msi_primary_key_record_info *info = param;
863 type = MSI_RecordGetInteger( rec, 4 );
864 if( type & MSITYPE_KEY )
871 table = MSI_RecordGetString( rec, 1 );
872 MSI_RecordSetStringW( info->rec, 0, table);
875 name = MSI_RecordGetString( rec, 3 );
876 MSI_RecordSetStringW( info->rec, info->n, name );
880 return ERROR_SUCCESS;
883 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
884 LPCWSTR table, MSIRECORD **prec )
886 static const WCHAR sql[] = {
887 's','e','l','e','c','t',' ','*',' ',
888 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
889 'w','h','e','r','e',' ',
890 '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
891 struct msi_primary_key_record_info info;
892 MSIQUERY *query = NULL;
895 r = MSI_OpenQuery( db, &query, sql, table );
896 if( r != ERROR_SUCCESS )
899 /* count the number of primary key records */
902 r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
903 if( r == ERROR_SUCCESS )
905 TRACE("Found %d primary keys\n", info.n );
907 /* allocate a record and fill in the names of the tables */
908 info.rec = MSI_CreateRecord( info.n );
910 r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
911 if( r == ERROR_SUCCESS )
914 msiobj_release( &info.rec->hdr );
916 msiobj_release( &query->hdr );
921 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
922 LPCWSTR table, MSIHANDLE* phRec )
924 MSIRECORD *rec = NULL;
928 TRACE("%d %s %p\n", hdb, debugstr_w(table), phRec);
930 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
934 IWineMsiRemoteDatabase *remote_database;
936 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
937 if ( !remote_database )
938 return ERROR_INVALID_HANDLE;
940 hr = IWineMsiRemoteDatabase_GetPrimaryKeys( remote_database, (BSTR)table, phRec );
941 IWineMsiRemoteDatabase_Release( remote_database );
945 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
946 return HRESULT_CODE(hr);
948 return ERROR_FUNCTION_FAILED;
951 return ERROR_SUCCESS;
954 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
955 if( r == ERROR_SUCCESS )
957 *phRec = alloc_msihandle( &rec->hdr );
959 r = ERROR_NOT_ENOUGH_MEMORY;
960 msiobj_release( &rec->hdr );
962 msiobj_release( &db->hdr );
967 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb,
968 LPCSTR table, MSIHANDLE* phRec)
970 LPWSTR szwTable = NULL;
973 TRACE("%d %s %p\n", hdb, debugstr_a(table), phRec);
977 szwTable = strdupAtoW( table );
979 return ERROR_OUTOFMEMORY;
981 r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
982 msi_free( szwTable );
987 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
988 MSIHANDLE hDatabase, LPCSTR szTableName)
990 LPWSTR szwTableName = NULL;
993 TRACE("%x %s\n", hDatabase, debugstr_a(szTableName));
997 szwTableName = strdupAtoW( szTableName );
999 return MSICONDITION_ERROR;
1001 r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
1002 msi_free( szwTableName );
1007 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
1008 MSIHANDLE hDatabase, LPCWSTR szTableName)
1013 TRACE("%x %s\n", hDatabase, debugstr_w(szTableName));
1015 db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
1019 MSICONDITION condition;
1020 IWineMsiRemoteDatabase *remote_database;
1022 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hDatabase );
1023 if ( !remote_database )
1024 return MSICONDITION_ERROR;
1026 hr = IWineMsiRemoteDatabase_IsTablePersistent( remote_database,
1027 (BSTR)szTableName, &condition );
1028 IWineMsiRemoteDatabase_Release( remote_database );
1031 return MSICONDITION_ERROR;
1036 r = MSI_DatabaseIsTablePersistent( db, szTableName );
1038 msiobj_release( &db->hdr );