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 MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec )
557 MSIVIEW *view = NULL;
559 if ( !query || !rec )
560 return ERROR_INVALID_HANDLE;
563 if ( !view || !view->ops->modify)
564 return ERROR_FUNCTION_FAILED;
566 return view->ops->modify( view, mode, rec );
569 UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode,
572 MSIQUERY *query = NULL;
573 MSIRECORD *rec = NULL;
574 UINT r = ERROR_FUNCTION_FAILED;
576 TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord);
578 query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
580 return ERROR_INVALID_HANDLE;
582 rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
583 r = MSI_ViewModify( query, eModifyMode, rec );
585 msiobj_release( &query->hdr );
587 msiobj_release( &rec->hdr );
592 MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer,
595 MSIQUERY *query = NULL;
596 static const WCHAR szError[] = { 0 };
597 MSIDBERROR r = MSIDBERROR_NOERROR;
600 FIXME("%ld %p %p - returns empty error string\n",
601 handle, szColumnNameBuffer, pcchBuf );
604 return MSIDBERROR_INVALIDARG;
606 query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
608 return MSIDBERROR_INVALIDARG;
610 len = lstrlenW( szError );
611 if( szColumnNameBuffer )
614 lstrcpyW( szColumnNameBuffer, szError );
616 r = MSIDBERROR_MOREDATA;
620 msiobj_release( &query->hdr );
624 MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer,
627 static const CHAR szError[] = { 0 };
628 MSIQUERY *query = NULL;
629 MSIDBERROR r = MSIDBERROR_NOERROR;
632 FIXME("%ld %p %p - returns empty error string\n",
633 handle, szColumnNameBuffer, pcchBuf );
636 return MSIDBERROR_INVALIDARG;
638 query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
640 return MSIDBERROR_INVALIDARG;
642 len = lstrlenA( szError );
643 if( szColumnNameBuffer )
646 lstrcpyA( szColumnNameBuffer, szError );
648 r = MSIDBERROR_MOREDATA;
652 msiobj_release( &query->hdr );
656 MSIHANDLE WINAPI MsiGetLastErrorRecord( void )
662 DEFINE_GUID( CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46);
664 UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db,
665 LPCWSTR szTransformFile, int iErrorCond )
668 UINT ret = ERROR_FUNCTION_FAILED;
669 IStorage *stg = NULL;
672 TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond);
674 r = StgOpenStorage( szTransformFile, NULL,
675 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
679 r = IStorage_Stat( stg, &stat, STATFLAG_NONAME );
683 if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) )
686 if( TRACE_ON( msi ) )
687 enum_stream_names( stg );
689 ret = msi_table_apply_transform( db, stg );
692 IStorage_Release( stg );
697 UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb,
698 LPCWSTR szTransformFile, int iErrorCond)
703 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
705 return ERROR_INVALID_HANDLE;
707 r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond );
708 msiobj_release( &db->hdr );
712 UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb,
713 LPCSTR szTransformFile, int iErrorCond)
718 TRACE("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
720 wstr = strdupAtoW( szTransformFile );
721 if( szTransformFile && !wstr )
722 return ERROR_NOT_ENOUGH_MEMORY;
724 ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond);
731 UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
732 LPCSTR szTransformFile, int iReserved1, int iReserved2 )
734 FIXME("%ld %ld %s %d %d\n", hdb, hdbref,
735 debugstr_a(szTransformFile), iReserved1, iReserved2);
736 return ERROR_CALL_NOT_IMPLEMENTED;
739 UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
740 LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
742 FIXME("%ld %ld %s %d %d\n", hdb, hdbref,
743 debugstr_w(szTransformFile), iReserved1, iReserved2);
744 return ERROR_CALL_NOT_IMPLEMENTED;
747 UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
754 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
756 return ERROR_INVALID_HANDLE;
758 /* FIXME: lock the database */
760 r = MSI_CommitTables( db );
762 /* FIXME: unlock the database */
764 msiobj_release( &db->hdr );
766 if (r == ERROR_SUCCESS)
768 msi_free( db->deletefile );
769 db->deletefile = NULL;
775 struct msi_primary_key_record_info
781 static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param )
783 struct msi_primary_key_record_info *info = param;
787 type = MSI_RecordGetInteger( rec, 4 );
788 if( type & MSITYPE_KEY )
793 name = MSI_RecordGetString( rec, 3 );
794 MSI_RecordSetStringW( info->rec, info->n, name );
798 return ERROR_SUCCESS;
801 UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db,
802 LPCWSTR table, MSIRECORD **prec )
804 static const WCHAR sql[] = {
805 's','e','l','e','c','t',' ','*',' ',
806 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ',
807 'w','h','e','r','e',' ',
808 '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 };
809 struct msi_primary_key_record_info info;
810 MSIQUERY *query = NULL;
814 r = MSI_OpenQuery( db, &query, sql, table );
815 if( r != ERROR_SUCCESS )
820 /* count the number of primary key records */
823 r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
824 if( r == ERROR_SUCCESS )
826 TRACE("Found %d primary keys\n", info.n );
828 /* allocate a record and fill in the names of the tables */
829 info.rec = MSI_CreateRecord( info.n );
831 r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info );
832 if( r == ERROR_SUCCESS )
835 msiobj_release( &info.rec->hdr );
837 msiobj_release( &query->hdr );
842 UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb,
843 LPCWSTR table, MSIHANDLE* phRec )
845 MSIRECORD *rec = NULL;
849 TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec);
851 db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
853 return ERROR_INVALID_HANDLE;
855 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec );
856 if( r == ERROR_SUCCESS )
858 *phRec = alloc_msihandle( &rec->hdr );
860 r = ERROR_NOT_ENOUGH_MEMORY;
861 msiobj_release( &rec->hdr );
863 msiobj_release( &db->hdr );
868 UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb,
869 LPCSTR table, MSIHANDLE* phRec)
871 LPWSTR szwTable = NULL;
874 TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec);
878 szwTable = strdupAtoW( table );
880 return ERROR_OUTOFMEMORY;
882 r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec );
883 msi_free( szwTable );
888 MSICONDITION WINAPI MsiDatabaseIsTablePersistentA(
889 MSIHANDLE hDatabase, LPCSTR szTableName)
891 LPWSTR szwTableName = NULL;
894 TRACE("%lx %s\n", hDatabase, debugstr_a(szTableName));
898 szwTableName = strdupAtoW( szTableName );
900 return MSICONDITION_ERROR;
902 r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName );
903 msi_free( szwTableName );
908 MSICONDITION WINAPI MsiDatabaseIsTablePersistentW(
909 MSIHANDLE hDatabase, LPCWSTR szTableName)
914 TRACE("%lx %s\n", hDatabase, debugstr_w(szTableName));
916 db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE );
918 return MSICONDITION_ERROR;
920 r = MSI_DatabaseIsTablePersistent( db, szTableName );
922 msiobj_release( &db->hdr );