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, LPCWSTR table_name, UINT *n )
62 LPWSTR haystack_table_name;
65 r = table->ops->get_dimensions( table, NULL, &count );
66 if( r != ERROR_SUCCESS )
69 for( i=1; i<=count; i++ )
74 r = table->ops->get_column_info( table, i, &col_name, NULL,
75 NULL, &haystack_table_name );
76 if( r != ERROR_SUCCESS )
78 x = strcmpW( name, col_name );
80 x |= strcmpW( table_name, haystack_table_name );
82 msi_free( haystack_table_name );
90 return ERROR_INVALID_PARAMETER;
93 UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb,
94 LPCSTR szQuery, MSIHANDLE *phView)
99 TRACE("%d %s %p\n", hdb, debugstr_a(szQuery), phView);
103 szwQuery = strdupAtoW( szQuery );
105 return ERROR_FUNCTION_FAILED;
110 r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
112 msi_free( szwQuery );
116 UINT MSI_DatabaseOpenViewW(MSIDATABASE *db,
117 LPCWSTR szQuery, MSIQUERY **pView)
122 TRACE("%s %p\n", debugstr_w(szQuery), pView);
125 return ERROR_INVALID_PARAMETER;
127 /* pre allocate a handle to hold a pointer to the view */
128 query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),
131 return ERROR_FUNCTION_FAILED;
133 msiobj_addref( &db->hdr );
135 list_init( &query->mem );
137 r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem );
138 if( r == ERROR_SUCCESS )
140 msiobj_addref( &query->hdr );
144 msiobj_release( &query->hdr );
148 UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )
154 /* construct the string */
158 query = msi_alloc( size*sizeof(WCHAR) );
160 res = vsnprintfW(query, size, fmt, va);
162 if (res == -1) size *= 2;
163 else if (res >= size) size = res + 1;
167 /* perform the query */
168 r = MSI_DatabaseOpenViewW(db, query, view);
173 UINT MSI_IterateRecords( MSIQUERY *view, LPDWORD count,
174 record_func func, LPVOID param )
176 MSIRECORD *rec = NULL;
177 UINT r, n = 0, max = 0;
179 r = MSI_ViewExecute( view, NULL );
180 if( r != ERROR_SUCCESS )
186 /* iterate a query */
187 for( n = 0; (max == 0) || (n < max); n++ )
189 r = MSI_ViewFetch( view, &rec );
190 if( r != ERROR_SUCCESS )
193 r = func( rec, param );
194 msiobj_release( &rec->hdr );
195 if( r != ERROR_SUCCESS )
199 MSI_ViewClose( view );
204 if( r == ERROR_NO_MORE_ITEMS )
210 /* return a single record from a query */
211 MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... )
213 MSIRECORD *rec = NULL;
214 MSIQUERY *view = NULL;
219 /* construct the string */
223 query = msi_alloc( size*sizeof(WCHAR) );
225 res = vsnprintfW(query, size, fmt, va);
227 if (res == -1) size *= 2;
228 else if (res >= size) size = res + 1;
232 /* perform the query */
233 r = MSI_DatabaseOpenViewW(db, query, &view);
236 if( r == ERROR_SUCCESS )
238 MSI_ViewExecute( view, NULL );
239 MSI_ViewFetch( view, &rec );
240 MSI_ViewClose( view );
241 msiobj_release( &view->hdr );
246 UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
247 LPCWSTR szQuery, MSIHANDLE *phView)
250 MSIQUERY *query = NULL;
253 TRACE("%s %p\n", debugstr_w(szQuery), phView);
255 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
259 IWineMsiRemoteDatabase *remote_database;
261 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
262 if ( !remote_database )
263 return ERROR_INVALID_HANDLE;
265 hr = IWineMsiRemoteDatabase_OpenView( remote_database, szQuery, phView );
266 IWineMsiRemoteDatabase_Release( remote_database );
270 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
271 return HRESULT_CODE(hr);
273 return ERROR_FUNCTION_FAILED;
276 return ERROR_SUCCESS;
279 ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
280 if( ret == ERROR_SUCCESS )
282 *phView = alloc_msihandle( &query->hdr );
284 ret = ERROR_NOT_ENOUGH_MEMORY;
285 msiobj_release( &query->hdr );
287 msiobj_release( &db->hdr );
292 UINT msi_view_get_row(MSIDATABASE *db, MSIVIEW *view, UINT row, MSIRECORD **rec)
294 UINT row_count = 0, col_count = 0, i, ival, ret, type;
296 TRACE("%p %p %d %p\n", db, view, row, rec);
298 ret = view->ops->get_dimensions(view, &row_count, &col_count);
303 return ERROR_INVALID_PARAMETER;
305 if (row >= row_count)
306 return ERROR_NO_MORE_ITEMS;
308 *rec = MSI_CreateRecord(col_count);
310 return ERROR_FUNCTION_FAILED;
312 for (i = 1; i <= col_count; i++)
314 ret = view->ops->get_column_info(view, i, NULL, &type, NULL, NULL);
317 ERR("Error getting column type for %d\n", i);
321 if (MSITYPE_IS_BINARY(type))
325 ret = view->ops->fetch_stream(view, row, i, &stm);
326 if ((ret == ERROR_SUCCESS) && stm)
328 MSI_RecordSetIStream(*rec, i, stm);
329 IStream_Release(stm);
332 WARN("failed to get stream\n");
337 ret = view->ops->fetch_int(view, row, i, &ival);
340 ERR("Error fetching data for %d\n", i);
344 if (! (type & MSITYPE_VALID))
345 ERR("Invalid type!\n");
347 /* check if it's nul (0) - if so, don't set anything */
351 if (type & MSITYPE_STRING)
355 sval = msi_string_lookup_id(db->strings, ival);
356 MSI_RecordSetStringW(*rec, i, sval);
360 if ((type & MSI_DATASIZEMASK) == 2)
361 MSI_RecordSetInteger(*rec, i, ival - (1<<15));
363 MSI_RecordSetInteger(*rec, i, ival - (1<<31));
367 return ERROR_SUCCESS;
370 UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
375 TRACE("%p %p\n", query, prec );
379 return ERROR_FUNCTION_FAILED;
381 r = msi_view_get_row(query->db, view, query->row, prec);
382 if (r == ERROR_SUCCESS)
385 MSI_RecordSetIntPtr(*prec, 0, (INT_PTR)query);
391 UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
394 MSIRECORD *rec = NULL;
397 TRACE("%d %p\n", hView, record);
400 return ERROR_INVALID_PARAMETER;
403 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
405 return ERROR_INVALID_HANDLE;
406 ret = MSI_ViewFetch( query, &rec );
407 if( ret == ERROR_SUCCESS )
409 *record = alloc_msihandle( &rec->hdr );
411 ret = ERROR_NOT_ENOUGH_MEMORY;
412 msiobj_release( &rec->hdr );
414 msiobj_release( &query->hdr );
418 UINT MSI_ViewClose(MSIQUERY *query)
422 TRACE("%p\n", query );
426 return ERROR_FUNCTION_FAILED;
427 if( !view->ops->close )
428 return ERROR_FUNCTION_FAILED;
430 return view->ops->close( view );
433 UINT WINAPI MsiViewClose(MSIHANDLE hView)
438 TRACE("%d\n", hView );
440 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
442 return ERROR_INVALID_HANDLE;
444 ret = MSI_ViewClose( query );
445 msiobj_release( &query->hdr );
449 UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
453 TRACE("%p %p\n", query, rec);
457 return ERROR_FUNCTION_FAILED;
458 if( !view->ops->execute )
459 return ERROR_FUNCTION_FAILED;
462 return view->ops->execute( view, rec );
465 UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
468 MSIRECORD *rec = NULL;
471 TRACE("%d %d\n", hView, hRec);
473 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
475 return ERROR_INVALID_HANDLE;
479 rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
482 ret = ERROR_INVALID_HANDLE;
487 msiobj_lock( &rec->hdr );
488 ret = MSI_ViewExecute( query, rec );
489 msiobj_unlock( &rec->hdr );
492 msiobj_release( &query->hdr );
494 msiobj_release( &rec->hdr );
499 static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field,
500 UINT type, BOOL temporary )
502 static const WCHAR fmt[] = { '%','d',0 };
505 if (MSITYPE_IS_BINARY(type))
507 else if (type & MSITYPE_LOCALIZABLE)
509 else if (type & MSITYPE_STRING)
524 if (type & MSITYPE_NULLABLE)
527 sprintfW( &szType[1], fmt, (type&0xff) );
529 TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
531 return MSI_RecordSetStringW( rec, field, szType );
534 UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec )
536 UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
538 MSIVIEW *view = query->view;
543 return ERROR_FUNCTION_FAILED;
545 if( !view->ops->get_dimensions )
546 return ERROR_FUNCTION_FAILED;
548 r = view->ops->get_dimensions( view, NULL, &count );
549 if( r != ERROR_SUCCESS )
552 return ERROR_INVALID_PARAMETER;
554 rec = MSI_CreateRecord( count );
556 return ERROR_FUNCTION_FAILED;
558 for( i=0; i<count; i++ )
561 r = view->ops->get_column_info( view, i+1, &name, &type, &temporary,
563 if( r != ERROR_SUCCESS )
565 if (info == MSICOLINFO_NAMES)
566 MSI_RecordSetStringW( rec, i+1, name );
568 msi_set_record_type_string( rec, i+1, type, temporary );
573 return ERROR_SUCCESS;
576 UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
578 MSIQUERY *query = NULL;
579 MSIRECORD *rec = NULL;
582 TRACE("%d %d %p\n", hView, info, hRec);
585 return ERROR_INVALID_PARAMETER;
587 if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
588 return ERROR_INVALID_PARAMETER;
590 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
592 return ERROR_INVALID_HANDLE;
594 r = MSI_ViewGetColumnInfo( query, info, &rec );
595 if ( r == ERROR_SUCCESS )
597 *hRec = alloc_msihandle( &rec->hdr );
599 r = ERROR_NOT_ENOUGH_MEMORY;
600 msiobj_release( &rec->hdr );
603 msiobj_release( &query->hdr );
608 UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec )
610 MSIVIEW *view = NULL;
613 if ( !query || !rec )
614 return ERROR_INVALID_HANDLE;
617 if ( !view || !view->ops->modify)
618 return ERROR_FUNCTION_FAILED;
620 if ( mode == MSIMODIFY_UPDATE && MSI_RecordGetIntPtr( rec, 0 ) != (INT_PTR)query )
621 return ERROR_FUNCTION_FAILED;
623 r = view->ops->modify( view, mode, rec, query->row );
624 if (mode == MSIMODIFY_DELETE && r == ERROR_SUCCESS)
630 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
633 MSIQUERY *query = NULL;
634 MSIRECORD *rec = NULL;
635 UINT r = ERROR_FUNCTION_FAILED;
637 TRACE("%d %x %d\n", hView, eModifyMode, hRecord);
639 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
641 return ERROR_INVALID_HANDLE;
643 rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
644 r = MSI_ViewModify( query, eModifyMode, rec );
646 msiobj_release( &query->hdr );
648 msiobj_release( &rec->hdr );
653 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
656 MSIQUERY *query = NULL;
657 static const WCHAR szError[] = { 0 };
658 MSIDBERROR r = MSIDBERROR_NOERROR;
661 FIXME("%d %p %p - returns empty error string\n",
662 handle, szColumnNameBuffer, pcchBuf );
665 return MSIDBERROR_INVALIDARG;
667 query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
669 return MSIDBERROR_INVALIDARG;
671 len = strlenW( szError );
672 if( szColumnNameBuffer )
675 lstrcpyW( szColumnNameBuffer, szError );
677 r = MSIDBERROR_MOREDATA;
681 msiobj_release( &query->hdr );
685 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
688 static const CHAR szError[] = { 0 };
689 MSIQUERY *query = NULL;
690 MSIDBERROR r = MSIDBERROR_NOERROR;
693 FIXME("%d %p %p - returns empty error string\n",
694 handle, szColumnNameBuffer, pcchBuf );
697 return MSIDBERROR_INVALIDARG;
699 query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
701 return MSIDBERROR_INVALIDARG;
703 len = strlen( szError );
704 if( szColumnNameBuffer )
707 lstrcpyA( szColumnNameBuffer, szError );
709 r = MSIDBERROR_MOREDATA;
713 msiobj_release( &query->hdr );
717 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
723 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
724 LPCWSTR szTransformFile, int iErrorCond )
727 UINT ret = ERROR_FUNCTION_FAILED;
728 IStorage *stg = NULL;
731 TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
733 r = StgOpenStorage( szTransformFile, NULL,
734 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
737 WARN("failed to open transform 0x%08x\n", r);
741 r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
745 if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
748 if( TRACE_ON( msi ) )
749 enum_stream_names( stg );
751 ret = msi_table_apply_transform( db, stg );
754 IStorage_Release( stg );
759 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
760 LPCWSTR szTransformFile, int iErrorCond)
765 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
768 IWineMsiRemoteDatabase *remote_database;
770 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
771 if ( !remote_database )
772 return ERROR_INVALID_HANDLE;
774 IWineMsiRemoteDatabase_Release( remote_database );
775 WARN("MsiDatabaseApplyTransform not allowed during a custom action!\n");
777 return ERROR_SUCCESS;
780 r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
781 msiobj_release( &db->hdr );
785 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb,
786 LPCSTR szTransformFile, int iErrorCond)
791 TRACE("%d %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
793 wstr = strdupAtoW( szTransformFile );
794 if( szTransformFile && !wstr )
795 return ERROR_NOT_ENOUGH_MEMORY;
797 ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
804 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
805 LPCSTR szTransformFile, int iReserved1, int iReserved2 )
807 FIXME("%d %d %s %d %d\n", hdb, hdbref,
808 debugstr_a(szTransformFile), iReserved1, iReserved2);
809 return ERROR_CALL_NOT_IMPLEMENTED;
812 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
813 LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
815 FIXME("%d %d %s %d %d\n", hdb, hdbref,
816 debugstr_w(szTransformFile), iReserved1, iReserved2);
817 return ERROR_CALL_NOT_IMPLEMENTED;
820 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
827 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
830 IWineMsiRemoteDatabase *remote_database;
832 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
833 if ( !remote_database )
834 return ERROR_INVALID_HANDLE;
836 IWineMsiRemoteDatabase_Release( remote_database );
837 WARN("not allowed during a custom action!\n");
839 return ERROR_SUCCESS;
842 /* FIXME: lock the database */
844 r = MSI_CommitTables( db );
845 if (r != ERROR_SUCCESS) ERR("Failed to commit tables!\n");
847 /* FIXME: unlock the database */
849 msiobj_release( &db->hdr );
851 if (r == ERROR_SUCCESS)
853 msi_free( db->deletefile );
854 db->deletefile = NULL;
860 struct msi_primary_key_record_info
866 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
868 struct msi_primary_key_record_info *info = param;
872 type = MSI_RecordGetInteger( rec, 4 );
873 if( type & MSITYPE_KEY )
880 table = MSI_RecordGetString( rec, 1 );
881 MSI_RecordSetStringW( info->rec, 0, table);
884 name = MSI_RecordGetString( rec, 3 );
885 MSI_RecordSetStringW( info->rec, info->n, name );
889 return ERROR_SUCCESS;
892 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
893 LPCWSTR table, MSIRECORD **prec )
895 static const WCHAR sql[] = {
896 's','e','l','e','c','t',' ','*',' ',
897 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
898 'w','h','e','r','e',' ',
899 '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
900 struct msi_primary_key_record_info info;
901 MSIQUERY *query = NULL;
904 if (!TABLE_Exists( db, table ))
905 return ERROR_INVALID_TABLE;
907 r = MSI_OpenQuery( db, &query, sql, table );
908 if( r != ERROR_SUCCESS )
911 /* count the number of primary key records */
914 r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
915 if( r == ERROR_SUCCESS )
917 TRACE("Found %d primary keys\n", info.n );
919 /* allocate a record and fill in the names of the tables */
920 info.rec = MSI_CreateRecord( info.n );
922 r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
923 if( r == ERROR_SUCCESS )
926 msiobj_release( &info.rec->hdr );
928 msiobj_release( &query->hdr );
933 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
934 LPCWSTR table, MSIHANDLE* phRec )
936 MSIRECORD *rec = NULL;
940 TRACE("%d %s %p\n", hdb, debugstr_w(table), phRec);
942 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
946 IWineMsiRemoteDatabase *remote_database;
948 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hdb );
949 if ( !remote_database )
950 return ERROR_INVALID_HANDLE;
952 hr = IWineMsiRemoteDatabase_GetPrimaryKeys( remote_database, table, phRec );
953 IWineMsiRemoteDatabase_Release( remote_database );
957 if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
958 return HRESULT_CODE(hr);
960 return ERROR_FUNCTION_FAILED;
963 return ERROR_SUCCESS;
966 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
967 if( r == ERROR_SUCCESS )
969 *phRec = alloc_msihandle( &rec->hdr );
971 r = ERROR_NOT_ENOUGH_MEMORY;
972 msiobj_release( &rec->hdr );
974 msiobj_release( &db->hdr );
979 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb,
980 LPCSTR table, MSIHANDLE* phRec)
982 LPWSTR szwTable = NULL;
985 TRACE("%d %s %p\n", hdb, debugstr_a(table), phRec);
989 szwTable = strdupAtoW( table );
991 return ERROR_OUTOFMEMORY;
993 r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
994 msi_free( szwTable );
999 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
1000 MSIHANDLE hDatabase, LPCSTR szTableName)
1002 LPWSTR szwTableName = NULL;
1005 TRACE("%x %s\n", hDatabase, debugstr_a(szTableName));
1009 szwTableName = strdupAtoW( szTableName );
1011 return MSICONDITION_ERROR;
1013 r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
1014 msi_free( szwTableName );
1019 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
1020 MSIHANDLE hDatabase, LPCWSTR szTableName)
1025 TRACE("%x %s\n", hDatabase, debugstr_w(szTableName));
1027 db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
1031 MSICONDITION condition;
1032 IWineMsiRemoteDatabase *remote_database;
1034 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( hDatabase );
1035 if ( !remote_database )
1036 return MSICONDITION_ERROR;
1038 hr = IWineMsiRemoteDatabase_IsTablePersistent( remote_database,
1039 szTableName, &condition );
1040 IWineMsiRemoteDatabase_Release( remote_database );
1043 return MSICONDITION_ERROR;
1048 r = MSI_DatabaseIsTablePersistent( db, szTableName );
1050 msiobj_release( &db->hdr );