Release 1.6-rc2.
[wine/testsucceed.git] / dlls / msi / tests / patch.c
blob81ef949d882ff4e7f919604603707274b05be8d3
1 /*
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
22 #define COBJMACROS
24 #include <stdio.h>
26 #include <windows.h>
27 #include <msiquery.h>
28 #include <msidefs.h>
29 #include <msi.h>
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 );
38 static BOOL (WINAPI *pOpenProcessToken)( HANDLE, DWORD, PHANDLE );
40 static const char *msifile = "winetest-patch.msi";
41 static const char *mspfile = "winetest-patch.msp";
43 static char CURR_DIR[MAX_PATH];
44 static char PROG_FILES_DIR[MAX_PATH];
45 static char COMMON_FILES_DIR[MAX_PATH];
47 /* msi database data */
49 static const char property_dat[] =
50 "Property\tValue\n"
51 "s72\tl0\n"
52 "Property\tProperty\n"
53 "Manufacturer\tWineHQ\n"
54 "ProductCode\t{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}\n"
55 "UpgradeCode\t{A2E3D643-4E2C-477F-A309-F76F552D5F43}\n"
56 "ProductLanguage\t1033\n"
57 "ProductName\tmsitest\n"
58 "ProductVersion\t1.1.1\n"
59 "PATCHNEWSUMMARYSUBJECT\tInstaller Database\n"
60 "MSIFASTINSTALL\t1\n";
62 static const char media_dat[] =
63 "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
64 "i2\ti4\tL64\tS255\tS32\tS72\n"
65 "Media\tDiskId\n"
66 "1\t1\t\t\tDISK1\t\n";
68 static const char file_dat[] =
69 "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
70 "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
71 "File\tFile\n"
72 "patch.txt\tpatch\tpatch.txt\t1000\t\t\t0\t1\n";
74 static const char directory_dat[] =
75 "Directory\tDirectory_Parent\tDefaultDir\n"
76 "s72\tS72\tl255\n"
77 "Directory\tDirectory\n"
78 "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
79 "ProgramFilesFolder\tTARGETDIR\t.\n"
80 "TARGETDIR\t\tSourceDir";
82 static const char component_dat[] =
83 "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
84 "s72\tS38\ts72\ti2\tS255\tS72\n"
85 "Component\tComponent\n"
86 "patch\t{4B79D87E-6D28-4FD3-92D6-CD9B26AF64F1}\tMSITESTDIR\t0\t\tpatch.txt\n";
88 static const char feature_dat[] =
89 "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
90 "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
91 "Feature\tFeature\n"
92 "patch\t\t\tpatch feature\t1\t1\tMSITESTDIR\t0\n";
94 static const char feature_comp_dat[] =
95 "Feature_\tComponent_\n"
96 "s38\ts72\n"
97 "FeatureComponents\tFeature_\tComponent_\n"
98 "patch\tpatch\n";
100 static const char install_exec_seq_dat[] =
101 "Action\tCondition\tSequence\n"
102 "s72\tS255\tI2\n"
103 "InstallExecuteSequence\tAction\n"
104 "LaunchConditions\t\t100\n"
105 "CostInitialize\t\t800\n"
106 "FileCost\t\t900\n"
107 "CostFinalize\t\t1000\n"
108 "InstallValidate\t\t1400\n"
109 "InstallInitialize\t\t1500\n"
110 "ProcessComponents\t\t1600\n"
111 "RemoveFiles\t\t1700\n"
112 "InstallFiles\t\t2000\n"
113 "RegisterUser\t\t3000\n"
114 "RegisterProduct\t\t3100\n"
115 "PublishFeatures\t\t5100\n"
116 "PublishProduct\t\t5200\n"
117 "InstallFinalize\t\t6000\n";
119 struct msi_table
121 const char *filename;
122 const char *data;
123 int size;
126 #define ADD_TABLE( x ) { #x".idt", x##_dat, sizeof(x##_dat) }
128 static const struct msi_table tables[] =
130 ADD_TABLE( directory ),
131 ADD_TABLE( file ),
132 ADD_TABLE( component ),
133 ADD_TABLE( feature ),
134 ADD_TABLE( feature_comp ),
135 ADD_TABLE( property ),
136 ADD_TABLE( install_exec_seq ),
137 ADD_TABLE( media )
140 static void init_function_pointers( void )
142 HMODULE hmsi = GetModuleHandleA( "msi.dll" );
143 HMODULE hadvapi32 = GetModuleHandleA( "advapi32.dll" );
145 #define GET_PROC( mod, func ) \
146 p ## func = (void *)GetProcAddress( mod, #func ); \
147 if (!p ## func) \
148 trace( "GetProcAddress(%s) failed\n", #func );
150 GET_PROC( hmsi, MsiApplyPatchA );
151 GET_PROC( hmsi, MsiGetPatchInfoExA );
152 GET_PROC( hmsi, MsiEnumPatchesExA );
154 GET_PROC( hadvapi32, OpenProcessToken );
155 #undef GET_PROC
158 static BOOL is_process_limited(void)
160 HANDLE token;
162 if (!pOpenProcessToken) return FALSE;
164 if (pOpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
166 BOOL ret;
167 TOKEN_ELEVATION_TYPE type = TokenElevationTypeDefault;
168 DWORD size;
170 ret = GetTokenInformation(token, TokenElevationType, &type, sizeof(type), &size);
171 CloseHandle(token);
172 return (ret && type == TokenElevationTypeLimited);
174 return FALSE;
177 static BOOL get_program_files_dir( char *buf, char *buf2 )
179 HKEY hkey;
180 DWORD type, size;
182 if (RegOpenKey( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &hkey ))
183 return FALSE;
185 size = MAX_PATH;
186 if (RegQueryValueExA( hkey, "ProgramFilesDir (x86)", 0, &type, (LPBYTE)buf, &size ) &&
187 RegQueryValueExA( hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size ))
189 RegCloseKey( hkey );
190 return FALSE;
192 size = MAX_PATH;
193 if (RegQueryValueExA( hkey, "CommonFilesDir", 0, &type, (LPBYTE)buf2, &size ))
195 RegCloseKey( hkey );
196 return FALSE;
198 RegCloseKey( hkey );
199 return TRUE;
202 static void create_file_data( const char *filename, const char *data, DWORD size )
204 HANDLE file;
205 DWORD written;
207 file = CreateFileA( filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
208 if (file == INVALID_HANDLE_VALUE)
209 return;
211 WriteFile( file, data, strlen( data ), &written, NULL );
212 if (size)
214 SetFilePointer( file, size, NULL, FILE_BEGIN );
215 SetEndOfFile( file );
217 CloseHandle( file );
220 #define create_file( name, size ) create_file_data( name, name, size )
222 static BOOL delete_pf( const char *rel_path, BOOL is_file )
224 char path[MAX_PATH];
226 strcpy( path, PROG_FILES_DIR );
227 strcat( path, "\\" );
228 strcat( path, rel_path );
230 if (is_file)
231 return DeleteFileA( path );
232 else
233 return RemoveDirectoryA( path );
236 static DWORD get_pf_file_size( const char *filename )
238 char path[MAX_PATH];
239 HANDLE file;
240 DWORD size;
242 strcpy( path, PROG_FILES_DIR );
243 strcat( path, "\\");
244 strcat( path, filename );
246 file = CreateFileA( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
247 if (file == INVALID_HANDLE_VALUE)
248 return INVALID_FILE_SIZE;
250 size = GetFileSize( file, NULL );
251 CloseHandle( file );
252 return size;
255 static void write_file( const char *filename, const char *data, DWORD data_size )
257 DWORD size;
258 HANDLE file = CreateFile( filename, GENERIC_WRITE, 0, NULL,
259 CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
260 WriteFile( file, data, data_size, &size, NULL );
261 CloseHandle( file );
264 static void set_suminfo( const char *filename )
266 UINT r;
267 MSIHANDLE hsi, hdb;
269 r = MsiOpenDatabaseA( filename, MSIDBOPEN_DIRECT, &hdb );
270 ok( r == ERROR_SUCCESS, "failed to open database %u\n", r );
272 r = MsiGetSummaryInformation( hdb, NULL, 7, &hsi );
273 ok( r == ERROR_SUCCESS, "failed to open summaryinfo %u\n", r );
275 r = MsiSummaryInfoSetProperty( hsi, 2, VT_LPSTR, 0, NULL, "Installation Database" );
276 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
278 r = MsiSummaryInfoSetProperty( hsi, 3, VT_LPSTR, 0, NULL, "Installation Database" );
279 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
281 r = MsiSummaryInfoSetProperty( hsi, 4, VT_LPSTR, 0, NULL, "WineHQ" );
282 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
284 r = MsiSummaryInfoSetProperty( hsi, 7, VT_LPSTR, 0, NULL, ";1033" );
285 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
287 r = MsiSummaryInfoSetProperty( hsi, 9, VT_LPSTR, 0, NULL, "{E528DDD6-4801-4BEC-BBB6-C5EE0FD097E9}" );
288 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
290 r = MsiSummaryInfoSetProperty( hsi, 14, VT_I4, 100, NULL, NULL );
291 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
293 r = MsiSummaryInfoSetProperty( hsi, 15, VT_I4, 0, NULL, NULL );
294 ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
296 r = MsiSummaryInfoPersist( hsi );
297 ok( r == ERROR_SUCCESS, "failed to persist suminfo %u\n", r );
299 r = MsiCloseHandle( hsi );
300 ok( r == ERROR_SUCCESS, "failed to close suminfo %u\n", r );
302 r = MsiCloseHandle( hdb );
303 ok( r == ERROR_SUCCESS, "failed to close database %u\n", r );
306 static void create_database( const char *filename, const struct msi_table *tables, UINT num_tables )
308 MSIHANDLE hdb;
309 UINT r, i;
311 r = MsiOpenDatabaseA( filename, MSIDBOPEN_CREATE, &hdb );
312 ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
314 /* import the tables into the database */
315 for (i = 0; i < num_tables; i++)
317 const struct msi_table *table = &tables[i];
319 write_file( table->filename, table->data, (table->size - 1) * sizeof(char) );
321 r = MsiDatabaseImportA( hdb, CURR_DIR, table->filename );
322 ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
324 DeleteFileA( table->filename );
327 r = MsiDatabaseCommit( hdb );
328 ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
330 MsiCloseHandle( hdb );
331 set_suminfo( filename );
334 /* data for generating a patch */
336 /* table names - encoded as in an msi database file */
337 static const WCHAR p_name0[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
338 static const WCHAR p_name1[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
339 static const WCHAR p_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
340 static const WCHAR p_name3[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
341 static const WCHAR p_name4[] = { 0x3a8c, 0x47cb, 0x45b0, 0x45ec, 0x45a8, 0x4837, 0}; /* CAB_msitest */
342 static const WCHAR p_name5[] = { 0x4840, 0x4596, 0x3e6c, 0x45e4, 0x42e6, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* MsiPatchSequence */
343 static const WCHAR p_name6[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
344 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
345 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
346 /* substorage names */
347 static const WCHAR p_name7[] = { 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075, 0x0070,
348 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* targetToupgraded */
349 static const WCHAR p_name8[] = { 0x0023, 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075,
350 0x0070, 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* #targetToupgraded */
352 /* data in each table */
353 static const WCHAR p_data0[] = { /* _Columns */
354 0x0001, 0x0001, 0x0001, 0x0001, 0x8001, 0x8002, 0x8003, 0x8004,
355 0x0002, 0x0003, 0x0004, 0x0005, 0xad00, 0xbd26, 0x8d00, 0x9502
357 static const WCHAR p_data1[] = { /* _Tables */
358 0x0001
360 static const char p_data2[] = { /* _StringData */
361 "MsiPatchSequencePatchFamilyProductCodeSequenceAttributes1.1.19388.37230913B8D18FBB64CACA239C74C11E3FA74"
363 static const WCHAR p_data3[] = { /* _StringPool */
364 /* len, refs */
365 0, 0, /* string 0 '' */
366 16, 5, /* string 1 'MsiPatchSequence' */
367 11, 1, /* string 2 'PatchFamily' */
368 11, 1, /* string 3 'ProductCode' */
369 8, 1, /* string 4 'Sequence' */
370 10, 1, /* string 5 'Attributes' */
371 15, 1, /* string 6 '1.1.19388.37230' */
372 32, 1, /* string 7 '913B8D18FBB64CACA239C74C11E3FA74' */
374 static const char p_data4[] = { /* CAB_msitest */
375 0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00,
376 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00,
377 0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9e,
378 0x03, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x12,
379 0xea, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
380 0x3c, 0xd4, 0x80, 0x20, 0x00, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e,
381 0x74, 0x78, 0x74, 0x00, 0x0b, 0x3c, 0xd6, 0xc1, 0x4a, 0x00, 0xea,
382 0x03, 0x5b, 0x80, 0x80, 0x8d, 0x00, 0x10, 0xa1, 0x3e, 0x00, 0x00,
383 0x00, 0x00, 0x03, 0x00, 0x40, 0x30, 0x0c, 0x43, 0xf8, 0xb4, 0x85,
384 0x4d, 0x96, 0x08, 0x0a, 0x92, 0xf0, 0x52, 0xfb, 0xbb, 0x82, 0xf9,
385 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x31, 0x7d,
386 0x56, 0xdf, 0xf7, 0x48, 0x7c, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
387 0x00, 0x00, 0x41, 0x80, 0xdf, 0xf7, 0xd8, 0x72, 0xbf, 0xb9, 0x63,
388 0x91, 0x0e, 0x57, 0x1f, 0xfa, 0x1a, 0x66, 0x54, 0x55
390 static const WCHAR p_data5[] = { /* MsiPatchSequence */
391 0x0007, 0x0000, 0x0006, 0x8000
393 static const char p_data6[] = { /* SummaryInformation */
394 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
395 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
396 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
397 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
398 0x30, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
399 0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00,
400 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x90,
401 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
402 0x0f, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
403 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x30, 0x46, 0x39, 0x36, 0x43,
404 0x44, 0x43, 0x30, 0x2d, 0x34, 0x43, 0x44, 0x46, 0x2d, 0x34, 0x33,
405 0x30, 0x34, 0x2d, 0x42, 0x32, 0x38, 0x33, 0x2d, 0x37, 0x42, 0x39,
406 0x32, 0x36, 0x34, 0x38, 0x38, 0x39, 0x45, 0x46, 0x37, 0x7d, 0x00,
407 0x00, 0x1e, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x39,
408 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42,
409 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39,
410 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41,
411 0x37, 0x34, 0x7d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x25, 0x00,
412 0x00, 0x00, 0x3a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f,
413 0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x3b, 0x3a, 0x23,
414 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f, 0x75, 0x70, 0x67,
415 0x72, 0x61, 0x64, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
416 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x61, 0x74, 0x63, 0x68,
417 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x00,
418 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
421 struct table_data {
422 LPCWSTR name;
423 const void *data;
424 DWORD size;
427 static const struct table_data table_patch_data[] = {
428 { p_name0, p_data0, sizeof p_data0 },
429 { p_name1, p_data1, sizeof p_data1 },
430 { p_name2, p_data2, sizeof p_data2 - 1 },
431 { p_name3, p_data3, sizeof p_data3 },
432 { p_name4, p_data4, sizeof p_data4 },
433 { p_name5, p_data5, sizeof p_data5 },
434 { p_name6, p_data6, sizeof p_data6 }
437 #define NUM_PATCH_TABLES (sizeof table_patch_data/sizeof table_patch_data[0])
439 static const WCHAR t1_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
440 static const WCHAR t1_name1[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
441 static const WCHAR t1_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
442 static const WCHAR t1_name3[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
443 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
444 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
446 static const WCHAR t1_data0[] = { /* File */
447 0x0008, 0x0001, 0x03ea, 0x8000
449 static const char t1_data1[] = { /* _StringData */
450 "patch.txt"
452 static const WCHAR t1_data2[] = { /* _StringPool */
453 /* len, refs */
454 0, 0, /* string 0 '' */
455 9, 1, /* string 1 'patch.txt' */
457 static const char t1_data3[] = { /* SummaryInformation */
458 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
459 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
460 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
461 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
462 0x30, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00,
463 0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00,
464 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8,
465 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00,
466 0x06, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
467 0x00, 0xd0, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xdc, 0x00,
468 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x08,
469 0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
470 0x04, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00,
471 0x00, 0x10, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x1e, 0x00,
472 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61,
473 0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74,
474 0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
475 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c,
476 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74, 0x61,
477 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
478 0x07, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6e, 0x65, 0x48, 0x51, 0x00,
479 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
480 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
481 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
482 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
483 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31,
484 0x30, 0x33, 0x33, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06,
485 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00, 0x00,
486 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b, 0x39, 0x31,
487 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
488 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
489 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
490 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x39, 0x31,
491 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
492 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
493 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
494 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x41, 0x32,
495 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45, 0x32, 0x43,
496 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30, 0x39, 0x2d,
497 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35, 0x46, 0x34,
498 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
499 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x22, 0x09
502 static const struct table_data table_transform1_data[] = {
503 { t1_name0, t1_data0, sizeof t1_data0 },
504 { t1_name1, t1_data1, sizeof t1_data1 - 1 },
505 { t1_name2, t1_data2, sizeof t1_data2 },
506 { t1_name3, t1_data3, sizeof t1_data3 }
509 #define NUM_TRANSFORM1_TABLES (sizeof table_transform1_data/sizeof table_transform1_data[0])
511 static const WCHAR t2_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
512 static const WCHAR t2_name1[] = { 0x4840, 0x4216, 0x4327, 0x4824, 0 }; /* Media */
513 static const WCHAR t2_name2[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
514 static const WCHAR t2_name3[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
515 static const WCHAR t2_name4[] = { 0x4840, 0x4559, 0x44f2, 0x4568, 0x4737, 0 }; /* Property */
516 static const WCHAR t2_name5[] = { 0x4840, 0x4119, 0x41b7, 0x3e6b, 0x41a4, 0x412e, 0x422a, 0 }; /* PatchPackage */
517 static const WCHAR t2_name6[] = { 0x4840, 0x4452, 0x45f6, 0x43e4, 0x3baf, 0x423b, 0x4626,
518 0x4237, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* InstallExecuteSequence */
519 static const WCHAR t2_name7[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
520 static const WCHAR t2_name8[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
521 static const WCHAR t2_name9[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
522 0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
523 0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
525 static const WCHAR t2_data0[] = { /* File */
526 0x00c0, 0x0001, 0x9000, 0x83e8
528 static const WCHAR t2_data1[] = { /* Media */
529 0x0601, 0x8002, 0x03e9, 0x8000, 0x0000, 0x0007, 0x0000, 0x0008
531 static const WCHAR t2_data2[] = { /* _Columns */
532 0x0401, 0x0009, 0x0000, 0x000a, 0xad48, 0x0401, 0x0009, 0x0000, /* 0x0401 = add row (1), 4 shorts */
533 0x000b, 0xa502, 0x0401, 0x0009, 0x0000, 0x000c, 0x8104, 0x0401,
534 0x0009, 0x0000, 0x000d, 0x8502, 0x0401, 0x0009, 0x0000, 0x000e,
535 0x9900, 0x0401, 0x0009, 0x0000, 0x000f, 0x9d48, 0x0401, 0x0010,
536 0x0000, 0x0011, 0xad26, 0x0401, 0x0010, 0x0000, 0x0012, 0x8502,
537 0x0401, 0x0014, 0x0000, 0x0015, 0xad26, 0x0401, 0x0014, 0x0000,
538 0x000e, 0x8900
540 static const WCHAR t2_data3[] = { /* _Tables */
541 0x0101, 0x0009, 0x0101, 0x0010, 0x0101, 0x0014
543 static const WCHAR t2_data4[] = { /* Property */
544 0x0201, 0x0002, 0x0003, 0x0201, 0x0004, 0x0005
546 static const WCHAR t2_data5[] = { /* PatchPackage */
547 0x0201, 0x0013, 0x8002
549 static const WCHAR t2_data6[] = { /* InstallExecuteSequence */
550 0x0301, 0x0006, 0x0000, 0x87d1
552 static const char t2_data7[] = { /* _StringData */
553 "patch.txtPATCHNEWSUMMARYSUBJECTInstallation DatabasePATCHNEWPACKAGECODE{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}"
554 "PatchFiles#CAB_msitestpropPatchFile_SequencePatchSizeAttributesHeaderStreamRef_PatchPackagePatchIdMedia_"
555 "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}MsiPatchHeadersStreamRef"
557 static const WCHAR t2_data8[] = { /* _StringPool */
558 /* len, refs */
559 0, 0, /* string 0 '' */
560 9, 1, /* string 1 'patch.txt' */
561 22, 1, /* string 2 'PATCHNEWSUMMARYSUBJECT' */
562 21, 1, /* string 3 'Installation Database' */
563 19, 1, /* string 4 'PATCHNEWPACKAGECODE' */
564 38, 1, /* string 5 '{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}' */
565 10, 1, /* string 6 'PatchFiles' */
566 12, 1, /* string 7 '#CAB_msitest' */
567 4, 1, /* string 8 'prop' */
568 5, 7, /* string 9 'Patch' */
569 5, 1, /* string 10 'File_' */
570 8, 1, /* string 11 'Sequence' */
571 9, 1, /* string 12 'PatchSize' */
572 10, 1, /* string 13 'Attributes' */
573 6, 2, /* string 14 'Header' */
574 10, 1, /* string 15 'StreamRef_' */
575 12, 3, /* string 16 'PatchPackage' */
576 7, 1, /* string 17 'PatchId' */
577 6, 1, /* string 18 'Media_' */
578 38, 1, /* string 19 '{0F96CDC0-4CDF-4304-B283-7B9264889EF7}' */
579 15, 3, /* string 20 'MsiPatchHeaders' */
580 9, 1 /* string 21 'StreamRef' */
582 static const char t2_data9[] = { /* SummaryInformation */
583 0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
584 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
585 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
586 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
587 0x30, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00,
588 0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00,
589 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x78,
590 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
591 0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
592 0x00, 0x9c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa8, 0x00,
593 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x09,
594 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
595 0x4c, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00,
596 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
597 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
598 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
599 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
600 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00,
601 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01,
602 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
603 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00,
604 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
605 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b,
606 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
607 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
608 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
609 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
610 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
611 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
612 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
613 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
614 0x41, 0x32, 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45,
615 0x32, 0x43, 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30,
616 0x39, 0x2d, 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35,
617 0x46, 0x34, 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2d,
618 0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x27, 0x09
621 static const struct table_data table_transform2_data[] = {
622 { t2_name0, t2_data0, sizeof t2_data0 },
623 { t2_name1, t2_data1, sizeof t2_data1 },
624 { t2_name2, t2_data2, sizeof t2_data2 },
625 { t2_name3, t2_data3, sizeof t2_data3 },
626 { t2_name4, t2_data4, sizeof t2_data4 },
627 { t2_name5, t2_data5, sizeof t2_data5 },
628 { t2_name6, t2_data6, sizeof t2_data6 },
629 { t2_name7, t2_data7, sizeof t2_data7 - 1 },
630 { t2_name8, t2_data8, sizeof t2_data8 },
631 { t2_name9, t2_data9, sizeof t2_data9 }
634 #define NUM_TRANSFORM2_TABLES (sizeof table_transform2_data/sizeof table_transform2_data[0])
636 static void write_tables( IStorage *stg, const struct table_data *tables, UINT num_tables )
638 IStream *stm;
639 DWORD i, count;
640 HRESULT r;
642 for (i = 0; i < num_tables; i++)
644 r = IStorage_CreateStream( stg, tables[i].name, STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
645 if (FAILED( r ))
647 ok( 0, "failed to create stream 0x%08x\n", r );
648 continue;
651 r = IStream_Write( stm, tables[i].data, tables[i].size, &count );
652 if (FAILED( r ) || count != tables[i].size)
653 ok( 0, "failed to write stream\n" );
654 IStream_Release( stm );
658 static void create_patch( const char *filename )
660 IStorage *stg = NULL, *stg1 = NULL, *stg2 = NULL;
661 WCHAR *filenameW;
662 HRESULT r;
663 int len;
664 const DWORD mode = STGM_CREATE|STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE;
666 const CLSID CLSID_MsiPatch = {0xc1086, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
667 const CLSID CLSID_MsiTransform = {0xc1082, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
669 len = MultiByteToWideChar( CP_ACP, 0, filename, -1, NULL, 0 );
670 filenameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
671 MultiByteToWideChar( CP_ACP, 0, filename, -1, filenameW, len );
673 r = StgCreateDocfile( filenameW, mode, 0, &stg );
674 HeapFree( GetProcessHeap(), 0, filenameW );
675 ok( r == S_OK, "failed to create storage 0x%08x\n", r );
676 if (!stg)
677 return;
679 r = IStorage_SetClass( stg, &CLSID_MsiPatch );
680 ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
682 write_tables( stg, table_patch_data, NUM_PATCH_TABLES );
684 r = IStorage_CreateStorage( stg, p_name7, mode, 0, 0, &stg1 );
685 ok( r == S_OK, "failed to create substorage 0x%08x\n", r );
687 r = IStorage_SetClass( stg1, &CLSID_MsiTransform );
688 ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
690 write_tables( stg1, table_transform1_data, NUM_TRANSFORM1_TABLES );
691 IStorage_Release( stg1 );
693 r = IStorage_CreateStorage( stg, p_name8, mode, 0, 0, &stg2 );
694 ok( r == S_OK, "failed to create substorage 0x%08x\n", r );
696 r = IStorage_SetClass( stg2, &CLSID_MsiTransform );
697 ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
699 write_tables( stg2, table_transform2_data, NUM_TRANSFORM2_TABLES );
700 IStorage_Release( stg2 );
701 IStorage_Release( stg );
704 static void test_simple_patch( void )
706 UINT r;
707 DWORD size;
708 char path[MAX_PATH], install_source[MAX_PATH], buffer[32];
709 const char *query;
710 MSIHANDLE hpackage, hdb, hview, hrec;
712 if (!pMsiApplyPatchA)
714 win_skip("MsiApplyPatchA is not available\n");
715 return;
717 if (is_process_limited())
719 skip("process is limited\n");
720 return;
723 CreateDirectoryA( "msitest", NULL );
724 create_file( "msitest\\patch.txt", 1000 );
726 create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
727 create_patch( mspfile );
729 MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
731 r = MsiInstallProductA( msifile, NULL );
732 if (r != ERROR_SUCCESS)
734 skip("Product installation failed with error code %u\n", r);
735 goto cleanup;
738 size = get_pf_file_size( "msitest\\patch.txt" );
739 ok( size == 1000, "expected 1000, got %u\n", size );
741 size = sizeof(install_source);
742 r = MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
743 "InstallSource", install_source, &size );
744 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
746 strcpy( path, CURR_DIR );
747 strcat( path, "\\" );
748 strcat( path, msifile );
750 r = MsiOpenPackageA( path, &hpackage );
751 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
753 hdb = MsiGetActiveDatabase( hpackage );
754 ok( hdb, "failed to get database handle\n" );
756 query = "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
757 r = MsiDatabaseOpenView( hdb, query, &hview );
758 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
760 r = MsiViewExecute( hview, 0 );
761 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
763 r = MsiViewFetch( hview, &hrec );
764 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
766 MsiCloseHandle( hrec );
767 MsiViewClose( hview );
768 MsiCloseHandle( hview );
770 query = "SELECT * FROM `Property` WHERE `Property` = 'PATCHNEWSUMMARYSUBJECT' "
771 "AND `Value` = 'Installer Database'";
772 r = MsiDatabaseOpenView( hdb, query, &hview );
773 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
775 r = MsiViewExecute( hview, 0 );
776 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
778 r = MsiViewFetch( hview, &hrec );
779 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
781 MsiCloseHandle( hrec );
782 MsiViewClose( hview );
783 MsiCloseHandle( hview );
785 buffer[0] = 0;
786 size = sizeof(buffer);
787 r = MsiGetProperty( hpackage, "PATCHNEWSUMMARYSUBJECT", buffer, &size );
788 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
789 ok( !strcmp( buffer, "Installer Database" ), "expected \'Installer Database\', got \'%s\'\n", buffer );
791 MsiCloseHandle( hdb );
792 MsiCloseHandle( hpackage );
794 r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
795 ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
796 "expected ERROR_SUCCESS, got %u\n", r );
798 if (r == ERROR_PATCH_PACKAGE_INVALID)
800 win_skip("Windows Installer < 3.0 detected\n");
801 goto uninstall;
804 size = get_pf_file_size( "msitest\\patch.txt" );
805 ok( size == 1002, "expected 1002, got %u\n", size );
807 /* show that MsiOpenPackage applies registered patches */
808 r = MsiOpenPackageA( path, &hpackage );
809 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
811 hdb = MsiGetActiveDatabase( hpackage );
812 ok( hdb, "failed to get database handle\n" );
814 query = "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
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_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
824 MsiCloseHandle( hrec );
825 MsiViewClose( hview );
826 MsiCloseHandle( hview );
828 query = "SELECT * FROM `Property` WHERE `Property` = 'PATCHNEWSUMMARYSUBJECT' "
829 "AND `Value` = 'Installation Database'";
830 r = MsiDatabaseOpenView( hdb, query, &hview );
831 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
833 r = MsiViewExecute( hview, 0 );
834 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
836 r = MsiViewFetch( hview, &hrec );
837 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
839 MsiCloseHandle( hrec );
840 MsiViewClose( hview );
841 MsiCloseHandle( hview );
843 buffer[0] = 0;
844 size = sizeof(buffer);
845 r = MsiGetProperty( hpackage, "PATCHNEWSUMMARYSUBJECT", buffer, &size );
846 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
847 ok( !strcmp( buffer, "Installation Database" ), "expected \'Installation Database\', got \'%s\'\n", buffer );
849 MsiCloseHandle( hdb );
850 MsiCloseHandle( hpackage );
852 /* show that patches are not committed to the local package database */
853 size = sizeof(path);
854 r = MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
855 "LocalPackage", path, &size );
856 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
858 r = MsiOpenDatabaseA( path, MSIDBOPEN_READONLY, &hdb );
859 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
861 r = MsiDatabaseOpenView( hdb, query, &hview );
862 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
864 r = MsiViewExecute( hview, 0 );
865 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
867 r = MsiViewFetch( hview, &hrec );
868 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
870 MsiCloseHandle( hrec );
871 MsiViewClose( hview );
872 MsiCloseHandle( hview );
873 MsiCloseHandle( hdb );
875 uninstall:
876 size = sizeof(path);
877 r = MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
878 "InstallSource", path, &size );
879 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
880 ok( !strcasecmp( path, install_source ), "wrong path %s\n", path );
882 r = MsiInstallProductA( msifile, "REMOVE=ALL" );
883 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
885 ok( !delete_pf( "msitest\\patch.txt", TRUE ), "file not removed\n" );
886 ok( !delete_pf( "msitest", FALSE ), "directory not removed\n" );
888 cleanup:
889 DeleteFileA( msifile );
890 DeleteFileA( mspfile );
891 DeleteFileA( "msitest\\patch.txt" );
892 RemoveDirectoryA( "msitest" );
895 static void test_MsiOpenDatabase( void )
897 UINT r;
898 MSIHANDLE hdb;
900 r = MsiOpenDatabase( mspfile, MSIDBOPEN_CREATE, &hdb );
901 ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
903 r = MsiDatabaseCommit( hdb );
904 ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
905 MsiCloseHandle( hdb );
907 r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
908 ok(r == ERROR_OPEN_FAILED, "expected ERROR_OPEN_FAILED, got %u\n", r);
909 DeleteFileA( mspfile );
911 r = MsiOpenDatabase( mspfile, MSIDBOPEN_CREATE + MSIDBOPEN_PATCHFILE, &hdb );
912 ok(r == ERROR_SUCCESS , "failed to open database %u\n", r);
914 r = MsiDatabaseCommit( hdb );
915 ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
916 MsiCloseHandle( hdb );
918 r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
919 ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
920 MsiCloseHandle( hdb );
921 DeleteFileA( mspfile );
923 create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
924 create_patch( mspfile );
926 r = MsiOpenDatabase( msifile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
927 ok(r == ERROR_OPEN_FAILED, "failed to open database %u\n", r );
929 r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
930 ok(r == ERROR_SUCCESS, "failed to open database %u\n", r );
931 MsiCloseHandle( hdb );
933 DeleteFileA( msifile );
934 DeleteFileA( mspfile );
937 static UINT find_entry( MSIHANDLE hdb, const char *table, const char *entry )
939 static char fmt[] = "SELECT * FROM `%s` WHERE `Name` = '%s'";
940 char query[0x100];
941 UINT r;
942 MSIHANDLE hview, hrec;
944 sprintf( query, fmt, table, entry );
945 r = MsiDatabaseOpenView( hdb, query, &hview );
946 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
948 r = MsiViewExecute( hview, 0 );
949 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
951 r = MsiViewFetch( hview, &hrec );
952 MsiViewClose( hview );
953 MsiCloseHandle( hview );
954 MsiCloseHandle( hrec );
955 return r;
958 static INT get_integer( MSIHANDLE hdb, UINT field, const char *query)
960 UINT r;
961 INT ret = -1;
962 MSIHANDLE hview, hrec;
964 r = MsiDatabaseOpenView( hdb, query, &hview );
965 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
967 r = MsiViewExecute( hview, 0 );
968 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
970 r = MsiViewFetch( hview, &hrec );
971 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
972 if (r == ERROR_SUCCESS)
974 UINT r_tmp;
975 ret = MsiRecordGetInteger( hrec, field );
976 MsiCloseHandle( hrec );
978 r_tmp = MsiViewFetch( hview, &hrec );
979 ok( r_tmp == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r);
982 MsiViewClose( hview );
983 MsiCloseHandle( hview );
984 return ret;
987 static char *get_string( MSIHANDLE hdb, UINT field, const char *query)
989 UINT r;
990 static char ret[MAX_PATH];
991 MSIHANDLE hview, hrec;
993 ret[0] = '\0';
995 r = MsiDatabaseOpenView( hdb, query, &hview );
996 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
998 r = MsiViewExecute( hview, 0 );
999 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1001 r = MsiViewFetch( hview, &hrec );
1002 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1003 if (r == ERROR_SUCCESS)
1005 UINT size = MAX_PATH;
1006 r = MsiRecordGetStringA( hrec, field, ret, &size );
1007 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
1008 MsiCloseHandle( hrec );
1010 r = MsiViewFetch( hview, &hrec );
1011 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r);
1014 MsiViewClose( hview );
1015 MsiCloseHandle( hview );
1016 return ret;
1019 static void test_system_tables( void )
1021 UINT r;
1022 char *cr;
1023 const char *query;
1024 MSIHANDLE hproduct, hdb, hview, hrec;
1025 static const char patchsource[] = "MSPSRC0F96CDC04CDF4304B2837B9264889EF7";
1027 if (!pMsiApplyPatchA)
1029 win_skip("MsiApplyPatchA is not available\n");
1030 return;
1032 if (is_process_limited())
1034 skip("process is limited\n");
1035 return;
1038 CreateDirectoryA( "msitest", NULL );
1039 create_file( "msitest\\patch.txt", 1000 );
1041 create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
1042 create_patch( mspfile );
1044 MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
1046 r = MsiInstallProductA( msifile, NULL );
1047 if (r != ERROR_SUCCESS)
1049 skip("Product installation failed with error code %d\n", r);
1050 goto cleanup;
1053 r = MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct );
1054 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1056 hdb = MsiGetActiveDatabase( hproduct );
1057 ok( hdb, "failed to get database handle\n" );
1059 r = find_entry( hdb, "_Streams", "\5SummaryInformation" );
1060 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1062 query = "SELECT * FROM `_Storages`";
1063 r = MsiDatabaseOpenView( hdb, query, &hview );
1064 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1066 r = MsiViewExecute( hview, 0 );
1067 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1069 r = MsiViewFetch( hview, &hrec );
1070 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
1072 r = find_entry( hdb, "_Tables", "Directory" );
1073 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1075 r = find_entry( hdb, "_Tables", "File" );
1076 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1078 r = find_entry( hdb, "_Tables", "Component" );
1079 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1081 r = find_entry( hdb, "_Tables", "Feature" );
1082 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1084 r = find_entry( hdb, "_Tables", "FeatureComponents" );
1085 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1087 r = find_entry( hdb, "_Tables", "Property" );
1088 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1090 r = find_entry( hdb, "_Tables", "InstallExecuteSequence" );
1091 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1093 r = find_entry( hdb, "_Tables", "Media" );
1094 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1096 r = get_integer( hdb, 1, "SELECT * FROM `Media` WHERE `VolumeLabel`=\'DISK1\'");
1097 ok( r == 1, "Got %u\n", r );
1099 r = find_entry( hdb, "_Tables", "_Property" );
1100 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1102 MsiCloseHandle( hrec );
1103 MsiViewClose( hview );
1104 MsiCloseHandle( hview );
1105 MsiCloseHandle( hdb );
1106 MsiCloseHandle( hproduct );
1108 r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
1109 ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
1110 "expected ERROR_SUCCESS, got %u\n", r );
1112 if (r == ERROR_PATCH_PACKAGE_INVALID)
1114 win_skip("Windows Installer < 3.0 detected\n");
1115 goto uninstall;
1118 r = MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct );
1119 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1121 hdb = MsiGetActiveDatabase( hproduct );
1122 ok( hdb, "failed to get database handle\n" );
1124 r = find_entry( hdb, "_Streams", "\5SummaryInformation" );
1125 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1127 query = "SELECT * FROM `_Storages`";
1128 r = MsiDatabaseOpenView( hdb, query, &hview );
1129 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1131 r = MsiViewExecute( hview, 0 );
1132 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1134 r = MsiViewFetch( hview, &hrec );
1135 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
1137 r = find_entry( hdb, "_Tables", "Directory" );
1138 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1140 r = find_entry( hdb, "_Tables", "File" );
1141 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1143 r = find_entry( hdb, "_Tables", "Component" );
1144 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1146 r = find_entry( hdb, "_Tables", "Feature" );
1147 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1149 r = find_entry( hdb, "_Tables", "FeatureComponents" );
1150 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1152 r = find_entry( hdb, "_Tables", "Property" );
1153 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1155 r = find_entry( hdb, "_Tables", "InstallExecuteSequence" );
1156 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1158 r = find_entry( hdb, "_Tables", "Media" );
1159 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1161 r = find_entry( hdb, "_Tables", "_Property" );
1162 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1164 r = find_entry( hdb, "_Tables", "MsiPatchHeaders" );
1165 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1167 r = find_entry( hdb, "_Tables", "Patch" );
1168 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1170 r = find_entry( hdb, "_Tables", "PatchPackage" );
1171 ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1173 cr = get_string( hdb, 6, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL");
1174 todo_wine ok( !strcmp(cr, patchsource), "Expected \"%s\", got \"%s\"\n", patchsource, cr );
1176 r = get_integer( hdb, 1, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL");
1177 todo_wine ok( r == 100, "Got %u\n", r );
1179 r = get_integer( hdb, 2, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL");
1180 todo_wine ok( r == 10000, "Got %u\n", r );
1182 r = get_integer( hdb, 1, "SELECT * FROM `Media` WHERE `VolumeLabel`=\'DISK1\'");
1183 ok( r == 1, "Got %u\n", r );
1185 cr = get_string( hdb, 4, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL");
1186 ok( !strcmp(cr, "#CAB_msitest"), "Expected \"#CAB_msitest\", got \"%s\"\n", cr );
1188 r = get_integer( hdb, 8, "SELECT * FROM `File` WHERE `File` = 'patch.txt'");
1189 ok( r == 10000, "Got %u\n", r );
1191 MsiCloseHandle( hrec );
1192 MsiViewClose( hview );
1193 MsiCloseHandle( hview );
1194 MsiCloseHandle( hdb );
1195 MsiCloseHandle( hproduct );
1197 uninstall:
1198 r = MsiInstallProductA( msifile, "REMOVE=ALL" );
1199 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1201 cleanup:
1202 DeleteFileA( msifile );
1203 DeleteFileA( mspfile );
1204 DeleteFileA( "msitest\\patch.txt" );
1205 RemoveDirectoryA( "msitest" );
1208 static void test_patch_registration( void )
1210 UINT r, size;
1211 char buffer[MAX_PATH], patch_code[39];
1213 if (!pMsiApplyPatchA || !pMsiGetPatchInfoExA || !pMsiEnumPatchesExA)
1215 win_skip("required functions not available\n");
1216 return;
1218 if (is_process_limited())
1220 skip("process is limited\n");
1221 return;
1224 CreateDirectoryA( "msitest", NULL );
1225 create_file( "msitest\\patch.txt", 1000 );
1227 create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
1228 create_patch( mspfile );
1230 MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
1232 r = MsiInstallProductA( msifile, NULL );
1233 if (r != ERROR_SUCCESS)
1235 skip("Product installation failed with error code %d\n", r);
1236 goto cleanup;
1239 r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
1240 ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
1241 "expected ERROR_SUCCESS, got %u\n", r );
1243 if (r == ERROR_PATCH_PACKAGE_INVALID)
1245 win_skip("Windows Installer < 3.0 detected\n");
1246 goto uninstall;
1249 buffer[0] = 0;
1250 size = sizeof(buffer);
1251 r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1252 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1253 NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
1254 INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1255 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1256 ok( buffer[0], "buffer empty\n" );
1258 buffer[0] = 0;
1259 size = sizeof(buffer);
1260 r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1261 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1262 NULL, MSIINSTALLCONTEXT_MACHINE,
1263 INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1264 ok( r == ERROR_UNKNOWN_PRODUCT, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r );
1266 buffer[0] = 0;
1267 size = sizeof(buffer);
1268 r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1269 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1270 NULL, MSIINSTALLCONTEXT_USERMANAGED,
1271 INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1272 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1273 ok( !buffer[0], "got %s\n", buffer );
1275 r = pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1276 NULL, MSIINSTALLCONTEXT_USERUNMANAGED, MSIPATCHSTATE_APPLIED,
1277 0, patch_code, NULL, NULL, NULL, NULL );
1278 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1279 ok( !strcmp( patch_code, "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}" ), "wrong patch code\n" );
1281 r = pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1282 NULL, MSIINSTALLCONTEXT_MACHINE, MSIPATCHSTATE_APPLIED,
1283 0, patch_code, NULL, NULL, NULL, NULL );
1284 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
1286 r = pMsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1287 NULL, MSIINSTALLCONTEXT_USERMANAGED, MSIPATCHSTATE_APPLIED,
1288 0, patch_code, NULL, NULL, NULL, NULL );
1289 ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
1291 uninstall:
1292 r = MsiInstallProductA( msifile, "REMOVE=ALL" );
1293 ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1295 buffer[0] = 0;
1296 size = sizeof(buffer);
1297 r = pMsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1298 "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1299 NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
1300 INSTALLPROPERTY_LOCALPACKAGE, buffer, &size );
1301 ok( r == ERROR_UNKNOWN_PRODUCT, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r );
1303 cleanup:
1304 DeleteFileA( msifile );
1305 DeleteFileA( mspfile );
1306 DeleteFileA( "msitest\\patch.txt" );
1307 RemoveDirectoryA( "msitest" );
1310 START_TEST(patch)
1312 DWORD len;
1313 char temp_path[MAX_PATH], prev_path[MAX_PATH];
1315 init_function_pointers();
1317 GetCurrentDirectoryA( MAX_PATH, prev_path );
1318 GetTempPath( MAX_PATH, temp_path );
1319 SetCurrentDirectoryA( temp_path );
1321 strcpy( CURR_DIR, temp_path );
1322 len = strlen( CURR_DIR );
1324 if (len && (CURR_DIR[len - 1] == '\\'))
1325 CURR_DIR[len - 1] = 0;
1327 get_program_files_dir( PROG_FILES_DIR, COMMON_FILES_DIR );
1329 test_simple_patch();
1330 test_MsiOpenDatabase();
1331 test_system_tables();
1332 test_patch_registration();
1334 SetCurrentDirectoryA( prev_path );