2 * Copyright 2010 Hans Leidekker for CodeWeavers
4 * A test program for patching MSI products.
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
21 #define _WIN32_MSI 300
31 #include "wine/test.h"
33 static UINT (WINAPI *pMsiApplyPatchA)( LPCSTR, LPCSTR, INSTALLTYPE, LPCSTR );
35 static const char *msifile = "winetest-patch.msi";
36 static const char *mspfile = "winetest-patch.msp";
38 static char CURR_DIR[MAX_PATH];
39 static char PROG_FILES_DIR[MAX_PATH];
40 static char COMMON_FILES_DIR[MAX_PATH];
42 /* msi database data */
44 static const char property_dat[] =
47 "Property\tProperty\n"
48 "Manufacturer\tWineHQ\n"
49 "ProductCode\t{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}\n"
50 "UpgradeCode\t{A2E3D643-4E2C-477F-A309-F76F552D5F43}\n"
51 "ProductLanguage\t1033\n"
52 "ProductName\tmsitest\n"
53 "ProductVersion\t1.1.1\n";
55 static const char media_dat[] =
56 "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
57 "i2\ti4\tL64\tS255\tS32\tS72\n"
59 "1\t1\t\t\tDISK1\t\n";
61 static const char file_dat[] =
62 "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
63 "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
65 "patch.txt\tpatch\tpatch.txt\t1000\t\t\t0\t1\n";
67 static const char directory_dat[] =
68 "Directory\tDirectory_Parent\tDefaultDir\n"
70 "Directory\tDirectory\n"
71 "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
72 "ProgramFilesFolder\tTARGETDIR\t.\n"
73 "TARGETDIR\t\tSourceDir";
75 static const char component_dat[] =
76 "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
77 "s72\tS38\ts72\ti2\tS255\tS72\n"
78 "Component\tComponent\n"
79 "patch\t{4B79D87E-6D28-4FD3-92D6-CD9B26AF64F1}\tMSITESTDIR\t0\t\tpatch.txt\n";
81 static const char feature_dat[] =
82 "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
83 "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
85 "patch\t\t\tpatch feature\t1\t1\tMSITESTDIR\t0\n";
87 static const char feature_comp_dat[] =
88 "Feature_\tComponent_\n"
90 "FeatureComponents\tFeature_\tComponent_\n"
93 static const char install_exec_seq_dat[] =
94 "Action\tCondition\tSequence\n"
96 "InstallExecuteSequence\tAction\n"
97 "LaunchConditions\t\t100\n"
98 "CostInitialize\t\t800\n"
100 "CostFinalize\t\t1000\n"
101 "InstallValidate\t\t1400\n"
102 "InstallInitialize\t\t1500\n"
103 "ProcessComponents\t\t1600\n"
104 "RemoveFiles\t\t1700\n"
105 "InstallFiles\t\t2000\n"
106 "RegisterUser\t\t3000\n"
107 "RegisterProduct\t\t3100\n"
108 "PublishFeatures\t\t5100\n"
109 "PublishProduct\t\t5200\n"
110 "InstallFinalize\t\t6000\n";
114 const char *filename;
119 #define ADD_TABLE( x ) { #x".idt", x##_dat, sizeof(x##_dat) }
121 static const struct msi_table tables[] =
123 ADD_TABLE( directory ),
125 ADD_TABLE( component ),
126 ADD_TABLE( feature ),
127 ADD_TABLE( feature_comp ),
128 ADD_TABLE( property ),
129 ADD_TABLE( install_exec_seq ),
133 static void init_function_pointers( void )
135 HMODULE hmsi = GetModuleHandleA( "msi.dll" );
137 #define GET_PROC( mod, func ) \
138 p ## func = (void *)GetProcAddress( mod, #func ); \
140 trace( "GetProcAddress(%s) failed\n", #func );
142 GET_PROC( hmsi, MsiApplyPatchA );
146 static BOOL get_program_files_dir( char *buf, char *buf2 )
151 if (RegOpenKey( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &hkey ))
155 if (RegQueryValueExA( hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size ))
161 if (RegQueryValueExA( hkey, "CommonFilesDir", 0, &type, (LPBYTE)buf2, &size ))
170 static void create_file_data( const char *filename, const char *data, DWORD size )
175 file = CreateFileA( filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
176 if (file == INVALID_HANDLE_VALUE)
179 WriteFile( file, data, strlen( data ), &written, NULL );
182 SetFilePointer( file, size, NULL, FILE_BEGIN );
183 SetEndOfFile( file );
188 #define create_file( name, size ) create_file_data( name, name, size )
190 static BOOL delete_pf( const char *rel_path, BOOL is_file )
194 strcpy( path, PROG_FILES_DIR );
195 strcat( path, "\\" );
196 strcat( path, rel_path );
199 return DeleteFileA( path );
201 return RemoveDirectoryA( path );
204 static DWORD get_pf_file_size( const char *filename )
210 strcpy( path, PROG_FILES_DIR );
212 strcat( path, filename );
214 file = CreateFileA( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
215 if (file == INVALID_HANDLE_VALUE)
216 return INVALID_FILE_SIZE;
218 size = GetFileSize( file, NULL );
223 static void write_file( const char *filename, const char *data, DWORD data_size )
226 HANDLE file = CreateFile( filename, GENERIC_WRITE, 0, NULL,
227 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
228 WriteFile( file, data, data_size, &size, NULL );
232 static void set_suminfo( const char *filename )
237 r = MsiOpenDatabaseA( filename, MSIDBOPEN_DIRECT, &hdb );
238 ok( r == ERROR_SUCCESS, "failed to open database %u\n", r );
240 r = MsiGetSummaryInformation( hdb, NULL, 7, &hsi );
241 ok( r == ERROR_SUCCESS, "failed to open summaryinfo %u\n", r );
243 r = MsiSummaryInfoSetProperty( hsi, 2, VT_LPSTR, 0, NULL, "Installation Database" );
244 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
246 r = MsiSummaryInfoSetProperty( hsi, 3, VT_LPSTR, 0, NULL, "Installation Database" );
247 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
249 r = MsiSummaryInfoSetProperty( hsi, 4, VT_LPSTR, 0, NULL, "WineHQ" );
250 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
252 r = MsiSummaryInfoSetProperty( hsi, 7, VT_LPSTR, 0, NULL, ";1033" );
253 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
255 r = MsiSummaryInfoSetProperty( hsi, 9, VT_LPSTR, 0, NULL, "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}" );
256 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
258 r = MsiSummaryInfoSetProperty( hsi, 14, VT_I4, 100, NULL, NULL );
259 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
261 r = MsiSummaryInfoSetProperty( hsi, 15, VT_I4, 0, NULL, NULL );
262 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
264 r = MsiSummaryInfoPersist( hsi );
265 ok( r == ERROR_SUCCESS, "failed to persist suminfo %u\n", r );
267 r = MsiCloseHandle( hsi );
268 ok( r == ERROR_SUCCESS, "failed to close suminfo %u\n", r );
270 r = MsiCloseHandle( hdb );
271 ok( r == ERROR_SUCCESS, "failed to close database %u\n", r );
274 static void create_database( const char *filename, const struct msi_table *tables, UINT num_tables )
279 r = MsiOpenDatabaseA( filename, MSIDBOPEN_CREATE, &hdb );
280 ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
282 /* import the tables into the database */
283 for (i = 0; i < num_tables; i++)
285 const struct msi_table *table = &tables[i];
287 write_file( table->filename, table->data, (table->size - 1) * sizeof(char) );
289 r = MsiDatabaseImportA( hdb, CURR_DIR, table->filename );
290 ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
292 DeleteFileA( table->filename );
295 r = MsiDatabaseCommit( hdb );
296 ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
298 MsiCloseHandle( hdb );
299 set_suminfo( filename );
302 /* data for generating a patch */
304 /* table names - encoded as in an msi database file */
305 static const WCHAR p_name0[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
306 static const WCHAR p_name1[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
307 static const WCHAR p_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
308 static const WCHAR p_name3[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
309 static const WCHAR p_name4[] = { 0x3a8c, 0x47cb, 0x45b0, 0x45ec, 0x45a8, 0x4837, 0}; /* CAB_msitest */
310 static const WCHAR p_name5[] = { 0x4840, 0x4596, 0x3e6c, 0x45e4, 0x42e6, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* MsiPatchSequence */
311 static const WCHAR p_name6[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
312 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
313 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
314 /* substorage names */
315 static const WCHAR p_name7[] = { 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075, 0x0070,
316 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* targetToupgraded */
317 static const WCHAR p_name8[] = { 0x0023, 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075,
318 0x0070, 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* #targetToupgraded */
320 /* data in each table */
321 static const WCHAR p_data0[] = { /* _Columns */
322 0x0001, 0x0001, 0x0001, 0x0001, 0x8001, 0x8002, 0x8003, 0x8004,
323 0x0002, 0x0003, 0x0004, 0x0005, 0xad00, 0xbd26, 0x8d00, 0x9502
325 static const WCHAR p_data1[] = { /* _Tables */
328 static const char p_data2[] = { /* _StringData */
329 "MsiPatchSequencePatchFamilyProductCodeSequenceAttributes1.1.19388.37230913B8D18FBB64CACA239C74C11E3FA74"
331 static const WCHAR p_data3[] = { /* _StringPool */
333 0, 0, /* string 0 '' */
334 16, 5, /* string 1 'MsiPatchSequence' */
335 11, 1, /* string 2 'PatchFamily' */
336 11, 1, /* string 3 'ProductCode' */
337 8, 1, /* string 4 'Sequence' */
338 10, 1, /* string 5 'Attributes' */
339 15, 1, /* string 6 '1.1.19388.37230' */
340 32, 1, /* string 7 '913B8D18FBB64CACA239C74C11E3FA74' */
342 static const char p_data4[] = { /* CAB_msitest */
343 0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00,
344 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00,
345 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9e,
346 0x03, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x12,
347 0xea, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
348 0x3c, 0xd4, 0x80, 0x20, 0x00, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e,
349 0x74, 0x78, 0x74, 0x00, 0x0b, 0x3c, 0xd6, 0xc1, 0x4a, 0x00, 0xea,
350 0x03, 0x5b, 0x80, 0x80, 0x8d, 0x00, 0x10, 0xa1, 0x3e, 0x00, 0x00,
351 0x00, 0x00, 0x03, 0x00, 0x40, 0x30, 0x0c, 0x43, 0xf8, 0xb4, 0x85,
352 0x4d, 0x96, 0x08, 0x0a, 0x92, 0xf0, 0x52, 0xfb, 0xbb, 0x82, 0xf9,
353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x31, 0x7d,
354 0x56, 0xdf, 0xf7, 0x48, 0x7c, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
355 0x00, 0x00, 0x41, 0x80, 0xdf, 0xf7, 0xd8, 0x72, 0xbf, 0xb9, 0x63,
356 0x91, 0x0e, 0x57, 0x1f, 0xfa, 0x1a, 0x66, 0x54, 0x55
358 static const WCHAR p_data5[] = { /* MsiPatchSequence */
359 0x0007, 0x0000, 0x0006, 0x8000
361 static const char p_data6[] = { /* SummaryInformation */
362 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
363 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
364 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
365 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
366 0x30, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
367 0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00,
368 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x90,
369 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
370 0x0f, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
371 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x30, 0x46, 0x39, 0x36, 0x43,
372 0x44, 0x43, 0x30, 0x2d, 0x34, 0x43, 0x44, 0x46, 0x2d, 0x34, 0x33,
373 0x30, 0x34, 0x2d, 0x42, 0x32, 0x38, 0x33, 0x2d, 0x37, 0x42, 0x39,
374 0x32, 0x36, 0x34, 0x38, 0x38, 0x39, 0x45, 0x46, 0x37, 0x7d, 0x00,
375 0x00, 0x1e, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x39,
376 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42,
377 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39,
378 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41,
379 0x37, 0x34, 0x7d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x25, 0x00,
380 0x00, 0x00, 0x3a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f,
381 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x3b, 0x3a, 0x23,
382 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f, 0x75, 0x70, 0x67,
383 0x72, 0x61, 0x64, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
384 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x61, 0x74, 0x63, 0x68,
385 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x00,
386 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
395 static const struct table_data table_patch_data[] = {
396 { p_name0, p_data0, sizeof p_data0 },
397 { p_name1, p_data1, sizeof p_data1 },
398 { p_name2, p_data2, sizeof p_data2 - 1 },
399 { p_name3, p_data3, sizeof p_data3 },
400 { p_name4, p_data4, sizeof p_data4 },
401 { p_name5, p_data5, sizeof p_data5 },
402 { p_name6, p_data6, sizeof p_data6 }
405 #define NUM_PATCH_TABLES (sizeof table_patch_data/sizeof table_patch_data[0])
407 static const WCHAR t1_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
408 static const WCHAR t1_name1[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
409 static const WCHAR t1_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
410 static const WCHAR t1_name3[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
411 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
412 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
414 static const WCHAR t1_data0[] = { /* File */
415 0x0008, 0x0001, 0x03ea, 0x8000
417 static const char t1_data1[] = { /* _StringData */
420 static const WCHAR t1_data2[] = { /* _StringPool */
422 0, 0, /* string 0 '' */
423 9, 1, /* string 1 'patch.txt' */
425 static const char t1_data3[] = { /* SummaryInformation */
426 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
427 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
429 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
430 0x30, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00,
431 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00,
432 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8,
433 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00,
434 0x06, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
435 0x00, 0xd0, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xdc, 0x00,
436 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x08,
437 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
438 0x04, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00,
439 0x00, 0x10, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x1e, 0x00,
440 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61,
441 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74,
442 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
443 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c,
444 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74, 0x61,
445 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
446 0x07, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6e, 0x65, 0x48, 0x51, 0x00,
447 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
448 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
449 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
450 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
451 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31,
452 0x30, 0x33, 0x33, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06,
453 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00, 0x00,
454 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b, 0x39, 0x31,
455 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
456 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
457 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
458 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x39, 0x31,
459 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
460 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
461 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
462 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x41, 0x32,
463 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45, 0x32, 0x43,
464 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30, 0x39, 0x2d,
465 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35, 0x46, 0x34,
466 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
467 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x22, 0x09
470 static const struct table_data table_transform1_data[] = {
471 { t1_name0, t1_data0, sizeof t1_data0 },
472 { t1_name1, t1_data1, sizeof t1_data1 - 1 },
473 { t1_name2, t1_data2, sizeof t1_data2 },
474 { t1_name3, t1_data3, sizeof t1_data3 }
477 #define NUM_TRANSFORM1_TABLES (sizeof table_transform1_data/sizeof table_transform1_data[0])
479 static const WCHAR t2_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
480 static const WCHAR t2_name1[] = { 0x4840, 0x4216, 0x4327, 0x4824, 0 }; /* Media */
481 static const WCHAR t2_name2[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
482 static const WCHAR t2_name3[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
483 static const WCHAR t2_name4[] = { 0x4840, 0x4559, 0x44f2, 0x4568, 0x4737, 0 }; /* Property */
484 static const WCHAR t2_name5[] = { 0x4840, 0x4119, 0x41b7, 0x3e6b, 0x41a4, 0x412e, 0x422a, 0 }; /* PatchPackage */
485 static const WCHAR t2_name6[] = { 0x4840, 0x4452, 0x45f6, 0x43e4, 0x3baf, 0x423b, 0x4626,
486 0x4237, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* InstallExecuteSequence */
487 static const WCHAR t2_name7[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
488 static const WCHAR t2_name8[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
489 static const WCHAR t2_name9[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
490 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
491 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
493 static const WCHAR t2_data0[] = { /* File */
494 0x00c0, 0x0001, 0x9000, 0x83e8
496 static const WCHAR t2_data1[] = { /* Media */
497 0x0601, 0x8002, 0x03e9, 0x8000, 0x0000, 0x0007, 0x0000, 0x0008
499 static const WCHAR t2_data2[] = { /* _Columns */
500 0x0401, 0x0009, 0x0000, 0x000a, 0xad48, 0x0401, 0x0009, 0x0000, /* 0x0401 = add row (1), 4 shorts */
501 0x000b, 0xa502, 0x0401, 0x0009, 0x0000, 0x000c, 0x8104, 0x0401,
502 0x0009, 0x0000, 0x000d, 0x8502, 0x0401, 0x0009, 0x0000, 0x000e,
503 0x9900, 0x0401, 0x0009, 0x0000, 0x000f, 0x9d48, 0x0401, 0x0010,
504 0x0000, 0x0011, 0xad26, 0x0401, 0x0010, 0x0000, 0x0012, 0x8502,
505 0x0401, 0x0014, 0x0000, 0x0015, 0xad26, 0x0401, 0x0014, 0x0000,
508 static const WCHAR t2_data3[] = { /* _Tables */
509 0x0101, 0x0009, 0x0101, 0x0010, 0x0101, 0x0014
511 static const WCHAR t2_data4[] = { /* Property */
512 0x0201, 0x0002, 0x0003, 0x0201, 0x0004, 0x0005
514 static const WCHAR t2_data5[] = { /* PatchPackage */
515 0x0201, 0x0013, 0x8002
517 static const WCHAR t2_data6[] = { /* InstallExecuteSequence */
518 0x0301, 0x0006, 0x0000, 0x87d1
520 static const char t2_data7[] = { /* _StringData */
521 "patch.txtPATCHNEWSUMMARYSUBJECTInstallation DatabasePATCHNEWPACKAGECODE{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}"
522 "PatchFiles#CAB_msitestpropPatchFile_SequencePatchSizeAttributesHeaderStreamRef_PatchPackagePatchIdMedia_"
523 "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}MsiPatchHeadersStreamRef"
525 static const WCHAR t2_data8[] = { /* _StringPool */
527 0, 0, /* string 0 '' */
528 9, 1, /* string 1 'patch.txt' */
529 22, 1, /* string 2 'PATCHNEWSUMMARYSUBJECT' */
530 21, 1, /* string 3 'Installation Database' */
531 19, 1, /* string 4 'PATCHNEWPACKAGECODE' */
532 38, 1, /* string 5 '{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}' */
533 10, 1, /* string 6 'PatchFiles' */
534 12, 1, /* string 7 '#CAB_msitest' */
535 4, 1, /* string 8 'prop' */
536 5, 7, /* string 9 'Patch' */
537 5, 1, /* string 10 'File_' */
538 8, 1, /* string 11 'Sequence' */
539 9, 1, /* string 12 'PatchSize' */
540 10, 1, /* string 13 'Attributes' */
541 6, 2, /* string 14 'Header' */
542 10, 1, /* string 15 'StreamRef_' */
543 12, 3, /* string 16 'PatchPackage' */
544 7, 1, /* string 17 'PatchId' */
545 6, 1, /* string 18 'Media_' */
546 38, 1, /* string 19 '{0F96CDC0-4CDF-4304-B283-7B9264889EF7}' */
547 15, 3, /* string 20 'MsiPatchHeaders' */
548 9, 1 /* string 21 'StreamRef' */
550 static const char t2_data9[] = { /* SummaryInformation */
551 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
552 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
553 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
554 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
555 0x30, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00,
556 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00,
557 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x78,
558 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
559 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
560 0x00, 0x9c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa8, 0x00,
561 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x09,
562 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
563 0x4c, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00,
564 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
565 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
566 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
567 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
568 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00,
569 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01,
570 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
571 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00,
572 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
573 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b,
574 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
575 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
576 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
577 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
578 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
579 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
580 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
581 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
582 0x41, 0x32, 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45,
583 0x32, 0x43, 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30,
584 0x39, 0x2d, 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35,
585 0x46, 0x34, 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2d,
586 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x27, 0x09
589 static const struct table_data table_transform2_data[] = {
590 { t2_name0, t2_data0, sizeof t2_data0 },
591 { t2_name1, t2_data1, sizeof t2_data1 },
592 { t2_name2, t2_data2, sizeof t2_data2 },
593 { t2_name3, t2_data3, sizeof t2_data3 },
594 { t2_name4, t2_data4, sizeof t2_data4 },
595 { t2_name5, t2_data5, sizeof t2_data5 },
596 { t2_name6, t2_data6, sizeof t2_data6 },
597 { t2_name7, t2_data7, sizeof t2_data7 - 1 },
598 { t2_name8, t2_data8, sizeof t2_data8 },
599 { t2_name9, t2_data9, sizeof t2_data9 }
602 #define NUM_TRANSFORM2_TABLES (sizeof table_transform2_data/sizeof table_transform2_data[0])
604 static void write_tables( IStorage *stg, const struct table_data *tables, UINT num_tables )
610 for (i = 0; i < num_tables; i++)
612 r = IStorage_CreateStream( stg, tables[i].name, STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
615 ok( 0, "failed to create stream 0x%08x\n", r );
619 r = IStream_Write( stm, tables[i].data, tables[i].size, &count );
620 if (FAILED( r ) || count != tables[i].size)
621 ok( 0, "failed to write stream\n" );
622 IStream_Release( stm );
626 static void create_patch( const char *filename )
628 IStorage *stg = NULL, *stg1 = NULL, *stg2 = NULL;
632 const DWORD mode = STGM_CREATE|STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE;
634 const CLSID CLSID_MsiPatch = {0xc1086, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
635 const CLSID CLSID_MsiTransform = {0xc1082, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
637 len = MultiByteToWideChar( CP_ACP, 0, filename, -1, NULL, 0 );
638 filenameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
639 MultiByteToWideChar( CP_ACP, 0, filename, -1, filenameW, len );
641 r = StgCreateDocfile( filenameW, mode, 0, &stg );
642 HeapFree( GetProcessHeap(), 0, filenameW );
643 ok( r == S_OK, "failed to create storage 0x%08x\n", r );
647 r = IStorage_SetClass( stg, &CLSID_MsiPatch );
648 ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
650 write_tables( stg, table_patch_data, NUM_PATCH_TABLES );
652 r = IStorage_CreateStorage( stg, p_name7, mode, 0, 0, &stg1 );
653 ok( r == S_OK, "failed to create substorage 0x%08x\n", r );
655 r = IStorage_SetClass( stg1, &CLSID_MsiTransform );
656 ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
658 write_tables( stg1, table_transform1_data, NUM_TRANSFORM1_TABLES );
659 IStorage_Release( stg1 );
661 r = IStorage_CreateStorage( stg, p_name8, mode, 0, 0, &stg2 );
662 ok( r == S_OK, "failed to create substorage 0x%08x\n", r );
664 r = IStorage_SetClass( stg2, &CLSID_MsiTransform );
665 ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
667 write_tables( stg2, table_transform2_data, NUM_TRANSFORM2_TABLES );
668 IStorage_Release( stg2 );
669 IStorage_Release( stg );
672 static void test_simple_patch( void )
678 MSIHANDLE hpackage, hdb, hview, hrec;
680 if (!pMsiApplyPatchA)
682 win_skip("MsiApplyPatchA is not available\n");
686 CreateDirectoryA( "msitest", NULL );
687 create_file( "msitest\\patch.txt", 1000 );
689 create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
690 create_patch( mspfile );
692 MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
694 r = MsiInstallProductA( msifile, NULL );
695 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
697 size = get_pf_file_size( "msitest\\patch.txt" );
698 ok( size == 1000, "expected 1000, got %u\n", size );
700 r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
701 ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
702 "expected ERROR_SUCCESS, got %u\n", r );
704 if (r == ERROR_PATCH_PACKAGE_INVALID)
706 win_skip("Windows Installer < 3.0 detected\n");
710 size = get_pf_file_size( "msitest\\patch.txt" );
711 ok( size == 1002, "expected 1002, got %u\n", size );
713 strcpy( path, CURR_DIR );
714 strcat( path, "\\" );
715 strcat( path, msifile );
717 r = MsiOpenPackageA( path, &hpackage );
718 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
720 hdb = MsiGetActiveDatabase( hpackage );
721 ok( hdb, "failed to get database handle\n" );
723 query = "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
724 r = MsiDatabaseOpenView( hdb, query, &hview );
725 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
727 r = MsiViewExecute( hview, 0 );
728 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
730 r = MsiViewFetch( hview, &hrec );
731 todo_wine ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
733 MsiCloseHandle( hrec );
734 MsiViewClose( hview );
735 MsiCloseHandle( hview );
736 MsiCloseHandle( hdb );
737 MsiCloseHandle( hpackage );
739 r = MsiInstallProductA( msifile, "REMOVE=ALL" );
740 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
742 ok( !delete_pf( "msitest\\patch.txt", TRUE ), "file not removed\n" );
743 ok( !delete_pf( "msitest", FALSE ), "directory not removed\n" );
745 DeleteFileA( msifile );
746 DeleteFileA( mspfile );
747 RemoveDirectoryA( "msitest" );
750 static void test_MsiOpenDatabase( void )
755 r = MsiOpenDatabase( mspfile, MSIDBOPEN_CREATE, &hdb );
756 ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
758 r = MsiDatabaseCommit( hdb );
759 ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
760 MsiCloseHandle( hdb );
762 r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
763 ok(r == ERROR_OPEN_FAILED, "expected ERROR_OPEN_FAILED, got %u\n", r);
764 DeleteFileA( mspfile );
766 r = MsiOpenDatabase( mspfile, MSIDBOPEN_CREATE + MSIDBOPEN_PATCHFILE, &hdb );
767 ok(r == ERROR_SUCCESS , "failed to open database %u\n", r);
769 r = MsiDatabaseCommit( hdb );
770 ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
771 MsiCloseHandle( hdb );
773 r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
774 ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
775 MsiCloseHandle( hdb );
776 DeleteFileA( mspfile );
778 create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
779 create_patch( mspfile );
781 r = MsiOpenDatabase( msifile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
782 ok(r == ERROR_OPEN_FAILED, "failed to open database %u\n", r );
784 r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
785 ok(r == ERROR_SUCCESS, "failed to open database %u\n", r );
786 MsiCloseHandle( hdb );
788 DeleteFileA( msifile );
789 DeleteFileA( mspfile );
795 char temp_path[MAX_PATH], prev_path[MAX_PATH];
797 init_function_pointers();
799 GetCurrentDirectoryA( MAX_PATH, prev_path );
800 GetTempPath( MAX_PATH, temp_path );
801 SetCurrentDirectoryA( temp_path );
803 strcpy( CURR_DIR, temp_path );
804 len = strlen( CURR_DIR );
806 if (len && (CURR_DIR[len - 1] == '\\'))
807 CURR_DIR[len - 1] = 0;
809 get_program_files_dir( PROG_FILES_DIR, COMMON_FILES_DIR );
812 test_MsiOpenDatabase();
814 SetCurrentDirectoryA( prev_path );