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"
40 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42 static void MSI_CloseView( MSIOBJECTHDR *arg )
44 MSIQUERY *query = (MSIQUERY*) arg;
47 if( query->view && query->view->ops->delete )
48 query->view->ops->delete( query->view );
49 msiobj_release( &query->db->hdr );
51 LIST_FOR_EACH_SAFE( ptr, t, &query->mem )
57 UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, UINT *n )
62 r = table->ops->get_dimensions( table, NULL, &count );
63 if( r != ERROR_SUCCESS )
66 for( i=1; i<=count; i++ )
71 r = table->ops->get_column_info( table, i, &col_name, NULL );
72 if( r != ERROR_SUCCESS )
74 x = lstrcmpW( name, col_name );
83 return ERROR_INVALID_PARAMETER;
86 UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb,
87 LPCSTR szQuery, MSIHANDLE *phView)
92 TRACE("%ld %s %p\n", hdb, debugstr_a(szQuery), phView);
96 szwQuery = strdupAtoW( szQuery );
98 return ERROR_FUNCTION_FAILED;
103 r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
105 msi_free( szwQuery );
109 UINT MSI_DatabaseOpenViewW(MSIDATABASE *db,
110 LPCWSTR szQuery, MSIQUERY **pView)
115 TRACE("%s %p\n", debugstr_w(szQuery), pView);
118 return ERROR_INVALID_PARAMETER;
120 /* pre allocate a handle to hold a pointer to the view */
121 query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),
124 return ERROR_FUNCTION_FAILED;
126 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);
174 UINT MSI_ReverseIterateRecords( MSIQUERY *view, DWORD *count,
175 record_func func, LPVOID param )
177 MSIRECORD *rec = NULL;
178 struct rec_list *add_rec;
180 UINT r, n = 0, max = 0;
182 r = MSI_ViewExecute( view, NULL );
183 if( r != ERROR_SUCCESS )
186 list_init( &records );
191 /* reverse the query */
192 for( n = 0; (max == 0) || (n < max); n++ )
194 r = MSI_ViewFetch( view, &rec );
195 if( r != ERROR_SUCCESS )
197 if ( r == ERROR_NO_MORE_ITEMS )
203 add_rec = msi_alloc( sizeof( *add_rec ) );
208 list_add_head( &records, &add_rec->entry );
211 /* iterate over the reversed records */
213 LIST_FOR_EACH_ENTRY( add_rec, &records, struct rec_list, entry )
216 r = func( add_rec->rec, param );
218 msiobj_release( &add_rec->rec->hdr );
219 if ( r != ERROR_SUCCESS )
226 MSI_ViewClose( view );
228 while ( !list_empty( &records ) )
230 add_rec = LIST_ENTRY( list_head( &records ), struct rec_list, entry );
231 list_remove( &add_rec->entry );
238 if( r == ERROR_NO_MORE_ITEMS )
244 UINT MSI_IterateRecords( MSIQUERY *view, DWORD *count,
245 record_func func, LPVOID param )
247 MSIRECORD *rec = NULL;
248 UINT r, n = 0, max = 0;
250 r = MSI_ViewExecute( view, NULL );
251 if( r != ERROR_SUCCESS )
257 /* iterate a query */
258 for( n = 0; (max == 0) || (n < max); n++ )
260 r = MSI_ViewFetch( view, &rec );
261 if( r != ERROR_SUCCESS )
264 r = func( rec, param );
265 msiobj_release( &rec->hdr );
266 if( r != ERROR_SUCCESS )
270 MSI_ViewClose( view );
275 if( r == ERROR_NO_MORE_ITEMS )
281 /* return a single record from a query */
282 MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... )
284 MSIRECORD *rec = NULL;
285 MSIQUERY *view = NULL;
290 /* construct the string */
294 query = msi_alloc( size*sizeof(WCHAR) );
296 res = vsnprintfW(query, size, fmt, va);
298 if (res == -1) size *= 2;
299 else if (res >= size) size = res + 1;
303 /* perform the query */
304 r = MSI_DatabaseOpenViewW(db, query, &view);
307 if( r == ERROR_SUCCESS )
309 MSI_ViewExecute( view, NULL );
310 MSI_ViewFetch( view, &rec );
311 MSI_ViewClose( view );
312 msiobj_release( &view->hdr );
317 UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
318 LPCWSTR szQuery, MSIHANDLE *phView)
321 MSIQUERY *query = NULL;
324 TRACE("%s %p\n", debugstr_w(szQuery), phView);
326 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
328 return ERROR_INVALID_HANDLE;
330 ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
331 if( ret == ERROR_SUCCESS )
333 *phView = alloc_msihandle( &query->hdr );
335 ret = ERROR_NOT_ENOUGH_MEMORY;
336 msiobj_release( &query->hdr );
338 msiobj_release( &db->hdr );
343 UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
347 UINT row_count = 0, col_count = 0, i, ival, ret, type;
349 TRACE("%p %p\n", query, prec );
353 return ERROR_FUNCTION_FAILED;
355 ret = view->ops->get_dimensions( view, &row_count, &col_count );
359 return ERROR_INVALID_PARAMETER;
361 if( query->row >= row_count )
362 return ERROR_NO_MORE_ITEMS;
364 rec = MSI_CreateRecord( col_count );
366 return ERROR_FUNCTION_FAILED;
368 for( i=1; i<=col_count; i++ )
370 ret = view->ops->get_column_info( view, i, NULL, &type );
373 ERR("Error getting column type for %d\n", i );
376 if (!MSITYPE_IS_BINARY(type))
378 ret = view->ops->fetch_int( view, query->row, i, &ival );
381 ERR("Error fetching data for %d\n", i );
384 if( ! (type & MSITYPE_VALID ) )
385 ERR("Invalid type!\n");
387 /* check if it's nul (0) - if so, don't set anything */
391 if( type & MSITYPE_STRING )
395 sval = msi_string_lookup_id( query->db->strings, ival );
396 MSI_RecordSetStringW( rec, i, sval );
400 if( (type & MSI_DATASIZEMASK) == 2 )
401 MSI_RecordSetInteger( rec, i, ival - (1<<15) );
403 MSI_RecordSetInteger( rec, i, ival - (1<<31) );
410 ret = view->ops->fetch_stream( view, query->row, i, &stm );
411 if( ( ret == ERROR_SUCCESS ) && stm )
413 MSI_RecordSetIStream( rec, i, stm );
414 IStream_Release( stm );
417 ERR("failed to get stream\n");
424 return ERROR_SUCCESS;
427 UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
430 MSIRECORD *rec = NULL;
433 TRACE("%ld %p\n", hView, record);
436 return ERROR_INVALID_PARAMETER;
439 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
441 return ERROR_INVALID_HANDLE;
442 ret = MSI_ViewFetch( query, &rec );
443 if( ret == ERROR_SUCCESS )
445 *record = alloc_msihandle( &rec->hdr );
447 ret = ERROR_NOT_ENOUGH_MEMORY;
448 msiobj_release( &rec->hdr );
450 msiobj_release( &query->hdr );
454 UINT MSI_ViewClose(MSIQUERY *query)
458 TRACE("%p\n", query );
462 return ERROR_FUNCTION_FAILED;
463 if( !view->ops->close )
464 return ERROR_FUNCTION_FAILED;
466 return view->ops->close( view );
469 UINT WINAPI MsiViewClose(MSIHANDLE hView)
474 TRACE("%ld\n", hView );
476 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
478 return ERROR_INVALID_HANDLE;
480 ret = MSI_ViewClose( query );
481 msiobj_release( &query->hdr );
485 UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
489 TRACE("%p %p\n", query, rec);
493 return ERROR_FUNCTION_FAILED;
494 if( !view->ops->execute )
495 return ERROR_FUNCTION_FAILED;
498 return view->ops->execute( view, rec );
501 UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
504 MSIRECORD *rec = NULL;
507 TRACE("%ld %ld\n", hView, hRec);
509 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
511 return ERROR_INVALID_HANDLE;
515 rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
518 ret = ERROR_INVALID_HANDLE;
523 msiobj_lock( &rec->hdr );
524 ret = MSI_ViewExecute( query, rec );
525 msiobj_unlock( &rec->hdr );
528 msiobj_release( &query->hdr );
530 msiobj_release( &rec->hdr );
535 static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, UINT type )
537 static const WCHAR fmt[] = { '%','d',0 };
540 if (MSITYPE_IS_BINARY(type))
542 else if (type & MSITYPE_LOCALIZABLE)
544 else if (type & MSITYPE_STRING)
548 if (type & MSITYPE_NULLABLE)
551 sprintfW( &szType[1], fmt, (type&0xff) );
553 TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
555 return MSI_RecordSetStringW( rec, field, szType );
558 UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec )
560 UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
562 MSIVIEW *view = query->view;
566 return ERROR_FUNCTION_FAILED;
568 if( !view->ops->get_dimensions )
569 return ERROR_FUNCTION_FAILED;
571 r = view->ops->get_dimensions( view, NULL, &count );
572 if( r != ERROR_SUCCESS )
575 return ERROR_INVALID_PARAMETER;
577 rec = MSI_CreateRecord( count );
579 return ERROR_FUNCTION_FAILED;
581 for( i=0; i<count; i++ )
584 r = view->ops->get_column_info( view, i+1, &name, &type );
585 if( r != ERROR_SUCCESS )
587 if (info == MSICOLINFO_NAMES)
588 MSI_RecordSetStringW( rec, i+1, name );
590 msi_set_record_type_string( rec, i+1, type);
595 return ERROR_SUCCESS;
598 UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
600 MSIQUERY *query = NULL;
601 MSIRECORD *rec = NULL;
604 TRACE("%ld %d %p\n", hView, info, hRec);
607 return ERROR_INVALID_PARAMETER;
609 if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
610 return ERROR_INVALID_PARAMETER;
612 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
614 return ERROR_INVALID_HANDLE;
616 r = MSI_ViewGetColumnInfo( query, info, &rec );
617 if ( r == ERROR_SUCCESS )
619 *hRec = alloc_msihandle( &rec->hdr );
621 r = ERROR_NOT_ENOUGH_MEMORY;
622 msiobj_release( &rec->hdr );
625 msiobj_release( &query->hdr );
630 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
633 MSIVIEW *view = NULL;
634 MSIQUERY *query = NULL;
635 MSIRECORD *rec = NULL;
636 UINT r = ERROR_FUNCTION_FAILED;
638 TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
640 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
642 return ERROR_INVALID_HANDLE;
648 if( !view->ops->modify )
651 rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
654 r = ERROR_INVALID_HANDLE;
658 r = view->ops->modify( view, eModifyMode, rec );
661 msiobj_release( &query->hdr );
663 msiobj_release( &rec->hdr );
668 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
671 MSIQUERY *query = NULL;
672 static const WCHAR szError[] = { 0 };
673 MSIDBERROR r = MSIDBERROR_NOERROR;
676 FIXME("%ld %p %p - returns empty error string\n",
677 handle, szColumnNameBuffer, pcchBuf );
680 return MSIDBERROR_INVALIDARG;
682 query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
684 return MSIDBERROR_INVALIDARG;
686 len = lstrlenW( szError );
687 if( szColumnNameBuffer )
690 lstrcpyW( szColumnNameBuffer, szError );
692 r = MSIDBERROR_MOREDATA;
696 msiobj_release( &query->hdr );
700 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
703 static const CHAR szError[] = { 0 };
704 MSIQUERY *query = NULL;
705 MSIDBERROR r = MSIDBERROR_NOERROR;
708 FIXME("%ld %p %p - returns empty error string\n",
709 handle, szColumnNameBuffer, pcchBuf );
712 return MSIDBERROR_INVALIDARG;
714 query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
716 return MSIDBERROR_INVALIDARG;
718 len = lstrlenA( szError );
719 if( szColumnNameBuffer )
722 lstrcpyA( szColumnNameBuffer, szError );
724 r = MSIDBERROR_MOREDATA;
728 msiobj_release( &query->hdr );
732 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
738 DEFINE_GUID( CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
740 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
741 LPCWSTR szTransformFile, int iErrorCond )
744 IStorage *stg = NULL;
746 TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
748 r = StgOpenStorage( szTransformFile, NULL,
749 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
753 if( TRACE_ON( msi ) )
754 enum_stream_names( stg );
756 r = msi_table_apply_transform( db, stg );
758 IStorage_Release( stg );
763 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
764 LPCWSTR szTransformFile, int iErrorCond)
769 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
771 return ERROR_INVALID_HANDLE;
773 r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
774 msiobj_release( &db->hdr );
778 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb,
779 LPCSTR szTransformFile, int iErrorCond)
784 TRACE("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
786 wstr = strdupAtoW( szTransformFile );
787 if( szTransformFile && !wstr )
788 return ERROR_NOT_ENOUGH_MEMORY;
790 ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
797 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
798 LPCSTR szTransformFile, int iReserved1, int iReserved2 )
800 FIXME("%ld %ld %s %d %d\n", hdb, hdbref,
801 debugstr_a(szTransformFile), iReserved1, iReserved2);
802 return ERROR_CALL_NOT_IMPLEMENTED;
805 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
806 LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
808 FIXME("%ld %ld %s %d %d\n", hdb, hdbref,
809 debugstr_w(szTransformFile), iReserved1, iReserved2);
810 return ERROR_CALL_NOT_IMPLEMENTED;
813 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
820 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
822 return ERROR_INVALID_HANDLE;
824 /* FIXME: lock the database */
826 r = MSI_CommitTables( db );
828 /* FIXME: unlock the database */
830 msiobj_release( &db->hdr );
832 if (r == ERROR_SUCCESS)
834 msi_free( db->deletefile );
835 db->deletefile = NULL;
841 struct msi_primary_key_record_info
847 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
849 struct msi_primary_key_record_info *info = param;
853 type = MSI_RecordGetInteger( rec, 4 );
854 if( type & MSITYPE_KEY )
859 name = MSI_RecordGetString( rec, 3 );
860 MSI_RecordSetStringW( info->rec, info->n, name );
864 return ERROR_SUCCESS;
867 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
868 LPCWSTR table, MSIRECORD **prec )
870 static const WCHAR sql[] = {
871 's','e','l','e','c','t',' ','*',' ',
872 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
873 'w','h','e','r','e',' ',
874 '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
875 struct msi_primary_key_record_info info;
876 MSIQUERY *query = NULL;
880 r = MSI_OpenQuery( db, &query, sql, table );
881 if( r != ERROR_SUCCESS )
886 /* count the number of primary key records */
889 r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
890 if( r == ERROR_SUCCESS )
892 TRACE("Found %d primary keys\n", info.n );
894 /* allocate a record and fill in the names of the tables */
895 info.rec = MSI_CreateRecord( info.n );
897 r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
898 if( r == ERROR_SUCCESS )
901 msiobj_release( &info.rec->hdr );
903 msiobj_release( &query->hdr );
908 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
909 LPCWSTR table, MSIHANDLE* phRec )
911 MSIRECORD *rec = NULL;
915 TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);
917 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
919 return ERROR_INVALID_HANDLE;
921 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
922 if( r == ERROR_SUCCESS )
924 *phRec = alloc_msihandle( &rec->hdr );
926 r = ERROR_NOT_ENOUGH_MEMORY;
927 msiobj_release( &rec->hdr );
929 msiobj_release( &db->hdr );
934 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb,
935 LPCSTR table, MSIHANDLE* phRec)
937 LPWSTR szwTable = NULL;
940 TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);
944 szwTable = strdupAtoW( table );
946 return ERROR_OUTOFMEMORY;
948 r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
949 msi_free( szwTable );
954 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
955 MSIHANDLE hDatabase, LPCSTR szTableName)
957 LPWSTR szwTableName = NULL;
960 TRACE("%lx %s\n", hDatabase, debugstr_a(szTableName));
964 szwTableName = strdupAtoW( szTableName );
966 return MSICONDITION_ERROR;
968 r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
969 msi_free( szwTableName );
974 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
975 MSIHANDLE hDatabase, LPCWSTR szTableName)
980 TRACE("%lx %s\n", hDatabase, debugstr_w(szTableName));
982 db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
984 return MSICONDITION_ERROR;
986 r = MSI_DatabaseIsTablePersistent( db, szTableName );
988 msiobj_release( &db->hdr );