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"
30 #include "wine/list.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
);
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("%ld %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
);
132 list_init( &query
->mem
);
134 r
= MSI_ParseSQL( db
, szQuery
, &query
->view
, &query
->mem
);
135 if( r
== ERROR_SUCCESS
)
137 msiobj_addref( &query
->hdr
);
141 msiobj_release( &query
->hdr
);
145 UINT
MSI_OpenQuery( MSIDATABASE
*db
, MSIQUERY
**view
, LPCWSTR fmt
, ... )
151 /* construct the string */
155 query
= msi_alloc( size
*sizeof(WCHAR
) );
157 res
= vsnprintfW(query
, size
, fmt
, va
);
159 if (res
== -1) size
*= 2;
160 else if (res
>= size
) size
= res
+ 1;
164 /* perform the query */
165 r
= MSI_DatabaseOpenViewW(db
, query
, view
);
176 UINT
MSI_ReverseIterateRecords( MSIQUERY
*view
, DWORD
*count
,
177 record_func func
, LPVOID param
)
179 MSIRECORD
*rec
= NULL
;
180 struct rec_list
*add_rec
;
182 UINT r
, n
= 0, max
= 0;
184 r
= MSI_ViewExecute( view
, NULL
);
185 if( r
!= ERROR_SUCCESS
)
188 list_init( &records
);
193 /* reverse the query */
194 for( n
= 0; (max
== 0) || (n
< max
); n
++ )
196 r
= MSI_ViewFetch( view
, &rec
);
197 if( r
!= ERROR_SUCCESS
)
199 if ( r
== ERROR_NO_MORE_ITEMS
)
205 add_rec
= msi_alloc( sizeof( *add_rec
) );
210 list_add_head( &records
, &add_rec
->entry
);
213 /* iterate over the reversed records */
215 LIST_FOR_EACH_ENTRY( add_rec
, &records
, struct rec_list
, entry
)
218 r
= func( add_rec
->rec
, param
);
220 msiobj_release( &add_rec
->rec
->hdr
);
221 if ( r
!= ERROR_SUCCESS
)
228 MSI_ViewClose( view
);
230 while ( !list_empty( &records
) )
232 add_rec
= LIST_ENTRY( list_head( &records
), struct rec_list
, entry
);
233 list_remove( &add_rec
->entry
);
240 if( r
== ERROR_NO_MORE_ITEMS
)
246 UINT
MSI_IterateRecords( MSIQUERY
*view
, DWORD
*count
,
247 record_func func
, LPVOID param
)
249 MSIRECORD
*rec
= NULL
;
250 UINT r
, n
= 0, max
= 0;
252 r
= MSI_ViewExecute( view
, NULL
);
253 if( r
!= ERROR_SUCCESS
)
259 /* iterate a query */
260 for( n
= 0; (max
== 0) || (n
< max
); n
++ )
262 r
= MSI_ViewFetch( view
, &rec
);
263 if( r
!= ERROR_SUCCESS
)
266 r
= func( rec
, param
);
267 msiobj_release( &rec
->hdr
);
268 if( r
!= ERROR_SUCCESS
)
272 MSI_ViewClose( view
);
277 if( r
== ERROR_NO_MORE_ITEMS
)
283 /* return a single record from a query */
284 MSIRECORD
*MSI_QueryGetRecord( MSIDATABASE
*db
, LPCWSTR fmt
, ... )
286 MSIRECORD
*rec
= NULL
;
287 MSIQUERY
*view
= NULL
;
292 /* construct the string */
296 query
= msi_alloc( size
*sizeof(WCHAR
) );
298 res
= vsnprintfW(query
, size
, fmt
, va
);
300 if (res
== -1) size
*= 2;
301 else if (res
>= size
) size
= res
+ 1;
305 /* perform the query */
306 r
= MSI_DatabaseOpenViewW(db
, query
, &view
);
309 if( r
== ERROR_SUCCESS
)
311 MSI_ViewExecute( view
, NULL
);
312 MSI_ViewFetch( view
, &rec
);
313 MSI_ViewClose( view
);
314 msiobj_release( &view
->hdr
);
319 UINT WINAPI
MsiDatabaseOpenViewW(MSIHANDLE hdb
,
320 LPCWSTR szQuery
, MSIHANDLE
*phView
)
323 MSIQUERY
*query
= NULL
;
326 TRACE("%s %p\n", debugstr_w(szQuery
), phView
);
328 db
= msihandle2msiinfo( hdb
, MSIHANDLETYPE_DATABASE
);
330 return ERROR_INVALID_HANDLE
;
332 ret
= MSI_DatabaseOpenViewW( db
, szQuery
, &query
);
333 if( ret
== ERROR_SUCCESS
)
335 *phView
= alloc_msihandle( &query
->hdr
);
337 ret
= ERROR_NOT_ENOUGH_MEMORY
;
338 msiobj_release( &query
->hdr
);
340 msiobj_release( &db
->hdr
);
345 UINT
MSI_ViewFetch(MSIQUERY
*query
, MSIRECORD
**prec
)
349 UINT row_count
= 0, col_count
= 0, i
, ival
, ret
, type
;
351 TRACE("%p %p\n", query
, prec
);
355 return ERROR_FUNCTION_FAILED
;
357 ret
= view
->ops
->get_dimensions( view
, &row_count
, &col_count
);
361 return ERROR_INVALID_PARAMETER
;
363 if( query
->row
>= row_count
)
364 return ERROR_NO_MORE_ITEMS
;
366 rec
= MSI_CreateRecord( col_count
);
368 return ERROR_FUNCTION_FAILED
;
370 for( i
=1; i
<=col_count
; i
++ )
372 ret
= view
->ops
->get_column_info( view
, i
, NULL
, &type
);
375 ERR("Error getting column type for %d\n", i
);
378 if (!MSITYPE_IS_BINARY(type
))
380 ret
= view
->ops
->fetch_int( view
, query
->row
, i
, &ival
);
383 ERR("Error fetching data for %d\n", i
);
386 if( ! (type
& MSITYPE_VALID
) )
387 ERR("Invalid type!\n");
389 /* check if it's nul (0) - if so, don't set anything */
393 if( type
& MSITYPE_STRING
)
397 sval
= msi_string_lookup_id( query
->db
->strings
, ival
);
398 MSI_RecordSetStringW( rec
, i
, sval
);
402 if( (type
& MSI_DATASIZEMASK
) == 2 )
403 MSI_RecordSetInteger( rec
, i
, ival
- (1<<15) );
405 MSI_RecordSetInteger( rec
, i
, ival
- (1<<31) );
412 ret
= view
->ops
->fetch_stream( view
, query
->row
, i
, &stm
);
413 if( ( ret
== ERROR_SUCCESS
) && stm
)
415 MSI_RecordSetIStream( rec
, i
, stm
);
416 IStream_Release( stm
);
419 ERR("failed to get stream\n");
426 return ERROR_SUCCESS
;
429 UINT WINAPI
MsiViewFetch(MSIHANDLE hView
, MSIHANDLE
*record
)
432 MSIRECORD
*rec
= NULL
;
435 TRACE("%ld %p\n", hView
, record
);
438 return ERROR_INVALID_PARAMETER
;
441 query
= msihandle2msiinfo( hView
, MSIHANDLETYPE_VIEW
);
443 return ERROR_INVALID_HANDLE
;
444 ret
= MSI_ViewFetch( query
, &rec
);
445 if( ret
== ERROR_SUCCESS
)
447 *record
= alloc_msihandle( &rec
->hdr
);
449 ret
= ERROR_NOT_ENOUGH_MEMORY
;
450 msiobj_release( &rec
->hdr
);
452 msiobj_release( &query
->hdr
);
456 UINT
MSI_ViewClose(MSIQUERY
*query
)
460 TRACE("%p\n", query
);
464 return ERROR_FUNCTION_FAILED
;
465 if( !view
->ops
->close
)
466 return ERROR_FUNCTION_FAILED
;
468 return view
->ops
->close( view
);
471 UINT WINAPI
MsiViewClose(MSIHANDLE hView
)
476 TRACE("%ld\n", hView
);
478 query
= msihandle2msiinfo( hView
, MSIHANDLETYPE_VIEW
);
480 return ERROR_INVALID_HANDLE
;
482 ret
= MSI_ViewClose( query
);
483 msiobj_release( &query
->hdr
);
487 UINT
MSI_ViewExecute(MSIQUERY
*query
, MSIRECORD
*rec
)
491 TRACE("%p %p\n", query
, rec
);
495 return ERROR_FUNCTION_FAILED
;
496 if( !view
->ops
->execute
)
497 return ERROR_FUNCTION_FAILED
;
500 return view
->ops
->execute( view
, rec
);
503 UINT WINAPI
MsiViewExecute(MSIHANDLE hView
, MSIHANDLE hRec
)
506 MSIRECORD
*rec
= NULL
;
509 TRACE("%ld %ld\n", hView
, hRec
);
511 query
= msihandle2msiinfo( hView
, MSIHANDLETYPE_VIEW
);
513 return ERROR_INVALID_HANDLE
;
517 rec
= msihandle2msiinfo( hRec
, MSIHANDLETYPE_RECORD
);
520 ret
= ERROR_INVALID_HANDLE
;
525 msiobj_lock( &rec
->hdr
);
526 ret
= MSI_ViewExecute( query
, rec
);
527 msiobj_unlock( &rec
->hdr
);
530 msiobj_release( &query
->hdr
);
532 msiobj_release( &rec
->hdr
);
537 static UINT
msi_set_record_type_string( MSIRECORD
*rec
, UINT field
, UINT type
)
539 static const WCHAR fmt
[] = { '%','d',0 };
542 if (MSITYPE_IS_BINARY(type
))
544 else if (type
& MSITYPE_LOCALIZABLE
)
546 else if (type
& MSITYPE_STRING
)
550 if (type
& MSITYPE_NULLABLE
)
553 sprintfW( &szType
[1], fmt
, (type
&0xff) );
555 TRACE("type %04x -> %s\n", type
, debugstr_w(szType
) );
557 return MSI_RecordSetStringW( rec
, field
, szType
);
560 UINT
MSI_ViewGetColumnInfo( MSIQUERY
*query
, MSICOLINFO info
, MSIRECORD
**prec
)
562 UINT r
= ERROR_FUNCTION_FAILED
, i
, count
= 0, type
;
564 MSIVIEW
*view
= query
->view
;
568 return ERROR_FUNCTION_FAILED
;
570 if( !view
->ops
->get_dimensions
)
571 return ERROR_FUNCTION_FAILED
;
573 r
= view
->ops
->get_dimensions( view
, NULL
, &count
);
574 if( r
!= ERROR_SUCCESS
)
577 return ERROR_INVALID_PARAMETER
;
579 rec
= MSI_CreateRecord( count
);
581 return ERROR_FUNCTION_FAILED
;
583 for( i
=0; i
<count
; i
++ )
586 r
= view
->ops
->get_column_info( view
, i
+1, &name
, &type
);
587 if( r
!= ERROR_SUCCESS
)
589 if (info
== MSICOLINFO_NAMES
)
590 MSI_RecordSetStringW( rec
, i
+1, name
);
592 msi_set_record_type_string( rec
, i
+1, type
);
597 return ERROR_SUCCESS
;
600 UINT WINAPI
MsiViewGetColumnInfo(MSIHANDLE hView
, MSICOLINFO info
, MSIHANDLE
*hRec
)
602 MSIQUERY
*query
= NULL
;
603 MSIRECORD
*rec
= NULL
;
606 TRACE("%ld %d %p\n", hView
, info
, hRec
);
609 return ERROR_INVALID_PARAMETER
;
611 if( info
!= MSICOLINFO_NAMES
&& info
!= MSICOLINFO_TYPES
)
612 return ERROR_INVALID_PARAMETER
;
614 query
= msihandle2msiinfo( hView
, MSIHANDLETYPE_VIEW
);
616 return ERROR_INVALID_HANDLE
;
618 r
= MSI_ViewGetColumnInfo( query
, info
, &rec
);
619 if ( r
== ERROR_SUCCESS
)
621 *hRec
= alloc_msihandle( &rec
->hdr
);
623 r
= ERROR_NOT_ENOUGH_MEMORY
;
624 msiobj_release( &rec
->hdr
);
627 msiobj_release( &query
->hdr
);
632 UINT WINAPI
MsiViewModify( MSIHANDLE hView
, MSIMODIFY eModifyMode
,
635 MSIVIEW
*view
= NULL
;
636 MSIQUERY
*query
= NULL
;
637 MSIRECORD
*rec
= NULL
;
638 UINT r
= ERROR_FUNCTION_FAILED
;
640 TRACE("%ld %x %ld\n", hView
, eModifyMode
, hRecord
);
642 query
= msihandle2msiinfo( hView
, MSIHANDLETYPE_VIEW
);
644 return ERROR_INVALID_HANDLE
;
650 if( !view
->ops
->modify
)
653 rec
= msihandle2msiinfo( hRecord
, MSIHANDLETYPE_RECORD
);
656 r
= ERROR_INVALID_HANDLE
;
660 r
= view
->ops
->modify( view
, eModifyMode
, rec
);
663 msiobj_release( &query
->hdr
);
665 msiobj_release( &rec
->hdr
);
670 MSIDBERROR WINAPI
MsiViewGetErrorW( MSIHANDLE handle
, LPWSTR szColumnNameBuffer
,
673 MSIQUERY
*query
= NULL
;
674 static const WCHAR szError
[] = { 0 };
675 MSIDBERROR r
= MSIDBERROR_NOERROR
;
678 FIXME("%ld %p %p - returns empty error string\n",
679 handle
, szColumnNameBuffer
, pcchBuf
);
682 return MSIDBERROR_INVALIDARG
;
684 query
= msihandle2msiinfo( handle
, MSIHANDLETYPE_VIEW
);
686 return MSIDBERROR_INVALIDARG
;
688 len
= lstrlenW( szError
);
689 if( szColumnNameBuffer
)
692 lstrcpyW( szColumnNameBuffer
, szError
);
694 r
= MSIDBERROR_MOREDATA
;
698 msiobj_release( &query
->hdr
);
702 MSIDBERROR WINAPI
MsiViewGetErrorA( MSIHANDLE handle
, LPSTR szColumnNameBuffer
,
705 static const CHAR szError
[] = { 0 };
706 MSIQUERY
*query
= NULL
;
707 MSIDBERROR r
= MSIDBERROR_NOERROR
;
710 FIXME("%ld %p %p - returns empty error string\n",
711 handle
, szColumnNameBuffer
, pcchBuf
);
714 return MSIDBERROR_INVALIDARG
;
716 query
= msihandle2msiinfo( handle
, MSIHANDLETYPE_VIEW
);
718 return MSIDBERROR_INVALIDARG
;
720 len
= lstrlenA( szError
);
721 if( szColumnNameBuffer
)
724 lstrcpyA( szColumnNameBuffer
, szError
);
726 r
= MSIDBERROR_MOREDATA
;
730 msiobj_release( &query
->hdr
);
734 MSIHANDLE WINAPI
MsiGetLastErrorRecord( void )
740 DEFINE_GUID( CLSID_MsiTransform
, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
742 UINT
MSI_DatabaseApplyTransformW( MSIDATABASE
*db
,
743 LPCWSTR szTransformFile
, int iErrorCond
)
746 UINT ret
= ERROR_FUNCTION_FAILED
;
747 IStorage
*stg
= NULL
;
750 TRACE("%p %s %d\n", db
, debugstr_w(szTransformFile
), iErrorCond
);
752 r
= StgOpenStorage( szTransformFile
, NULL
,
753 STGM_DIRECT
|STGM_READ
|STGM_SHARE_DENY_WRITE
, NULL
, 0, &stg
);
757 r
= IStorage_Stat( stg
, &stat
, STATFLAG_NONAME
);
761 if ( !IsEqualGUID( &stat
.clsid
, &CLSID_MsiTransform
) )
764 if( TRACE_ON( msi
) )
765 enum_stream_names( stg
);
767 ret
= msi_table_apply_transform( db
, stg
);
770 IStorage_Release( stg
);
775 UINT WINAPI
MsiDatabaseApplyTransformW( MSIHANDLE hdb
,
776 LPCWSTR szTransformFile
, int iErrorCond
)
781 db
= msihandle2msiinfo( hdb
, MSIHANDLETYPE_DATABASE
);
783 return ERROR_INVALID_HANDLE
;
785 r
= MSI_DatabaseApplyTransformW( db
, szTransformFile
, iErrorCond
);
786 msiobj_release( &db
->hdr
);
790 UINT WINAPI
MsiDatabaseApplyTransformA( MSIHANDLE hdb
,
791 LPCSTR szTransformFile
, int iErrorCond
)
796 TRACE("%ld %s %d\n", hdb
, debugstr_a(szTransformFile
), iErrorCond
);
798 wstr
= strdupAtoW( szTransformFile
);
799 if( szTransformFile
&& !wstr
)
800 return ERROR_NOT_ENOUGH_MEMORY
;
802 ret
= MsiDatabaseApplyTransformW( hdb
, wstr
, iErrorCond
);
809 UINT WINAPI
MsiDatabaseGenerateTransformA( MSIHANDLE hdb
, MSIHANDLE hdbref
,
810 LPCSTR szTransformFile
, int iReserved1
, int iReserved2
)
812 FIXME("%ld %ld %s %d %d\n", hdb
, hdbref
,
813 debugstr_a(szTransformFile
), iReserved1
, iReserved2
);
814 return ERROR_CALL_NOT_IMPLEMENTED
;
817 UINT WINAPI
MsiDatabaseGenerateTransformW( MSIHANDLE hdb
, MSIHANDLE hdbref
,
818 LPCWSTR szTransformFile
, int iReserved1
, int iReserved2
)
820 FIXME("%ld %ld %s %d %d\n", hdb
, hdbref
,
821 debugstr_w(szTransformFile
), iReserved1
, iReserved2
);
822 return ERROR_CALL_NOT_IMPLEMENTED
;
825 UINT WINAPI
MsiDatabaseCommit( MSIHANDLE hdb
)
832 db
= msihandle2msiinfo( hdb
, MSIHANDLETYPE_DATABASE
);
834 return ERROR_INVALID_HANDLE
;
836 /* FIXME: lock the database */
838 r
= MSI_CommitTables( db
);
840 /* FIXME: unlock the database */
842 msiobj_release( &db
->hdr
);
844 if (r
== ERROR_SUCCESS
)
846 msi_free( db
->deletefile
);
847 db
->deletefile
= NULL
;
853 struct msi_primary_key_record_info
859 static UINT
msi_primary_key_iterator( MSIRECORD
*rec
, LPVOID param
)
861 struct msi_primary_key_record_info
*info
= param
;
865 type
= MSI_RecordGetInteger( rec
, 4 );
866 if( type
& MSITYPE_KEY
)
871 name
= MSI_RecordGetString( rec
, 3 );
872 MSI_RecordSetStringW( info
->rec
, info
->n
, name
);
876 return ERROR_SUCCESS
;
879 UINT
MSI_DatabaseGetPrimaryKeys( MSIDATABASE
*db
,
880 LPCWSTR table
, MSIRECORD
**prec
)
882 static const WCHAR sql
[] = {
883 's','e','l','e','c','t',' ','*',' ',
884 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
885 'w','h','e','r','e',' ',
886 '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
887 struct msi_primary_key_record_info info
;
888 MSIQUERY
*query
= NULL
;
892 r
= MSI_OpenQuery( db
, &query
, sql
, table
);
893 if( r
!= ERROR_SUCCESS
)
898 /* count the number of primary key records */
901 r
= MSI_IterateRecords( query
, 0, msi_primary_key_iterator
, &info
);
902 if( r
== ERROR_SUCCESS
)
904 TRACE("Found %d primary keys\n", info
.n
);
906 /* allocate a record and fill in the names of the tables */
907 info
.rec
= MSI_CreateRecord( info
.n
);
909 r
= MSI_IterateRecords( query
, 0, msi_primary_key_iterator
, &info
);
910 if( r
== ERROR_SUCCESS
)
913 msiobj_release( &info
.rec
->hdr
);
915 msiobj_release( &query
->hdr
);
920 UINT WINAPI
MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb
,
921 LPCWSTR table
, MSIHANDLE
* phRec
)
923 MSIRECORD
*rec
= NULL
;
927 TRACE("%ld %s %p\n", hdb
, debugstr_w(table
), phRec
);
929 db
= msihandle2msiinfo( hdb
, MSIHANDLETYPE_DATABASE
);
931 return ERROR_INVALID_HANDLE
;
933 r
= MSI_DatabaseGetPrimaryKeys( db
, table
, &rec
);
934 if( r
== ERROR_SUCCESS
)
936 *phRec
= alloc_msihandle( &rec
->hdr
);
938 r
= ERROR_NOT_ENOUGH_MEMORY
;
939 msiobj_release( &rec
->hdr
);
941 msiobj_release( &db
->hdr
);
946 UINT WINAPI
MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb
,
947 LPCSTR table
, MSIHANDLE
* phRec
)
949 LPWSTR szwTable
= NULL
;
952 TRACE("%ld %s %p\n", hdb
, debugstr_a(table
), phRec
);
956 szwTable
= strdupAtoW( table
);
958 return ERROR_OUTOFMEMORY
;
960 r
= MsiDatabaseGetPrimaryKeysW( hdb
, szwTable
, phRec
);
961 msi_free( szwTable
);
966 MSICONDITION WINAPI
MsiDatabaseIsTablePersistentA(
967 MSIHANDLE hDatabase
, LPCSTR szTableName
)
969 LPWSTR szwTableName
= NULL
;
972 TRACE("%lx %s\n", hDatabase
, debugstr_a(szTableName
));
976 szwTableName
= strdupAtoW( szTableName
);
978 return MSICONDITION_ERROR
;
980 r
= MsiDatabaseIsTablePersistentW( hDatabase
, szwTableName
);
981 msi_free( szwTableName
);
986 MSICONDITION WINAPI
MsiDatabaseIsTablePersistentW(
987 MSIHANDLE hDatabase
, LPCWSTR szTableName
)
992 TRACE("%lx %s\n", hDatabase
, debugstr_w(szTableName
));
994 db
= msihandle2msiinfo( hDatabase
, MSIHANDLETYPE_DATABASE
);
996 return MSICONDITION_ERROR
;
998 r
= MSI_DatabaseIsTablePersistent( db
, szTableName
);
1000 msiobj_release( &db
->hdr
);