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 (x86)", 0, &type
, (LPBYTE
)buf
, &size
) &&
163 RegQueryValueExA( hkey
, "ProgramFilesDir", 0, &type
, (LPBYTE
)buf
, &size
))
169 if (RegQueryValueExA( hkey
, "CommonFilesDir", 0, &type
, (LPBYTE
)buf2
, &size
))
178 static void create_file_data( const char *filename
, const char *data
, DWORD size
)
183 file
= CreateFileA( filename
, GENERIC_WRITE
, 0, NULL
, CREATE_ALWAYS
, 0, NULL
);
184 if (file
== INVALID_HANDLE_VALUE
)
187 WriteFile( file
, data
, strlen( data
), &written
, NULL
);
190 SetFilePointer( file
, size
, NULL
, FILE_BEGIN
);
191 SetEndOfFile( file
);
196 #define create_file( name, size ) create_file_data( name, name, size )
198 static BOOL
delete_pf( const char *rel_path
, BOOL is_file
)
202 strcpy( path
, PROG_FILES_DIR
);
203 strcat( path
, "\\" );
204 strcat( path
, rel_path
);
207 return DeleteFileA( path
);
209 return RemoveDirectoryA( path
);
212 static DWORD
get_pf_file_size( const char *filename
)
218 strcpy( path
, PROG_FILES_DIR
);
220 strcat( path
, filename
);
222 file
= CreateFileA( path
, GENERIC_READ
, FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, 0, NULL
);
223 if (file
== INVALID_HANDLE_VALUE
)
224 return INVALID_FILE_SIZE
;
226 size
= GetFileSize( file
, NULL
);
231 static void write_file( const char *filename
, const char *data
, DWORD data_size
)
234 HANDLE file
= CreateFile( filename
, GENERIC_WRITE
, 0, NULL
,
235 CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, NULL
);
236 WriteFile( file
, data
, data_size
, &size
, NULL
);
240 static void set_suminfo( const char *filename
)
245 r
= MsiOpenDatabaseA( filename
, MSIDBOPEN_DIRECT
, &hdb
);
246 ok( r
== ERROR_SUCCESS
, "failed to open database %u\n", r
);
248 r
= MsiGetSummaryInformation( hdb
, NULL
, 7, &hsi
);
249 ok( r
== ERROR_SUCCESS
, "failed to open summaryinfo %u\n", r
);
251 r
= MsiSummaryInfoSetProperty( hsi
, 2, VT_LPSTR
, 0, NULL
, "Installation Database" );
252 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
254 r
= MsiSummaryInfoSetProperty( hsi
, 3, VT_LPSTR
, 0, NULL
, "Installation Database" );
255 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
257 r
= MsiSummaryInfoSetProperty( hsi
, 4, VT_LPSTR
, 0, NULL
, "WineHQ" );
258 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
260 r
= MsiSummaryInfoSetProperty( hsi
, 7, VT_LPSTR
, 0, NULL
, ";1033" );
261 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
263 r
= MsiSummaryInfoSetProperty( hsi
, 9, VT_LPSTR
, 0, NULL
, "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}" );
264 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
266 r
= MsiSummaryInfoSetProperty( hsi
, 14, VT_I4
, 100, NULL
, NULL
);
267 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
269 r
= MsiSummaryInfoSetProperty( hsi
, 15, VT_I4
, 0, NULL
, NULL
);
270 ok( r
== ERROR_SUCCESS
, "failed to set summary info %u\n", r
);
272 r
= MsiSummaryInfoPersist( hsi
);
273 ok( r
== ERROR_SUCCESS
, "failed to persist suminfo %u\n", r
);
275 r
= MsiCloseHandle( hsi
);
276 ok( r
== ERROR_SUCCESS
, "failed to close suminfo %u\n", r
);
278 r
= MsiCloseHandle( hdb
);
279 ok( r
== ERROR_SUCCESS
, "failed to close database %u\n", r
);
282 static void create_database( const char *filename
, const struct msi_table
*tables
, UINT num_tables
)
287 r
= MsiOpenDatabaseA( filename
, MSIDBOPEN_CREATE
, &hdb
);
288 ok(r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
290 /* import the tables into the database */
291 for (i
= 0; i
< num_tables
; i
++)
293 const struct msi_table
*table
= &tables
[i
];
295 write_file( table
->filename
, table
->data
, (table
->size
- 1) * sizeof(char) );
297 r
= MsiDatabaseImportA( hdb
, CURR_DIR
, table
->filename
);
298 ok(r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
300 DeleteFileA( table
->filename
);
303 r
= MsiDatabaseCommit( hdb
);
304 ok(r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
306 MsiCloseHandle( hdb
);
307 set_suminfo( filename
);
310 /* data for generating a patch */
312 /* table names - encoded as in an msi database file */
313 static const WCHAR p_name0
[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
314 static const WCHAR p_name1
[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
315 static const WCHAR p_name2
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
316 static const WCHAR p_name3
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
317 static const WCHAR p_name4
[] = { 0x3a8c, 0x47cb, 0x45b0, 0x45ec, 0x45a8, 0x4837, 0}; /* CAB_msitest */
318 static const WCHAR p_name5
[] = { 0x4840, 0x4596, 0x3e6c, 0x45e4, 0x42e6, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* MsiPatchSequence */
319 static const WCHAR p_name6
[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
320 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
321 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
322 /* substorage names */
323 static const WCHAR p_name7
[] = { 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075, 0x0070,
324 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* targetToupgraded */
325 static const WCHAR p_name8
[] = { 0x0023, 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075,
326 0x0070, 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* #targetToupgraded */
328 /* data in each table */
329 static const WCHAR p_data0
[] = { /* _Columns */
330 0x0001, 0x0001, 0x0001, 0x0001, 0x8001, 0x8002, 0x8003, 0x8004,
331 0x0002, 0x0003, 0x0004, 0x0005, 0xad00, 0xbd26, 0x8d00, 0x9502
333 static const WCHAR p_data1
[] = { /* _Tables */
336 static const char p_data2
[] = { /* _StringData */
337 "MsiPatchSequencePatchFamilyProductCodeSequenceAttributes1.1.19388.37230913B8D18FBB64CACA239C74C11E3FA74"
339 static const WCHAR p_data3
[] = { /* _StringPool */
341 0, 0, /* string 0 '' */
342 16, 5, /* string 1 'MsiPatchSequence' */
343 11, 1, /* string 2 'PatchFamily' */
344 11, 1, /* string 3 'ProductCode' */
345 8, 1, /* string 4 'Sequence' */
346 10, 1, /* string 5 'Attributes' */
347 15, 1, /* string 6 '1.1.19388.37230' */
348 32, 1, /* string 7 '913B8D18FBB64CACA239C74C11E3FA74' */
350 static const char p_data4
[] = { /* CAB_msitest */
351 0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00,
352 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00,
353 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9e,
354 0x03, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x12,
355 0xea, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
356 0x3c, 0xd4, 0x80, 0x20, 0x00, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e,
357 0x74, 0x78, 0x74, 0x00, 0x0b, 0x3c, 0xd6, 0xc1, 0x4a, 0x00, 0xea,
358 0x03, 0x5b, 0x80, 0x80, 0x8d, 0x00, 0x10, 0xa1, 0x3e, 0x00, 0x00,
359 0x00, 0x00, 0x03, 0x00, 0x40, 0x30, 0x0c, 0x43, 0xf8, 0xb4, 0x85,
360 0x4d, 0x96, 0x08, 0x0a, 0x92, 0xf0, 0x52, 0xfb, 0xbb, 0x82, 0xf9,
361 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x31, 0x7d,
362 0x56, 0xdf, 0xf7, 0x48, 0x7c, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
363 0x00, 0x00, 0x41, 0x80, 0xdf, 0xf7, 0xd8, 0x72, 0xbf, 0xb9, 0x63,
364 0x91, 0x0e, 0x57, 0x1f, 0xfa, 0x1a, 0x66, 0x54, 0x55
366 static const WCHAR p_data5
[] = { /* MsiPatchSequence */
367 0x0007, 0x0000, 0x0006, 0x8000
369 static const char p_data6
[] = { /* SummaryInformation */
370 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
371 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
372 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
373 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
374 0x30, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
375 0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00,
376 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x90,
377 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
378 0x0f, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
379 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x30, 0x46, 0x39, 0x36, 0x43,
380 0x44, 0x43, 0x30, 0x2d, 0x34, 0x43, 0x44, 0x46, 0x2d, 0x34, 0x33,
381 0x30, 0x34, 0x2d, 0x42, 0x32, 0x38, 0x33, 0x2d, 0x37, 0x42, 0x39,
382 0x32, 0x36, 0x34, 0x38, 0x38, 0x39, 0x45, 0x46, 0x37, 0x7d, 0x00,
383 0x00, 0x1e, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x39,
384 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42,
385 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39,
386 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41,
387 0x37, 0x34, 0x7d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x25, 0x00,
388 0x00, 0x00, 0x3a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f,
389 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x3b, 0x3a, 0x23,
390 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f, 0x75, 0x70, 0x67,
391 0x72, 0x61, 0x64, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
392 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x61, 0x74, 0x63, 0x68,
393 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x00,
394 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
403 static const struct table_data table_patch_data
[] = {
404 { p_name0
, p_data0
, sizeof p_data0
},
405 { p_name1
, p_data1
, sizeof p_data1
},
406 { p_name2
, p_data2
, sizeof p_data2
- 1 },
407 { p_name3
, p_data3
, sizeof p_data3
},
408 { p_name4
, p_data4
, sizeof p_data4
},
409 { p_name5
, p_data5
, sizeof p_data5
},
410 { p_name6
, p_data6
, sizeof p_data6
}
413 #define NUM_PATCH_TABLES (sizeof table_patch_data/sizeof table_patch_data[0])
415 static const WCHAR t1_name0
[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
416 static const WCHAR t1_name1
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
417 static const WCHAR t1_name2
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
418 static const WCHAR t1_name3
[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
419 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
420 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
422 static const WCHAR t1_data0
[] = { /* File */
423 0x0008, 0x0001, 0x03ea, 0x8000
425 static const char t1_data1
[] = { /* _StringData */
428 static const WCHAR t1_data2
[] = { /* _StringPool */
430 0, 0, /* string 0 '' */
431 9, 1, /* string 1 'patch.txt' */
433 static const char t1_data3
[] = { /* SummaryInformation */
434 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
435 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
436 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
437 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
438 0x30, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00,
439 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00,
440 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8,
441 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00,
442 0x06, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
443 0x00, 0xd0, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xdc, 0x00,
444 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x08,
445 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
446 0x04, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00,
447 0x00, 0x10, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x1e, 0x00,
448 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61,
449 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74,
450 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
451 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c,
452 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74, 0x61,
453 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
454 0x07, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6e, 0x65, 0x48, 0x51, 0x00,
455 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
456 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
457 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
458 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
459 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31,
460 0x30, 0x33, 0x33, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06,
461 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00, 0x00,
462 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b, 0x39, 0x31,
463 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
464 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
465 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
466 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x39, 0x31,
467 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
468 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
469 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
470 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x41, 0x32,
471 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45, 0x32, 0x43,
472 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30, 0x39, 0x2d,
473 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35, 0x46, 0x34,
474 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
475 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x22, 0x09
478 static const struct table_data table_transform1_data
[] = {
479 { t1_name0
, t1_data0
, sizeof t1_data0
},
480 { t1_name1
, t1_data1
, sizeof t1_data1
- 1 },
481 { t1_name2
, t1_data2
, sizeof t1_data2
},
482 { t1_name3
, t1_data3
, sizeof t1_data3
}
485 #define NUM_TRANSFORM1_TABLES (sizeof table_transform1_data/sizeof table_transform1_data[0])
487 static const WCHAR t2_name0
[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
488 static const WCHAR t2_name1
[] = { 0x4840, 0x4216, 0x4327, 0x4824, 0 }; /* Media */
489 static const WCHAR t2_name2
[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
490 static const WCHAR t2_name3
[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
491 static const WCHAR t2_name4
[] = { 0x4840, 0x4559, 0x44f2, 0x4568, 0x4737, 0 }; /* Property */
492 static const WCHAR t2_name5
[] = { 0x4840, 0x4119, 0x41b7, 0x3e6b, 0x41a4, 0x412e, 0x422a, 0 }; /* PatchPackage */
493 static const WCHAR t2_name6
[] = { 0x4840, 0x4452, 0x45f6, 0x43e4, 0x3baf, 0x423b, 0x4626,
494 0x4237, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* InstallExecuteSequence */
495 static const WCHAR t2_name7
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
496 static const WCHAR t2_name8
[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
497 static const WCHAR t2_name9
[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
498 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
499 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
501 static const WCHAR t2_data0
[] = { /* File */
502 0x00c0, 0x0001, 0x9000, 0x83e8
504 static const WCHAR t2_data1
[] = { /* Media */
505 0x0601, 0x8002, 0x03e9, 0x8000, 0x0000, 0x0007, 0x0000, 0x0008
507 static const WCHAR t2_data2
[] = { /* _Columns */
508 0x0401, 0x0009, 0x0000, 0x000a, 0xad48, 0x0401, 0x0009, 0x0000, /* 0x0401 = add row (1), 4 shorts */
509 0x000b, 0xa502, 0x0401, 0x0009, 0x0000, 0x000c, 0x8104, 0x0401,
510 0x0009, 0x0000, 0x000d, 0x8502, 0x0401, 0x0009, 0x0000, 0x000e,
511 0x9900, 0x0401, 0x0009, 0x0000, 0x000f, 0x9d48, 0x0401, 0x0010,
512 0x0000, 0x0011, 0xad26, 0x0401, 0x0010, 0x0000, 0x0012, 0x8502,
513 0x0401, 0x0014, 0x0000, 0x0015, 0xad26, 0x0401, 0x0014, 0x0000,
516 static const WCHAR t2_data3
[] = { /* _Tables */
517 0x0101, 0x0009, 0x0101, 0x0010, 0x0101, 0x0014
519 static const WCHAR t2_data4
[] = { /* Property */
520 0x0201, 0x0002, 0x0003, 0x0201, 0x0004, 0x0005
522 static const WCHAR t2_data5
[] = { /* PatchPackage */
523 0x0201, 0x0013, 0x8002
525 static const WCHAR t2_data6
[] = { /* InstallExecuteSequence */
526 0x0301, 0x0006, 0x0000, 0x87d1
528 static const char t2_data7
[] = { /* _StringData */
529 "patch.txtPATCHNEWSUMMARYSUBJECTInstallation DatabasePATCHNEWPACKAGECODE{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}"
530 "PatchFiles#CAB_msitestpropPatchFile_SequencePatchSizeAttributesHeaderStreamRef_PatchPackagePatchIdMedia_"
531 "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}MsiPatchHeadersStreamRef"
533 static const WCHAR t2_data8
[] = { /* _StringPool */
535 0, 0, /* string 0 '' */
536 9, 1, /* string 1 'patch.txt' */
537 22, 1, /* string 2 'PATCHNEWSUMMARYSUBJECT' */
538 21, 1, /* string 3 'Installation Database' */
539 19, 1, /* string 4 'PATCHNEWPACKAGECODE' */
540 38, 1, /* string 5 '{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}' */
541 10, 1, /* string 6 'PatchFiles' */
542 12, 1, /* string 7 '#CAB_msitest' */
543 4, 1, /* string 8 'prop' */
544 5, 7, /* string 9 'Patch' */
545 5, 1, /* string 10 'File_' */
546 8, 1, /* string 11 'Sequence' */
547 9, 1, /* string 12 'PatchSize' */
548 10, 1, /* string 13 'Attributes' */
549 6, 2, /* string 14 'Header' */
550 10, 1, /* string 15 'StreamRef_' */
551 12, 3, /* string 16 'PatchPackage' */
552 7, 1, /* string 17 'PatchId' */
553 6, 1, /* string 18 'Media_' */
554 38, 1, /* string 19 '{0F96CDC0-4CDF-4304-B283-7B9264889EF7}' */
555 15, 3, /* string 20 'MsiPatchHeaders' */
556 9, 1 /* string 21 'StreamRef' */
558 static const char t2_data9
[] = { /* SummaryInformation */
559 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
560 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
561 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
562 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
563 0x30, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00,
564 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00,
565 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x78,
566 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
567 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
568 0x00, 0x9c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa8, 0x00,
569 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x09,
570 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
571 0x4c, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00,
572 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
573 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
574 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
575 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
576 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00,
577 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01,
578 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
579 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00,
580 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
581 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b,
582 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
583 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
584 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
585 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
586 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
587 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
588 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
589 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
590 0x41, 0x32, 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45,
591 0x32, 0x43, 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30,
592 0x39, 0x2d, 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35,
593 0x46, 0x34, 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2d,
594 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x27, 0x09
597 static const struct table_data table_transform2_data
[] = {
598 { t2_name0
, t2_data0
, sizeof t2_data0
},
599 { t2_name1
, t2_data1
, sizeof t2_data1
},
600 { t2_name2
, t2_data2
, sizeof t2_data2
},
601 { t2_name3
, t2_data3
, sizeof t2_data3
},
602 { t2_name4
, t2_data4
, sizeof t2_data4
},
603 { t2_name5
, t2_data5
, sizeof t2_data5
},
604 { t2_name6
, t2_data6
, sizeof t2_data6
},
605 { t2_name7
, t2_data7
, sizeof t2_data7
- 1 },
606 { t2_name8
, t2_data8
, sizeof t2_data8
},
607 { t2_name9
, t2_data9
, sizeof t2_data9
}
610 #define NUM_TRANSFORM2_TABLES (sizeof table_transform2_data/sizeof table_transform2_data[0])
612 static void write_tables( IStorage
*stg
, const struct table_data
*tables
, UINT num_tables
)
618 for (i
= 0; i
< num_tables
; i
++)
620 r
= IStorage_CreateStream( stg
, tables
[i
].name
, STGM_WRITE
|STGM_SHARE_EXCLUSIVE
, 0, 0, &stm
);
623 ok( 0, "failed to create stream 0x%08x\n", r
);
627 r
= IStream_Write( stm
, tables
[i
].data
, tables
[i
].size
, &count
);
628 if (FAILED( r
) || count
!= tables
[i
].size
)
629 ok( 0, "failed to write stream\n" );
630 IStream_Release( stm
);
634 static void create_patch( const char *filename
)
636 IStorage
*stg
= NULL
, *stg1
= NULL
, *stg2
= NULL
;
640 const DWORD mode
= STGM_CREATE
|STGM_READWRITE
|STGM_DIRECT
|STGM_SHARE_EXCLUSIVE
;
642 const CLSID CLSID_MsiPatch
= {0xc1086, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
643 const CLSID CLSID_MsiTransform
= {0xc1082, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
645 len
= MultiByteToWideChar( CP_ACP
, 0, filename
, -1, NULL
, 0 );
646 filenameW
= HeapAlloc( GetProcessHeap(), 0, len
* sizeof(WCHAR
) );
647 MultiByteToWideChar( CP_ACP
, 0, filename
, -1, filenameW
, len
);
649 r
= StgCreateDocfile( filenameW
, mode
, 0, &stg
);
650 HeapFree( GetProcessHeap(), 0, filenameW
);
651 ok( r
== S_OK
, "failed to create storage 0x%08x\n", r
);
655 r
= IStorage_SetClass( stg
, &CLSID_MsiPatch
);
656 ok( r
== S_OK
, "failed to set storage type 0x%08x\n", r
);
658 write_tables( stg
, table_patch_data
, NUM_PATCH_TABLES
);
660 r
= IStorage_CreateStorage( stg
, p_name7
, mode
, 0, 0, &stg1
);
661 ok( r
== S_OK
, "failed to create substorage 0x%08x\n", r
);
663 r
= IStorage_SetClass( stg1
, &CLSID_MsiTransform
);
664 ok( r
== S_OK
, "failed to set storage type 0x%08x\n", r
);
666 write_tables( stg1
, table_transform1_data
, NUM_TRANSFORM1_TABLES
);
667 IStorage_Release( stg1
);
669 r
= IStorage_CreateStorage( stg
, p_name8
, mode
, 0, 0, &stg2
);
670 ok( r
== S_OK
, "failed to create substorage 0x%08x\n", r
);
672 r
= IStorage_SetClass( stg2
, &CLSID_MsiTransform
);
673 ok( r
== S_OK
, "failed to set storage type 0x%08x\n", r
);
675 write_tables( stg2
, table_transform2_data
, NUM_TRANSFORM2_TABLES
);
676 IStorage_Release( stg2
);
677 IStorage_Release( stg
);
680 static void test_simple_patch( void )
684 char path
[MAX_PATH
], install_source
[MAX_PATH
];
686 MSIHANDLE hpackage
, hdb
, hview
, hrec
;
688 if (!pMsiApplyPatchA
)
690 win_skip("MsiApplyPatchA is not available\n");
694 CreateDirectoryA( "msitest", NULL
);
695 create_file( "msitest\\patch.txt", 1000 );
697 create_database( msifile
, tables
, sizeof(tables
) / sizeof(struct msi_table
) );
698 create_patch( mspfile
);
700 MsiSetInternalUI( INSTALLUILEVEL_NONE
, NULL
);
702 r
= MsiInstallProductA( msifile
, NULL
);
703 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
705 size
= get_pf_file_size( "msitest\\patch.txt" );
706 ok( size
== 1000, "expected 1000, got %u\n", size
);
708 size
= sizeof(install_source
);
709 r
= MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
710 "InstallSource", install_source
, &size
);
711 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
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 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
733 MsiCloseHandle( hrec
);
734 MsiViewClose( hview
);
735 MsiCloseHandle( hview
);
737 query
= "SELECT * FROM `Property` WHERE `Property` = 'PATCHNEWSUMMARYSUBJECT' "
738 "AND `Value` = 'Installer Database'";
739 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
740 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
742 r
= MsiViewExecute( hview
, 0 );
743 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
745 r
= MsiViewFetch( hview
, &hrec
);
746 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
748 MsiCloseHandle( hrec
);
749 MsiViewClose( hview
);
750 MsiCloseHandle( hview
);
752 MsiCloseHandle( hdb
);
753 MsiCloseHandle( hpackage
);
755 r
= MsiApplyPatchA( mspfile
, NULL
, INSTALLTYPE_DEFAULT
, NULL
);
756 ok( r
== ERROR_SUCCESS
|| broken( r
== ERROR_PATCH_PACKAGE_INVALID
), /* version 2.0 */
757 "expected ERROR_SUCCESS, got %u\n", r
);
759 if (r
== ERROR_PATCH_PACKAGE_INVALID
)
761 win_skip("Windows Installer < 3.0 detected\n");
765 size
= get_pf_file_size( "msitest\\patch.txt" );
766 ok( size
== 1002, "expected 1002, got %u\n", size
);
768 /* show that MsiOpenPackage applies registered patches */
769 r
= MsiOpenPackageA( path
, &hpackage
);
770 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
772 hdb
= MsiGetActiveDatabase( hpackage
);
773 ok( hdb
, "failed to get database handle\n" );
775 query
= "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
776 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
777 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
779 r
= MsiViewExecute( hview
, 0 );
780 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
782 r
= MsiViewFetch( hview
, &hrec
);
783 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
785 MsiCloseHandle( hrec
);
786 MsiViewClose( hview
);
787 MsiCloseHandle( hview
);
789 query
= "SELECT * FROM `Property` WHERE `Property` = 'PATCHNEWSUMMARYSUBJECT' "
790 "AND `Value` = 'Installation Database'";
791 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
792 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
794 r
= MsiViewExecute( hview
, 0 );
795 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
797 r
= MsiViewFetch( hview
, &hrec
);
798 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
800 MsiCloseHandle( hrec
);
801 MsiViewClose( hview
);
802 MsiCloseHandle( hview
);
804 MsiCloseHandle( hdb
);
805 MsiCloseHandle( hpackage
);
807 /* show that patches are not committed to the local package database */
809 r
= MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
810 "LocalPackage", path
, &size
);
811 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
813 r
= MsiOpenDatabaseA( path
, MSIDBOPEN_READONLY
, &hdb
);
814 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
816 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
817 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
819 r
= MsiViewExecute( hview
, 0 );
820 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
822 r
= MsiViewFetch( hview
, &hrec
);
823 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
825 MsiCloseHandle( hrec
);
826 MsiViewClose( hview
);
827 MsiCloseHandle( hview
);
828 MsiCloseHandle( hdb
);
831 r
= MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
832 "InstallSource", path
, &size
);
833 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
834 ok( !strcasecmp( path
, install_source
), "wrong path %s\n", path
);
836 r
= MsiInstallProductA( msifile
, "REMOVE=ALL" );
837 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
839 ok( !delete_pf( "msitest\\patch.txt", TRUE
), "file not removed\n" );
840 ok( !delete_pf( "msitest", FALSE
), "directory not removed\n" );
842 DeleteFileA( msifile
);
843 DeleteFileA( mspfile
);
844 RemoveDirectoryA( "msitest" );
847 static void test_MsiOpenDatabase( void )
852 r
= MsiOpenDatabase( mspfile
, MSIDBOPEN_CREATE
, &hdb
);
853 ok(r
== ERROR_SUCCESS
, "failed to open database %u\n", r
);
855 r
= MsiDatabaseCommit( hdb
);
856 ok(r
== ERROR_SUCCESS
, "failed to commit database %u\n", r
);
857 MsiCloseHandle( hdb
);
859 r
= MsiOpenDatabase( mspfile
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &hdb
);
860 ok(r
== ERROR_OPEN_FAILED
, "expected ERROR_OPEN_FAILED, got %u\n", r
);
861 DeleteFileA( mspfile
);
863 r
= MsiOpenDatabase( mspfile
, MSIDBOPEN_CREATE
+ MSIDBOPEN_PATCHFILE
, &hdb
);
864 ok(r
== ERROR_SUCCESS
, "failed to open database %u\n", r
);
866 r
= MsiDatabaseCommit( hdb
);
867 ok(r
== ERROR_SUCCESS
, "failed to commit database %u\n", r
);
868 MsiCloseHandle( hdb
);
870 r
= MsiOpenDatabase( mspfile
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &hdb
);
871 ok(r
== ERROR_SUCCESS
, "failed to open database %u\n", r
);
872 MsiCloseHandle( hdb
);
873 DeleteFileA( mspfile
);
875 create_database( msifile
, tables
, sizeof(tables
) / sizeof(struct msi_table
) );
876 create_patch( mspfile
);
878 r
= MsiOpenDatabase( msifile
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &hdb
);
879 ok(r
== ERROR_OPEN_FAILED
, "failed to open database %u\n", r
);
881 r
= MsiOpenDatabase( mspfile
, MSIDBOPEN_READONLY
+ MSIDBOPEN_PATCHFILE
, &hdb
);
882 ok(r
== ERROR_SUCCESS
, "failed to open database %u\n", r
);
883 MsiCloseHandle( hdb
);
885 DeleteFileA( msifile
);
886 DeleteFileA( mspfile
);
889 static UINT
find_entry( MSIHANDLE hdb
, const char *table
, const char *entry
)
891 static char fmt
[] = "SELECT * FROM `%s` WHERE `Name` = '%s'";
894 MSIHANDLE hview
, hrec
;
896 sprintf( query
, fmt
, table
, entry
);
897 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
898 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
900 r
= MsiViewExecute( hview
, 0 );
901 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
903 r
= MsiViewFetch( hview
, &hrec
);
904 MsiViewClose( hview
);
905 MsiCloseHandle( hview
);
906 MsiCloseHandle( hrec
);
910 static void test_system_tables( void )
914 MSIHANDLE hproduct
, hdb
, hview
, hrec
;
916 if (!pMsiApplyPatchA
)
918 win_skip("MsiApplyPatchA is not available\n");
922 CreateDirectoryA( "msitest", NULL
);
923 create_file( "msitest\\patch.txt", 1000 );
925 create_database( msifile
, tables
, sizeof(tables
) / sizeof(struct msi_table
) );
926 create_patch( mspfile
);
928 MsiSetInternalUI( INSTALLUILEVEL_NONE
, NULL
);
930 r
= MsiInstallProductA( msifile
, NULL
);
931 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
933 r
= MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct
);
934 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
936 hdb
= MsiGetActiveDatabase( hproduct
);
937 ok( hdb
, "failed to get database handle\n" );
939 r
= find_entry( hdb
, "_Streams", "\5SummaryInformation" );
940 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
942 query
= "SELECT * FROM `_Storages`";
943 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
944 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
946 r
= MsiViewExecute( hview
, 0 );
947 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
949 r
= MsiViewFetch( hview
, &hrec
);
950 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
952 r
= find_entry( hdb
, "_Tables", "Directory" );
953 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
955 r
= find_entry( hdb
, "_Tables", "File" );
956 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
958 r
= find_entry( hdb
, "_Tables", "Component" );
959 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
961 r
= find_entry( hdb
, "_Tables", "Feature" );
962 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
964 r
= find_entry( hdb
, "_Tables", "FeatureComponents" );
965 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
967 r
= find_entry( hdb
, "_Tables", "Property" );
968 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
970 r
= find_entry( hdb
, "_Tables", "InstallExecuteSequence" );
971 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
973 r
= find_entry( hdb
, "_Tables", "Media" );
974 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
976 r
= find_entry( hdb
, "_Tables", "_Property" );
977 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
979 MsiCloseHandle( hrec
);
980 MsiViewClose( hview
);
981 MsiCloseHandle( hview
);
982 MsiCloseHandle( hdb
);
983 MsiCloseHandle( hproduct
);
985 r
= MsiApplyPatchA( mspfile
, NULL
, INSTALLTYPE_DEFAULT
, NULL
);
986 ok( r
== ERROR_SUCCESS
|| broken( r
== ERROR_PATCH_PACKAGE_INVALID
), /* version 2.0 */
987 "expected ERROR_SUCCESS, got %u\n", r
);
989 if (r
== ERROR_PATCH_PACKAGE_INVALID
)
991 win_skip("Windows Installer < 3.0 detected\n");
995 r
= MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct
);
996 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
998 hdb
= MsiGetActiveDatabase( hproduct
);
999 ok( hdb
, "failed to get database handle\n" );
1001 r
= find_entry( hdb
, "_Streams", "\5SummaryInformation" );
1002 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1004 query
= "SELECT * FROM `_Storages`";
1005 r
= MsiDatabaseOpenView( hdb
, query
, &hview
);
1006 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1008 r
= MsiViewExecute( hview
, 0 );
1009 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1011 r
= MsiViewFetch( hview
, &hrec
);
1012 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
1014 r
= find_entry( hdb
, "_Tables", "Directory" );
1015 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1017 r
= find_entry( hdb
, "_Tables", "File" );
1018 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1020 r
= find_entry( hdb
, "_Tables", "Component" );
1021 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1023 r
= find_entry( hdb
, "_Tables", "Feature" );
1024 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1026 r
= find_entry( hdb
, "_Tables", "FeatureComponents" );
1027 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1029 r
= find_entry( hdb
, "_Tables", "Property" );
1030 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1032 r
= find_entry( hdb
, "_Tables", "InstallExecuteSequence" );
1033 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1035 r
= find_entry( hdb
, "_Tables", "Media" );
1036 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1038 r
= find_entry( hdb
, "_Tables", "_Property" );
1039 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1041 r
= find_entry( hdb
, "_Tables", "MsiPatchHeaders" );
1042 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1044 r
= find_entry( hdb
, "_Tables", "Patch" );
1045 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1047 r
= find_entry( hdb
, "_Tables", "PatchPackage" );
1048 ok( r
== ERROR_SUCCESS
, "failed to find entry %u\n", r
);
1050 MsiCloseHandle( hrec
);
1051 MsiViewClose( hview
);
1052 MsiCloseHandle( hview
);
1053 MsiCloseHandle( hdb
);
1054 MsiCloseHandle( hproduct
);
1056 r
= MsiInstallProductA( msifile
, "REMOVE=ALL" );
1057 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1059 DeleteFileA( msifile
);
1060 DeleteFileA( mspfile
);
1061 RemoveDirectoryA( "msitest" );
1064 static void test_patch_registration( void )
1067 char buffer
[MAX_PATH
], patch_code
[39];
1069 if (!pMsiApplyPatchA
|| !pMsiGetPatchInfoExA
|| !pMsiEnumPatchesExA
)
1071 win_skip("required functions not available\n");
1075 CreateDirectoryA( "msitest", NULL
);
1076 create_file( "msitest\\patch.txt", 1000 );
1078 create_database( msifile
, tables
, sizeof(tables
) / sizeof(struct msi_table
) );
1079 create_patch( mspfile
);
1081 MsiSetInternalUI( INSTALLUILEVEL_NONE
, NULL
);
1083 r
= MsiInstallProductA( msifile
, NULL
);
1084 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1086 r
= MsiApplyPatchA( mspfile
, NULL
, INSTALLTYPE_DEFAULT
, NULL
);
1087 ok( r
== ERROR_SUCCESS
|| broken( r
== ERROR_PATCH_PACKAGE_INVALID
), /* version 2.0 */
1088 "expected ERROR_SUCCESS, got %u\n", r
);
1090 if (r
== ERROR_PATCH_PACKAGE_INVALID
)
1092 win_skip("Windows Installer < 3.0 detected\n");
1097 size
= sizeof(buffer
);
1098 r
= pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1099 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1100 NULL
, MSIINSTALLCONTEXT_USERUNMANAGED
,
1101 INSTALLPROPERTY_LOCALPACKAGE
, buffer
, &size
);
1102 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1103 ok( buffer
[0], "buffer empty\n" );
1106 size
= sizeof(buffer
);
1107 r
= pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1108 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1109 NULL
, MSIINSTALLCONTEXT_MACHINE
,
1110 INSTALLPROPERTY_LOCALPACKAGE
, buffer
, &size
);
1111 ok( r
== ERROR_UNKNOWN_PRODUCT
, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r
);
1114 size
= sizeof(buffer
);
1115 r
= pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1116 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1117 NULL
, MSIINSTALLCONTEXT_USERMANAGED
,
1118 INSTALLPROPERTY_LOCALPACKAGE
, buffer
, &size
);
1119 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1120 ok( !buffer
[0], "got %s\n", buffer
);
1122 r
= pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1123 NULL
, MSIINSTALLCONTEXT_USERUNMANAGED
, MSIPATCHSTATE_APPLIED
,
1124 0, patch_code
, NULL
, NULL
, NULL
, NULL
);
1125 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1126 ok( !strcmp( patch_code
, "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}" ), "wrong patch code\n" );
1128 r
= pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1129 NULL
, MSIINSTALLCONTEXT_MACHINE
, MSIPATCHSTATE_APPLIED
,
1130 0, patch_code
, NULL
, NULL
, NULL
, NULL
);
1131 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
1133 r
= pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1134 NULL
, MSIINSTALLCONTEXT_USERMANAGED
, MSIPATCHSTATE_APPLIED
,
1135 0, patch_code
, NULL
, NULL
, NULL
, NULL
);
1136 ok( r
== ERROR_NO_MORE_ITEMS
, "expected ERROR_NO_MORE_ITEMS, got %u\n", r
);
1138 r
= MsiInstallProductA( msifile
, "REMOVE=ALL" );
1139 ok( r
== ERROR_SUCCESS
, "expected ERROR_SUCCESS, got %u\n", r
);
1142 size
= sizeof(buffer
);
1143 r
= pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1144 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1145 NULL
, MSIINSTALLCONTEXT_USERUNMANAGED
,
1146 INSTALLPROPERTY_LOCALPACKAGE
, buffer
, &size
);
1147 ok( r
== ERROR_UNKNOWN_PRODUCT
, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r
);
1149 DeleteFileA( msifile
);
1150 DeleteFileA( mspfile
);
1151 RemoveDirectoryA( "msitest" );
1157 char temp_path
[MAX_PATH
], prev_path
[MAX_PATH
];
1159 init_function_pointers();
1161 GetCurrentDirectoryA( MAX_PATH
, prev_path
);
1162 GetTempPath( MAX_PATH
, temp_path
);
1163 SetCurrentDirectoryA( temp_path
);
1165 strcpy( CURR_DIR
, temp_path
);
1166 len
= strlen( CURR_DIR
);
1168 if (len
&& (CURR_DIR
[len
- 1] == '\\'))
1169 CURR_DIR
[len
- 1] = 0;
1171 get_program_files_dir( PROG_FILES_DIR
, COMMON_FILES_DIR
);
1173 test_simple_patch();
1174 test_MsiOpenDatabase();
1175 test_system_tables();
1176 test_patch_registration();
1178 SetCurrentDirectoryA( prev_path
);