Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygendatabase.c
Go to the documentation of this file.
00001 /* 00002 * Implementation of the Microsoft Installer (msi.dll) 00003 * 00004 * Copyright 2002,2003,2004,2005 Mike McCormack for CodeWeavers 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Lesser General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2.1 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Lesser General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Lesser General Public 00017 * License along with this library; if not, write to the Free Software 00018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 00019 */ 00020 00021 #include <stdarg.h> 00022 #include <stdio.h> 00023 00024 #define COBJMACROS 00025 #define NONAMELESSUNION 00026 00027 #include "windef.h" 00028 #include "winbase.h" 00029 #include "winreg.h" 00030 #include "winnls.h" 00031 #include "wine/debug.h" 00032 #include "wine/unicode.h" 00033 #include "msi.h" 00034 #include "msiquery.h" 00035 #include "msipriv.h" 00036 #include "objidl.h" 00037 #include "objbase.h" 00038 #include "msiserver.h" 00039 #include "query.h" 00040 00041 #include "initguid.h" 00042 00043 WINE_DEFAULT_DEBUG_CHANNEL(msi); 00044 00045 /* 00046 * .MSI file format 00047 * 00048 * An .msi file is a structured storage file. 00049 * It contains a number of streams. 00050 * A stream for each table in the database. 00051 * Two streams for the string table in the database. 00052 * Any binary data in a table is a reference to a stream. 00053 */ 00054 00055 #define IS_INTMSIDBOPEN(x) (((ULONG_PTR)(x) >> 16) == 0) 00056 00057 typedef struct tagMSITRANSFORM { 00058 struct list entry; 00059 IStorage *stg; 00060 } MSITRANSFORM; 00061 00062 typedef struct tagMSISTREAM { 00063 struct list entry; 00064 IStorage *stg; 00065 IStream *stm; 00066 } MSISTREAM; 00067 00068 static UINT find_open_stream( MSIDATABASE *db, IStorage *stg, LPCWSTR name, IStream **stm ) 00069 { 00070 MSISTREAM *stream; 00071 00072 LIST_FOR_EACH_ENTRY( stream, &db->streams, MSISTREAM, entry ) 00073 { 00074 HRESULT r; 00075 STATSTG stat; 00076 00077 if (stream->stg != stg) continue; 00078 00079 r = IStream_Stat( stream->stm, &stat, 0 ); 00080 if( FAILED( r ) ) 00081 { 00082 WARN("failed to stat stream r = %08x!\n", r); 00083 continue; 00084 } 00085 00086 if( !strcmpW( name, stat.pwcsName ) ) 00087 { 00088 TRACE("found %s\n", debugstr_w(name)); 00089 *stm = stream->stm; 00090 CoTaskMemFree( stat.pwcsName ); 00091 return ERROR_SUCCESS; 00092 } 00093 00094 CoTaskMemFree( stat.pwcsName ); 00095 } 00096 00097 return ERROR_FUNCTION_FAILED; 00098 } 00099 00100 UINT msi_clone_open_stream( MSIDATABASE *db, IStorage *stg, LPCWSTR name, IStream **stm ) 00101 { 00102 IStream *stream; 00103 00104 if (find_open_stream( db, stg, name, &stream ) == ERROR_SUCCESS) 00105 { 00106 HRESULT r; 00107 LARGE_INTEGER pos; 00108 00109 r = IStream_Clone( stream, stm ); 00110 if( FAILED( r ) ) 00111 { 00112 WARN("failed to clone stream r = %08x!\n", r); 00113 return ERROR_FUNCTION_FAILED; 00114 } 00115 00116 pos.QuadPart = 0; 00117 r = IStream_Seek( *stm, pos, STREAM_SEEK_SET, NULL ); 00118 if( FAILED( r ) ) 00119 { 00120 IStream_Release( *stm ); 00121 return ERROR_FUNCTION_FAILED; 00122 } 00123 00124 return ERROR_SUCCESS; 00125 } 00126 00127 return ERROR_FUNCTION_FAILED; 00128 } 00129 00130 UINT msi_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm ) 00131 { 00132 HRESULT r; 00133 IStorage *stg; 00134 WCHAR decoded[MAX_STREAM_NAME_LEN]; 00135 00136 decode_streamname( stname, decoded ); 00137 TRACE("%s -> %s\n", debugstr_w(stname), debugstr_w(decoded)); 00138 00139 if (msi_clone_open_stream( db, db->storage, stname, stm ) == ERROR_SUCCESS) 00140 return ERROR_SUCCESS; 00141 00142 r = IStorage_OpenStream( db->storage, stname, NULL, 00143 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm ); 00144 if( FAILED( r ) ) 00145 { 00146 MSITRANSFORM *transform; 00147 00148 LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry ) 00149 { 00150 r = IStorage_OpenStream( transform->stg, stname, NULL, 00151 STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm ); 00152 if (SUCCEEDED(r)) 00153 { 00154 stg = transform->stg; 00155 break; 00156 } 00157 } 00158 } 00159 else stg = db->storage; 00160 00161 if( SUCCEEDED(r) ) 00162 { 00163 MSISTREAM *stream; 00164 00165 if (!(stream = msi_alloc( sizeof(MSISTREAM) ))) return ERROR_NOT_ENOUGH_MEMORY; 00166 stream->stg = stg; 00167 IStream_AddRef( stg ); 00168 stream->stm = *stm; 00169 IStream_AddRef( *stm ); 00170 list_add_tail( &db->streams, &stream->entry ); 00171 } 00172 00173 return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED; 00174 } 00175 00176 static void free_transforms( MSIDATABASE *db ) 00177 { 00178 while( !list_empty( &db->transforms ) ) 00179 { 00180 MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ), 00181 MSITRANSFORM, entry ); 00182 list_remove( &t->entry ); 00183 IStorage_Release( t->stg ); 00184 msi_free( t ); 00185 } 00186 } 00187 00188 void msi_destroy_stream( MSIDATABASE *db, const WCHAR *stname ) 00189 { 00190 MSISTREAM *stream, *stream2; 00191 00192 LIST_FOR_EACH_ENTRY_SAFE( stream, stream2, &db->streams, MSISTREAM, entry ) 00193 { 00194 HRESULT r; 00195 STATSTG stat; 00196 00197 r = IStream_Stat( stream->stm, &stat, 0 ); 00198 if (FAILED(r)) 00199 { 00200 WARN("failed to stat stream r = %08x\n", r); 00201 continue; 00202 } 00203 00204 if (!strcmpW( stname, stat.pwcsName )) 00205 { 00206 TRACE("destroying %s\n", debugstr_w(stname)); 00207 00208 list_remove( &stream->entry ); 00209 IStream_Release( stream->stm ); 00210 IStream_Release( stream->stg ); 00211 IStorage_DestroyElement( stream->stg, stname ); 00212 msi_free( stream ); 00213 CoTaskMemFree( stat.pwcsName ); 00214 break; 00215 } 00216 CoTaskMemFree( stat.pwcsName ); 00217 } 00218 } 00219 00220 static void free_streams( MSIDATABASE *db ) 00221 { 00222 while( !list_empty( &db->streams ) ) 00223 { 00224 MSISTREAM *s = LIST_ENTRY(list_head( &db->streams ), MSISTREAM, entry); 00225 list_remove( &s->entry ); 00226 IStream_Release( s->stm ); 00227 IStream_Release( s->stg ); 00228 msi_free( s ); 00229 } 00230 } 00231 00232 void append_storage_to_db( MSIDATABASE *db, IStorage *stg ) 00233 { 00234 MSITRANSFORM *t; 00235 00236 t = msi_alloc( sizeof *t ); 00237 t->stg = stg; 00238 IStorage_AddRef( stg ); 00239 list_add_head( &db->transforms, &t->entry ); 00240 00241 /* the transform may add or replace streams */ 00242 free_streams( db ); 00243 } 00244 00245 static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg ) 00246 { 00247 MSIDATABASE *db = (MSIDATABASE *) arg; 00248 00249 msi_free(db->path); 00250 free_cached_tables( db ); 00251 free_streams( db ); 00252 free_transforms( db ); 00253 if (db->strings) msi_destroy_stringtable( db->strings ); 00254 IStorage_Release( db->storage ); 00255 if (db->deletefile) 00256 { 00257 DeleteFileW( db->deletefile ); 00258 msi_free( db->deletefile ); 00259 } 00260 } 00261 00262 static HRESULT db_initialize( IStorage *stg, const GUID *clsid ) 00263 { 00264 static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 }; 00265 HRESULT hr; 00266 00267 hr = IStorage_SetClass( stg, clsid ); 00268 if (FAILED( hr )) 00269 { 00270 WARN("failed to set class id 0x%08x\n", hr); 00271 return hr; 00272 } 00273 00274 /* create the _Tables stream */ 00275 hr = write_stream_data( stg, szTables, NULL, 0, TRUE ); 00276 if (FAILED( hr )) 00277 { 00278 WARN("failed to create _Tables stream 0x%08x\n", hr); 00279 return hr; 00280 } 00281 00282 hr = msi_init_string_table( stg ); 00283 if (FAILED( hr )) 00284 { 00285 WARN("failed to initialize string table 0x%08x\n", hr); 00286 return hr; 00287 } 00288 00289 hr = IStorage_Commit( stg, 0 ); 00290 if (FAILED( hr )) 00291 { 00292 WARN("failed to commit changes 0x%08x\n", hr); 00293 return hr; 00294 } 00295 00296 return S_OK; 00297 } 00298 00299 UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb) 00300 { 00301 IStorage *stg = NULL; 00302 HRESULT r; 00303 MSIDATABASE *db = NULL; 00304 UINT ret = ERROR_FUNCTION_FAILED; 00305 LPCWSTR szMode, save_path; 00306 STATSTG stat; 00307 BOOL created = FALSE, patch = FALSE; 00308 WCHAR path[MAX_PATH]; 00309 00310 TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) ); 00311 00312 if( !pdb ) 00313 return ERROR_INVALID_PARAMETER; 00314 00315 if (szPersist - MSIDBOPEN_PATCHFILE >= MSIDBOPEN_READONLY && 00316 szPersist - MSIDBOPEN_PATCHFILE <= MSIDBOPEN_CREATEDIRECT) 00317 { 00318 TRACE("Database is a patch\n"); 00319 szPersist -= MSIDBOPEN_PATCHFILE; 00320 patch = TRUE; 00321 } 00322 00323 save_path = szDBPath; 00324 szMode = szPersist; 00325 if( !IS_INTMSIDBOPEN(szPersist) ) 00326 { 00327 if (!CopyFileW( szDBPath, szPersist, FALSE )) 00328 return ERROR_OPEN_FAILED; 00329 00330 szDBPath = szPersist; 00331 szPersist = MSIDBOPEN_TRANSACT; 00332 created = TRUE; 00333 } 00334 00335 if( szPersist == MSIDBOPEN_READONLY ) 00336 { 00337 r = StgOpenStorage( szDBPath, NULL, 00338 STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); 00339 } 00340 else if( szPersist == MSIDBOPEN_CREATE ) 00341 { 00342 r = StgCreateDocfile( szDBPath, 00343 STGM_CREATE|STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg ); 00344 00345 if( SUCCEEDED(r) ) 00346 r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase ); 00347 created = TRUE; 00348 } 00349 else if( szPersist == MSIDBOPEN_CREATEDIRECT ) 00350 { 00351 r = StgCreateDocfile( szDBPath, 00352 STGM_CREATE|STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg ); 00353 00354 if( SUCCEEDED(r) ) 00355 r = db_initialize( stg, patch ? &CLSID_MsiPatch : &CLSID_MsiDatabase ); 00356 created = TRUE; 00357 } 00358 else if( szPersist == MSIDBOPEN_TRANSACT ) 00359 { 00360 r = StgOpenStorage( szDBPath, NULL, 00361 STGM_TRANSACTED|STGM_READWRITE|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); 00362 } 00363 else if( szPersist == MSIDBOPEN_DIRECT ) 00364 { 00365 r = StgOpenStorage( szDBPath, NULL, 00366 STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg); 00367 } 00368 else 00369 { 00370 ERR("unknown flag %p\n",szPersist); 00371 return ERROR_INVALID_PARAMETER; 00372 } 00373 00374 if( FAILED( r ) || !stg ) 00375 { 00376 WARN("open failed r = %08x for %s\n", r, debugstr_w(szDBPath)); 00377 return ERROR_FUNCTION_FAILED; 00378 } 00379 00380 r = IStorage_Stat( stg, &stat, STATFLAG_NONAME ); 00381 if( FAILED( r ) ) 00382 { 00383 FIXME("Failed to stat storage\n"); 00384 goto end; 00385 } 00386 00387 if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) && 00388 !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) && 00389 !IsEqualGUID( &stat.clsid, &CLSID_MsiTransform ) ) 00390 { 00391 ERR("storage GUID is not a MSI database GUID %s\n", 00392 debugstr_guid(&stat.clsid) ); 00393 goto end; 00394 } 00395 00396 if ( patch && !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) 00397 { 00398 ERR("storage GUID is not the MSI patch GUID %s\n", 00399 debugstr_guid(&stat.clsid) ); 00400 ret = ERROR_OPEN_FAILED; 00401 goto end; 00402 } 00403 00404 db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE), 00405 MSI_CloseDatabase ); 00406 if( !db ) 00407 { 00408 FIXME("Failed to allocate a handle\n"); 00409 goto end; 00410 } 00411 00412 if (!strchrW( save_path, '\\' )) 00413 { 00414 GetCurrentDirectoryW( MAX_PATH, path ); 00415 lstrcatW( path, szBackSlash ); 00416 lstrcatW( path, save_path ); 00417 } 00418 else 00419 lstrcpyW( path, save_path ); 00420 00421 db->path = strdupW( path ); 00422 db->media_transform_offset = MSI_INITIAL_MEDIA_TRANSFORM_OFFSET; 00423 db->media_transform_disk_id = MSI_INITIAL_MEDIA_TRANSFORM_DISKID; 00424 00425 if( TRACE_ON( msi ) ) 00426 enum_stream_names( stg ); 00427 00428 db->storage = stg; 00429 db->mode = szMode; 00430 if (created) 00431 db->deletefile = strdupW( szDBPath ); 00432 list_init( &db->tables ); 00433 list_init( &db->transforms ); 00434 list_init( &db->streams ); 00435 00436 db->strings = msi_load_string_table( stg, &db->bytes_per_strref ); 00437 if( !db->strings ) 00438 goto end; 00439 00440 ret = ERROR_SUCCESS; 00441 00442 msiobj_addref( &db->hdr ); 00443 IStorage_AddRef( stg ); 00444 *pdb = db; 00445 00446 end: 00447 if( db ) 00448 msiobj_release( &db->hdr ); 00449 if( stg ) 00450 IStorage_Release( stg ); 00451 00452 return ret; 00453 } 00454 00455 UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB) 00456 { 00457 MSIDATABASE *db; 00458 UINT ret; 00459 00460 TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB); 00461 00462 ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db ); 00463 if( ret == ERROR_SUCCESS ) 00464 { 00465 *phDB = alloc_msihandle( &db->hdr ); 00466 if (! *phDB) 00467 ret = ERROR_NOT_ENOUGH_MEMORY; 00468 msiobj_release( &db->hdr ); 00469 } 00470 00471 return ret; 00472 } 00473 00474 UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB) 00475 { 00476 HRESULT r = ERROR_FUNCTION_FAILED; 00477 LPWSTR szwDBPath = NULL, szwPersist = NULL; 00478 00479 TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB); 00480 00481 if( szDBPath ) 00482 { 00483 szwDBPath = strdupAtoW( szDBPath ); 00484 if( !szwDBPath ) 00485 goto end; 00486 } 00487 00488 if( !IS_INTMSIDBOPEN(szPersist) ) 00489 { 00490 szwPersist = strdupAtoW( szPersist ); 00491 if( !szwPersist ) 00492 goto end; 00493 } 00494 else 00495 szwPersist = (LPWSTR)(DWORD_PTR)szPersist; 00496 00497 r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB ); 00498 00499 end: 00500 if( !IS_INTMSIDBOPEN(szPersist) ) 00501 msi_free( szwPersist ); 00502 msi_free( szwDBPath ); 00503 00504 return r; 00505 } 00506 00507 static LPWSTR msi_read_text_archive(LPCWSTR path, DWORD *len) 00508 { 00509 HANDLE file; 00510 LPSTR data = NULL; 00511 LPWSTR wdata = NULL; 00512 DWORD read, size = 0; 00513 00514 file = CreateFileW( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL ); 00515 if (file == INVALID_HANDLE_VALUE) 00516 return NULL; 00517 00518 size = GetFileSize( file, NULL ); 00519 if (!(data = msi_alloc( size ))) goto done; 00520 00521 if (!ReadFile( file, data, size, &read, NULL ) || read != size) goto done; 00522 00523 while (!data[size - 1]) size--; 00524 *len = MultiByteToWideChar( CP_ACP, 0, data, size, NULL, 0 ); 00525 if ((wdata = msi_alloc( (*len + 1) * sizeof(WCHAR) ))) 00526 { 00527 MultiByteToWideChar( CP_ACP, 0, data, size, wdata, *len ); 00528 wdata[*len] = 0; 00529 } 00530 00531 done: 00532 CloseHandle( file ); 00533 msi_free( data ); 00534 return wdata; 00535 } 00536 00537 static void msi_parse_line(LPWSTR *line, LPWSTR **entries, DWORD *num_entries, DWORD *len) 00538 { 00539 LPWSTR ptr = *line, save; 00540 DWORD i, count = 1, chars_left = *len; 00541 00542 *entries = NULL; 00543 00544 /* stay on this line */ 00545 while (chars_left && *ptr != '\n') 00546 { 00547 /* entries are separated by tabs */ 00548 if (*ptr == '\t') 00549 count++; 00550 00551 ptr++; 00552 chars_left--; 00553 } 00554 00555 *entries = msi_alloc(count * sizeof(LPWSTR)); 00556 if (!*entries) 00557 return; 00558 00559 /* store pointers into the data */ 00560 chars_left = *len; 00561 for (i = 0, ptr = *line; i < count; i++) 00562 { 00563 while (chars_left && *ptr == '\r') 00564 { 00565 ptr++; 00566 chars_left--; 00567 } 00568 save = ptr; 00569 00570 while (chars_left && *ptr != '\t' && *ptr != '\n' && *ptr != '\r') 00571 { 00572 if (!*ptr) *ptr = '\n'; /* convert embedded nulls to \n */ 00573 if (ptr > *line && *ptr == '\x19' && *(ptr - 1) == '\x11') 00574 { 00575 *ptr = '\n'; 00576 *(ptr - 1) = '\r'; 00577 } 00578 ptr++; 00579 chars_left--; 00580 } 00581 00582 /* NULL-separate the data */ 00583 if (*ptr == '\n' || *ptr == '\r') 00584 { 00585 while (chars_left && (*ptr == '\n' || *ptr == '\r')) 00586 { 00587 *(ptr++) = 0; 00588 chars_left--; 00589 } 00590 } 00591 else if (*ptr) 00592 { 00593 *(ptr++) = 0; 00594 chars_left--; 00595 } 00596 (*entries)[i] = save; 00597 } 00598 00599 /* move to the next line if there's more, else EOF */ 00600 *line = ptr; 00601 *len = chars_left; 00602 if (num_entries) 00603 *num_entries = count; 00604 } 00605 00606 static LPWSTR msi_build_createsql_prelude(LPWSTR table) 00607 { 00608 LPWSTR prelude; 00609 DWORD size; 00610 00611 static const WCHAR create_fmt[] = {'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','%','s','`',' ','(',' ',0}; 00612 00613 size = sizeof(create_fmt)/sizeof(create_fmt[0]) + lstrlenW(table) - 2; 00614 prelude = msi_alloc(size * sizeof(WCHAR)); 00615 if (!prelude) 00616 return NULL; 00617 00618 sprintfW(prelude, create_fmt, table); 00619 return prelude; 00620 } 00621 00622 static LPWSTR msi_build_createsql_columns(LPWSTR *columns_data, LPWSTR *types, DWORD num_columns) 00623 { 00624 LPWSTR columns, p; 00625 LPCWSTR type; 00626 DWORD sql_size = 1, i, len; 00627 WCHAR expanded[128], *ptr; 00628 WCHAR size[10], comma[2], extra[30]; 00629 00630 static const WCHAR column_fmt[] = {'`','%','s','`',' ','%','s','%','s','%','s','%','s',' ',0}; 00631 static const WCHAR size_fmt[] = {'(','%','s',')',0}; 00632 static const WCHAR type_char[] = {'C','H','A','R',0}; 00633 static const WCHAR type_int[] = {'I','N','T',0}; 00634 static const WCHAR type_long[] = {'L','O','N','G',0}; 00635 static const WCHAR type_object[] = {'O','B','J','E','C','T',0}; 00636 static const WCHAR type_notnull[] = {' ','N','O','T',' ','N','U','L','L',0}; 00637 static const WCHAR localizable[] = {' ','L','O','C','A','L','I','Z','A','B','L','E',0}; 00638 00639 columns = msi_alloc_zero(sql_size * sizeof(WCHAR)); 00640 if (!columns) 00641 return NULL; 00642 00643 for (i = 0; i < num_columns; i++) 00644 { 00645 type = NULL; 00646 comma[1] = size[0] = extra[0] = '\0'; 00647 00648 if (i == num_columns - 1) 00649 comma[0] = '\0'; 00650 else 00651 comma[0] = ','; 00652 00653 ptr = &types[i][1]; 00654 len = atolW(ptr); 00655 extra[0] = '\0'; 00656 00657 switch (types[i][0]) 00658 { 00659 case 'l': 00660 lstrcpyW(extra, type_notnull); 00661 /* fall through */ 00662 case 'L': 00663 lstrcatW(extra, localizable); 00664 type = type_char; 00665 sprintfW(size, size_fmt, ptr); 00666 break; 00667 case 's': 00668 lstrcpyW(extra, type_notnull); 00669 /* fall through */ 00670 case 'S': 00671 type = type_char; 00672 sprintfW(size, size_fmt, ptr); 00673 break; 00674 case 'i': 00675 lstrcpyW(extra, type_notnull); 00676 /* fall through */ 00677 case 'I': 00678 if (len <= 2) 00679 type = type_int; 00680 else if (len == 4) 00681 type = type_long; 00682 else 00683 { 00684 WARN("invalid int width %u\n", len); 00685 msi_free(columns); 00686 return NULL; 00687 } 00688 break; 00689 case 'v': 00690 lstrcpyW(extra, type_notnull); 00691 /* fall through */ 00692 case 'V': 00693 type = type_object; 00694 break; 00695 default: 00696 ERR("Unknown type: %c\n", types[i][0]); 00697 msi_free(columns); 00698 return NULL; 00699 } 00700 00701 sprintfW(expanded, column_fmt, columns_data[i], type, size, extra, comma); 00702 sql_size += lstrlenW(expanded); 00703 00704 p = msi_realloc(columns, sql_size * sizeof(WCHAR)); 00705 if (!p) 00706 { 00707 msi_free(columns); 00708 return NULL; 00709 } 00710 columns = p; 00711 00712 lstrcatW(columns, expanded); 00713 } 00714 00715 return columns; 00716 } 00717 00718 static LPWSTR msi_build_createsql_postlude(LPWSTR *primary_keys, DWORD num_keys) 00719 { 00720 LPWSTR postlude, keys, ptr; 00721 DWORD size, key_size, i; 00722 00723 static const WCHAR key_fmt[] = {'`','%','s','`',',',' ',0}; 00724 static const WCHAR postlude_fmt[] = {'P','R','I','M','A','R','Y',' ','K','E','Y',' ','%','s',')',0}; 00725 00726 for (i = 0, size = 1; i < num_keys; i++) 00727 size += lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) - 2; 00728 00729 keys = msi_alloc(size * sizeof(WCHAR)); 00730 if (!keys) 00731 return NULL; 00732 00733 for (i = 0, ptr = keys; i < num_keys; i++) 00734 { 00735 key_size = lstrlenW(key_fmt) + lstrlenW(primary_keys[i]) -2; 00736 sprintfW(ptr, key_fmt, primary_keys[i]); 00737 ptr += key_size; 00738 } 00739 00740 /* remove final ', ' */ 00741 *(ptr - 2) = '\0'; 00742 00743 size = lstrlenW(postlude_fmt) + size - 1; 00744 postlude = msi_alloc(size * sizeof(WCHAR)); 00745 if (!postlude) 00746 goto done; 00747 00748 sprintfW(postlude, postlude_fmt, keys); 00749 00750 done: 00751 msi_free(keys); 00752 return postlude; 00753 } 00754 00755 static UINT msi_add_table_to_db(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, LPWSTR *labels, DWORD num_labels, DWORD num_columns) 00756 { 00757 UINT r = ERROR_OUTOFMEMORY; 00758 DWORD size; 00759 MSIQUERY *view; 00760 LPWSTR create_sql = NULL; 00761 LPWSTR prelude, columns_sql, postlude; 00762 00763 prelude = msi_build_createsql_prelude(labels[0]); 00764 columns_sql = msi_build_createsql_columns(columns, types, num_columns); 00765 postlude = msi_build_createsql_postlude(labels + 1, num_labels - 1); /* skip over table name */ 00766 00767 if (!prelude || !columns_sql || !postlude) 00768 goto done; 00769 00770 size = lstrlenW(prelude) + lstrlenW(columns_sql) + lstrlenW(postlude) + 1; 00771 create_sql = msi_alloc(size * sizeof(WCHAR)); 00772 if (!create_sql) 00773 goto done; 00774 00775 lstrcpyW(create_sql, prelude); 00776 lstrcatW(create_sql, columns_sql); 00777 lstrcatW(create_sql, postlude); 00778 00779 r = MSI_DatabaseOpenViewW( db, create_sql, &view ); 00780 if (r != ERROR_SUCCESS) 00781 goto done; 00782 00783 r = MSI_ViewExecute(view, NULL); 00784 MSI_ViewClose(view); 00785 msiobj_release(&view->hdr); 00786 00787 done: 00788 msi_free(prelude); 00789 msi_free(columns_sql); 00790 msi_free(postlude); 00791 msi_free(create_sql); 00792 return r; 00793 } 00794 00795 static LPWSTR msi_import_stream_filename(LPCWSTR path, LPCWSTR name) 00796 { 00797 DWORD len; 00798 LPWSTR fullname, ptr; 00799 00800 len = lstrlenW(path) + lstrlenW(name) + 1; 00801 fullname = msi_alloc(len*sizeof(WCHAR)); 00802 if (!fullname) 00803 return NULL; 00804 00805 lstrcpyW( fullname, path ); 00806 00807 /* chop off extension from path */ 00808 ptr = strrchrW(fullname, '.'); 00809 if (!ptr) 00810 { 00811 msi_free (fullname); 00812 return NULL; 00813 } 00814 *ptr++ = '\\'; 00815 lstrcpyW( ptr, name ); 00816 return fullname; 00817 } 00818 00819 static UINT construct_record(DWORD num_columns, LPWSTR *types, 00820 LPWSTR *data, LPWSTR path, MSIRECORD **rec) 00821 { 00822 UINT i; 00823 00824 *rec = MSI_CreateRecord(num_columns); 00825 if (!*rec) 00826 return ERROR_OUTOFMEMORY; 00827 00828 for (i = 0; i < num_columns; i++) 00829 { 00830 switch (types[i][0]) 00831 { 00832 case 'L': case 'l': case 'S': case 's': 00833 MSI_RecordSetStringW(*rec, i + 1, data[i]); 00834 break; 00835 case 'I': case 'i': 00836 if (*data[i]) 00837 MSI_RecordSetInteger(*rec, i + 1, atoiW(data[i])); 00838 break; 00839 case 'V': case 'v': 00840 if (*data[i]) 00841 { 00842 UINT r; 00843 LPWSTR file = msi_import_stream_filename(path, data[i]); 00844 if (!file) 00845 return ERROR_FUNCTION_FAILED; 00846 00847 r = MSI_RecordSetStreamFromFileW(*rec, i + 1, file); 00848 msi_free (file); 00849 if (r != ERROR_SUCCESS) 00850 return ERROR_FUNCTION_FAILED; 00851 } 00852 break; 00853 default: 00854 ERR("Unhandled column type: %c\n", types[i][0]); 00855 msiobj_release(&(*rec)->hdr); 00856 return ERROR_FUNCTION_FAILED; 00857 } 00858 } 00859 00860 return ERROR_SUCCESS; 00861 } 00862 00863 static UINT msi_add_records_to_table(MSIDATABASE *db, LPWSTR *columns, LPWSTR *types, 00864 LPWSTR *labels, LPWSTR **records, 00865 int num_columns, int num_records, 00866 LPWSTR path) 00867 { 00868 UINT r; 00869 int i; 00870 MSIQUERY *view; 00871 MSIRECORD *rec; 00872 00873 static const WCHAR select[] = { 00874 'S','E','L','E','C','T',' ','*',' ', 00875 'F','R','O','M',' ','`','%','s','`',0 00876 }; 00877 00878 r = MSI_OpenQuery(db, &view, select, labels[0]); 00879 if (r != ERROR_SUCCESS) 00880 return r; 00881 00882 while (MSI_ViewFetch(view, &rec) != ERROR_NO_MORE_ITEMS) 00883 { 00884 r = MSI_ViewModify(view, MSIMODIFY_DELETE, rec); 00885 msiobj_release(&rec->hdr); 00886 if (r != ERROR_SUCCESS) 00887 goto done; 00888 } 00889 00890 for (i = 0; i < num_records; i++) 00891 { 00892 r = construct_record(num_columns, types, records[i], path, &rec); 00893 if (r != ERROR_SUCCESS) 00894 goto done; 00895 00896 r = MSI_ViewModify(view, MSIMODIFY_INSERT, rec); 00897 if (r != ERROR_SUCCESS) 00898 { 00899 msiobj_release(&rec->hdr); 00900 goto done; 00901 } 00902 00903 msiobj_release(&rec->hdr); 00904 } 00905 00906 done: 00907 msiobj_release(&view->hdr); 00908 return r; 00909 } 00910 00911 static UINT MSI_DatabaseImport(MSIDATABASE *db, LPCWSTR folder, LPCWSTR file) 00912 { 00913 UINT r; 00914 DWORD len, i; 00915 DWORD num_labels, num_types; 00916 DWORD num_columns, num_records = 0; 00917 LPWSTR *columns, *types, *labels; 00918 LPWSTR path, ptr, data; 00919 LPWSTR **records = NULL; 00920 LPWSTR **temp_records; 00921 00922 static const WCHAR suminfo[] = 00923 {'_','S','u','m','m','a','r','y','I','n','f','o','r','m','a','t','i','o','n',0}; 00924 static const WCHAR forcecodepage[] = 00925 {'_','F','o','r','c','e','C','o','d','e','p','a','g','e',0}; 00926 00927 TRACE("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) ); 00928 00929 if( folder == NULL || file == NULL ) 00930 return ERROR_INVALID_PARAMETER; 00931 00932 len = lstrlenW(folder) + lstrlenW(szBackSlash) + lstrlenW(file) + 1; 00933 path = msi_alloc( len * sizeof(WCHAR) ); 00934 if (!path) 00935 return ERROR_OUTOFMEMORY; 00936 00937 lstrcpyW( path, folder ); 00938 lstrcatW( path, szBackSlash ); 00939 lstrcatW( path, file ); 00940 00941 data = msi_read_text_archive( path, &len ); 00942 00943 ptr = data; 00944 msi_parse_line( &ptr, &columns, &num_columns, &len ); 00945 msi_parse_line( &ptr, &types, &num_types, &len ); 00946 msi_parse_line( &ptr, &labels, &num_labels, &len ); 00947 00948 if (num_columns == 1 && !columns[0][0] && num_labels == 1 && !labels[0][0] && 00949 num_types == 2 && !strcmpW( types[1], forcecodepage )) 00950 { 00951 r = msi_set_string_table_codepage( db->strings, atoiW( types[0] ) ); 00952 goto done; 00953 } 00954 00955 if (num_columns != num_types) 00956 { 00957 r = ERROR_FUNCTION_FAILED; 00958 goto done; 00959 } 00960 00961 records = msi_alloc(sizeof(LPWSTR *)); 00962 if (!records) 00963 { 00964 r = ERROR_OUTOFMEMORY; 00965 goto done; 00966 } 00967 00968 /* read in the table records */ 00969 while (len) 00970 { 00971 msi_parse_line( &ptr, &records[num_records], NULL, &len ); 00972 00973 num_records++; 00974 temp_records = msi_realloc(records, (num_records + 1) * sizeof(LPWSTR *)); 00975 if (!temp_records) 00976 { 00977 r = ERROR_OUTOFMEMORY; 00978 goto done; 00979 } 00980 records = temp_records; 00981 } 00982 00983 if (!strcmpW(labels[0], suminfo)) 00984 { 00985 r = msi_add_suminfo( db, records, num_records, num_columns ); 00986 if (r != ERROR_SUCCESS) 00987 { 00988 r = ERROR_FUNCTION_FAILED; 00989 goto done; 00990 } 00991 } 00992 else 00993 { 00994 if (!TABLE_Exists(db, labels[0])) 00995 { 00996 r = msi_add_table_to_db( db, columns, types, labels, num_labels, num_columns ); 00997 if (r != ERROR_SUCCESS) 00998 { 00999 r = ERROR_FUNCTION_FAILED; 01000 goto done; 01001 } 01002 } 01003 01004 r = msi_add_records_to_table( db, columns, types, labels, records, num_columns, num_records, path ); 01005 } 01006 01007 done: 01008 msi_free(path); 01009 msi_free(data); 01010 msi_free(columns); 01011 msi_free(types); 01012 msi_free(labels); 01013 01014 for (i = 0; i < num_records; i++) 01015 msi_free(records[i]); 01016 01017 msi_free(records); 01018 01019 return r; 01020 } 01021 01022 UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename) 01023 { 01024 MSIDATABASE *db; 01025 UINT r; 01026 01027 TRACE("%x %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename)); 01028 01029 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); 01030 if( !db ) 01031 { 01032 IWineMsiRemoteDatabase *remote_database; 01033 01034 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle ); 01035 if ( !remote_database ) 01036 return ERROR_INVALID_HANDLE; 01037 01038 IWineMsiRemoteDatabase_Release( remote_database ); 01039 WARN("MsiDatabaseImport not allowed during a custom action!\n"); 01040 01041 return ERROR_SUCCESS; 01042 } 01043 01044 r = MSI_DatabaseImport( db, szFolder, szFilename ); 01045 msiobj_release( &db->hdr ); 01046 return r; 01047 } 01048 01049 UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle, 01050 LPCSTR szFolder, LPCSTR szFilename ) 01051 { 01052 LPWSTR path = NULL, file = NULL; 01053 UINT r = ERROR_OUTOFMEMORY; 01054 01055 TRACE("%x %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename)); 01056 01057 if( szFolder ) 01058 { 01059 path = strdupAtoW( szFolder ); 01060 if( !path ) 01061 goto end; 01062 } 01063 01064 if( szFilename ) 01065 { 01066 file = strdupAtoW( szFilename ); 01067 if( !file ) 01068 goto end; 01069 } 01070 01071 r = MsiDatabaseImportW( handle, path, file ); 01072 01073 end: 01074 msi_free( path ); 01075 msi_free( file ); 01076 01077 return r; 01078 } 01079 01080 static UINT msi_export_record( HANDLE handle, MSIRECORD *row, UINT start ) 01081 { 01082 UINT i, count, len, r = ERROR_SUCCESS; 01083 const char *sep; 01084 char *buffer; 01085 DWORD sz; 01086 01087 len = 0x100; 01088 buffer = msi_alloc( len ); 01089 if ( !buffer ) 01090 return ERROR_OUTOFMEMORY; 01091 01092 count = MSI_RecordGetFieldCount( row ); 01093 for ( i=start; i<=count; i++ ) 01094 { 01095 sz = len; 01096 r = MSI_RecordGetStringA( row, i, buffer, &sz ); 01097 if (r == ERROR_MORE_DATA) 01098 { 01099 char *p = msi_realloc( buffer, sz + 1 ); 01100 if (!p) 01101 break; 01102 len = sz + 1; 01103 buffer = p; 01104 } 01105 sz = len; 01106 r = MSI_RecordGetStringA( row, i, buffer, &sz ); 01107 if (r != ERROR_SUCCESS) 01108 break; 01109 01110 if (!WriteFile( handle, buffer, sz, &sz, NULL )) 01111 { 01112 r = ERROR_FUNCTION_FAILED; 01113 break; 01114 } 01115 01116 sep = (i < count) ? "\t" : "\r\n"; 01117 if (!WriteFile( handle, sep, strlen(sep), &sz, NULL )) 01118 { 01119 r = ERROR_FUNCTION_FAILED; 01120 break; 01121 } 01122 } 01123 msi_free( buffer ); 01124 return r; 01125 } 01126 01127 static UINT msi_export_row( MSIRECORD *row, void *arg ) 01128 { 01129 return msi_export_record( arg, row, 1 ); 01130 } 01131 01132 static UINT msi_export_forcecodepage( HANDLE handle, UINT codepage ) 01133 { 01134 static const char fmt[] = "\r\n\r\n%u\t_ForceCodepage\r\n"; 01135 char data[sizeof(fmt) + 10]; 01136 DWORD sz; 01137 01138 sprintf( data, fmt, codepage ); 01139 01140 sz = lstrlenA(data) + 1; 01141 if (!WriteFile(handle, data, sz, &sz, NULL)) 01142 return ERROR_FUNCTION_FAILED; 01143 01144 return ERROR_SUCCESS; 01145 } 01146 01147 static UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table, 01148 LPCWSTR folder, LPCWSTR file ) 01149 { 01150 static const WCHAR query[] = { 01151 's','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','%','s',0 }; 01152 static const WCHAR forcecodepage[] = { 01153 '_','F','o','r','c','e','C','o','d','e','p','a','g','e',0 }; 01154 MSIRECORD *rec = NULL; 01155 MSIQUERY *view = NULL; 01156 LPWSTR filename; 01157 HANDLE handle; 01158 UINT len, r; 01159 01160 TRACE("%p %s %s %s\n", db, debugstr_w(table), 01161 debugstr_w(folder), debugstr_w(file) ); 01162 01163 if( folder == NULL || file == NULL ) 01164 return ERROR_INVALID_PARAMETER; 01165 01166 len = lstrlenW(folder) + lstrlenW(file) + 2; 01167 filename = msi_alloc(len * sizeof (WCHAR)); 01168 if (!filename) 01169 return ERROR_OUTOFMEMORY; 01170 01171 lstrcpyW( filename, folder ); 01172 lstrcatW( filename, szBackSlash ); 01173 lstrcatW( filename, file ); 01174 01175 handle = CreateFileW( filename, GENERIC_READ | GENERIC_WRITE, 0, 01176 NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); 01177 msi_free( filename ); 01178 if (handle == INVALID_HANDLE_VALUE) 01179 return ERROR_FUNCTION_FAILED; 01180 01181 if (!strcmpW( table, forcecodepage )) 01182 { 01183 UINT codepage = msi_get_string_table_codepage( db->strings ); 01184 r = msi_export_forcecodepage( handle, codepage ); 01185 goto done; 01186 } 01187 01188 r = MSI_OpenQuery( db, &view, query, table ); 01189 if (r == ERROR_SUCCESS) 01190 { 01191 /* write out row 1, the column names */ 01192 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec); 01193 if (r == ERROR_SUCCESS) 01194 { 01195 msi_export_record( handle, rec, 1 ); 01196 msiobj_release( &rec->hdr ); 01197 } 01198 01199 /* write out row 2, the column types */ 01200 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_TYPES, &rec); 01201 if (r == ERROR_SUCCESS) 01202 { 01203 msi_export_record( handle, rec, 1 ); 01204 msiobj_release( &rec->hdr ); 01205 } 01206 01207 /* write out row 3, the table name + keys */ 01208 r = MSI_DatabaseGetPrimaryKeys( db, table, &rec ); 01209 if (r == ERROR_SUCCESS) 01210 { 01211 MSI_RecordSetStringW( rec, 0, table ); 01212 msi_export_record( handle, rec, 0 ); 01213 msiobj_release( &rec->hdr ); 01214 } 01215 01216 /* write out row 4 onwards, the data */ 01217 r = MSI_IterateRecords( view, 0, msi_export_row, handle ); 01218 msiobj_release( &view->hdr ); 01219 } 01220 01221 done: 01222 CloseHandle( handle ); 01223 return r; 01224 } 01225 01226 /*********************************************************************** 01227 * MsiExportDatabaseW [MSI.@] 01228 * 01229 * Writes a file containing the table data as tab separated ASCII. 01230 * 01231 * The format is as follows: 01232 * 01233 * row1 : colname1 <tab> colname2 <tab> .... colnameN <cr> <lf> 01234 * row2 : coltype1 <tab> coltype2 <tab> .... coltypeN <cr> <lf> 01235 * row3 : tablename <tab> key1 <tab> key2 <tab> ... keyM <cr> <lf> 01236 * 01237 * Followed by the data, starting at row 1 with one row per line 01238 * 01239 * row4 : data <tab> data <tab> data <tab> ... data <cr> <lf> 01240 */ 01241 UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable, 01242 LPCWSTR szFolder, LPCWSTR szFilename ) 01243 { 01244 MSIDATABASE *db; 01245 UINT r; 01246 01247 TRACE("%x %s %s %s\n", handle, debugstr_w(szTable), 01248 debugstr_w(szFolder), debugstr_w(szFilename)); 01249 01250 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); 01251 if( !db ) 01252 { 01253 IWineMsiRemoteDatabase *remote_database; 01254 01255 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle ); 01256 if ( !remote_database ) 01257 return ERROR_INVALID_HANDLE; 01258 01259 IWineMsiRemoteDatabase_Release( remote_database ); 01260 WARN("MsiDatabaseExport not allowed during a custom action!\n"); 01261 01262 return ERROR_SUCCESS; 01263 } 01264 01265 r = MSI_DatabaseExport( db, szTable, szFolder, szFilename ); 01266 msiobj_release( &db->hdr ); 01267 return r; 01268 } 01269 01270 UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable, 01271 LPCSTR szFolder, LPCSTR szFilename ) 01272 { 01273 LPWSTR path = NULL, file = NULL, table = NULL; 01274 UINT r = ERROR_OUTOFMEMORY; 01275 01276 TRACE("%x %s %s %s\n", handle, debugstr_a(szTable), 01277 debugstr_a(szFolder), debugstr_a(szFilename)); 01278 01279 if( szTable ) 01280 { 01281 table = strdupAtoW( szTable ); 01282 if( !table ) 01283 goto end; 01284 } 01285 01286 if( szFolder ) 01287 { 01288 path = strdupAtoW( szFolder ); 01289 if( !path ) 01290 goto end; 01291 } 01292 01293 if( szFilename ) 01294 { 01295 file = strdupAtoW( szFilename ); 01296 if( !file ) 01297 goto end; 01298 } 01299 01300 r = MsiDatabaseExportW( handle, table, path, file ); 01301 01302 end: 01303 msi_free( table ); 01304 msi_free( path ); 01305 msi_free( file ); 01306 01307 return r; 01308 } 01309 01310 UINT WINAPI MsiDatabaseMergeA(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge, 01311 LPCSTR szTableName) 01312 { 01313 UINT r; 01314 LPWSTR table; 01315 01316 TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge, 01317 debugstr_a(szTableName)); 01318 01319 table = strdupAtoW(szTableName); 01320 r = MsiDatabaseMergeW(hDatabase, hDatabaseMerge, table); 01321 01322 msi_free(table); 01323 return r; 01324 } 01325 01326 typedef struct _tagMERGETABLE 01327 { 01328 struct list entry; 01329 struct list rows; 01330 LPWSTR name; 01331 DWORD numconflicts; 01332 LPWSTR *columns; 01333 DWORD numcolumns; 01334 LPWSTR *types; 01335 DWORD numtypes; 01336 LPWSTR *labels; 01337 DWORD numlabels; 01338 } MERGETABLE; 01339 01340 typedef struct _tagMERGEROW 01341 { 01342 struct list entry; 01343 MSIRECORD *data; 01344 } MERGEROW; 01345 01346 typedef struct _tagMERGEDATA 01347 { 01348 MSIDATABASE *db; 01349 MSIDATABASE *merge; 01350 MERGETABLE *curtable; 01351 MSIQUERY *curview; 01352 struct list *tabledata; 01353 } MERGEDATA; 01354 01355 static BOOL merge_type_match(LPCWSTR type1, LPCWSTR type2) 01356 { 01357 if (((type1[0] == 'l') || (type1[0] == 's')) && 01358 ((type2[0] == 'l') || (type2[0] == 's'))) 01359 return TRUE; 01360 01361 if (((type1[0] == 'L') || (type1[0] == 'S')) && 01362 ((type2[0] == 'L') || (type2[0] == 'S'))) 01363 return TRUE; 01364 01365 return !strcmpW( type1, type2 ); 01366 } 01367 01368 static UINT merge_verify_colnames(MSIQUERY *dbview, MSIQUERY *mergeview) 01369 { 01370 MSIRECORD *dbrec, *mergerec; 01371 UINT r, i, count; 01372 01373 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_NAMES, &dbrec); 01374 if (r != ERROR_SUCCESS) 01375 return r; 01376 01377 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_NAMES, &mergerec); 01378 if (r != ERROR_SUCCESS) 01379 return r; 01380 01381 count = MSI_RecordGetFieldCount(dbrec); 01382 for (i = 1; i <= count; i++) 01383 { 01384 if (!MSI_RecordGetString(mergerec, i)) 01385 break; 01386 01387 if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) )) 01388 { 01389 r = ERROR_DATATYPE_MISMATCH; 01390 goto done; 01391 } 01392 } 01393 01394 msiobj_release(&dbrec->hdr); 01395 msiobj_release(&mergerec->hdr); 01396 dbrec = mergerec = NULL; 01397 01398 r = MSI_ViewGetColumnInfo(dbview, MSICOLINFO_TYPES, &dbrec); 01399 if (r != ERROR_SUCCESS) 01400 return r; 01401 01402 r = MSI_ViewGetColumnInfo(mergeview, MSICOLINFO_TYPES, &mergerec); 01403 if (r != ERROR_SUCCESS) 01404 return r; 01405 01406 count = MSI_RecordGetFieldCount(dbrec); 01407 for (i = 1; i <= count; i++) 01408 { 01409 if (!MSI_RecordGetString(mergerec, i)) 01410 break; 01411 01412 if (!merge_type_match(MSI_RecordGetString(dbrec, i), 01413 MSI_RecordGetString(mergerec, i))) 01414 { 01415 r = ERROR_DATATYPE_MISMATCH; 01416 break; 01417 } 01418 } 01419 01420 done: 01421 msiobj_release(&dbrec->hdr); 01422 msiobj_release(&mergerec->hdr); 01423 01424 return r; 01425 } 01426 01427 static UINT merge_verify_primary_keys(MSIDATABASE *db, MSIDATABASE *mergedb, 01428 LPCWSTR table) 01429 { 01430 MSIRECORD *dbrec, *mergerec = NULL; 01431 UINT r, i, count; 01432 01433 r = MSI_DatabaseGetPrimaryKeys(db, table, &dbrec); 01434 if (r != ERROR_SUCCESS) 01435 return r; 01436 01437 r = MSI_DatabaseGetPrimaryKeys(mergedb, table, &mergerec); 01438 if (r != ERROR_SUCCESS) 01439 goto done; 01440 01441 count = MSI_RecordGetFieldCount(dbrec); 01442 if (count != MSI_RecordGetFieldCount(mergerec)) 01443 { 01444 r = ERROR_DATATYPE_MISMATCH; 01445 goto done; 01446 } 01447 01448 for (i = 1; i <= count; i++) 01449 { 01450 if (strcmpW( MSI_RecordGetString( dbrec, i ), MSI_RecordGetString( mergerec, i ) )) 01451 { 01452 r = ERROR_DATATYPE_MISMATCH; 01453 goto done; 01454 } 01455 } 01456 01457 done: 01458 msiobj_release(&dbrec->hdr); 01459 msiobj_release(&mergerec->hdr); 01460 01461 return r; 01462 } 01463 01464 static LPWSTR get_key_value(MSIQUERY *view, LPCWSTR key, MSIRECORD *rec) 01465 { 01466 MSIRECORD *colnames; 01467 LPWSTR str, val; 01468 UINT r, i = 0, sz = 0; 01469 int cmp; 01470 01471 r = MSI_ViewGetColumnInfo(view, MSICOLINFO_NAMES, &colnames); 01472 if (r != ERROR_SUCCESS) 01473 return NULL; 01474 01475 do 01476 { 01477 str = msi_dup_record_field(colnames, ++i); 01478 cmp = strcmpW( key, str ); 01479 msi_free(str); 01480 } while (cmp); 01481 01482 msiobj_release(&colnames->hdr); 01483 01484 r = MSI_RecordGetStringW(rec, i, NULL, &sz); 01485 if (r != ERROR_SUCCESS) 01486 return NULL; 01487 sz++; 01488 01489 if (MSI_RecordGetString(rec, i)) /* check record field is a string */ 01490 { 01491 /* quote string record fields */ 01492 const WCHAR szQuote[] = {'\'', 0}; 01493 sz += 2; 01494 val = msi_alloc(sz*sizeof(WCHAR)); 01495 if (!val) 01496 return NULL; 01497 01498 lstrcpyW(val, szQuote); 01499 r = MSI_RecordGetStringW(rec, i, val+1, &sz); 01500 lstrcpyW(val+1+sz, szQuote); 01501 } 01502 else 01503 { 01504 /* do not quote integer record fields */ 01505 val = msi_alloc(sz*sizeof(WCHAR)); 01506 if (!val) 01507 return NULL; 01508 01509 r = MSI_RecordGetStringW(rec, i, val, &sz); 01510 } 01511 01512 if (r != ERROR_SUCCESS) 01513 { 01514 ERR("failed to get string!\n"); 01515 msi_free(val); 01516 return NULL; 01517 } 01518 01519 return val; 01520 } 01521 01522 static LPWSTR create_diff_row_query(MSIDATABASE *merge, MSIQUERY *view, 01523 LPWSTR table, MSIRECORD *rec) 01524 { 01525 LPWSTR query = NULL, clause = NULL, val; 01526 LPCWSTR setptr, key; 01527 DWORD size, oldsize; 01528 MSIRECORD *keys; 01529 UINT r, i, count; 01530 01531 static const WCHAR keyset[] = { 01532 '`','%','s','`',' ','=',' ','%','s',' ','A','N','D',' ',0}; 01533 static const WCHAR lastkeyset[] = { 01534 '`','%','s','`',' ','=',' ','%','s',' ',0}; 01535 static const WCHAR fmt[] = {'S','E','L','E','C','T',' ','*',' ', 01536 'F','R','O','M',' ','`','%','s','`',' ', 01537 'W','H','E','R','E',' ','%','s',0}; 01538 01539 r = MSI_DatabaseGetPrimaryKeys(merge, table, &keys); 01540 if (r != ERROR_SUCCESS) 01541 return NULL; 01542 01543 clause = msi_alloc_zero(sizeof(WCHAR)); 01544 if (!clause) 01545 goto done; 01546 01547 size = 1; 01548 count = MSI_RecordGetFieldCount(keys); 01549 for (i = 1; i <= count; i++) 01550 { 01551 key = MSI_RecordGetString(keys, i); 01552 val = get_key_value(view, key, rec); 01553 01554 if (i == count) 01555 setptr = lastkeyset; 01556 else 01557 setptr = keyset; 01558 01559 oldsize = size; 01560 size += lstrlenW(setptr) + lstrlenW(key) + lstrlenW(val) - 4; 01561 clause = msi_realloc(clause, size * sizeof (WCHAR)); 01562 if (!clause) 01563 { 01564 msi_free(val); 01565 goto done; 01566 } 01567 01568 sprintfW(clause + oldsize - 1, setptr, key, val); 01569 msi_free(val); 01570 } 01571 01572 size = lstrlenW(fmt) + lstrlenW(table) + lstrlenW(clause) + 1; 01573 query = msi_alloc(size * sizeof(WCHAR)); 01574 if (!query) 01575 goto done; 01576 01577 sprintfW(query, fmt, table, clause); 01578 01579 done: 01580 msi_free(clause); 01581 msiobj_release(&keys->hdr); 01582 return query; 01583 } 01584 01585 static UINT merge_diff_row(MSIRECORD *rec, LPVOID param) 01586 { 01587 MERGEDATA *data = param; 01588 MERGETABLE *table = data->curtable; 01589 MERGEROW *mergerow; 01590 MSIQUERY *dbview = NULL; 01591 MSIRECORD *row = NULL; 01592 LPWSTR query = NULL; 01593 UINT r = ERROR_SUCCESS; 01594 01595 if (TABLE_Exists(data->db, table->name)) 01596 { 01597 query = create_diff_row_query(data->merge, data->curview, table->name, rec); 01598 if (!query) 01599 return ERROR_OUTOFMEMORY; 01600 01601 r = MSI_DatabaseOpenViewW(data->db, query, &dbview); 01602 if (r != ERROR_SUCCESS) 01603 goto done; 01604 01605 r = MSI_ViewExecute(dbview, NULL); 01606 if (r != ERROR_SUCCESS) 01607 goto done; 01608 01609 r = MSI_ViewFetch(dbview, &row); 01610 if (r == ERROR_SUCCESS && !MSI_RecordsAreEqual(rec, row)) 01611 { 01612 table->numconflicts++; 01613 goto done; 01614 } 01615 else if (r != ERROR_NO_MORE_ITEMS) 01616 goto done; 01617 01618 r = ERROR_SUCCESS; 01619 } 01620 01621 mergerow = msi_alloc(sizeof(MERGEROW)); 01622 if (!mergerow) 01623 { 01624 r = ERROR_OUTOFMEMORY; 01625 goto done; 01626 } 01627 01628 mergerow->data = MSI_CloneRecord(rec); 01629 if (!mergerow->data) 01630 { 01631 r = ERROR_OUTOFMEMORY; 01632 msi_free(mergerow); 01633 goto done; 01634 } 01635 01636 list_add_tail(&table->rows, &mergerow->entry); 01637 01638 done: 01639 msi_free(query); 01640 msiobj_release(&row->hdr); 01641 msiobj_release(&dbview->hdr); 01642 return r; 01643 } 01644 01645 static UINT msi_get_table_labels(MSIDATABASE *db, LPCWSTR table, LPWSTR **labels, DWORD *numlabels) 01646 { 01647 UINT r, i, count; 01648 MSIRECORD *prec = NULL; 01649 01650 r = MSI_DatabaseGetPrimaryKeys(db, table, &prec); 01651 if (r != ERROR_SUCCESS) 01652 return r; 01653 01654 count = MSI_RecordGetFieldCount(prec); 01655 *numlabels = count + 1; 01656 *labels = msi_alloc((*numlabels)*sizeof(LPWSTR)); 01657 if (!*labels) 01658 { 01659 r = ERROR_OUTOFMEMORY; 01660 goto end; 01661 } 01662 01663 (*labels)[0] = strdupW(table); 01664 for (i=1; i<=count; i++ ) 01665 { 01666 (*labels)[i] = strdupW(MSI_RecordGetString(prec, i)); 01667 } 01668 01669 end: 01670 msiobj_release( &prec->hdr ); 01671 return r; 01672 } 01673 01674 static UINT msi_get_query_columns(MSIQUERY *query, LPWSTR **columns, DWORD *numcolumns) 01675 { 01676 UINT r, i, count; 01677 MSIRECORD *prec = NULL; 01678 01679 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_NAMES, &prec); 01680 if (r != ERROR_SUCCESS) 01681 return r; 01682 01683 count = MSI_RecordGetFieldCount(prec); 01684 *columns = msi_alloc(count*sizeof(LPWSTR)); 01685 if (!*columns) 01686 { 01687 r = ERROR_OUTOFMEMORY; 01688 goto end; 01689 } 01690 01691 for (i=1; i<=count; i++ ) 01692 { 01693 (*columns)[i-1] = strdupW(MSI_RecordGetString(prec, i)); 01694 } 01695 01696 *numcolumns = count; 01697 01698 end: 01699 msiobj_release( &prec->hdr ); 01700 return r; 01701 } 01702 01703 static UINT msi_get_query_types(MSIQUERY *query, LPWSTR **types, DWORD *numtypes) 01704 { 01705 UINT r, i, count; 01706 MSIRECORD *prec = NULL; 01707 01708 r = MSI_ViewGetColumnInfo(query, MSICOLINFO_TYPES, &prec); 01709 if (r != ERROR_SUCCESS) 01710 return r; 01711 01712 count = MSI_RecordGetFieldCount(prec); 01713 *types = msi_alloc(count*sizeof(LPWSTR)); 01714 if (!*types) 01715 { 01716 r = ERROR_OUTOFMEMORY; 01717 goto end; 01718 } 01719 01720 *numtypes = count; 01721 for (i=1; i<=count; i++ ) 01722 { 01723 (*types)[i-1] = strdupW(MSI_RecordGetString(prec, i)); 01724 } 01725 01726 end: 01727 msiobj_release( &prec->hdr ); 01728 return r; 01729 } 01730 01731 static void merge_free_rows(MERGETABLE *table) 01732 { 01733 struct list *item, *cursor; 01734 01735 LIST_FOR_EACH_SAFE(item, cursor, &table->rows) 01736 { 01737 MERGEROW *row = LIST_ENTRY(item, MERGEROW, entry); 01738 01739 list_remove(&row->entry); 01740 msiobj_release(&row->data->hdr); 01741 msi_free(row); 01742 } 01743 } 01744 01745 static void free_merge_table(MERGETABLE *table) 01746 { 01747 UINT i; 01748 01749 if (table->labels != NULL) 01750 { 01751 for (i = 0; i < table->numlabels; i++) 01752 msi_free(table->labels[i]); 01753 01754 msi_free(table->labels); 01755 } 01756 01757 if (table->columns != NULL) 01758 { 01759 for (i = 0; i < table->numcolumns; i++) 01760 msi_free(table->columns[i]); 01761 01762 msi_free(table->columns); 01763 } 01764 01765 if (table->types != NULL) 01766 { 01767 for (i = 0; i < table->numtypes; i++) 01768 msi_free(table->types[i]); 01769 01770 msi_free(table->types); 01771 } 01772 01773 msi_free(table->name); 01774 merge_free_rows(table); 01775 01776 msi_free(table); 01777 } 01778 01779 static UINT msi_get_merge_table (MSIDATABASE *db, LPCWSTR name, MERGETABLE **ptable) 01780 { 01781 UINT r; 01782 MERGETABLE *table; 01783 MSIQUERY *mergeview = NULL; 01784 01785 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ', 01786 'F','R','O','M',' ','`','%','s','`',0}; 01787 01788 table = msi_alloc_zero(sizeof(MERGETABLE)); 01789 if (!table) 01790 { 01791 *ptable = NULL; 01792 return ERROR_OUTOFMEMORY; 01793 } 01794 01795 r = msi_get_table_labels(db, name, &table->labels, &table->numlabels); 01796 if (r != ERROR_SUCCESS) 01797 goto err; 01798 01799 r = MSI_OpenQuery(db, &mergeview, query, name); 01800 if (r != ERROR_SUCCESS) 01801 goto err; 01802 01803 r = msi_get_query_columns(mergeview, &table->columns, &table->numcolumns); 01804 if (r != ERROR_SUCCESS) 01805 goto err; 01806 01807 r = msi_get_query_types(mergeview, &table->types, &table->numtypes); 01808 if (r != ERROR_SUCCESS) 01809 goto err; 01810 01811 list_init(&table->rows); 01812 01813 table->name = strdupW(name); 01814 table->numconflicts = 0; 01815 01816 msiobj_release(&mergeview->hdr); 01817 *ptable = table; 01818 return ERROR_SUCCESS; 01819 01820 err: 01821 msiobj_release(&mergeview->hdr); 01822 free_merge_table(table); 01823 *ptable = NULL; 01824 return r; 01825 } 01826 01827 static UINT merge_diff_tables(MSIRECORD *rec, LPVOID param) 01828 { 01829 MERGEDATA *data = param; 01830 MERGETABLE *table; 01831 MSIQUERY *dbview = NULL; 01832 MSIQUERY *mergeview = NULL; 01833 LPCWSTR name; 01834 UINT r; 01835 01836 static const WCHAR query[] = {'S','E','L','E','C','T',' ','*',' ', 01837 'F','R','O','M',' ','`','%','s','`',0}; 01838 01839 name = MSI_RecordGetString(rec, 1); 01840 01841 r = MSI_OpenQuery(data->merge, &mergeview, query, name); 01842 if (r != ERROR_SUCCESS) 01843 goto done; 01844 01845 if (TABLE_Exists(data->db, name)) 01846 { 01847 r = MSI_OpenQuery(data->db, &dbview, query, name); 01848 if (r != ERROR_SUCCESS) 01849 goto done; 01850 01851 r = merge_verify_colnames(dbview, mergeview); 01852 if (r != ERROR_SUCCESS) 01853 goto done; 01854 01855 r = merge_verify_primary_keys(data->db, data->merge, name); 01856 if (r != ERROR_SUCCESS) 01857 goto done; 01858 } 01859 01860 r = msi_get_merge_table(data->merge, name, &table); 01861 if (r != ERROR_SUCCESS) 01862 goto done; 01863 01864 data->curtable = table; 01865 data->curview = mergeview; 01866 r = MSI_IterateRecords(mergeview, NULL, merge_diff_row, data); 01867 if (r != ERROR_SUCCESS) 01868 { 01869 free_merge_table(table); 01870 goto done; 01871 } 01872 01873 list_add_tail(data->tabledata, &table->entry); 01874 01875 done: 01876 msiobj_release(&dbview->hdr); 01877 msiobj_release(&mergeview->hdr); 01878 return r; 01879 } 01880 01881 static UINT gather_merge_data(MSIDATABASE *db, MSIDATABASE *merge, 01882 struct list *tabledata) 01883 { 01884 static const WCHAR query[] = { 01885 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', 01886 '`','_','T','a','b','l','e','s','`',0}; 01887 MSIQUERY *view; 01888 MERGEDATA data; 01889 UINT r; 01890 01891 r = MSI_DatabaseOpenViewW(merge, query, &view); 01892 if (r != ERROR_SUCCESS) 01893 return r; 01894 01895 data.db = db; 01896 data.merge = merge; 01897 data.tabledata = tabledata; 01898 r = MSI_IterateRecords(view, NULL, merge_diff_tables, &data); 01899 msiobj_release(&view->hdr); 01900 return r; 01901 } 01902 01903 static UINT merge_table(MSIDATABASE *db, MERGETABLE *table) 01904 { 01905 UINT r; 01906 MERGEROW *row; 01907 MSIVIEW *tv; 01908 01909 if (!TABLE_Exists(db, table->name)) 01910 { 01911 r = msi_add_table_to_db(db, table->columns, table->types, 01912 table->labels, table->numlabels, table->numcolumns); 01913 if (r != ERROR_SUCCESS) 01914 return ERROR_FUNCTION_FAILED; 01915 } 01916 01917 LIST_FOR_EACH_ENTRY(row, &table->rows, MERGEROW, entry) 01918 { 01919 r = TABLE_CreateView(db, table->name, &tv); 01920 if (r != ERROR_SUCCESS) 01921 return r; 01922 01923 r = tv->ops->insert_row(tv, row->data, -1, FALSE); 01924 tv->ops->delete(tv); 01925 01926 if (r != ERROR_SUCCESS) 01927 return r; 01928 } 01929 01930 return ERROR_SUCCESS; 01931 } 01932 01933 static UINT update_merge_errors(MSIDATABASE *db, LPCWSTR error, 01934 LPWSTR table, DWORD numconflicts) 01935 { 01936 UINT r; 01937 MSIQUERY *view; 01938 01939 static const WCHAR create[] = { 01940 'C','R','E','A','T','E',' ','T','A','B','L','E',' ', 01941 '`','%','s','`',' ','(','`','T','a','b','l','e','`',' ', 01942 'C','H','A','R','(','2','5','5',')',' ','N','O','T',' ', 01943 'N','U','L','L',',',' ','`','N','u','m','R','o','w','M','e','r','g','e', 01944 'C','o','n','f','l','i','c','t','s','`',' ','S','H','O','R','T',' ', 01945 'N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R','Y',' ', 01946 'K','E','Y',' ','`','T','a','b','l','e','`',')',0}; 01947 static const WCHAR insert[] = { 01948 'I','N','S','E','R','T',' ','I','N','T','O',' ', 01949 '`','%','s','`',' ','(','`','T','a','b','l','e','`',',',' ', 01950 '`','N','u','m','R','o','w','M','e','r','g','e', 01951 'C','o','n','f','l','i','c','t','s','`',')',' ','V','A','L','U','E','S', 01952 ' ','(','\'','%','s','\'',',',' ','%','d',')',0}; 01953 01954 if (!TABLE_Exists(db, error)) 01955 { 01956 r = MSI_OpenQuery(db, &view, create, error); 01957 if (r != ERROR_SUCCESS) 01958 return r; 01959 01960 r = MSI_ViewExecute(view, NULL); 01961 msiobj_release(&view->hdr); 01962 if (r != ERROR_SUCCESS) 01963 return r; 01964 } 01965 01966 r = MSI_OpenQuery(db, &view, insert, error, table, numconflicts); 01967 if (r != ERROR_SUCCESS) 01968 return r; 01969 01970 r = MSI_ViewExecute(view, NULL); 01971 msiobj_release(&view->hdr); 01972 return r; 01973 } 01974 01975 UINT WINAPI MsiDatabaseMergeW(MSIHANDLE hDatabase, MSIHANDLE hDatabaseMerge, 01976 LPCWSTR szTableName) 01977 { 01978 struct list tabledata = LIST_INIT(tabledata); 01979 struct list *item, *cursor; 01980 MSIDATABASE *db, *merge; 01981 MERGETABLE *table; 01982 BOOL conflicts; 01983 UINT r; 01984 01985 TRACE("(%d, %d, %s)\n", hDatabase, hDatabaseMerge, 01986 debugstr_w(szTableName)); 01987 01988 if (szTableName && !*szTableName) 01989 return ERROR_INVALID_TABLE; 01990 01991 db = msihandle2msiinfo(hDatabase, MSIHANDLETYPE_DATABASE); 01992 merge = msihandle2msiinfo(hDatabaseMerge, MSIHANDLETYPE_DATABASE); 01993 if (!db || !merge) 01994 { 01995 r = ERROR_INVALID_HANDLE; 01996 goto done; 01997 } 01998 01999 r = gather_merge_data(db, merge, &tabledata); 02000 if (r != ERROR_SUCCESS) 02001 goto done; 02002 02003 conflicts = FALSE; 02004 LIST_FOR_EACH_ENTRY(table, &tabledata, MERGETABLE, entry) 02005 { 02006 if (table->numconflicts) 02007 { 02008 conflicts = TRUE; 02009 02010 r = update_merge_errors(db, szTableName, table->name, 02011 table->numconflicts); 02012 if (r != ERROR_SUCCESS) 02013 break; 02014 } 02015 else 02016 { 02017 r = merge_table(db, table); 02018 if (r != ERROR_SUCCESS) 02019 break; 02020 } 02021 } 02022 02023 LIST_FOR_EACH_SAFE(item, cursor, &tabledata) 02024 { 02025 MERGETABLE *table = LIST_ENTRY(item, MERGETABLE, entry); 02026 list_remove(&table->entry); 02027 free_merge_table(table); 02028 } 02029 02030 if (conflicts) 02031 r = ERROR_FUNCTION_FAILED; 02032 02033 done: 02034 msiobj_release(&db->hdr); 02035 msiobj_release(&merge->hdr); 02036 return r; 02037 } 02038 02039 MSIDBSTATE WINAPI MsiGetDatabaseState( MSIHANDLE handle ) 02040 { 02041 MSIDBSTATE ret = MSIDBSTATE_READ; 02042 MSIDATABASE *db; 02043 02044 TRACE("%d\n", handle); 02045 02046 db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); 02047 if( !db ) 02048 { 02049 IWineMsiRemoteDatabase *remote_database; 02050 02051 remote_database = (IWineMsiRemoteDatabase *)msi_get_remote( handle ); 02052 if ( !remote_database ) 02053 return MSIDBSTATE_ERROR; 02054 02055 IWineMsiRemoteDatabase_Release( remote_database ); 02056 WARN("MsiGetDatabaseState not allowed during a custom action!\n"); 02057 02058 return MSIDBSTATE_READ; 02059 } 02060 02061 if (db->mode != MSIDBOPEN_READONLY ) 02062 ret = MSIDBSTATE_WRITE; 02063 msiobj_release( &db->hdr ); 02064 02065 return ret; 02066 } 02067 02068 typedef struct _msi_remote_database_impl { 02069 IWineMsiRemoteDatabase IWineMsiRemoteDatabase_iface; 02070 MSIHANDLE database; 02071 LONG refs; 02072 } msi_remote_database_impl; 02073 02074 static inline msi_remote_database_impl *impl_from_IWineMsiRemoteDatabase( IWineMsiRemoteDatabase *iface ) 02075 { 02076 return CONTAINING_RECORD(iface, msi_remote_database_impl, IWineMsiRemoteDatabase_iface); 02077 } 02078 02079 static HRESULT WINAPI mrd_QueryInterface( IWineMsiRemoteDatabase *iface, 02080 REFIID riid,LPVOID *ppobj) 02081 { 02082 if( IsEqualCLSID( riid, &IID_IUnknown ) || 02083 IsEqualCLSID( riid, &IID_IWineMsiRemoteDatabase ) ) 02084 { 02085 IUnknown_AddRef( iface ); 02086 *ppobj = iface; 02087 return S_OK; 02088 } 02089 02090 return E_NOINTERFACE; 02091 } 02092 02093 static ULONG WINAPI mrd_AddRef( IWineMsiRemoteDatabase *iface ) 02094 { 02095 msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface ); 02096 02097 return InterlockedIncrement( &This->refs ); 02098 } 02099 02100 static ULONG WINAPI mrd_Release( IWineMsiRemoteDatabase *iface ) 02101 { 02102 msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface ); 02103 ULONG r; 02104 02105 r = InterlockedDecrement( &This->refs ); 02106 if (r == 0) 02107 { 02108 MsiCloseHandle( This->database ); 02109 msi_free( This ); 02110 } 02111 return r; 02112 } 02113 02114 static HRESULT WINAPI mrd_IsTablePersistent( IWineMsiRemoteDatabase *iface, 02115 LPCWSTR table, MSICONDITION *persistent ) 02116 { 02117 msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface ); 02118 *persistent = MsiDatabaseIsTablePersistentW(This->database, table); 02119 return S_OK; 02120 } 02121 02122 static HRESULT WINAPI mrd_GetPrimaryKeys( IWineMsiRemoteDatabase *iface, 02123 LPCWSTR table, MSIHANDLE *keys ) 02124 { 02125 msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface ); 02126 UINT r = MsiDatabaseGetPrimaryKeysW(This->database, table, keys); 02127 return HRESULT_FROM_WIN32(r); 02128 } 02129 02130 static HRESULT WINAPI mrd_GetSummaryInformation( IWineMsiRemoteDatabase *iface, 02131 UINT updatecount, MSIHANDLE *suminfo ) 02132 { 02133 msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface ); 02134 UINT r = MsiGetSummaryInformationW(This->database, NULL, updatecount, suminfo); 02135 return HRESULT_FROM_WIN32(r); 02136 } 02137 02138 static HRESULT WINAPI mrd_OpenView( IWineMsiRemoteDatabase *iface, 02139 LPCWSTR query, MSIHANDLE *view ) 02140 { 02141 msi_remote_database_impl *This = impl_from_IWineMsiRemoteDatabase( iface ); 02142 UINT r = MsiDatabaseOpenViewW(This->database, query, view); 02143 return HRESULT_FROM_WIN32(r); 02144 } 02145 02146 static HRESULT WINAPI mrd_SetMsiHandle( IWineMsiRemoteDatabase *iface, MSIHANDLE handle ) 02147 { 02148 msi_remote_database_impl* This = impl_from_IWineMsiRemoteDatabase( iface ); 02149 This->database = handle; 02150 return S_OK; 02151 } 02152 02153 static const IWineMsiRemoteDatabaseVtbl msi_remote_database_vtbl = 02154 { 02155 mrd_QueryInterface, 02156 mrd_AddRef, 02157 mrd_Release, 02158 mrd_IsTablePersistent, 02159 mrd_GetPrimaryKeys, 02160 mrd_GetSummaryInformation, 02161 mrd_OpenView, 02162 mrd_SetMsiHandle, 02163 }; 02164 02165 HRESULT create_msi_remote_database( IUnknown *pOuter, LPVOID *ppObj ) 02166 { 02167 msi_remote_database_impl *This; 02168 02169 This = msi_alloc( sizeof *This ); 02170 if (!This) 02171 return E_OUTOFMEMORY; 02172 02173 This->IWineMsiRemoteDatabase_iface.lpVtbl = &msi_remote_database_vtbl; 02174 This->database = 0; 02175 This->refs = 1; 02176 02177 *ppObj = This; 02178 02179 return S_OK; 02180 } Generated on Fri May 25 2012 04:16:59 for ReactOS by
1.7.6.1
|