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"
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
43 static void MSI_CloseView( MSIOBJECTHDR *arg )
45 MSIQUERY *query = (MSIQUERY*) arg;
48 if( query->view && query->view->ops->delete )
49 query->view->ops->delete( query->view );
50 msiobj_release( &query->db->hdr );
52 LIST_FOR_EACH_SAFE( ptr, t, &query->mem )
58 UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, UINT *n )
63 r = table->ops->get_dimensions( table, NULL, &count );
64 if( r != ERROR_SUCCESS )
67 for( i=1; i<=count; i++ )
72 r = table->ops->get_column_info( table, i, &col_name, NULL );
73 if( r != ERROR_SUCCESS )
75 x = lstrcmpW( name, col_name );
84 return ERROR_INVALID_PARAMETER;
87 UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb,
88 LPCSTR szQuery, MSIHANDLE *phView)
93 TRACE("%ld %s %p\n", hdb, debugstr_a(szQuery), phView);
97 szwQuery = strdupAtoW( szQuery );
99 return ERROR_FUNCTION_FAILED;
104 r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
106 msi_free( szwQuery );
110 UINT MSI_DatabaseOpenViewW(MSIDATABASE *db,
111 LPCWSTR szQuery, MSIQUERY **pView)
116 TRACE("%s %p\n", debugstr_w(szQuery), pView);
119 return ERROR_INVALID_PARAMETER;
121 /* pre allocate a handle to hold a pointer to the view */
122 query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY),
125 return ERROR_FUNCTION_FAILED;
127 msiobj_addref( &db->hdr );
131 list_init( &query->mem );
133 r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem );
134 if( r == ERROR_SUCCESS )
136 msiobj_addref( &query->hdr );
140 msiobj_release( &query->hdr );
144 UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... )
150 /* construct the string */
154 query = msi_alloc( size*sizeof(WCHAR) );
156 res = vsnprintfW(query, size, fmt, va);
158 if (res == -1) size *= 2;
159 else if (res >= size) size = res + 1;
163 /* perform the query */
164 r = MSI_DatabaseOpenViewW(db, query, view);
169 UINT MSI_IterateRecords( MSIQUERY *view, DWORD *count,
170 record_func func, LPVOID param )
172 MSIRECORD *rec = NULL;
173 UINT r, n = 0, max = 0;
175 r = MSI_ViewExecute( view, NULL );
176 if( r != ERROR_SUCCESS )
182 /* iterate a query */
183 for( n = 0; (max == 0) || (n < max); n++ )
185 r = MSI_ViewFetch( view, &rec );
186 if( r != ERROR_SUCCESS )
189 r = func( rec, param );
190 msiobj_release( &rec->hdr );
191 if( r != ERROR_SUCCESS )
195 MSI_ViewClose( view );
200 if( r == ERROR_NO_MORE_ITEMS )
206 /* return a single record from a query */
207 MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... )
209 MSIRECORD *rec = NULL;
210 MSIQUERY *view = NULL;
215 /* construct the string */
219 query = msi_alloc( size*sizeof(WCHAR) );
221 res = vsnprintfW(query, size, fmt, va);
223 if (res == -1) size *= 2;
224 else if (res >= size) size = res + 1;
228 /* perform the query */
229 r = MSI_DatabaseOpenViewW(db, query, &view);
232 if( r == ERROR_SUCCESS )
234 MSI_ViewExecute( view, NULL );
235 MSI_ViewFetch( view, &rec );
236 MSI_ViewClose( view );
237 msiobj_release( &view->hdr );
242 UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
243 LPCWSTR szQuery, MSIHANDLE *phView)
246 MSIQUERY *query = NULL;
249 TRACE("%s %p\n", debugstr_w(szQuery), phView);
251 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
253 return ERROR_INVALID_HANDLE;
255 ret = MSI_DatabaseOpenViewW( db, szQuery, &query );
256 if( ret == ERROR_SUCCESS )
258 *phView = alloc_msihandle( &query->hdr );
260 ret = ERROR_NOT_ENOUGH_MEMORY;
261 msiobj_release( &query->hdr );
263 msiobj_release( &db->hdr );
268 UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec)
272 UINT row_count = 0, col_count = 0, i, ival, ret, type;
274 TRACE("%p %p\n", query, prec );
278 return ERROR_FUNCTION_FAILED;
280 ret = view->ops->get_dimensions( view, &row_count, &col_count );
284 return ERROR_INVALID_PARAMETER;
286 if( query->row >= row_count )
287 return ERROR_NO_MORE_ITEMS;
289 rec = MSI_CreateRecord( col_count );
291 return ERROR_FUNCTION_FAILED;
293 for( i=1; i<=col_count; i++ )
295 ret = view->ops->get_column_info( view, i, NULL, &type );
298 ERR("Error getting column type for %d\n", i );
301 if (!MSITYPE_IS_BINARY(type))
303 ret = view->ops->fetch_int( view, query->row, i, &ival );
306 ERR("Error fetching data for %d\n", i );
309 if( ! (type & MSITYPE_VALID ) )
310 ERR("Invalid type!\n");
312 /* check if it's nul (0) - if so, don't set anything */
316 if( type & MSITYPE_STRING )
320 sval = msi_string_lookup_id( query->db->strings, ival );
321 MSI_RecordSetStringW( rec, i, sval );
325 if( (type & MSI_DATASIZEMASK) == 2 )
326 MSI_RecordSetInteger( rec, i, ival - (1<<15) );
328 MSI_RecordSetInteger( rec, i, ival - (1<<31) );
335 ret = view->ops->fetch_stream( view, query->row, i, &stm );
336 if( ( ret == ERROR_SUCCESS ) && stm )
338 MSI_RecordSetIStream( rec, i, stm );
339 IStream_Release( stm );
342 ERR("failed to get stream\n");
349 return ERROR_SUCCESS;
352 UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
355 MSIRECORD *rec = NULL;
358 TRACE("%ld %p\n", hView, record);
361 return ERROR_INVALID_PARAMETER;
364 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
366 return ERROR_INVALID_HANDLE;
367 ret = MSI_ViewFetch( query, &rec );
368 if( ret == ERROR_SUCCESS )
370 *record = alloc_msihandle( &rec->hdr );
372 ret = ERROR_NOT_ENOUGH_MEMORY;
373 msiobj_release( &rec->hdr );
375 msiobj_release( &query->hdr );
379 UINT MSI_ViewClose(MSIQUERY *query)
383 TRACE("%p\n", query );
387 return ERROR_FUNCTION_FAILED;
388 if( !view->ops->close )
389 return ERROR_FUNCTION_FAILED;
391 return view->ops->close( view );
394 UINT WINAPI MsiViewClose(MSIHANDLE hView)
399 TRACE("%ld\n", hView );
401 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
403 return ERROR_INVALID_HANDLE;
405 ret = MSI_ViewClose( query );
406 msiobj_release( &query->hdr );
410 UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec )
414 TRACE("%p %p\n", query, rec);
418 return ERROR_FUNCTION_FAILED;
419 if( !view->ops->execute )
420 return ERROR_FUNCTION_FAILED;
423 return view->ops->execute( view, rec );
426 UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
429 MSIRECORD *rec = NULL;
432 TRACE("%ld %ld\n", hView, hRec);
434 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
436 return ERROR_INVALID_HANDLE;
440 rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD );
443 ret = ERROR_INVALID_HANDLE;
448 msiobj_lock( &rec->hdr );
449 ret = MSI_ViewExecute( query, rec );
450 msiobj_unlock( &rec->hdr );
453 msiobj_release( &query->hdr );
455 msiobj_release( &rec->hdr );
460 static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, UINT type )
462 static const WCHAR fmt[] = { '%','d',0 };
465 if (MSITYPE_IS_BINARY(type))
467 else if (type & MSITYPE_LOCALIZABLE)
469 else if (type & MSITYPE_STRING)
473 if (type & MSITYPE_NULLABLE)
476 sprintfW( &szType[1], fmt, (type&0xff) );
478 TRACE("type %04x -> %s\n", type, debugstr_w(szType) );
480 return MSI_RecordSetStringW( rec, field, szType );
483 UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec )
485 UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type;
487 MSIVIEW *view = query->view;
491 return ERROR_FUNCTION_FAILED;
493 if( !view->ops->get_dimensions )
494 return ERROR_FUNCTION_FAILED;
496 r = view->ops->get_dimensions( view, NULL, &count );
497 if( r != ERROR_SUCCESS )
500 return ERROR_INVALID_PARAMETER;
502 rec = MSI_CreateRecord( count );
504 return ERROR_FUNCTION_FAILED;
506 for( i=0; i<count; i++ )
509 r = view->ops->get_column_info( view, i+1, &name, &type );
510 if( r != ERROR_SUCCESS )
512 if (info == MSICOLINFO_NAMES)
513 MSI_RecordSetStringW( rec, i+1, name );
515 msi_set_record_type_string( rec, i+1, type);
520 return ERROR_SUCCESS;
523 UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
525 MSIQUERY *query = NULL;
526 MSIRECORD *rec = NULL;
529 TRACE("%ld %d %p\n", hView, info, hRec);
532 return ERROR_INVALID_PARAMETER;
534 if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES )
535 return ERROR_INVALID_PARAMETER;
537 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
539 return ERROR_INVALID_HANDLE;
541 r = MSI_ViewGetColumnInfo( query, info, &rec );
542 if ( r == ERROR_SUCCESS )
544 *hRec = alloc_msihandle( &rec->hdr );
546 r = ERROR_NOT_ENOUGH_MEMORY;
547 msiobj_release( &rec->hdr );
550 msiobj_release( &query->hdr );
555 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
558 MSIVIEW *view = NULL;
559 MSIQUERY *query = NULL;
560 MSIRECORD *rec = NULL;
561 UINT r = ERROR_FUNCTION_FAILED;
563 TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
565 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
567 return ERROR_INVALID_HANDLE;
573 if( !view->ops->modify )
576 rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
579 r = ERROR_INVALID_HANDLE;
583 r = view->ops->modify( view, eModifyMode, rec );
586 msiobj_release( &query->hdr );
588 msiobj_release( &rec->hdr );
593 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
596 MSIQUERY *query = NULL;
597 static const WCHAR szError[] = { 0 };
598 MSIDBERROR r = MSIDBERROR_NOERROR;
601 FIXME("%ld %p %p - returns empty error string\n",
602 handle, szColumnNameBuffer, pcchBuf );
605 return MSIDBERROR_INVALIDARG;
607 query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
609 return MSIDBERROR_INVALIDARG;
611 len = lstrlenW( szError );
612 if( szColumnNameBuffer )
615 lstrcpyW( szColumnNameBuffer, szError );
617 r = MSIDBERROR_MOREDATA;
621 msiobj_release( &query->hdr );
625 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
628 static const CHAR szError[] = { 0 };
629 MSIQUERY *query = NULL;
630 MSIDBERROR r = MSIDBERROR_NOERROR;
633 FIXME("%ld %p %p - returns empty error string\n",
634 handle, szColumnNameBuffer, pcchBuf );
637 return MSIDBERROR_INVALIDARG;
639 query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
641 return MSIDBERROR_INVALIDARG;
643 len = lstrlenA( szError );
644 if( szColumnNameBuffer )
647 lstrcpyA( szColumnNameBuffer, szError );
649 r = MSIDBERROR_MOREDATA;
653 msiobj_release( &query->hdr );
657 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
663 DEFINE_GUID( CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
665 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
666 LPCWSTR szTransformFile, int iErrorCond )
669 UINT ret = ERROR_FUNCTION_FAILED;
670 IStorage *stg = NULL;
673 TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
675 r = StgOpenStorage( szTransformFile, NULL,
676 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
680 r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
684 if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
687 if( TRACE_ON( msi ) )
688 enum_stream_names( stg );
690 ret = msi_table_apply_transform( db, stg );
693 IStorage_Release( stg );
698 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
699 LPCWSTR szTransformFile, int iErrorCond)
704 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
706 return ERROR_INVALID_HANDLE;
708 r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
709 msiobj_release( &db->hdr );
713 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb,
714 LPCSTR szTransformFile, int iErrorCond)
719 TRACE("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
721 wstr = strdupAtoW( szTransformFile );
722 if( szTransformFile && !wstr )
723 return ERROR_NOT_ENOUGH_MEMORY;
725 ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
732 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
733 LPCSTR szTransformFile, int iReserved1, int iReserved2 )
735 FIXME("%ld %ld %s %d %d\n", hdb, hdbref,
736 debugstr_a(szTransformFile), iReserved1, iReserved2);
737 return ERROR_CALL_NOT_IMPLEMENTED;
740 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
741 LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
743 FIXME("%ld %ld %s %d %d\n", hdb, hdbref,
744 debugstr_w(szTransformFile), iReserved1, iReserved2);
745 return ERROR_CALL_NOT_IMPLEMENTED;
748 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
755 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
757 return ERROR_INVALID_HANDLE;
759 /* FIXME: lock the database */
761 r = MSI_CommitTables( db );
763 /* FIXME: unlock the database */
765 msiobj_release( &db->hdr );
767 if (r == ERROR_SUCCESS)
769 msi_free( db->deletefile );
770 db->deletefile = NULL;
776 struct msi_primary_key_record_info
782 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
784 struct msi_primary_key_record_info *info = param;
788 type = MSI_RecordGetInteger( rec, 4 );
789 if( type & MSITYPE_KEY )
794 name = MSI_RecordGetString( rec, 3 );
795 MSI_RecordSetStringW( info->rec, info->n, name );
799 return ERROR_SUCCESS;
802 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
803 LPCWSTR table, MSIRECORD **prec )
805 static const WCHAR sql[] = {
806 's','e','l','e','c','t',' ','*',' ',
807 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
808 'w','h','e','r','e',' ',
809 '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
810 struct msi_primary_key_record_info info;
811 MSIQUERY *query = NULL;
815 r = MSI_OpenQuery( db, &query, sql, table );
816 if( r != ERROR_SUCCESS )
821 /* count the number of primary key records */
824 r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
825 if( r == ERROR_SUCCESS )
827 TRACE("Found %d primary keys\n", info.n );
829 /* allocate a record and fill in the names of the tables */
830 info.rec = MSI_CreateRecord( info.n );
832 r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
833 if( r == ERROR_SUCCESS )
836 msiobj_release( &info.rec->hdr );
838 msiobj_release( &query->hdr );
843 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
844 LPCWSTR table, MSIHANDLE* phRec )
846 MSIRECORD *rec = NULL;
850 TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);
852 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
854 return ERROR_INVALID_HANDLE;
856 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
857 if( r == ERROR_SUCCESS )
859 *phRec = alloc_msihandle( &rec->hdr );
861 r = ERROR_NOT_ENOUGH_MEMORY;
862 msiobj_release( &rec->hdr );
864 msiobj_release( &db->hdr );
869 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb,
870 LPCSTR table, MSIHANDLE* phRec)
872 LPWSTR szwTable = NULL;
875 TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);
879 szwTable = strdupAtoW( table );
881 return ERROR_OUTOFMEMORY;
883 r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
884 msi_free( szwTable );
889 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
890 MSIHANDLE hDatabase, LPCSTR szTableName)
892 LPWSTR szwTableName = NULL;
895 TRACE("%lx %s\n", hDatabase, debugstr_a(szTableName));
899 szwTableName = strdupAtoW( szTableName );
901 return MSICONDITION_ERROR;
903 r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
904 msi_free( szwTableName );
909 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
910 MSIHANDLE hDatabase, LPCWSTR szTableName)
915 TRACE("%lx %s\n", hDatabase, debugstr_w(szTableName));
917 db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
919 return MSICONDITION_ERROR;
921 r = MSI_DatabaseIsTablePersistent( db, szTableName );
923 msiobj_release( &db->hdr );