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"
58 "PATCHNEWSUMMARYSUBJECT\tInstaller Database\n";
60 static const char media_dat
[] =
61 "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
62 "i2\ti4\tL64\tS255\tS32\tS72\n"
64 "1\t1\t\t\tDISK1\t\n";
66 static const char file_dat
[] =
67 "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
68 "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
70 "patch.txt\tpatch\tpatch.txt\t1000\t\t\t0\t1\n";
72 static const char directory_dat
[] =
73 "Directory\tDirectory_Parent\tDefaultDir\n"
75 "Directory\tDirectory\n"
76 "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
77 "ProgramFilesFolder\tTARGETDIR\t.\n"
78 "TARGETDIR\t\tSourceDir";
80 static const char component_dat
[] =
81 "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
82 "s72\tS38\ts72\ti2\tS255\tS72\n"
83 "Component\tComponent\n"
84 "patch\t{4B79D87E-6D28-4FD3-92D6-CD9B26AF64F1}\tMSITESTDIR\t0\t\tpatch.txt\n";
86 static const char feature_dat
[] =
87 "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
88 "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
90 "patch\t\t\tpatch feature\t1\t1\tMSITESTDIR\t0\n";
92 static const char feature_comp_dat
[] =
93 "Feature_\tComponent_\n"
95 "FeatureComponents\tFeature_\tComponent_\n"
98 static const char install_exec_seq_dat
[] =
99 "Action\tCondition\tSequence\n"
101 "InstallExecuteSequence\tAction\n"
102 "LaunchConditions\t\t100\n"
103 "CostInitialize\t\t800\n"
105 "CostFinalize\t\t1000\n"
106 "InstallValidate\t\t1400\n"
107 "InstallInitialize\t\t1500\n"
108 "ProcessComponents\t\t1600\n"
109 "RemoveFiles\t\t1700\n"
110 "InstallFiles\t\t2000\n"
111 "RegisterUser\t\t3000\n"
112 "RegisterProduct\t\t3100\n"
113 "PublishFeatures\t\t5100\n"
114 "PublishProduct\t\t5200\n"
115 "InstallFinalize\t\t6000\n";
119 const char *filename
;
124 #define ADD_TABLE( x ) { #x".idt", x##_dat, sizeof(x##_dat) }
126 static const struct msi_table tables
[] =
128 ADD_TABLE( directory
),
130 ADD_TABLE( component
),
131 ADD_TABLE( feature
),
132 ADD_TABLE( feature_comp
),
133 ADD_TABLE( property
),
134 ADD_TABLE( install_exec_seq
),
138 static void init_function_pointers( void )
140 HMODULE hmsi
= GetModuleHandleA( "msi.dll" );
142 #define GET_PROC( mod, func ) \
143 p ## func = (void *)GetProcAddress( mod, #func ); \
145 trace( "GetProcAddress(%s) failed\n", #func );
147 GET_PROC( hmsi
, MsiApplyPatchA
);
148 GET_PROC( hmsi
, MsiGetPatchInfoExA
);
149 GET_PROC( hmsi
, MsiEnumPatchesExA
);
153 static BOOL
get_program_files_dir( char *buf
, char *buf2
)
158 if (RegOpenKey( HKEY_LOCAL_MACHINE
, "Software\\Microsoft\\Windows\\CurrentVersion", &hkey
))
162 if (RegQueryValueExA( hkey
, "ProgramFilesDir", 0, &type
, (LPBYTE
)buf
, &size
))
168 if (RegQueryValueExA( hkey
, "CommonFilesDir", 0, &type
, (LPBYTE
)buf2
, &size
))
177 static void create_file_data( const char *filename
, const char *data
, DWORD size
)
182 file
= CreateFileA( filename
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
183 if (file
== INVALID_HANDLE_VALUE
)
186 WriteFile( file
, data
, strlen( data
), &written
, NULL
);
189 SetFilePointer( file
, size
, NULL
, FILE_BEGIN
);
190 SetEndOfFile( file
);
195 #define create_file( name, size ) create_file_data( name, name, size )
197 static BOOL
delete_pf( const char *rel_path
, BOOL is_file
)
201 strcpy( path
, PROG_FILES_DIR
);
202 strcat( path
, "\\" );
203 strcat( path
, rel_path
);
206 return DeleteFileA( path
);
208 return RemoveDirectoryA( path
);
211 static DWORD
get_pf_file_size( const char *filename
)
217 strcpy( path
, PROG_FILES_DIR
);
219 strcat( path
, filename
);
221 file
= CreateFileA( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
222 if (file
== INVALID_HANDLE_VALUE
)
223 return INVALID_FILE_SIZE
;
225 size
= GetFileSize( file
, NULL
);
230 static void write_file( const char *filename
, const char *data
, DWORD data_size
)
233 HANDLE file
= CreateFile( filename
, GENERIC_WRITE
, 0, NULL
,
234 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
235 WriteFile( file
, data
, data_size
, &size
, NULL
);
239 static void set_suminfo( const char *filename
)
244 r
= MsiOpenDatabaseA( filename
, MSIDBOPEN_DIRECT
, &hdb
);
245 ok( r
== ERROR_SUCCESS
, "failed to open database %u\n", r
);
247 r
= MsiGetSummaryInformation( hdb
, NULL
, 7, &hsi
);
248 ok( r
== ERROR_SUCCESS
, "failed to open summaryinfo %u\n", r
);
250 r
= MsiSummaryInfoSetProperty( hsi
, 2, VT_LPSTR
, 0, NULL
, "Installation Database" );
251 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
253 r
= MsiSummaryInfoSetProperty( hsi
, 3, VT_LPSTR
, 0, NULL
, "Installation Database" );
254 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
256 r
= MsiSummaryInfoSetProperty( hsi
, 4, VT_LPSTR
, 0, NULL
, "WineHQ" );
257 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
259 r
= MsiSummaryInfoSetProperty( hsi
, 7, VT_LPSTR
, 0, NULL
, ";1033" );
260 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
262 r
= MsiSummaryInfoSetProperty( hsi
, 9, VT_LPSTR
, 0, NULL
, "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}" );
263 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
265 r
= MsiSummaryInfoSetProperty( hsi
, 14, VT_I4
, 100, NULL
, NULL
);
266 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
268 r
= MsiSummaryInfoSetProperty( hsi
, 15, VT_I4
, 0, NULL
, NULL
);
269 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
271 r
= MsiSummaryInfoPersist( hsi
);
272 ok( r
== ERROR_SUCCESS
, "failed to persist suminfo %u\n", r
);
274 r
= MsiCloseHandle( hsi
);
275 ok( r
== ERROR_SUCCESS
, "failed to close suminfo %u\n", r
);
277 r
= MsiCloseHandle( hdb
);
278 ok( r
== ERROR_SUCCESS
, "failed to close database %u\n", r
);
281 static void create_database( const char *filename
, const struct msi_table
*tables
, UINT num_tables
)
286 r
= MsiOpenDatabaseA( filename
, MSIDBOPEN_CREATE
, &hdb
);
287 ok(r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
289 /* import the tables into the database */
290 for (i
= 0; i
< num_tables
; i
++)
292 const struct msi_table
*table
= &tables
[i
];
294 write_file( table
->filename
, table
->data
, (table
->size
- 1) * sizeof(char) );
296 r
= MsiDatabaseImportA( hdb
, CURR_DIR
, table
->filename
);
297 ok(r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
299 DeleteFileA( table
->filename
);
302 r
= MsiDatabaseCommit( hdb
);
303 ok(r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
305 MsiCloseHandle( hdb
);
306 set_suminfo( filename
);
309 /* data for generating a patch */
311 /* table names - encoded as in an msi database file */
312 static const WCHAR p_name0
[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
313 static const WCHAR p_name1
[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
314 static const WCHAR p_name2
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
315 static const WCHAR p_name3
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
316 static const WCHAR p_name4
[] = { 0x3a8c, 0x47cb, 0x45b0, 0x45ec, 0x45a8, 0x4837, 0}; /* CAB_msitest */
317 static const WCHAR p_name5
[] = { 0x4840, 0x4596, 0x3e6c, 0x45e4, 0x42e6, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* MsiPatchSequence */
318 static const WCHAR p_name6
[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
319 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
320 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
321 /* substorage names */
322 static const WCHAR p_name7
[] = { 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075, 0x0070,
323 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* targetToupgraded */
324 static const WCHAR p_name8
[] = { 0x0023, 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075,
325 0x0070, 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* #targetToupgraded */
327 /* data in each table */
328 static const WCHAR p_data0
[] = { /* _Columns */
329 0x0001, 0x0001, 0x0001, 0x0001, 0x8001, 0x8002, 0x8003, 0x8004,
330 0x0002, 0x0003, 0x0004, 0x0005, 0xad00, 0xbd26, 0x8d00, 0x9502
332 static const WCHAR p_data1
[] = { /* _Tables */
335 static const char p_data2
[] = { /* _StringData */
336 "MsiPatchSequencePatchFamilyProductCodeSequenceAttributes1.1.19388.37230913B8D18FBB64CACA239C74C11E3FA74"
338 static const WCHAR p_data3
[] = { /* _StringPool */
340 0, 0, /* string 0 '' */
341 16, 5, /* string 1 'MsiPatchSequence' */
342 11, 1, /* string 2 'PatchFamily' */
343 11, 1, /* string 3 'ProductCode' */
344 8, 1, /* string 4 'Sequence' */
345 10, 1, /* string 5 'Attributes' */
346 15, 1, /* string 6 '1.1.19388.37230' */
347 32, 1, /* string 7 '913B8D18FBB64CACA239C74C11E3FA74' */
349 static const char p_data4
[] = { /* CAB_msitest */
350 0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00,
351 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00,
352 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9e,
353 0x03, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x12,
354 0xea, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
355 0x3c, 0xd4, 0x80, 0x20, 0x00, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e,
356 0x74, 0x78, 0x74, 0x00, 0x0b, 0x3c, 0xd6, 0xc1, 0x4a, 0x00, 0xea,
357 0x03, 0x5b, 0x80, 0x80, 0x8d, 0x00, 0x10, 0xa1, 0x3e, 0x00, 0x00,
358 0x00, 0x00, 0x03, 0x00, 0x40, 0x30, 0x0c, 0x43, 0xf8, 0xb4, 0x85,
359 0x4d, 0x96, 0x08, 0x0a, 0x92, 0xf0, 0x52, 0xfb, 0xbb, 0x82, 0xf9,
360 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x31, 0x7d,
361 0x56, 0xdf, 0xf7, 0x48, 0x7c, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
362 0x00, 0x00, 0x41, 0x80, 0xdf, 0xf7, 0xd8, 0x72, 0xbf, 0xb9, 0x63,
363 0x91, 0x0e, 0x57, 0x1f, 0xfa, 0x1a, 0x66, 0x54, 0x55
365 static const WCHAR p_data5
[] = { /* MsiPatchSequence */
366 0x0007, 0x0000, 0x0006, 0x8000
368 static const char p_data6
[] = { /* SummaryInformation */
369 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
370 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
371 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
372 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
373 0x30, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
374 0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00,
375 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x90,
376 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
377 0x0f, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
378 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x30, 0x46, 0x39, 0x36, 0x43,
379 0x44, 0x43, 0x30, 0x2d, 0x34, 0x43, 0x44, 0x46, 0x2d, 0x34, 0x33,
380 0x30, 0x34, 0x2d, 0x42, 0x32, 0x38, 0x33, 0x2d, 0x37, 0x42, 0x39,
381 0x32, 0x36, 0x34, 0x38, 0x38, 0x39, 0x45, 0x46, 0x37, 0x7d, 0x00,
382 0x00, 0x1e, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x39,
383 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42,
384 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39,
385 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41,
386 0x37, 0x34, 0x7d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x25, 0x00,
387 0x00, 0x00, 0x3a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f,
388 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x3b, 0x3a, 0x23,
389 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f, 0x75, 0x70, 0x67,
390 0x72, 0x61, 0x64, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
391 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x61, 0x74, 0x63, 0x68,
392 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x00,
393 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
402 static const struct table_data table_patch_data
[] = {
403 { p_name0
, p_data0
, sizeof p_data0
},
404 { p_name1
, p_data1
, sizeof p_data1
},
405 { p_name2
, p_data2
, sizeof p_data2
- 1 },
406 { p_name3
, p_data3
, sizeof p_data3
},
407 { p_name4
, p_data4
, sizeof p_data4
},
408 { p_name5
, p_data5
, sizeof p_data5
},
409 { p_name6
, p_data6
, sizeof p_data6
}
412 #define NUM_PATCH_TABLES (sizeof table_patch_data/sizeof table_patch_data[0])
414 static const WCHAR t1_name0
[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
415 static const WCHAR t1_name1
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
416 static const WCHAR t1_name2
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
417 static const WCHAR t1_name3
[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
418 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
419 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
421 static const WCHAR t1_data0
[] = { /* File */
422 0x0008, 0x0001, 0x03ea, 0x8000
424 static const char t1_data1
[] = { /* _StringData */
427 static const WCHAR t1_data2
[] = { /* _StringPool */
429 0, 0, /* string 0 '' */
430 9, 1, /* string 1 'patch.txt' */
432 static const char t1_data3
[] = { /* SummaryInformation */
433 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
434 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
435 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
436 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
437 0x30, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00,
438 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00,
439 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8,
440 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00,
441 0x06, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
442 0x00, 0xd0, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xdc, 0x00,
443 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x08,
444 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
445 0x04, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00,
446 0x00, 0x10, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x1e, 0x00,
447 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61,
448 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74,
449 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
450 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c,
451 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74, 0x61,
452 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
453 0x07, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6e, 0x65, 0x48, 0x51, 0x00,
454 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
455 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
456 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
457 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
458 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31,
459 0x30, 0x33, 0x33, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06,
460 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00, 0x00,
461 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b, 0x39, 0x31,
462 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
463 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
464 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
465 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x39, 0x31,
466 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
467 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
468 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
469 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x41, 0x32,
470 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45, 0x32, 0x43,
471 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30, 0x39, 0x2d,
472 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35, 0x46, 0x34,
473 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
474 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x22, 0x09
477 static const struct table_data table_transform1_data
[] = {
478 { t1_name0
, t1_data0
, sizeof t1_data0
},
479 { t1_name1
, t1_data1
, sizeof t1_data1
- 1 },
480 { t1_name2
, t1_data2
, sizeof t1_data2
},
481 { t1_name3
, t1_data3
, sizeof t1_data3
}
484 #define NUM_TRANSFORM1_TABLES (sizeof table_transform1_data/sizeof table_transform1_data[0])
486 static const WCHAR t2_name0
[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
487 static const WCHAR t2_name1
[] = { 0x4840, 0x4216, 0x4327, 0x4824, 0 }; /* Media */
488 static const WCHAR t2_name2
[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
489 static const WCHAR t2_name3
[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
490 static const WCHAR t2_name4
[] = { 0x4840, 0x4559, 0x44f2, 0x4568, 0x4737, 0 }; /* Property */
491 static const WCHAR t2_name5
[] = { 0x4840, 0x4119, 0x41b7, 0x3e6b, 0x41a4, 0x412e, 0x422a, 0 }; /* PatchPackage */
492 static const WCHAR t2_name6
[] = { 0x4840, 0x4452, 0x45f6, 0x43e4, 0x3baf, 0x423b, 0x4626,
493 0x4237, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* InstallExecuteSequence */
494 static const WCHAR t2_name7
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
495 static const WCHAR t2_name8
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
496 static const WCHAR t2_name9
[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
497 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
498 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
500 static const WCHAR t2_data0
[] = { /* File */
501 0x00c0, 0x0001, 0x9000, 0x83e8
503 static const WCHAR t2_data1
[] = { /* Media */
504 0x0601, 0x8002, 0x03e9, 0x8000, 0x0000, 0x0007, 0x0000, 0x0008
506 static const WCHAR t2_data2
[] = { /* _Columns */
507 0x0401, 0x0009, 0x0000, 0x000a, 0xad48, 0x0401, 0x0009, 0x0000, /* 0x0401 = add row (1), 4 shorts */
508 0x000b, 0xa502, 0x0401, 0x0009, 0x0000, 0x000c, 0x8104, 0x0401,
509 0x0009, 0x0000, 0x000d, 0x8502, 0x0401, 0x0009, 0x0000, 0x000e,
510 0x9900, 0x0401, 0x0009, 0x0000, 0x000f, 0x9d48, 0x0401, 0x0010,
511 0x0000, 0x0011, 0xad26, 0x0401, 0x0010, 0x0000, 0x0012, 0x8502,
512 0x0401, 0x0014, 0x0000, 0x0015, 0xad26, 0x0401, 0x0014, 0x0000,
515 static const WCHAR t2_data3
[] = { /* _Tables */
516 0x0101, 0x0009, 0x0101, 0x0010, 0x0101, 0x0014
518 static const WCHAR t2_data4
[] = { /* Property */
519 0x0201, 0x0002, 0x0003, 0x0201, 0x0004, 0x0005
521 static const WCHAR t2_data5
[] = { /* PatchPackage */
522 0x0201, 0x0013, 0x8002
524 static const WCHAR t2_data6
[] = { /* InstallExecuteSequence */
525 0x0301, 0x0006, 0x0000, 0x87d1
527 static const char t2_data7
[] = { /* _StringData */
528 "patch.txtPATCHNEWSUMMARYSUBJECTInstallation DatabasePATCHNEWPACKAGECODE{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}"
529 "PatchFiles#CAB_msitestpropPatchFile_SequencePatchSizeAttributesHeaderStreamRef_PatchPackagePatchIdMedia_"
530 "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}MsiPatchHeadersStreamRef"
532 static const WCHAR t2_data8
[] = { /* _StringPool */
534 0, 0, /* string 0 '' */
535 9, 1, /* string 1 'patch.txt' */
536 22, 1, /* string 2 'PATCHNEWSUMMARYSUBJECT' */
537 21, 1, /* string 3 'Installation Database' */
538 19, 1, /* string 4 'PATCHNEWPACKAGECODE' */
539 38, 1, /* string 5 '{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}' */
540 10, 1, /* string 6 'PatchFiles' */
541 12, 1, /* string 7 '#CAB_msitest' */
542 4, 1, /* string 8 'prop' */
543 5, 7, /* string 9 'Patch' */
544 5, 1, /* string 10 'File_' */
545 8, 1, /* string 11 'Sequence' */
546 9, 1, /* string 12 'PatchSize' */
547 10, 1, /* string 13 'Attributes' */
548 6, 2, /* string 14 'Header' */
549 10, 1, /* string 15 'StreamRef_' */
550 12, 3, /* string 16 'PatchPackage' */
551 7, 1, /* string 17 'PatchId' */
552 6, 1, /* string 18 'Media_' */
553 38, 1, /* string 19 '{0F96CDC0-4CDF-4304-B283-7B9264889EF7}' */
554 15, 3, /* string 20 'MsiPatchHeaders' */
555 9, 1 /* string 21 'StreamRef' */
557 static const char t2_data9
[] = { /* SummaryInformation */
558 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
559 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
560 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
561 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
562 0x30, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00,
563 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00,
564 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x78,
565 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
566 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
567 0x00, 0x9c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa8, 0x00,
568 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x09,
569 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
570 0x4c, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00,
571 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
572 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
573 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
574 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
575 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00,
576 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01,
577 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
578 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00,
579 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
580 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b,
581 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
582 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
583 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
584 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
585 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
586 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
587 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
588 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
589 0x41, 0x32, 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45,
590 0x32, 0x43, 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30,
591 0x39, 0x2d, 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35,
592 0x46, 0x34, 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2d,
593 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x27, 0x09
596 static const struct table_data table_transform2_data
[] = {
597 { t2_name0
, t2_data0
, sizeof t2_data0
},
598 { t2_name1
, t2_data1
, sizeof t2_data1
},
599 { t2_name2
, t2_data2
, sizeof t2_data2
},
600 { t2_name3
, t2_data3
, sizeof t2_data3
},
601 { t2_name4
, t2_data4
, sizeof t2_data4
},
602 { t2_name5
, t2_data5
, sizeof t2_data5
},
603 { t2_name6
, t2_data6
, sizeof t2_data6
},
604 { t2_name7
, t2_data7
, sizeof t2_data7
- 1 },
605 { t2_name8
, t2_data8
, sizeof t2_data8
},
606 { t2_name9
, t2_data9
, sizeof t2_data9
}
609 #define NUM_TRANSFORM2_TABLES (sizeof table_transform2_data/sizeof table_transform2_data[0])
611 static void write_tables( IStorage
*stg
, const struct table_data
*tables
, UINT num_tables
)
617 for (i
= 0; i
< num_tables
; i
++)
619 r
= IStorage_CreateStream( stg
, tables
[i
].name
, STGM_WRITE
|STGM_SHARE_EXCLUSIVE
, 0, 0, &stm
);
622 ok( 0, "failed to create stream 0x%08x\n", r
);
626 r
= IStream_Write( stm
, tables
[i
].data
, tables
[i
].size
, &count
);
627 if (FAILED( r
) || count
!= tables
[i
].size
)
628 ok( 0, "failed to write stream\n" );
629 IStream_Release( stm
);
633 static void create_patch( const char *filename
)
635 IStorage
*stg
= NULL
, *stg1
= NULL
, *stg2
= NULL
;
639 const DWORD mode
= STGM_CREATE
|STGM_READWRITE
|STGM_DIRECT
|STGM_SHARE_EXCLUSIVE
;
641 const CLSID CLSID_MsiPatch
= {0xc1086, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
642 const CLSID CLSID_MsiTransform
= {0xc1082, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
644 len
= MultiByteToWideChar( CP_ACP
, 0, filename
, -1, NULL
, 0 );
645 filenameW
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
646 MultiByteToWideChar( CP_ACP
, 0, filename
, -1, filenameW
, len
);
648 r
= StgCreateDocfile( filenameW
, mode
, 0, &stg
);
649 HeapFree( GetProcessHeap(), 0, filenameW
);
650 ok( r
== S_OK
, "failed to create storage 0x%08x\n", r
);
654 r
= IStorage_SetClass( stg
, &CLSID_MsiPatch
);
655 ok( r
== S_OK
, "failed to set storage type 0x%08x\n", r
);
657 write_tables( stg
, table_patch_data
, NUM_PATCH_TABLES
);
659 r
= IStorage_CreateStorage( stg
, p_name7
, mode
, 0, 0, &stg1
);
660 ok( r
== S_OK
, "failed to create substorage 0x%08x\n", r
);
662 r
= IStorage_SetClass( stg1
, &CLSID_MsiTransform
);
663 ok( r
== S_OK
, "failed to set storage type 0x%08x\n", r
);
665 write_tables( stg1
, table_transform1_data
, NUM_TRANSFORM1_TABLES
);
666 IStorage_Release( stg1
);
668 r
= IStorage_CreateStorage( stg
, p_name8
, mode
, 0, 0, &stg2
);
669 ok( r
== S_OK
, "failed to create substorage 0x%08x\n", r
);
671 r
= IStorage_SetClass( stg2
, &CLSID_MsiTransform
);
672 ok( r
== S_OK
, "failed to set storage type 0x%08x\n", r
);
674 write_tables( stg2
, table_transform2_data
, NUM_TRANSFORM2_TABLES
);
675 IStorage_Release( stg2
);
676 IStorage_Release( stg
);
679 static void test_simple_patch( void )
683 char path
[MAX_PATH
], install_source
[MAX_PATH
];
685 MSIHANDLE hpackage
, hdb
, hview
, hrec
;
687 if (!pMsiApplyPatchA
)
689 win_skip("MsiApplyPatchA is not available\n");
693 CreateDirectoryA( "msitest", NULL
);
694 create_file( "msitest\\patch.txt", 1000 );
696 create_database( msifile
, tables
, sizeof(tables
) / sizeof(struct msi_table
) );
697 create_patch( mspfile
);
699 MsiSetInternalUI( INSTALLUILEVEL_NONE
, NULL
);
701 r
= MsiInstallProductA( msifile
, NULL
);
702 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
704 size
= get_pf_file_size( "msitest\\patch.txt" );
705 ok( size
== 1000, "expected 1000, got %u\n", size
);
707 size
= sizeof(install_source
);
708 r
= MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
709 "InstallSource", install_source
, &size
);
710 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
712 strcpy( path
, CURR_DIR
);
713 strcat( path
, "\\" );
714 strcat( path
, msifile
);
716 r
= MsiOpenPackageA( path
, &hpackage
);
717 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
719 hdb
= MsiGetActiveDatabase( hpackage
);
720 ok( hdb
, "failed to get database handle\n" );
722 query
= "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
723 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
724 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
726 r
= MsiViewExecute( hview
, 0 );
727 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
729 r
= MsiViewFetch( hview
, &hrec
);
730 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
732 MsiCloseHandle( hrec
);
733 MsiViewClose( hview
);
734 MsiCloseHandle( hview
);
736 query
= "SELECT * FROM `Property` WHERE `Property` = 'PATCHNEWSUMMARYSUBJECT' "
737 "AND `Value` = 'Installer Database'";
738 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
739 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
741 r
= MsiViewExecute( hview
, 0 );
742 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
744 r
= MsiViewFetch( hview
, &hrec
);
745 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
747 MsiCloseHandle( hrec
);
748 MsiViewClose( hview
);
749 MsiCloseHandle( hview
);
751 MsiCloseHandle( hdb
);
752 MsiCloseHandle( hpackage
);
754 r
= MsiApplyPatchA( mspfile
, NULL
, INSTALLTYPE_DEFAULT
, NULL
);
755 ok( r
== ERROR_SUCCESS
|| broken( r
== ERROR_PATCH_PACKAGE_INVALID
), /* version 2.0 */
756 "expected ERROR_SUCCESS, got %u\n", r
);
758 if (r
== ERROR_PATCH_PACKAGE_INVALID
)
760 win_skip("Windows Installer < 3.0 detected\n");
764 size
= get_pf_file_size( "msitest\\patch.txt" );
765 ok( size
== 1002, "expected 1002, got %u\n", size
);
767 /* show that MsiOpenPackage applies registered patches */
768 r
= MsiOpenPackageA( path
, &hpackage
);
769 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
771 hdb
= MsiGetActiveDatabase( hpackage
);
772 ok( hdb
, "failed to get database handle\n" );
774 query
= "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
775 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
776 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
778 r
= MsiViewExecute( hview
, 0 );
779 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
781 r
= MsiViewFetch( hview
, &hrec
);
782 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
784 MsiCloseHandle( hrec
);
785 MsiViewClose( hview
);
786 MsiCloseHandle( hview
);
788 query
= "SELECT * FROM `Property` WHERE `Property` = 'PATCHNEWSUMMARYSUBJECT' "
789 "AND `Value` = 'Installation Database'";
790 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
791 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
793 r
= MsiViewExecute( hview
, 0 );
794 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
796 r
= MsiViewFetch( hview
, &hrec
);
797 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
799 MsiCloseHandle( hrec
);
800 MsiViewClose( hview
);
801 MsiCloseHandle( hview
);
803 MsiCloseHandle( hdb
);
804 MsiCloseHandle( hpackage
);
806 /* show that patches are not committed to the local package database */
808 r
= MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
809 "LocalPackage", path
, &size
);
810 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
812 r
= MsiOpenDatabaseA( path
, MSIDBOPEN_READONLY
, &hdb
);
813 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
815 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
816 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
818 r
= MsiViewExecute( hview
, 0 );
819 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
821 r
= MsiViewFetch( hview
, &hrec
);
822 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
824 MsiCloseHandle( hrec
);
825 MsiViewClose( hview
);
826 MsiCloseHandle( hview
);
827 MsiCloseHandle( hdb
);
830 r
= MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
831 "InstallSource", path
, &size
);
832 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
833 ok( !strcasecmp( path
, install_source
), "wrong path %s\n", path
);
835 r
= MsiInstallProductA( msifile
, "REMOVE=ALL" );
836 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
838 ok( !delete_pf( "msitest\\patch.txt", TRUE
), "file not removed\n" );
839 ok( !delete_pf( "msitest", FALSE
), "directory not removed\n" );
841 DeleteFileA( msifile
);
842 DeleteFileA( mspfile
);
843 RemoveDirectoryA( "msitest" );
846 static void test_MsiOpenDatabase( void )
851 r
= MsiOpenDatabase( mspfile
, MSIDBOPEN_CREATE
, &hdb
);
852 ok(r
== ERROR_SUCCESS
, "failed to open database %u\n", r
);
854 r
= MsiDatabaseCommit( hdb
);
855 ok(r
== ERROR_SUCCESS
, "failed to commit database %u\n", r
);
856 MsiCloseHandle( hdb
);
858 r
= MsiOpenDatabase( mspfile
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &hdb
);
859 ok(r
== ERROR_OPEN_FAILED
, "expected ERROR_OPEN_FAILED, got %u\n", r
);
860 DeleteFileA( mspfile
);
862 r
= MsiOpenDatabase( mspfile
, MSIDBOPEN_CREATE
+ MSIDBOPEN_PATCHFILE
, &hdb
);
863 ok(r
== ERROR_SUCCESS
, "failed to open database %u\n", r
);
865 r
= MsiDatabaseCommit( hdb
);
866 ok(r
== ERROR_SUCCESS
, "failed to commit database %u\n", r
);
867 MsiCloseHandle( hdb
);
869 r
= MsiOpenDatabase( mspfile
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &hdb
);
870 ok(r
== ERROR_SUCCESS
, "failed to open database %u\n", r
);
871 MsiCloseHandle( hdb
);
872 DeleteFileA( mspfile
);
874 create_database( msifile
, tables
, sizeof(tables
) / sizeof(struct msi_table
) );
875 create_patch( mspfile
);
877 r
= MsiOpenDatabase( msifile
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &hdb
);
878 ok(r
== ERROR_OPEN_FAILED
, "failed to open database %u\n", r
);
880 r
= MsiOpenDatabase( mspfile
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &hdb
);
881 ok(r
== ERROR_SUCCESS
, "failed to open database %u\n", r
);
882 MsiCloseHandle( hdb
);
884 DeleteFileA( msifile
);
885 DeleteFileA( mspfile
);
888 static UINT
find_entry( MSIHANDLE hdb
, const char *table
, const char *entry
)
890 static char fmt
[] = "SELECT * FROM `%s` WHERE `Name` = '%s'";
893 MSIHANDLE hview
, hrec
;
895 sprintf( query
, fmt
, table
, entry
);
896 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
897 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
899 r
= MsiViewExecute( hview
, 0 );
900 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
902 r
= MsiViewFetch( hview
, &hrec
);
903 MsiViewClose( hview
);
904 MsiCloseHandle( hview
);
905 MsiCloseHandle( hrec
);
909 static void test_system_tables( void )
913 MSIHANDLE hproduct
, hdb
, hview
, hrec
;
915 if (!pMsiApplyPatchA
)
917 win_skip("MsiApplyPatchA is not available\n");
921 CreateDirectoryA( "msitest", NULL
);
922 create_file( "msitest\\patch.txt", 1000 );
924 create_database( msifile
, tables
, sizeof(tables
) / sizeof(struct msi_table
) );
925 create_patch( mspfile
);
927 MsiSetInternalUI( INSTALLUILEVEL_NONE
, NULL
);
929 r
= MsiInstallProductA( msifile
, NULL
);
930 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
932 r
= MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct
);
933 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
935 hdb
= MsiGetActiveDatabase( hproduct
);
936 ok( hdb
, "failed to get database handle\n" );
938 r
= find_entry( hdb
, "_Streams", "\5SummaryInformation" );
939 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
941 query
= "SELECT * FROM `_Storages`";
942 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
943 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
945 r
= MsiViewExecute( hview
, 0 );
946 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
948 r
= MsiViewFetch( hview
, &hrec
);
949 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
951 r
= find_entry( hdb
, "_Tables", "Directory" );
952 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
954 r
= find_entry( hdb
, "_Tables", "File" );
955 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
957 r
= find_entry( hdb
, "_Tables", "Component" );
958 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
960 r
= find_entry( hdb
, "_Tables", "Feature" );
961 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
963 r
= find_entry( hdb
, "_Tables", "FeatureComponents" );
964 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
966 r
= find_entry( hdb
, "_Tables", "Property" );
967 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
969 r
= find_entry( hdb
, "_Tables", "InstallExecuteSequence" );
970 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
972 r
= find_entry( hdb
, "_Tables", "Media" );
973 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
975 r
= find_entry( hdb
, "_Tables", "_Property" );
976 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
978 MsiCloseHandle( hrec
);
979 MsiViewClose( hview
);
980 MsiCloseHandle( hview
);
981 MsiCloseHandle( hdb
);
982 MsiCloseHandle( hproduct
);
984 r
= MsiApplyPatchA( mspfile
, NULL
, INSTALLTYPE_DEFAULT
, NULL
);
985 ok( r
== ERROR_SUCCESS
|| broken( r
== ERROR_PATCH_PACKAGE_INVALID
), /* version 2.0 */
986 "expected ERROR_SUCCESS, got %u\n", r
);
988 if (r
== ERROR_PATCH_PACKAGE_INVALID
)
990 win_skip("Windows Installer < 3.0 detected\n");
994 r
= MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct
);
995 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
997 hdb
= MsiGetActiveDatabase( hproduct
);
998 ok( hdb
, "failed to get database handle\n" );
1000 r
= find_entry( hdb
, "_Streams", "\5SummaryInformation" );
1001 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1003 query
= "SELECT * FROM `_Storages`";
1004 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
1005 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1007 r
= MsiViewExecute( hview
, 0 );
1008 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1010 r
= MsiViewFetch( hview
, &hrec
);
1011 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
1013 r
= find_entry( hdb
, "_Tables", "Directory" );
1014 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1016 r
= find_entry( hdb
, "_Tables", "File" );
1017 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1019 r
= find_entry( hdb
, "_Tables", "Component" );
1020 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1022 r
= find_entry( hdb
, "_Tables", "Feature" );
1023 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1025 r
= find_entry( hdb
, "_Tables", "FeatureComponents" );
1026 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1028 r
= find_entry( hdb
, "_Tables", "Property" );
1029 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1031 r
= find_entry( hdb
, "_Tables", "InstallExecuteSequence" );
1032 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1034 r
= find_entry( hdb
, "_Tables", "Media" );
1035 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1037 r
= find_entry( hdb
, "_Tables", "_Property" );
1038 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1040 r
= find_entry( hdb
, "_Tables", "MsiPatchHeaders" );
1041 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1043 r
= find_entry( hdb
, "_Tables", "Patch" );
1044 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1046 r
= find_entry( hdb
, "_Tables", "PatchPackage" );
1047 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1049 MsiCloseHandle( hrec
);
1050 MsiViewClose( hview
);
1051 MsiCloseHandle( hview
);
1052 MsiCloseHandle( hdb
);
1053 MsiCloseHandle( hproduct
);
1055 r
= MsiInstallProductA( msifile
, "REMOVE=ALL" );
1056 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1058 DeleteFileA( msifile
);
1059 DeleteFileA( mspfile
);
1060 RemoveDirectoryA( "msitest" );
1063 static void test_patch_registration( void )
1066 char buffer
[MAX_PATH
], patch_code
[39];
1068 if (!pMsiApplyPatchA
|| !pMsiGetPatchInfoExA
|| !pMsiEnumPatchesExA
)
1070 win_skip("required functions not available\n");
1074 CreateDirectoryA( "msitest", NULL
);
1075 create_file( "msitest\\patch.txt", 1000 );
1077 create_database( msifile
, tables
, sizeof(tables
) / sizeof(struct msi_table
) );
1078 create_patch( mspfile
);
1080 MsiSetInternalUI( INSTALLUILEVEL_NONE
, NULL
);
1082 r
= MsiInstallProductA( msifile
, NULL
);
1083 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1085 r
= MsiApplyPatchA( mspfile
, NULL
, INSTALLTYPE_DEFAULT
, NULL
);
1086 ok( r
== ERROR_SUCCESS
|| broken( r
== ERROR_PATCH_PACKAGE_INVALID
), /* version 2.0 */
1087 "expected ERROR_SUCCESS, got %u\n", r
);
1089 if (r
== ERROR_PATCH_PACKAGE_INVALID
)
1091 win_skip("Windows Installer < 3.0 detected\n");
1096 size
= sizeof(buffer
);
1097 r
= pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1098 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1099 NULL
, MSIINSTALLCONTEXT_USERUNMANAGED
,
1100 INSTALLPROPERTY_LOCALPACKAGE
, buffer
, &size
);
1101 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1102 ok( buffer
[0], "buffer empty\n" );
1105 size
= sizeof(buffer
);
1106 r
= pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1107 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1108 NULL
, MSIINSTALLCONTEXT_MACHINE
,
1109 INSTALLPROPERTY_LOCALPACKAGE
, buffer
, &size
);
1110 ok( r
== ERROR_UNKNOWN_PRODUCT
, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r
);
1113 size
= sizeof(buffer
);
1114 r
= pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1115 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1116 NULL
, MSIINSTALLCONTEXT_USERMANAGED
,
1117 INSTALLPROPERTY_LOCALPACKAGE
, buffer
, &size
);
1118 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1119 ok( !buffer
[0], "got %s\n", buffer
);
1121 r
= pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1122 NULL
, MSIINSTALLCONTEXT_USERUNMANAGED
, MSIPATCHSTATE_APPLIED
,
1123 0, patch_code
, NULL
, NULL
, NULL
, NULL
);
1124 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1125 ok( !strcmp( patch_code
, "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}" ), "wrong patch code\n" );
1127 r
= pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1128 NULL
, MSIINSTALLCONTEXT_MACHINE
, MSIPATCHSTATE_APPLIED
,
1129 0, patch_code
, NULL
, NULL
, NULL
, NULL
);
1130 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
1132 r
= pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1133 NULL
, MSIINSTALLCONTEXT_USERMANAGED
, MSIPATCHSTATE_APPLIED
,
1134 0, patch_code
, NULL
, NULL
, NULL
, NULL
);
1135 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
1137 r
= MsiInstallProductA( msifile
, "REMOVE=ALL" );
1138 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1141 size
= sizeof(buffer
);
1142 r
= pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1143 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1144 NULL
, MSIINSTALLCONTEXT_USERUNMANAGED
,
1145 INSTALLPROPERTY_LOCALPACKAGE
, buffer
, &size
);
1146 ok( r
== ERROR_UNKNOWN_PRODUCT
, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r
);
1148 DeleteFileA( msifile
);
1149 DeleteFileA( mspfile
);
1150 RemoveDirectoryA( "msitest" );
1156 char temp_path
[MAX_PATH
], prev_path
[MAX_PATH
];
1158 init_function_pointers();
1160 GetCurrentDirectoryA( MAX_PATH
, prev_path
);
1161 GetTempPath( MAX_PATH
, temp_path
);
1162 SetCurrentDirectoryA( temp_path
);
1164 strcpy( CURR_DIR
, temp_path
);
1165 len
= strlen( CURR_DIR
);
1167 if (len
&& (CURR_DIR
[len
- 1] == '\\'))
1168 CURR_DIR
[len
- 1] = 0;
1170 get_program_files_dir( PROG_FILES_DIR
, COMMON_FILES_DIR
);
1172 test_simple_patch();
1173 test_MsiOpenDatabase();
1174 test_system_tables();
1175 test_patch_registration();
1177 SetCurrentDirectoryA( prev_path
);