/* * Implementation of the Microsoft Installer (msi.dll) * * Copyright 2002-2005 Mike McCormack for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include <stdarg.h> #define COBJMACROS #include "windef.h" #include "winbase.h" #include "winerror.h" #include "wine/debug.h" #include "wine/exception.h" #include "msi.h" #include "msiquery.h" #include "objbase.h" #include "objidl.h" #include "winnls.h" #include "msipriv.h" #include "query.h" #include "winemsi.h" #include "initguid.h" WINE_DEFAULT_DEBUG_CHANNEL(msi); static void MSI_CloseView( MSIOBJECTHDR *arg ) { MSIQUERY *query = (MSIQUERY*) arg; struct list *ptr, *t; if( query->view && query->view->ops->delete ) query->view->ops->delete( query->view ); msiobj_release( &query->db->hdr ); LIST_FOR_EACH_SAFE( ptr, t, &query->mem ) { msi_free( ptr ); } } UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, LPCWSTR table_name, UINT *n ) { LPCWSTR col_name, haystack_table_name; UINT i, count, r; r = table->ops->get_dimensions( table, NULL, &count ); if( r != ERROR_SUCCESS ) return r; for( i=1; i<=count; i++ ) { INT x; r = table->ops->get_column_info( table, i, &col_name, NULL, NULL, &haystack_table_name ); if( r != ERROR_SUCCESS ) return r; x = wcscmp( name, col_name ); if( table_name ) x |= wcscmp( table_name, haystack_table_name ); if( !x ) { *n = i; return ERROR_SUCCESS; } } return ERROR_INVALID_PARAMETER; } UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb, LPCSTR szQuery, MSIHANDLE *phView) { UINT r; LPWSTR szwQuery; TRACE("%d %s %p\n", hdb, debugstr_a(szQuery), phView); if( szQuery ) { szwQuery = strdupAtoW( szQuery ); if( !szwQuery ) return ERROR_FUNCTION_FAILED; } else szwQuery = NULL; r = MsiDatabaseOpenViewW( hdb, szwQuery, phView); msi_free( szwQuery ); return r; } UINT MSI_DatabaseOpenViewW(MSIDATABASE *db, LPCWSTR szQuery, MSIQUERY **pView) { MSIQUERY *query; UINT r; TRACE("%s %p\n", debugstr_w(szQuery), pView); /* pre allocate a handle to hold a pointer to the view */ query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY), MSI_CloseView ); if( !query ) return ERROR_FUNCTION_FAILED; msiobj_addref( &db->hdr ); query->db = db; list_init( &query->mem ); r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem ); if( r == ERROR_SUCCESS ) { msiobj_addref( &query->hdr ); *pView = query; } msiobj_release( &query->hdr ); return r; } UINT WINAPIV MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... ) { UINT r; int size = 100, res; LPWSTR query; /* construct the string */ for (;;) { __ms_va_list va; query = msi_alloc( size*sizeof(WCHAR) ); __ms_va_start(va, fmt); res = vswprintf(query, size, fmt, va); __ms_va_end(va); if (res == -1) size *= 2; else if (res >= size) size = res + 1; else break; msi_free( query ); } /* perform the query */ r = MSI_DatabaseOpenViewW(db, query, view); msi_free(query); return r; } UINT MSI_IterateRecords( MSIQUERY *view, LPDWORD count, record_func func, LPVOID param ) { MSIRECORD *rec = NULL; UINT r, n = 0, max = 0; r = MSI_ViewExecute( view, NULL ); if( r != ERROR_SUCCESS ) return r; if( count ) max = *count; /* iterate a query */ for( n = 0; (max == 0) || (n < max); n++ ) { r = MSI_ViewFetch( view, &rec ); if( r != ERROR_SUCCESS ) break; if (func) r = func( rec, param ); msiobj_release( &rec->hdr ); if( r != ERROR_SUCCESS ) break; } MSI_ViewClose( view ); if( count ) *count = n; if( r == ERROR_NO_MORE_ITEMS ) r = ERROR_SUCCESS; return r; } /* return a single record from a query */ MSIRECORD * WINAPIV MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... ) { MSIRECORD *rec = NULL; MSIQUERY *view = NULL; UINT r; int size = 100, res; LPWSTR query; /* construct the string */ for (;;) { __ms_va_list va; query = msi_alloc( size*sizeof(WCHAR) ); __ms_va_start(va, fmt); res = vswprintf(query, size, fmt, va); __ms_va_end(va); if (res == -1) size *= 2; else if (res >= size) size = res + 1; else break; msi_free( query ); } /* perform the query */ r = MSI_DatabaseOpenViewW(db, query, &view); msi_free(query); if( r == ERROR_SUCCESS ) { MSI_ViewExecute( view, NULL ); MSI_ViewFetch( view, &rec ); MSI_ViewClose( view ); msiobj_release( &view->hdr ); } return rec; } UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb, LPCWSTR szQuery, MSIHANDLE *phView) { MSIDATABASE *db; MSIQUERY *query = NULL; UINT ret; TRACE("%s %p\n", debugstr_w(szQuery), phView); if (!phView) return ERROR_INVALID_PARAMETER; if (!szQuery) return ERROR_BAD_QUERY_SYNTAX; db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); if( !db ) { MSIHANDLE remote, remote_view; if (!(remote = msi_get_remote(hdb))) return ERROR_INVALID_HANDLE; __TRY { ret = remote_DatabaseOpenView(remote, szQuery, &remote_view); } __EXCEPT(rpc_filter) { ret = GetExceptionCode(); } __ENDTRY if (!ret) *phView = alloc_msi_remote_handle(remote_view); return ret; } ret = MSI_DatabaseOpenViewW( db, szQuery, &query ); if( ret == ERROR_SUCCESS ) { *phView = alloc_msihandle( &query->hdr ); if (! *phView) ret = ERROR_NOT_ENOUGH_MEMORY; msiobj_release( &query->hdr ); } msiobj_release( &db->hdr ); return ret; } UINT msi_view_refresh_row(MSIDATABASE *db, MSIVIEW *view, UINT row, MSIRECORD *rec) { UINT row_count = 0, col_count = 0, i, ival, ret, type; TRACE("%p %p %d %p\n", db, view, row, rec); ret = view->ops->get_dimensions(view, &row_count, &col_count); if (ret) return ret; if (!col_count) return ERROR_INVALID_PARAMETER; for (i = 1; i <= col_count; i++) { ret = view->ops->get_column_info(view, i, NULL, &type, NULL, NULL); if (ret) { ERR("Error getting column type for %d\n", i); continue; } if (MSITYPE_IS_BINARY(type)) { IStream *stm = NULL; ret = view->ops->fetch_stream(view, row, i, &stm); if ((ret == ERROR_SUCCESS) && stm) { MSI_RecordSetIStream(rec, i, stm); IStream_Release(stm); } else WARN("failed to get stream\n"); continue; } ret = view->ops->fetch_int(view, row, i, &ival); if (ret) { ERR("Error fetching data for %d\n", i); continue; } if (! (type & MSITYPE_VALID)) ERR("Invalid type!\n"); if (type & MSITYPE_STRING) { int len; const WCHAR *sval = msi_string_lookup(db->strings, ival, &len); msi_record_set_string(rec, i, sval, len); } else { if ((type & MSI_DATASIZEMASK) == 2) MSI_RecordSetInteger(rec, i, ival ? ival - (1<<15) : MSI_NULL_INTEGER); else MSI_RecordSetInteger(rec, i, ival - (1u<<31)); } } return ERROR_SUCCESS; } UINT msi_view_get_row(MSIDATABASE *db, MSIVIEW *view, UINT row, MSIRECORD **rec) { UINT row_count = 0, col_count = 0, r; MSIRECORD *object; TRACE("view %p, row %u, rec %p.\n", view, row, rec); if ((r = view->ops->get_dimensions(view, &row_count, &col_count))) return r; if (row >= row_count) return ERROR_NO_MORE_ITEMS; if (!(object = MSI_CreateRecord( col_count ))) return ERROR_OUTOFMEMORY; if ((r = msi_view_refresh_row(db, view, row, object))) msiobj_release( &object->hdr ); else *rec = object; return r; } UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec) { MSIVIEW *view; UINT r; TRACE("%p %p\n", query, prec ); view = query->view; if( !view ) return ERROR_FUNCTION_FAILED; r = msi_view_get_row(query->db, view, query->row, prec); if (r == ERROR_SUCCESS) { query->row ++; (*prec)->cookie = (UINT64)(ULONG_PTR)query; MSI_RecordSetInteger(*prec, 0, 1); } return r; } UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record) { MSIQUERY *query; MSIRECORD *rec = NULL; UINT ret; TRACE("%d %p\n", hView, record); if( !record ) return ERROR_INVALID_PARAMETER; *record = 0; query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); if (!query) { struct wire_record *wire_rec = NULL; MSIHANDLE remote; if (!(remote = msi_get_remote(hView))) return ERROR_INVALID_HANDLE; __TRY { ret = remote_ViewFetch(remote, &wire_rec); } __EXCEPT(rpc_filter) { ret = GetExceptionCode(); } __ENDTRY if (!ret) { ret = unmarshal_record(wire_rec, record); free_remote_record(wire_rec); } return ret; } ret = MSI_ViewFetch( query, &rec ); if( ret == ERROR_SUCCESS ) { *record = alloc_msihandle( &rec->hdr ); if (! *record) ret = ERROR_NOT_ENOUGH_MEMORY; msiobj_release( &rec->hdr ); } msiobj_release( &query->hdr ); return ret; } UINT MSI_ViewClose(MSIQUERY *query) { MSIVIEW *view; TRACE("%p\n", query ); view = query->view; if( !view ) return ERROR_FUNCTION_FAILED; if( !view->ops->close ) return ERROR_FUNCTION_FAILED; return view->ops->close( view ); } UINT WINAPI MsiViewClose(MSIHANDLE hView) { MSIQUERY *query; UINT ret; TRACE("%d\n", hView ); query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); if (!query) { MSIHANDLE remote; if (!(remote = msi_get_remote(hView))) return ERROR_INVALID_HANDLE; __TRY { ret = remote_ViewClose(remote); } __EXCEPT(rpc_filter) { ret = GetExceptionCode(); } __ENDTRY return ret; } ret = MSI_ViewClose( query ); msiobj_release( &query->hdr ); return ret; } UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec ) { MSIVIEW *view; TRACE("%p %p\n", query, rec); view = query->view; if( !view ) return ERROR_FUNCTION_FAILED; if( !view->ops->execute ) return ERROR_FUNCTION_FAILED; query->row = 0; return view->ops->execute( view, rec ); } UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec) { MSIQUERY *query; MSIRECORD *rec = NULL; UINT ret; TRACE("%d %d\n", hView, hRec); if( hRec ) { rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD ); if( !rec ) return ERROR_INVALID_HANDLE; } query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); if( !query ) { MSIHANDLE remote; if (!(remote = msi_get_remote(hView))) return ERROR_INVALID_HANDLE; __TRY { ret = remote_ViewExecute(remote, rec ? (struct wire_record *)&rec->count : NULL); } __EXCEPT(rpc_filter) { ret = GetExceptionCode(); } __ENDTRY if (rec) msiobj_release(&rec->hdr); return ret; } msiobj_lock( &rec->hdr ); ret = MSI_ViewExecute( query, rec ); msiobj_unlock( &rec->hdr ); msiobj_release( &query->hdr ); if( rec ) msiobj_release( &rec->hdr ); return ret; } static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, UINT type, BOOL temporary ) { static const WCHAR fmt[] = { '%','d',0 }; WCHAR szType[0x10]; if (MSITYPE_IS_BINARY(type)) szType[0] = 'v'; else if (type & MSITYPE_LOCALIZABLE) szType[0] = 'l'; else if (type & MSITYPE_UNKNOWN) szType[0] = 'f'; else if (type & MSITYPE_STRING) { if (temporary) szType[0] = 'g'; else szType[0] = 's'; } else { if (temporary) szType[0] = 'j'; else szType[0] = 'i'; } if (type & MSITYPE_NULLABLE) szType[0] &= ~0x20; swprintf( &szType[1], ARRAY_SIZE(szType) - 1, fmt, (type&0xff) ); TRACE("type %04x -> %s\n", type, debugstr_w(szType) ); return MSI_RecordSetStringW( rec, field, szType ); } UINT MSI_ViewGetColumnInfo( MSIQUERY *query, MSICOLINFO info, MSIRECORD **prec ) { UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type; MSIRECORD *rec; MSIVIEW *view = query->view; LPCWSTR name; BOOL temporary; if( !view ) return ERROR_FUNCTION_FAILED; if( !view->ops->get_dimensions ) return ERROR_FUNCTION_FAILED; r = view->ops->get_dimensions( view, NULL, &count ); if( r != ERROR_SUCCESS ) return r; if( !count ) return ERROR_INVALID_PARAMETER; rec = MSI_CreateRecord( count ); if( !rec ) return ERROR_FUNCTION_FAILED; for( i=0; i<count; i++ ) { name = NULL; r = view->ops->get_column_info( view, i+1, &name, &type, &temporary, NULL ); if( r != ERROR_SUCCESS ) continue; if (info == MSICOLINFO_NAMES) MSI_RecordSetStringW( rec, i+1, name ); else msi_set_record_type_string( rec, i+1, type, temporary ); } *prec = rec; return ERROR_SUCCESS; } UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec) { MSIQUERY *query = NULL; MSIRECORD *rec = NULL; UINT r; TRACE("%d %d %p\n", hView, info, hRec); if( !hRec ) return ERROR_INVALID_PARAMETER; if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES ) return ERROR_INVALID_PARAMETER; query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); if (!query) { struct wire_record *wire_rec = NULL; MSIHANDLE remote; if (!(remote = msi_get_remote(hView))) return ERROR_INVALID_HANDLE; __TRY { r = remote_ViewGetColumnInfo(remote, info, &wire_rec); } __EXCEPT(rpc_filter) { r = GetExceptionCode(); } __ENDTRY if (!r) { r = unmarshal_record(wire_rec, hRec); free_remote_record(wire_rec); } return r; } r = MSI_ViewGetColumnInfo( query, info, &rec ); if ( r == ERROR_SUCCESS ) { *hRec = alloc_msihandle( &rec->hdr ); if ( !*hRec ) r = ERROR_NOT_ENOUGH_MEMORY; msiobj_release( &rec->hdr ); } msiobj_release( &query->hdr ); return r; } UINT MSI_ViewModify( MSIQUERY *query, MSIMODIFY mode, MSIRECORD *rec ) { MSIVIEW *view = NULL; UINT r; if ( !query || !rec ) return ERROR_INVALID_HANDLE; view = query->view; if ( !view || !view->ops->modify) return ERROR_FUNCTION_FAILED; if ( mode == MSIMODIFY_UPDATE && rec->cookie != (UINT64)(ULONG_PTR)query ) return ERROR_FUNCTION_FAILED; r = view->ops->modify( view, mode, rec, query->row - 1 ); if (mode == MSIMODIFY_DELETE && r == ERROR_SUCCESS) query->row--; return r; } UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode, MSIHANDLE hRecord) { MSIQUERY *query = NULL; MSIRECORD *rec = NULL; UINT r = ERROR_FUNCTION_FAILED; TRACE("%d %x %d\n", hView, eModifyMode, hRecord); rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); if (!rec) return ERROR_INVALID_HANDLE; query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); if (!query) { struct wire_record *wire_refreshed = NULL; MSIHANDLE remote; if (!(remote = msi_get_remote(hView))) return ERROR_INVALID_HANDLE; __TRY { r = remote_ViewModify(remote, eModifyMode, (struct wire_record *)&rec->count, &wire_refreshed); } __EXCEPT(rpc_filter) { r = GetExceptionCode(); } __ENDTRY if (!r && (eModifyMode == MSIMODIFY_REFRESH || eModifyMode == MSIMODIFY_SEEK)) { r = copy_remote_record(wire_refreshed, hRecord); free_remote_record(wire_refreshed); } msiobj_release(&rec->hdr); return r; } r = MSI_ViewModify( query, eModifyMode, rec ); msiobj_release( &query->hdr ); msiobj_release(&rec->hdr); return r; } MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR buffer, LPDWORD buflen ) { MSIQUERY *query; const WCHAR *column; MSIDBERROR r; TRACE("%u %p %p\n", handle, buffer, buflen); if (!buflen) return MSIDBERROR_INVALIDARG; if (!(query = msihandle2msiinfo(handle, MSIHANDLETYPE_VIEW))) { WCHAR *remote_column = NULL; MSIHANDLE remote; if (!(remote = msi_get_remote(handle))) return MSIDBERROR_INVALIDARG; if (!*buflen) return MSIDBERROR_FUNCTIONERROR; __TRY { r = remote_ViewGetError(remote, &remote_column); } __EXCEPT(rpc_filter) { r = GetExceptionCode(); } __ENDTRY; if (msi_strncpyW(remote_column ? remote_column : szEmpty, -1, buffer, buflen) == ERROR_MORE_DATA) r = MSIDBERROR_MOREDATA; if (remote_column) midl_user_free(remote_column); return r; } if ((r = query->view->error)) column = query->view->error_column; else column = szEmpty; if (msi_strncpyW(column, -1, buffer, buflen) == ERROR_MORE_DATA) r = MSIDBERROR_MOREDATA; msiobj_release( &query->hdr ); return r; } MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR buffer, LPDWORD buflen ) { MSIQUERY *query; const WCHAR *column; MSIDBERROR r; TRACE("%u %p %p\n", handle, buffer, buflen); if (!buflen) return MSIDBERROR_INVALIDARG; if (!(query = msihandle2msiinfo(handle, MSIHANDLETYPE_VIEW))) { WCHAR *remote_column = NULL; MSIHANDLE remote; if (!(remote = msi_get_remote(handle))) return MSIDBERROR_INVALIDARG; if (!*buflen) return MSIDBERROR_FUNCTIONERROR; __TRY { r = remote_ViewGetError(remote, &remote_column); } __EXCEPT(rpc_filter) { r = GetExceptionCode(); } __ENDTRY; if (msi_strncpyWtoA(remote_column ? remote_column : szEmpty, -1, buffer, buflen, FALSE) == ERROR_MORE_DATA) r = MSIDBERROR_MOREDATA; if (remote_column) midl_user_free(remote_column); return r; } if ((r = query->view->error)) column = query->view->error_column; else column = szEmpty; if (msi_strncpyWtoA(column, -1, buffer, buflen, FALSE) == ERROR_MORE_DATA) r = MSIDBERROR_MOREDATA; msiobj_release( &query->hdr ); return r; } MSIHANDLE WINAPI MsiGetLastErrorRecord( void ) { FIXME("\n"); return 0; } UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db, const WCHAR *transform, int error_cond ) { HRESULT hr; UINT ret = ERROR_FUNCTION_FAILED; IStorage *stg; STATSTG stat; TRACE( "%p %s %08x\n", db, debugstr_w(transform), error_cond ); if (*transform == ':') { hr = IStorage_OpenStorage( db->storage, transform + 1, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg ); if (FAILED( hr )) { WARN( "failed to open substorage transform 0x%08x\n", hr ); return ERROR_FUNCTION_FAILED; } } else { hr = StgOpenStorage( transform, NULL, STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg ); if (FAILED( hr )) { WARN( "failed to open file transform 0x%08x\n", hr ); return ERROR_FUNCTION_FAILED; } } hr = IStorage_Stat( stg, &stat, STATFLAG_NONAME ); if (FAILED( hr )) goto end; if (!IsEqualGUID( &stat.clsid, &CLSID_MsiTransform )) goto end; if (TRACE_ON( msi )) enum_stream_names( stg ); ret = msi_table_apply_transform( db, stg ); end: IStorage_Release( stg ); return ret; } UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, const WCHAR *transform, int error_cond ) { MSIDATABASE *db; UINT r; if (error_cond) FIXME( "ignoring error conditions\n" ); if (!(db = msihandle2msiinfo(hdb, MSIHANDLETYPE_DATABASE))) return ERROR_INVALID_HANDLE; r = MSI_DatabaseApplyTransformW( db, transform, error_cond ); msiobj_release( &db->hdr ); return r; } UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, const char *transform, int error_cond ) { WCHAR *wstr; UINT ret; TRACE( "%d %s %08x\n", hdb, debugstr_a(transform), error_cond ); wstr = strdupAtoW( transform ); if (transform && !wstr) return ERROR_NOT_ENOUGH_MEMORY; ret = MsiDatabaseApplyTransformW( hdb, wstr, error_cond ); msi_free( wstr ); return ret; } UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref, LPCSTR szTransformFile, int iReserved1, int iReserved2 ) { FIXME("%d %d %s %d %d\n", hdb, hdbref, debugstr_a(szTransformFile), iReserved1, iReserved2); return ERROR_CALL_NOT_IMPLEMENTED; } UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref, LPCWSTR szTransformFile, int iReserved1, int iReserved2 ) { FIXME("%d %d %s %d %d\n", hdb, hdbref, debugstr_w(szTransformFile), iReserved1, iReserved2); return ERROR_CALL_NOT_IMPLEMENTED; } UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb ) { MSIDATABASE *db; UINT r; TRACE("%d\n", hdb); db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); if( !db ) { MSIHANDLE remote; if (!(remote = msi_get_remote(hdb))) return ERROR_INVALID_HANDLE; WARN("not allowed during a custom action!\n"); return ERROR_SUCCESS; } if (db->mode == MSI_OPEN_READONLY) { msiobj_release( &db->hdr ); return ERROR_SUCCESS; } /* FIXME: lock the database */ r = msi_commit_streams( db ); if (r != ERROR_SUCCESS) ERR("Failed to commit streams!\n"); else { r = MSI_CommitTables( db ); if (r != ERROR_SUCCESS) ERR("Failed to commit tables!\n"); } /* FIXME: unlock the database */ msiobj_release( &db->hdr ); if (r == ERROR_SUCCESS) { msi_free( db->deletefile ); db->deletefile = NULL; } return r; } struct msi_primary_key_record_info { DWORD n; MSIRECORD *rec; }; static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param ) { struct msi_primary_key_record_info *info = param; LPCWSTR name, table; DWORD type; type = MSI_RecordGetInteger( rec, 4 ); if( type & MSITYPE_KEY ) { info->n++; if( info->rec ) { if ( info->n == 1 ) { table = MSI_RecordGetString( rec, 1 ); MSI_RecordSetStringW( info->rec, 0, table); } name = MSI_RecordGetString( rec, 3 ); MSI_RecordSetStringW( info->rec, info->n, name ); } } return ERROR_SUCCESS; } UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db, LPCWSTR table, MSIRECORD **prec ) { static const WCHAR sql[] = { 's','e','l','e','c','t',' ','*',' ', 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ', 'w','h','e','r','e',' ', '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 }; struct msi_primary_key_record_info info; MSIQUERY *query = NULL; UINT r; if (!TABLE_Exists( db, table )) return ERROR_INVALID_TABLE; r = MSI_OpenQuery( db, &query, sql, table ); if( r != ERROR_SUCCESS ) return r; /* count the number of primary key records */ info.n = 0; info.rec = 0; r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info ); if( r == ERROR_SUCCESS ) { TRACE("Found %d primary keys\n", info.n ); /* allocate a record and fill in the names of the tables */ info.rec = MSI_CreateRecord( info.n ); info.n = 0; r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info ); if( r == ERROR_SUCCESS ) *prec = info.rec; else msiobj_release( &info.rec->hdr ); } msiobj_release( &query->hdr ); return r; } UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb, LPCWSTR table, MSIHANDLE* phRec ) { MSIRECORD *rec = NULL; MSIDATABASE *db; UINT r; TRACE("%d %s %p\n", hdb, debugstr_w(table), phRec); db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); if( !db ) { struct wire_record *wire_rec = NULL; MSIHANDLE remote; if (!(remote = msi_get_remote(hdb))) return ERROR_INVALID_HANDLE; __TRY { r = remote_DatabaseGetPrimaryKeys(remote, table, &wire_rec); } __EXCEPT(rpc_filter) { r = GetExceptionCode(); } __ENDTRY if (!r) { r = unmarshal_record(wire_rec, phRec); free_remote_record(wire_rec); } return r; } r = MSI_DatabaseGetPrimaryKeys( db, table, &rec ); if( r == ERROR_SUCCESS ) { *phRec = alloc_msihandle( &rec->hdr ); if (! *phRec) r = ERROR_NOT_ENOUGH_MEMORY; msiobj_release( &rec->hdr ); } msiobj_release( &db->hdr ); return r; } UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, LPCSTR table, MSIHANDLE* phRec) { LPWSTR szwTable = NULL; UINT r; TRACE("%d %s %p\n", hdb, debugstr_a(table), phRec); if( table ) { szwTable = strdupAtoW( table ); if( !szwTable ) return ERROR_OUTOFMEMORY; } r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec ); msi_free( szwTable ); return r; } MSICONDITION WINAPI MsiDatabaseIsTablePersistentA( MSIHANDLE hDatabase, LPCSTR szTableName) { LPWSTR szwTableName = NULL; MSICONDITION r; TRACE("%x %s\n", hDatabase, debugstr_a(szTableName)); if( szTableName ) { szwTableName = strdupAtoW( szTableName ); if( !szwTableName ) return MSICONDITION_ERROR; } r = MsiDatabaseIsTablePersistentW( hDatabase, szwTableName ); msi_free( szwTableName ); return r; } MSICONDITION WINAPI MsiDatabaseIsTablePersistentW( MSIHANDLE hDatabase, LPCWSTR szTableName) { MSIDATABASE *db; MSICONDITION r; TRACE("%x %s\n", hDatabase, debugstr_w(szTableName)); db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE ); if( !db ) { MSIHANDLE remote; if (!(remote = msi_get_remote(hDatabase))) return MSICONDITION_ERROR; __TRY { r = remote_DatabaseIsTablePersistent(remote, szTableName); } __EXCEPT(rpc_filter) { r = MSICONDITION_ERROR; } __ENDTRY return r; } r = MSI_DatabaseIsTablePersistent( db, szTableName ); msiobj_release( &db->hdr ); return r; } UINT __cdecl s_remote_ViewClose(MSIHANDLE view) { return MsiViewClose(view); } UINT __cdecl s_remote_ViewExecute(MSIHANDLE view, struct wire_record *remote_rec) { MSIHANDLE rec = 0; UINT r; if ((r = unmarshal_record(remote_rec, &rec))) return r; r = MsiViewExecute(view, rec); MsiCloseHandle(rec); return r; } UINT __cdecl s_remote_ViewFetch(MSIHANDLE view, struct wire_record **rec) { MSIHANDLE handle; UINT r = MsiViewFetch(view, &handle); *rec = NULL; if (!r) *rec = marshal_record(handle); MsiCloseHandle(handle); return r; } UINT __cdecl s_remote_ViewGetColumnInfo(MSIHANDLE view, MSICOLINFO info, struct wire_record **rec) { MSIHANDLE handle; UINT r = MsiViewGetColumnInfo(view, info, &handle); *rec = NULL; if (!r) *rec = marshal_record(handle); MsiCloseHandle(handle); return r; } MSIDBERROR __cdecl s_remote_ViewGetError(MSIHANDLE view, LPWSTR *column) { WCHAR empty[1]; DWORD size = 1; UINT r; r = MsiViewGetErrorW(view, empty, &size); if (r == MSIDBERROR_MOREDATA) { if (!(*column = midl_user_allocate(++size * sizeof(WCHAR)))) return MSIDBERROR_FUNCTIONERROR; r = MsiViewGetErrorW(view, *column, &size); } return r; } UINT __cdecl s_remote_ViewModify(MSIHANDLE view, MSIMODIFY mode, struct wire_record *remote_rec, struct wire_record **remote_refreshed) { MSIHANDLE handle = 0; UINT r; if ((r = unmarshal_record(remote_rec, &handle))) return r; r = MsiViewModify(view, mode, handle); *remote_refreshed = NULL; if (!r && (mode == MSIMODIFY_REFRESH || mode == MSIMODIFY_SEEK)) *remote_refreshed = marshal_record(handle); MsiCloseHandle(handle); return r; }