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 );
34 static UINT (WINAPI *pMsiGetPatchInfoExA)( LPCSTR, LPCSTR, LPCSTR, MSIINSTALLCONTEXT,
35 LPCSTR, LPSTR, DWORD * );
36 static UINT (WINAPI *pMsiEnumPatchesExA)( LPCSTR, LPCSTR, DWORD, DWORD, DWORD, LPSTR,
37 LPSTR, MSIINSTALLCONTEXT *, LPSTR, LPDWORD );
39 static const char *msifile = "winetest-patch.msi";
40 static const char *mspfile = "winetest-patch.msp";
42 static char CURR_DIR[MAX_PATH];
43 static char PROG_FILES_DIR[MAX_PATH];
44 static char COMMON_FILES_DIR[MAX_PATH];
46 /* msi database data */
48 static const char property_dat[] =
51 "Property\tProperty\n"
52 "Manufacturer\tWineHQ\n"
53 "ProductCode\t{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}\n"
54 "UpgradeCode\t{A2E3D643-4E2C-477F-A309-F76F552D5F43}\n"
55 "ProductLanguage\t1033\n"
56 "ProductName\tmsitest\n"
57 "ProductVersion\t1.1.1\n";
59 static const char media_dat[] =
60 "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
61 "i2\ti4\tL64\tS255\tS32\tS72\n"
63 "1\t1\t\t\tDISK1\t\n";
65 static const char file_dat[] =
66 "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
67 "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
69 "patch.txt\tpatch\tpatch.txt\t1000\t\t\t0\t1\n";
71 static const char directory_dat[] =
72 "Directory\tDirectory_Parent\tDefaultDir\n"
74 "Directory\tDirectory\n"
75 "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
76 "ProgramFilesFolder\tTARGETDIR\t.\n"
77 "TARGETDIR\t\tSourceDir";
79 static const char component_dat[] =
80 "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
81 "s72\tS38\ts72\ti2\tS255\tS72\n"
82 "Component\tComponent\n"
83 "patch\t{4B79D87E-6D28-4FD3-92D6-CD9B26AF64F1}\tMSITESTDIR\t0\t\tpatch.txt\n";
85 static const char feature_dat[] =
86 "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
87 "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
89 "patch\t\t\tpatch feature\t1\t1\tMSITESTDIR\t0\n";
91 static const char feature_comp_dat[] =
92 "Feature_\tComponent_\n"
94 "FeatureComponents\tFeature_\tComponent_\n"
97 static const char install_exec_seq_dat[] =
98 "Action\tCondition\tSequence\n"
100 "InstallExecuteSequence\tAction\n"
101 "LaunchConditions\t\t100\n"
102 "CostInitialize\t\t800\n"
104 "CostFinalize\t\t1000\n"
105 "InstallValidate\t\t1400\n"
106 "InstallInitialize\t\t1500\n"
107 "ProcessComponents\t\t1600\n"
108 "RemoveFiles\t\t1700\n"
109 "InstallFiles\t\t2000\n"
110 "RegisterUser\t\t3000\n"
111 "RegisterProduct\t\t3100\n"
112 "PublishFeatures\t\t5100\n"
113 "PublishProduct\t\t5200\n"
114 "InstallFinalize\t\t6000\n";
118 const char *filename;
123 #define ADD_TABLE( x ) { #x".idt", x##_dat, sizeof(x##_dat) }
125 static const struct msi_table tables[] =
127 ADD_TABLE( directory ),
129 ADD_TABLE( component ),
130 ADD_TABLE( feature ),
131 ADD_TABLE( feature_comp ),
132 ADD_TABLE( property ),
133 ADD_TABLE( install_exec_seq ),
137 static void init_function_pointers( void )
139 HMODULE hmsi = GetModuleHandleA( "msi.dll" );
141 #define GET_PROC( mod, func ) \
142 p ## func = (void *)GetProcAddress( mod, #func ); \
144 trace( "GetProcAddress(%s) failed\n", #func );
146 GET_PROC( hmsi, MsiApplyPatchA );
147 GET_PROC( hmsi, MsiGetPatchInfoExA );
148 GET_PROC( hmsi, MsiEnumPatchesExA );
152 static BOOL get_program_files_dir( char *buf, char *buf2 )
157 if (RegOpenKey( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &hkey ))
161 if (RegQueryValueExA( hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size ))
167 if (RegQueryValueExA( hkey, "CommonFilesDir", 0, &type, (LPBYTE)buf2, &size ))
176 static void create_file_data( const char *filename, const char *data, DWORD size )
181 file = CreateFileA( filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
182 if (file == INVALID_HANDLE_VALUE)
185 WriteFile( file, data, strlen( data ), &written, NULL );
188 SetFilePointer( file, size, NULL, FILE_BEGIN );
189 SetEndOfFile( file );
194 #define create_file( name, size ) create_file_data( name, name, size )
196 static BOOL delete_pf( const char *rel_path, BOOL is_file )
200 strcpy( path, PROG_FILES_DIR );
201 strcat( path, "\\" );
202 strcat( path, rel_path );
205 return DeleteFileA( path );
207 return RemoveDirectoryA( path );
210 static DWORD get_pf_file_size( const char *filename )
216 strcpy( path, PROG_FILES_DIR );
218 strcat( path, filename );
220 file = CreateFileA( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
221 if (file == INVALID_HANDLE_VALUE)
222 return INVALID_FILE_SIZE;
224 size = GetFileSize( file, NULL );
229 static void write_file( const char *filename, const char *data, DWORD data_size )
232 HANDLE file = CreateFile( filename, GENERIC_WRITE, 0, NULL,
233 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
234 WriteFile( file, data, data_size, &size, NULL );
238 static void set_suminfo( const char *filename )
243 r = MsiOpenDatabaseA( filename, MSIDBOPEN_DIRECT, &hdb );
244 ok( r == ERROR_SUCCESS, "failed to open database %u\n", r );
246 r = MsiGetSummaryInformation( hdb, NULL, 7, &hsi );
247 ok( r == ERROR_SUCCESS, "failed to open summaryinfo %u\n", r );
249 r = MsiSummaryInfoSetProperty( hsi, 2, VT_LPSTR, 0, NULL, "Installation Database" );
250 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
252 r = MsiSummaryInfoSetProperty( hsi, 3, VT_LPSTR, 0, NULL, "Installation Database" );
253 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
255 r = MsiSummaryInfoSetProperty( hsi, 4, VT_LPSTR, 0, NULL, "WineHQ" );
256 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
258 r = MsiSummaryInfoSetProperty( hsi, 7, VT_LPSTR, 0, NULL, ";1033" );
259 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
261 r = MsiSummaryInfoSetProperty( hsi, 9, VT_LPSTR, 0, NULL, "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}" );
262 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
264 r = MsiSummaryInfoSetProperty( hsi, 14, VT_I4, 100, NULL, NULL );
265 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
267 r = MsiSummaryInfoSetProperty( hsi, 15, VT_I4, 0, NULL, NULL );
268 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
270 r = MsiSummaryInfoPersist( hsi );
271 ok( r == ERROR_SUCCESS, "failed to persist suminfo %u\n", r );
273 r = MsiCloseHandle( hsi );
274 ok( r == ERROR_SUCCESS, "failed to close suminfo %u\n", r );
276 r = MsiCloseHandle( hdb );
277 ok( r == ERROR_SUCCESS, "failed to close database %u\n", r );
280 static void create_database( const char *filename, const struct msi_table *tables, UINT num_tables )
285 r = MsiOpenDatabaseA( filename, MSIDBOPEN_CREATE, &hdb );
286 ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
288 /* import the tables into the database */
289 for (i = 0; i < num_tables; i++)
291 const struct msi_table *table = &tables[i];
293 write_file( table->filename, table->data, (table->size - 1) * sizeof(char) );
295 r = MsiDatabaseImportA( hdb, CURR_DIR, table->filename );
296 ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
298 DeleteFileA( table->filename );
301 r = MsiDatabaseCommit( hdb );
302 ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
304 MsiCloseHandle( hdb );
305 set_suminfo( filename );
308 /* data for generating a patch */
310 /* table names - encoded as in an msi database file */
311 static const WCHAR p_name0[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
312 static const WCHAR p_name1[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
313 static const WCHAR p_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
314 static const WCHAR p_name3[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
315 static const WCHAR p_name4[] = { 0x3a8c, 0x47cb, 0x45b0, 0x45ec, 0x45a8, 0x4837, 0}; /* CAB_msitest */
316 static const WCHAR p_name5[] = { 0x4840, 0x4596, 0x3e6c, 0x45e4, 0x42e6, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* MsiPatchSequence */
317 static const WCHAR p_name6[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
318 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
319 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
320 /* substorage names */
321 static const WCHAR p_name7[] = { 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075, 0x0070,
322 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* targetToupgraded */
323 static const WCHAR p_name8[] = { 0x0023, 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075,
324 0x0070, 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* #targetToupgraded */
326 /* data in each table */
327 static const WCHAR p_data0[] = { /* _Columns */
328 0x0001, 0x0001, 0x0001, 0x0001, 0x8001, 0x8002, 0x8003, 0x8004,
329 0x0002, 0x0003, 0x0004, 0x0005, 0xad00, 0xbd26, 0x8d00, 0x9502
331 static const WCHAR p_data1[] = { /* _Tables */
334 static const char p_data2[] = { /* _StringData */
335 "MsiPatchSequencePatchFamilyProductCodeSequenceAttributes1.1.19388.37230913B8D18FBB64CACA239C74C11E3FA74"
337 static const WCHAR p_data3[] = { /* _StringPool */
339 0, 0, /* string 0 '' */
340 16, 5, /* string 1 'MsiPatchSequence' */
341 11, 1, /* string 2 'PatchFamily' */
342 11, 1, /* string 3 'ProductCode' */
343 8, 1, /* string 4 'Sequence' */
344 10, 1, /* string 5 'Attributes' */
345 15, 1, /* string 6 '1.1.19388.37230' */
346 32, 1, /* string 7 '913B8D18FBB64CACA239C74C11E3FA74' */
348 static const char p_data4[] = { /* CAB_msitest */
349 0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00,
350 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00,
351 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9e,
352 0x03, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x12,
353 0xea, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
354 0x3c, 0xd4, 0x80, 0x20, 0x00, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e,
355 0x74, 0x78, 0x74, 0x00, 0x0b, 0x3c, 0xd6, 0xc1, 0x4a, 0x00, 0xea,
356 0x03, 0x5b, 0x80, 0x80, 0x8d, 0x00, 0x10, 0xa1, 0x3e, 0x00, 0x00,
357 0x00, 0x00, 0x03, 0x00, 0x40, 0x30, 0x0c, 0x43, 0xf8, 0xb4, 0x85,
358 0x4d, 0x96, 0x08, 0x0a, 0x92, 0xf0, 0x52, 0xfb, 0xbb, 0x82, 0xf9,
359 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x31, 0x7d,
360 0x56, 0xdf, 0xf7, 0x48, 0x7c, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
361 0x00, 0x00, 0x41, 0x80, 0xdf, 0xf7, 0xd8, 0x72, 0xbf, 0xb9, 0x63,
362 0x91, 0x0e, 0x57, 0x1f, 0xfa, 0x1a, 0x66, 0x54, 0x55
364 static const WCHAR p_data5[] = { /* MsiPatchSequence */
365 0x0007, 0x0000, 0x0006, 0x8000
367 static const char p_data6[] = { /* SummaryInformation */
368 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
369 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
370 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
371 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
372 0x30, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
373 0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00,
374 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x90,
375 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
376 0x0f, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
377 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x30, 0x46, 0x39, 0x36, 0x43,
378 0x44, 0x43, 0x30, 0x2d, 0x34, 0x43, 0x44, 0x46, 0x2d, 0x34, 0x33,
379 0x30, 0x34, 0x2d, 0x42, 0x32, 0x38, 0x33, 0x2d, 0x37, 0x42, 0x39,
380 0x32, 0x36, 0x34, 0x38, 0x38, 0x39, 0x45, 0x46, 0x37, 0x7d, 0x00,
381 0x00, 0x1e, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x39,
382 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42,
383 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39,
384 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41,
385 0x37, 0x34, 0x7d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x25, 0x00,
386 0x00, 0x00, 0x3a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f,
387 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x3b, 0x3a, 0x23,
388 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f, 0x75, 0x70, 0x67,
389 0x72, 0x61, 0x64, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
390 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x61, 0x74, 0x63, 0x68,
391 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x00,
392 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
401 static const struct table_data table_patch_data[] = {
402 { p_name0, p_data0, sizeof p_data0 },
403 { p_name1, p_data1, sizeof p_data1 },
404 { p_name2, p_data2, sizeof p_data2 - 1 },
405 { p_name3, p_data3, sizeof p_data3 },
406 { p_name4, p_data4, sizeof p_data4 },
407 { p_name5, p_data5, sizeof p_data5 },
408 { p_name6, p_data6, sizeof p_data6 }
411 #define NUM_PATCH_TABLES (sizeof table_patch_data/sizeof table_patch_data[0])
413 static const WCHAR t1_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
414 static const WCHAR t1_name1[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
415 static const WCHAR t1_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
416 static const WCHAR t1_name3[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
417 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
418 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
420 static const WCHAR t1_data0[] = { /* File */
421 0x0008, 0x0001, 0x03ea, 0x8000
423 static const char t1_data1[] = { /* _StringData */
426 static const WCHAR t1_data2[] = { /* _StringPool */
428 0, 0, /* string 0 '' */
429 9, 1, /* string 1 'patch.txt' */
431 static const char t1_data3[] = { /* SummaryInformation */
432 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
433 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
434 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
435 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
436 0x30, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00,
437 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00,
438 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8,
439 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00,
440 0x06, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
441 0x00, 0xd0, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xdc, 0x00,
442 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x08,
443 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
444 0x04, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00,
445 0x00, 0x10, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x1e, 0x00,
446 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61,
447 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74,
448 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
449 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c,
450 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74, 0x61,
451 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
452 0x07, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6e, 0x65, 0x48, 0x51, 0x00,
453 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
454 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
455 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
456 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
457 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31,
458 0x30, 0x33, 0x33, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06,
459 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00, 0x00,
460 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b, 0x39, 0x31,
461 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
462 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
463 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
464 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x39, 0x31,
465 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
466 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
467 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
468 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x41, 0x32,
469 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45, 0x32, 0x43,
470 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30, 0x39, 0x2d,
471 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35, 0x46, 0x34,
472 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
473 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x22, 0x09
476 static const struct table_data table_transform1_data[] = {
477 { t1_name0, t1_data0, sizeof t1_data0 },
478 { t1_name1, t1_data1, sizeof t1_data1 - 1 },
479 { t1_name2, t1_data2, sizeof t1_data2 },
480 { t1_name3, t1_data3, sizeof t1_data3 }
483 #define NUM_TRANSFORM1_TABLES (sizeof table_transform1_data/sizeof table_transform1_data[0])
485 static const WCHAR t2_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
486 static const WCHAR t2_name1[] = { 0x4840, 0x4216, 0x4327, 0x4824, 0 }; /* Media */
487 static const WCHAR t2_name2[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
488 static const WCHAR t2_name3[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
489 static const WCHAR t2_name4[] = { 0x4840, 0x4559, 0x44f2, 0x4568, 0x4737, 0 }; /* Property */
490 static const WCHAR t2_name5[] = { 0x4840, 0x4119, 0x41b7, 0x3e6b, 0x41a4, 0x412e, 0x422a, 0 }; /* PatchPackage */
491 static const WCHAR t2_name6[] = { 0x4840, 0x4452, 0x45f6, 0x43e4, 0x3baf, 0x423b, 0x4626,
492 0x4237, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* InstallExecuteSequence */
493 static const WCHAR t2_name7[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
494 static const WCHAR t2_name8[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
495 static const WCHAR t2_name9[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
496 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
497 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
499 static const WCHAR t2_data0[] = { /* File */
500 0x00c0, 0x0001, 0x9000, 0x83e8
502 static const WCHAR t2_data1[] = { /* Media */
503 0x0601, 0x8002, 0x03e9, 0x8000, 0x0000, 0x0007, 0x0000, 0x0008
505 static const WCHAR t2_data2[] = { /* _Columns */
506 0x0401, 0x0009, 0x0000, 0x000a, 0xad48, 0x0401, 0x0009, 0x0000, /* 0x0401 = add row (1), 4 shorts */
507 0x000b, 0xa502, 0x0401, 0x0009, 0x0000, 0x000c, 0x8104, 0x0401,
508 0x0009, 0x0000, 0x000d, 0x8502, 0x0401, 0x0009, 0x0000, 0x000e,
509 0x9900, 0x0401, 0x0009, 0x0000, 0x000f, 0x9d48, 0x0401, 0x0010,
510 0x0000, 0x0011, 0xad26, 0x0401, 0x0010, 0x0000, 0x0012, 0x8502,
511 0x0401, 0x0014, 0x0000, 0x0015, 0xad26, 0x0401, 0x0014, 0x0000,
514 static const WCHAR t2_data3[] = { /* _Tables */
515 0x0101, 0x0009, 0x0101, 0x0010, 0x0101, 0x0014
517 static const WCHAR t2_data4[] = { /* Property */
518 0x0201, 0x0002, 0x0003, 0x0201, 0x0004, 0x0005
520 static const WCHAR t2_data5[] = { /* PatchPackage */
521 0x0201, 0x0013, 0x8002
523 static const WCHAR t2_data6[] = { /* InstallExecuteSequence */
524 0x0301, 0x0006, 0x0000, 0x87d1
526 static const char t2_data7[] = { /* _StringData */
527 "patch.txtPATCHNEWSUMMARYSUBJECTInstallation DatabasePATCHNEWPACKAGECODE{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}"
528 "PatchFiles#CAB_msitestpropPatchFile_SequencePatchSizeAttributesHeaderStreamRef_PatchPackagePatchIdMedia_"
529 "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}MsiPatchHeadersStreamRef"
531 static const WCHAR t2_data8[] = { /* _StringPool */
533 0, 0, /* string 0 '' */
534 9, 1, /* string 1 'patch.txt' */
535 22, 1, /* string 2 'PATCHNEWSUMMARYSUBJECT' */
536 21, 1, /* string 3 'Installation Database' */
537 19, 1, /* string 4 'PATCHNEWPACKAGECODE' */
538 38, 1, /* string 5 '{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}' */
539 10, 1, /* string 6 'PatchFiles' */
540 12, 1, /* string 7 '#CAB_msitest' */
541 4, 1, /* string 8 'prop' */
542 5, 7, /* string 9 'Patch' */
543 5, 1, /* string 10 'File_' */
544 8, 1, /* string 11 'Sequence' */
545 9, 1, /* string 12 'PatchSize' */
546 10, 1, /* string 13 'Attributes' */
547 6, 2, /* string 14 'Header' */
548 10, 1, /* string 15 'StreamRef_' */
549 12, 3, /* string 16 'PatchPackage' */
550 7, 1, /* string 17 'PatchId' */
551 6, 1, /* string 18 'Media_' */
552 38, 1, /* string 19 '{0F96CDC0-4CDF-4304-B283-7B9264889EF7}' */
553 15, 3, /* string 20 'MsiPatchHeaders' */
554 9, 1 /* string 21 'StreamRef' */
556 static const char t2_data9[] = { /* SummaryInformation */
557 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
558 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
559 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
560 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
561 0x30, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00,
562 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00,
563 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x78,
564 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
565 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
566 0x00, 0x9c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa8, 0x00,
567 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x09,
568 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
569 0x4c, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00,
570 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
571 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
572 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
573 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
574 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00,
575 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01,
576 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
577 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00,
578 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
579 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b,
580 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
581 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
582 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
583 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
584 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
585 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
586 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
587 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
588 0x41, 0x32, 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45,
589 0x32, 0x43, 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30,
590 0x39, 0x2d, 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35,
591 0x46, 0x34, 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2d,
592 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x27, 0x09
595 static const struct table_data table_transform2_data[] = {
596 { t2_name0, t2_data0, sizeof t2_data0 },
597 { t2_name1, t2_data1, sizeof t2_data1 },
598 { t2_name2, t2_data2, sizeof t2_data2 },
599 { t2_name3, t2_data3, sizeof t2_data3 },
600 { t2_name4, t2_data4, sizeof t2_data4 },
601 { t2_name5, t2_data5, sizeof t2_data5 },
602 { t2_name6, t2_data6, sizeof t2_data6 },
603 { t2_name7, t2_data7, sizeof t2_data7 - 1 },
604 { t2_name8, t2_data8, sizeof t2_data8 },
605 { t2_name9, t2_data9, sizeof t2_data9 }
608 #define NUM_TRANSFORM2_TABLES (sizeof table_transform2_data/sizeof table_transform2_data[0])
610 static void write_tables( IStorage *stg, const struct table_data *tables, UINT num_tables )
616 for (i = 0; i < num_tables; i++)
618 r = IStorage_CreateStream( stg, tables[i].name, STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
621 ok( 0, "failed to create stream 0x%08x\n", r );
625 r = IStream_Write( stm, tables[i].data, tables[i].size, &count );
626 if (FAILED( r ) || count != tables[i].size)
627 ok( 0, "failed to write stream\n" );
628 IStream_Release( stm );
632 static void create_patch( const char *filename )
634 IStorage *stg = NULL, *stg1 = NULL, *stg2 = NULL;
638 const DWORD mode = STGM_CREATE|STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE;
640 const CLSID CLSID_MsiPatch = {0xc1086, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
641 const CLSID CLSID_MsiTransform = {0xc1082, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
643 len = MultiByteToWideChar( CP_ACP, 0, filename, -1, NULL, 0 );
644 filenameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
645 MultiByteToWideChar( CP_ACP, 0, filename, -1, filenameW, len );
647 r = StgCreateDocfile( filenameW, mode, 0, &stg );
648 HeapFree( GetProcessHeap(), 0, filenameW );
649 ok( r == S_OK, "failed to create storage 0x%08x\n", r );
653 r = IStorage_SetClass( stg, &CLSID_MsiPatch );
654 ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
656 write_tables( stg, table_patch_data, NUM_PATCH_TABLES );
658 r = IStorage_CreateStorage( stg, p_name7, mode, 0, 0, &stg1 );
659 ok( r == S_OK, "failed to create substorage 0x%08x\n", r );
661 r = IStorage_SetClass( stg1, &CLSID_MsiTransform );
662 ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
664 write_tables( stg1, table_transform1_data, NUM_TRANSFORM1_TABLES );
665 IStorage_Release( stg1 );
667 r = IStorage_CreateStorage( stg, p_name8, mode, 0, 0, &stg2 );
668 ok( r == S_OK, "failed to create substorage 0x%08x\n", r );
670 r = IStorage_SetClass( stg2, &CLSID_MsiTransform );
671 ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
673 write_tables( stg2, table_transform2_data, NUM_TRANSFORM2_TABLES );
674 IStorage_Release( stg2 );
675 IStorage_Release( stg );
678 static void test_simple_patch( void )
682 char path[MAX_PATH], install_source[MAX_PATH];
684 MSIHANDLE hpackage, hdb, hview, hrec;
686 if (!pMsiApplyPatchA)
688 win_skip("MsiApplyPatchA is not available\n");
692 CreateDirectoryA( "msitest", NULL );
693 create_file( "msitest\\patch.txt", 1000 );
695 create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
696 create_patch( mspfile );
698 MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
700 r = MsiInstallProductA( msifile, NULL );
701 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
703 size = get_pf_file_size( "msitest\\patch.txt" );
704 ok( size == 1000, "expected 1000, got %u\n", size );
706 size = sizeof(install_source);
707 r = MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
708 "InstallSource", install_source, &size );
709 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
711 r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
712 ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
713 "expected ERROR_SUCCESS, got %u\n", r );
715 if (r == ERROR_PATCH_PACKAGE_INVALID)
717 win_skip("Windows Installer < 3.0 detected\n");
721 size = get_pf_file_size( "msitest\\patch.txt" );
722 ok( size == 1002, "expected 1002, got %u\n", size );
724 strcpy( path, CURR_DIR );
725 strcat( path, "\\" );
726 strcat( path, msifile );
728 /* show that MsiOpenPackage applies registered patches */
729 r = MsiOpenPackageA( path, &hpackage );
730 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
732 hdb = MsiGetActiveDatabase( hpackage );
733 ok( hdb, "failed to get database handle\n" );
735 query = "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
736 r = MsiDatabaseOpenView( hdb, query, &hview );
737 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
739 r = MsiViewExecute( hview, 0 );
740 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
742 r = MsiViewFetch( hview, &hrec );
743 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
745 MsiCloseHandle( hrec );
746 MsiViewClose( hview );
747 MsiCloseHandle( hview );
748 MsiCloseHandle( hdb );
749 MsiCloseHandle( hpackage );
751 /* show that patches are not committed to the local package database */
753 r = MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
754 "LocalPackage", path, &size );
755 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
757 r = MsiOpenDatabaseA( path, MSIDBOPEN_READONLY, &hdb );
758 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
760 r = MsiDatabaseOpenView( hdb, query, &hview );
761 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
763 r = MsiViewExecute( hview, 0 );
764 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
766 r = MsiViewFetch( hview, &hrec );
767 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
769 MsiCloseHandle( hrec );
770 MsiViewClose( hview );
771 MsiCloseHandle( hview );
772 MsiCloseHandle( hdb );
775 r = MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
776 "InstallSource", path, &size );
777 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
778 ok( !strcasecmp( path, install_source ), "wrong path %s\n", path );
780 r = MsiInstallProductA( msifile, "REMOVE=ALL" );
781 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
783 ok( !delete_pf( "msitest\\patch.txt", TRUE ), "file not removed\n" );
784 ok( !delete_pf( "msitest", FALSE ), "directory not removed\n" );
786 DeleteFileA( msifile );
787 DeleteFileA( mspfile );
788 RemoveDirectoryA( "msitest" );
791 static void test_MsiOpenDatabase( void )
796 r = MsiOpenDatabase( mspfile, MSIDBOPEN_CREATE, &hdb );
797 ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
799 r = MsiDatabaseCommit( hdb );
800 ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
801 MsiCloseHandle( hdb );
803 r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
804 ok(r == ERROR_OPEN_FAILED, "expected ERROR_OPEN_FAILED, got %u\n", r);
805 DeleteFileA( mspfile );
807 r = MsiOpenDatabase( mspfile, MSIDBOPEN_CREATE + MSIDBOPEN_PATCHFILE, &hdb );
808 ok(r == ERROR_SUCCESS , "failed to open database %u\n", r);
810 r = MsiDatabaseCommit( hdb );
811 ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
812 MsiCloseHandle( hdb );
814 r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
815 ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
816 MsiCloseHandle( hdb );
817 DeleteFileA( mspfile );
819 create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
820 create_patch( mspfile );
822 r = MsiOpenDatabase( msifile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
823 ok(r == ERROR_OPEN_FAILED, "failed to open database %u\n", r );
825 r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
826 ok(r == ERROR_SUCCESS, "failed to open database %u\n", r );
827 MsiCloseHandle( hdb );
829 DeleteFileA( msifile );
830 DeleteFileA( mspfile );
833 static UINT find_entry( MSIHANDLE hdb, const char *table, const char *entry )
835 static char fmt[] = "SELECT * FROM `%s` WHERE `Name` = '%s'";
838 MSIHANDLE hview, hrec;
840 sprintf( query, fmt, table, entry );
841 r = MsiDatabaseOpenView( hdb, query, &hview );
842 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
844 r = MsiViewExecute( hview, 0 );
845 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
847 r = MsiViewFetch( hview, &hrec );
848 MsiViewClose( hview );
849 MsiCloseHandle( hview );
850 MsiCloseHandle( hrec );
854 static void test_system_tables( void )
858 MSIHANDLE hproduct, hdb, hview, hrec;
860 if (!pMsiApplyPatchA)
862 win_skip("MsiApplyPatchA is not available\n");
866 CreateDirectoryA( "msitest", NULL );
867 create_file( "msitest\\patch.txt", 1000 );
869 create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
870 create_patch( mspfile );
872 MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
874 r = MsiInstallProductA( msifile, NULL );
875 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
877 r = MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct );
878 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
880 hdb = MsiGetActiveDatabase( hproduct );
881 ok( hdb, "failed to get database handle\n" );
883 r = find_entry( hdb, "_Streams", "\5SummaryInformation" );
884 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
886 query = "SELECT * FROM `_Storages`";
887 r = MsiDatabaseOpenView( hdb, query, &hview );
888 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
890 r = MsiViewExecute( hview, 0 );
891 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
893 r = MsiViewFetch( hview, &hrec );
894 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
896 r = find_entry( hdb, "_Tables", "Directory" );
897 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
899 r = find_entry( hdb, "_Tables", "File" );
900 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
902 r = find_entry( hdb, "_Tables", "Component" );
903 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
905 r = find_entry( hdb, "_Tables", "Feature" );
906 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
908 r = find_entry( hdb, "_Tables", "FeatureComponents" );
909 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
911 r = find_entry( hdb, "_Tables", "Property" );
912 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
914 r = find_entry( hdb, "_Tables", "InstallExecuteSequence" );
915 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
917 r = find_entry( hdb, "_Tables", "Media" );
918 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
920 r = find_entry( hdb, "_Tables", "_Property" );
921 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
923 MsiCloseHandle( hrec );
924 MsiViewClose( hview );
925 MsiCloseHandle( hview );
926 MsiCloseHandle( hdb );
927 MsiCloseHandle( hproduct );
929 r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
930 ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
931 "expected ERROR_SUCCESS, got %u\n", r );
933 if (r == ERROR_PATCH_PACKAGE_INVALID)
935 win_skip("Windows Installer < 3.0 detected\n");
939 r = MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct );
940 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
942 hdb = MsiGetActiveDatabase( hproduct );
943 ok( hdb, "failed to get database handle\n" );
945 r = find_entry( hdb, "_Streams", "\5SummaryInformation" );
946 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
948 query = "SELECT * FROM `_Storages`";
949 r = MsiDatabaseOpenView( hdb, query, &hview );
950 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
952 r = MsiViewExecute( hview, 0 );
953 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
955 r = MsiViewFetch( hview, &hrec );
956 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
958 r = find_entry( hdb, "_Tables", "Directory" );
959 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
961 r = find_entry( hdb, "_Tables", "File" );
962 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
964 r = find_entry( hdb, "_Tables", "Component" );
965 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
967 r = find_entry( hdb, "_Tables", "Feature" );
968 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
970 r = find_entry( hdb, "_Tables", "FeatureComponents" );
971 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
973 r = find_entry( hdb, "_Tables", "Property" );
974 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
976 r = find_entry( hdb, "_Tables", "InstallExecuteSequence" );
977 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
979 r = find_entry( hdb, "_Tables", "Media" );
980 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
982 r = find_entry( hdb, "_Tables", "_Property" );
983 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
985 r = find_entry( hdb, "_Tables", "MsiPatchHeaders" );
986 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
988 r = find_entry( hdb, "_Tables", "Patch" );
989 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
991 r = find_entry( hdb, "_Tables", "PatchPackage" );
992 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
994 MsiCloseHandle( hrec );
995 MsiViewClose( hview );
996 MsiCloseHandle( hview );
997 MsiCloseHandle( hdb );
998 MsiCloseHandle( hproduct );
1000 r = MsiInstallProductA( msifile, "REMOVE=ALL" );
1001 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1003 DeleteFileA( msifile );
1004 DeleteFileA( mspfile );
1005 RemoveDirectoryA( "msitest" );
1008 static void test_patch_registration( void )
1011 char buffer[MAX_PATH], patch_code[39];
1013 if (!pMsiApplyPatchA || !pMsiGetPatchInfoExA || !pMsiEnumPatchesExA)
1015 win_skip("required functions not available\n");
1019 CreateDirectoryA( "msitest", NULL );
1020 create_file( "msitest\\patch.txt", 1000 );
1022 create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
1023 create_patch( mspfile );
1025 MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
1027 r = MsiInstallProductA( msifile, NULL );
1028 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1030 r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
1031 ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
1032 "expected ERROR_SUCCESS, got %u\n", r );
1034 if (r == ERROR_PATCH_PACKAGE_INVALID)
1036 win_skip("Windows Installer < 3.0 detected\n");
1041 size = sizeof(buffer);
1042 r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1043 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1044 NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
1045 INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1046 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1047 ok( buffer[0], "buffer empty\n" );
1050 size = sizeof(buffer);
1051 r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1052 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1053 NULL, MSIINSTALLCONTEXT_MACHINE,
1054 INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1055 ok( r == ERROR_UNKNOWN_PRODUCT, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r );
1058 size = sizeof(buffer);
1059 r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1060 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1061 NULL, MSIINSTALLCONTEXT_USERMANAGED,
1062 INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1063 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1064 ok( !buffer[0], "got %s\n", buffer );
1066 r = pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1067 NULL, MSIINSTALLCONTEXT_USERUNMANAGED, MSIPATCHSTATE_APPLIED,
1068 0, patch_code, NULL, NULL, NULL, NULL );
1069 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1070 ok( !strcmp( patch_code, "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}" ), "wrong patch code\n" );
1072 r = pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1073 NULL, MSIINSTALLCONTEXT_MACHINE, MSIPATCHSTATE_APPLIED,
1074 0, patch_code, NULL, NULL, NULL, NULL );
1075 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
1077 r = pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1078 NULL, MSIINSTALLCONTEXT_USERMANAGED, MSIPATCHSTATE_APPLIED,
1079 0, patch_code, NULL, NULL, NULL, NULL );
1080 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
1082 r = MsiInstallProductA( msifile, "REMOVE=ALL" );
1083 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1086 size = sizeof(buffer);
1087 r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1088 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1089 NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
1090 INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1091 ok( r == ERROR_UNKNOWN_PRODUCT, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r );
1093 DeleteFileA( msifile );
1094 DeleteFileA( mspfile );
1095 RemoveDirectoryA( "msitest" );
1101 char temp_path[MAX_PATH], prev_path[MAX_PATH];
1103 init_function_pointers();
1105 GetCurrentDirectoryA( MAX_PATH, prev_path );
1106 GetTempPath( MAX_PATH, temp_path );
1107 SetCurrentDirectoryA( temp_path );
1109 strcpy( CURR_DIR, temp_path );
1110 len = strlen( CURR_DIR );
1112 if (len && (CURR_DIR[len - 1] == '\\'))
1113 CURR_DIR[len - 1] = 0;
1115 get_program_files_dir( PROG_FILES_DIR, COMMON_FILES_DIR );
1117 test_simple_patch();
1118 test_MsiOpenDatabase();
1119 test_system_tables();
1120 test_patch_registration();
1122 SetCurrentDirectoryA( prev_path );