xref: /reactos/modules/rostests/winetests/msi/patch.c (revision f4be6dc3)
1 /*
2  * Copyright 2010 Hans Leidekker for CodeWeavers
3  *
4  * A test program for patching MSI products.
5  *
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.
10  *
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.
15  *
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
19  */
20 
21 #define _WIN32_MSI 300
22 #define COBJMACROS
23 
24 #include <stdio.h>
25 
26 #include <windows.h>
27 #include <msiquery.h>
28 #include <msidefs.h>
29 #include <msi.h>
30 #include <wtypes.h>
31 
32 #include "wine/test.h"
33 #include "utils.h"
34 
35 static const char *msifile = "winetest-patch.msi";
36 static const char *mspfile = "winetest-patch.msp";
37 static const WCHAR msifileW[] = L"winetest-patch.msi";
38 static const WCHAR mspfileW[] = L"winetest-patch.msp";
39 
40 /* msi database data */
41 
42 static const char property_dat[] =
43     "Property\tValue\n"
44     "s72\tl0\n"
45     "Property\tProperty\n"
46     "Manufacturer\tWineHQ\n"
47     "ProductCode\t{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}\n"
48     "UpgradeCode\t{A2E3D643-4E2C-477F-A309-F76F552D5F43}\n"
49     "ProductLanguage\t1033\n"
50     "ProductName\tmsitest\n"
51     "ProductVersion\t1.1.1\n"
52     "PATCHNEWSUMMARYSUBJECT\tInstaller Database\n"
53     "MSIFASTINSTALL\t1\n";
54 
55 static const char media_dat[] =
56     "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
57     "i2\ti4\tL64\tS255\tS32\tS72\n"
58     "Media\tDiskId\n"
59     "1\t1\t\t\tDISK1\t\n";
60 
61 static const char file_dat[] =
62     "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
63     "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
64     "File\tFile\n"
65     "patch.txt\tpatch\tpatch.txt\t1000\t\t\t0\t1\n"
66     "disable.txt\tdisable\tdisable.txt\t1000\t\t\t0\t1\n";
67 
68 static const char directory_dat[] =
69     "Directory\tDirectory_Parent\tDefaultDir\n"
70     "s72\tS72\tl255\n"
71     "Directory\tDirectory\n"
72     "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
73     "ProgramFilesFolder\tTARGETDIR\t.\n"
74     "TARGETDIR\t\tSourceDir";
75 
76 static const char component_dat[] =
77     "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
78     "s72\tS38\ts72\ti2\tS255\tS72\n"
79     "Component\tComponent\n"
80     "patch\t{4B79D87E-6D28-4FD3-92D6-CD9B26AF64F1}\tMSITESTDIR\t0\t\tpatch.txt\n"
81     "disable\t{BDDBA0EE-0031-4591-ADC0-33308175AC19}\tMSITESTDIR\t0\t\tdisable.txt\n";
82 
83 static const char feature_dat[] =
84     "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
85     "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
86     "Feature\tFeature\n"
87     "patch\t\t\tpatch feature\t1\t1\tMSITESTDIR\t0\n"
88     "disable\t\t\tdisabled feature\t1\t1\tMSITESTDIR\t0\n";
89 
90 static const char feature_comp_dat[] =
91     "Feature_\tComponent_\n"
92     "s38\ts72\n"
93     "FeatureComponents\tFeature_\tComponent_\n"
94     "patch\tpatch\n"
95     "disable\tdisable\n";
96 
97 static const char install_exec_seq_dat[] =
98     "Action\tCondition\tSequence\n"
99     "s72\tS255\tI2\n"
100     "InstallExecuteSequence\tAction\n"
101     "LaunchConditions\t\t100\n"
102     "CostInitialize\t\t800\n"
103     "FileCost\t\t900\n"
104     "CostFinalize\t\t1000\n"
105     "InstallValidate\t\t1400\n"
106     "InstallInitialize\t\t1500\n"
107     "ProcessComponents\t\t1600\n"
108     "RemoveFiles\t\t1700\n"
109     "InstallFiles\t\t2000\n"
110     "RegisterUser\t\t3000\n"
111     "RegisterProduct\t\t3100\n"
112     "PublishFeatures\t\t5100\n"
113     "PublishProduct\t\t5200\n"
114     "UnpublishFeatures\t\t5300\n"
115     "InstallFinalize\t\t6000\n";
116 
117 static const char condition_dat[] =
118     "Feature_\tLevel\tCondition\n"
119     "s38\ti2\tS255\n"
120     "Condition\tFeature_\tLevel\n"
121     "disable\t0\tDISABLE_FEATURE\n";
122 
123 struct msi_table
124 {
125     const char *filename;
126     const char *data;
127     int size;
128 };
129 
130 static const struct msi_table tables[] =
131 {
132     ADD_TABLE( directory ),
133     ADD_TABLE( file ),
134     ADD_TABLE( component ),
135     ADD_TABLE( feature ),
136     ADD_TABLE( feature_comp ),
137     ADD_TABLE( property ),
138     ADD_TABLE( install_exec_seq ),
139     ADD_TABLE( media ),
140     ADD_TABLE( condition )
141 };
142 
get_program_files_dir(char * buf,char * buf2)143 static BOOL get_program_files_dir( char *buf, char *buf2 )
144 {
145     HKEY hkey;
146     DWORD type, size;
147 
148     if (RegOpenKeyA( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &hkey ))
149         return FALSE;
150 
151     size = MAX_PATH;
152     if (RegQueryValueExA( hkey, "ProgramFilesDir (x86)", 0, &type, (LPBYTE)buf, &size ) &&
153         RegQueryValueExA( hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size ))
154     {
155         RegCloseKey( hkey );
156         return FALSE;
157     }
158     size = MAX_PATH;
159     if (RegQueryValueExA( hkey, "CommonFilesDir", 0, &type, (LPBYTE)buf2, &size ))
160     {
161         RegCloseKey( hkey );
162         return FALSE;
163     }
164     RegCloseKey( hkey );
165     return TRUE;
166 }
167 
get_pf_file_size(const char * filename)168 static DWORD get_pf_file_size( const char *filename )
169 {
170     char path[MAX_PATH];
171     HANDLE file;
172     DWORD size;
173 
174     strcpy( path, PROG_FILES_DIR );
175     strcat( path, "\\");
176     strcat( path, filename );
177 
178     file = CreateFileA( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
179     if (file == INVALID_HANDLE_VALUE)
180         return INVALID_FILE_SIZE;
181 
182     size = GetFileSize( file, NULL );
183     CloseHandle( file );
184     return size;
185 }
186 
write_file(const char * filename,const char * data,DWORD data_size)187 static void write_file( const char *filename, const char *data, DWORD data_size )
188 {
189     DWORD size;
190     HANDLE file = CreateFileA( filename, GENERIC_WRITE, 0, NULL,
191                                CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
192     WriteFile( file, data, data_size, &size, NULL );
193     CloseHandle( file );
194 }
195 
set_suminfo(const WCHAR * filename)196 static void set_suminfo( const WCHAR *filename )
197 {
198     UINT r;
199     MSIHANDLE hsi, hdb;
200 
201     r = MsiOpenDatabaseW( filename, MSIDBOPEN_DIRECT, &hdb );
202     ok( r == ERROR_SUCCESS, "failed to open database %u\n", r );
203 
204     r = MsiGetSummaryInformationA( hdb, NULL, 7, &hsi );
205     ok( r == ERROR_SUCCESS, "failed to open summaryinfo %u\n", r );
206 
207     r = MsiSummaryInfoSetPropertyA( hsi, 2, VT_LPSTR, 0, NULL, "Installation Database" );
208     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
209 
210     r = MsiSummaryInfoSetPropertyA( hsi, 3, VT_LPSTR, 0, NULL, "Installation Database" );
211     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
212 
213     r = MsiSummaryInfoSetPropertyA( hsi, 4, VT_LPSTR, 0, NULL, "WineHQ" );
214     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
215 
216     r = MsiSummaryInfoSetPropertyA( hsi, 7, VT_LPSTR, 0, NULL, ";1033" );
217     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
218 
219     r = MsiSummaryInfoSetPropertyA( hsi, 9, VT_LPSTR, 0, NULL, "{E528DDD6-4801-4BEC-BBB6-C5EE0FD097E9}" );
220     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
221 
222     r = MsiSummaryInfoSetPropertyA( hsi, 14, VT_I4, 100, NULL, NULL );
223     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
224 
225     r = MsiSummaryInfoSetPropertyA( hsi, 15, VT_I4, 0, NULL, NULL );
226     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
227 
228     r = MsiSummaryInfoPersist( hsi );
229     ok( r == ERROR_SUCCESS, "failed to persist suminfo %u\n", r );
230 
231     r = MsiCloseHandle( hsi );
232     ok( r == ERROR_SUCCESS, "failed to close suminfo %u\n", r );
233 
234     r = MsiCloseHandle( hdb );
235     ok( r == ERROR_SUCCESS, "failed to close database %u\n", r );
236 }
237 
create_database_suminfo(const char * filename,const struct msi_table * tables,UINT num_tables)238 static void create_database_suminfo( const char *filename, const struct msi_table *tables, UINT num_tables )
239 {
240     MSIHANDLE hdb;
241     UINT r, i;
242     WCHAR *filenameW;
243     int len;
244 
245     len = MultiByteToWideChar( CP_ACP, 0, filename, -1, NULL, 0 );
246     if (!(filenameW = malloc( len * sizeof(WCHAR) ))) return;
247     MultiByteToWideChar( CP_ACP, 0, filename, -1, filenameW, len );
248 
249     r = MsiOpenDatabaseW( filenameW, MSIDBOPEN_CREATE, &hdb );
250     ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
251 
252     /* import the tables into the database */
253     for (i = 0; i < num_tables; i++)
254     {
255         const struct msi_table *table = &tables[i];
256 
257         write_file( table->filename, table->data, (table->size - 1) * sizeof(char) );
258 
259         r = MsiDatabaseImportA( hdb, CURR_DIR, table->filename );
260         ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
261 
262         DeleteFileA( table->filename );
263     }
264 
265     r = MsiDatabaseCommit( hdb );
266     ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
267 
268     MsiCloseHandle( hdb );
269     set_suminfo( filenameW );
270     free( filenameW );
271 }
272 
273 /* data for generating a patch */
274 
275 /* table names - encoded as in an msi database file */
276 static const WCHAR p_name0[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
277 static const WCHAR p_name1[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
278 static const WCHAR p_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
279 static const WCHAR p_name3[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
280 static const WCHAR p_name4[] = { 0x3a8c, 0x47cb, 0x45b0, 0x45ec, 0x45a8, 0x4837, 0}; /* CAB_msitest */
281 static const WCHAR p_name5[] = { 0x4840, 0x4596, 0x3e6c, 0x45e4, 0x42e6, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* MsiPatchSequence */
282 static const WCHAR p_name6[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
283                                  0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
284                                  0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
285 /* substorage names */
286 static const WCHAR p_name7[] = { 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075, 0x0070,
287                                  0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* targetToupgraded */
288 static const WCHAR p_name8[] = { 0x0023, 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075,
289                                  0x0070, 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* #targetToupgraded */
290 
291 /* data in each table */
292 static const WCHAR p_data0[] = { /* _Columns */
293     0x0001, 0x0001, 0x0001, 0x0001, 0x8001, 0x8002, 0x8003, 0x8004,
294     0x0002, 0x0003, 0x0004, 0x0005, 0xad00, 0xbd26, 0x8d00, 0x9502
295 };
296 static const WCHAR p_data1[] = { /* _Tables */
297     0x0001
298 };
299 static const char p_data2[] = { /* _StringData */
300     "MsiPatchSequencePatchFamilyProductCodeSequenceAttributes1.1.19388.37230913B8D18FBB64CACA239C74C11E3FA74"
301 };
302 static const WCHAR p_data3[] = { /* _StringPool */
303 /* len, refs */
304      0,  0,     /* string 0 '' */
305     16,  5,     /* string 1 'MsiPatchSequence' */
306     11,  1,     /* string 2 'PatchFamily' */
307     11,  1,     /* string 3 'ProductCode' */
308      8,  1,     /* string 4 'Sequence' */
309     10,  1,     /* string 5 'Attributes' */
310     15,  1,     /* string 6 '1.1.19388.37230' */
311     32,  1,     /* string 7 '913B8D18FBB64CACA239C74C11E3FA74' */
312 };
313 static const char p_data4[] = { /* CAB_msitest */
314     0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, 0x00,
315     0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00,
316     0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
317     0x00, 0x00, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
318     0xea, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
319     0x3c, 0xd4, 0xb8, 0x20, 0x00, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e,
320     0x74, 0x78, 0x74, 0x00, 0xe8, 0x03, 0x00, 0x00, 0xea, 0x03, 0x00,
321     0x00, 0x00, 0x00, 0xcb, 0x50, 0x17, 0x7e, 0x20, 0x00, 0x66, 0x69,
322     0x6c, 0x65, 0x2e, 0x74, 0x78, 0x74, 0x00, 0xb0, 0xb2, 0xb2, 0x25,
323     0x2d, 0x00, 0xd2, 0x07, 0x43, 0x4b, 0xcb, 0x2d, 0xce, 0x2c, 0x49,
324     0x2d, 0x2e, 0x89, 0x29, 0x48, 0x2c, 0x49, 0xce, 0x48, 0x4d, 0xd1,
325     0x2b, 0xa9, 0x28, 0x51, 0x18, 0x05, 0xa3, 0x60, 0x14, 0x0c, 0x37,
326     0x90, 0x8b, 0x9c, 0xd3, 0x41, 0xf9, 0x9c, 0x61, 0x14, 0x8c, 0x82,
327     0x51, 0x30, 0xdc, 0x00, 0x00
328 };
329 static const WCHAR p_data5[] = { /* MsiPatchSequence */
330     0x0007, 0x0000, 0x0006, 0x8000
331 };
332 static const char p_data6[] = { /* SummaryInformation */
333     0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
334     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
336     0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
337     0x30, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
338     0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00,
339     0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x90,
340     0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
341     0x0f, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
342     0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x30, 0x46, 0x39, 0x36, 0x43,
343     0x44, 0x43, 0x30, 0x2d, 0x34, 0x43, 0x44, 0x46, 0x2d, 0x34, 0x33,
344     0x30, 0x34, 0x2d, 0x42, 0x32, 0x38, 0x33, 0x2d, 0x37, 0x42, 0x39,
345     0x32, 0x36, 0x34, 0x38, 0x38, 0x39, 0x45, 0x46, 0x37, 0x7d, 0x00,
346     0x00, 0x1e, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x39,
347     0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42,
348     0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39,
349     0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41,
350     0x37, 0x34, 0x7d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x25, 0x00,
351     0x00, 0x00, 0x3a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f,
352     0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x3b, 0x3a, 0x23,
353     0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f, 0x75, 0x70, 0x67,
354     0x72, 0x61, 0x64, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
355     0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x61, 0x74, 0x63, 0x68,
356     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x00,
357     0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
358 };
359 
360 struct table_data {
361     LPCWSTR name;
362     const void *data;
363     DWORD size;
364 };
365 
366 static const struct table_data table_patch_data[] = {
367     { p_name0, p_data0, sizeof p_data0 },
368     { p_name1, p_data1, sizeof p_data1 },
369     { p_name2, p_data2, sizeof p_data2 - 1 },
370     { p_name3, p_data3, sizeof p_data3 },
371     { p_name4, p_data4, sizeof p_data4 },
372     { p_name5, p_data5, sizeof p_data5 },
373     { p_name6, p_data6, sizeof p_data6 }
374 };
375 
376 static const WCHAR t1_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
377 static const WCHAR t1_name1[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
378 static const WCHAR t1_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
379 static const WCHAR t1_name3[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
380                                   0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
381                                   0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
382 
383 static const WCHAR t1_data0[] = { /* File */
384     0x0008, 0x0001, 0x03ea, 0x8000
385 };
386 static const char t1_data1[] = { /* _StringData */
387     "patch.txt"
388 };
389 static const WCHAR t1_data2[] = { /* _StringPool */
390 /* len, refs */
391      0,  0,     /* string 0 '' */
392      9,  1,     /* string 1 'patch.txt' */
393 };
394 static const char t1_data3[] = { /* SummaryInformation */
395     0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
396     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
397     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
398     0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
399     0x30, 0x00, 0x00, 0x00, 0x9f, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00,
400     0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00,
401     0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8,
402     0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00,
403     0x06, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
404     0x00, 0xd0, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xdc, 0x00,
405     0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x08,
406     0x00, 0x00, 0x00, 0xf7, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
407     0x07, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x8f, 0x01, 0x00,
408     0x00, 0x10, 0x00, 0x00, 0x00, 0x97, 0x01, 0x00, 0x00, 0x1e, 0x00,
409     0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61,
410     0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74,
411     0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
412     0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c,
413     0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74, 0x61,
414     0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
415     0x07, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6e, 0x65, 0x48, 0x51, 0x00,
416     0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
417     0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
418     0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
419     0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
420     0x00, 0x1e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x78, 0x38,
421     0x36, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00, 0x00, 0x1e, 0x00,
422     0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33,
423     0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00,
424     0x7b, 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46,
425     0x42, 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32,
426     0x33, 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33,
427     0x46, 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b,
428     0x7b, 0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46,
429     0x42, 0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32,
430     0x33, 0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33,
431     0x46, 0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b,
432     0x7b, 0x41, 0x32, 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34,
433     0x45, 0x32, 0x43, 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33,
434     0x30, 0x39, 0x2d, 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44,
435     0x35, 0x46, 0x34, 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
436     0x64, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x22,
437     0x09
438 };
439 
440 static const struct table_data table_transform1_data[] = {
441     { t1_name0, t1_data0, sizeof t1_data0 },
442     { t1_name1, t1_data1, sizeof t1_data1 - 1 },
443     { t1_name2, t1_data2, sizeof t1_data2 },
444     { t1_name3, t1_data3, sizeof t1_data3 }
445 };
446 
447 static const WCHAR t2_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
448 static const WCHAR t2_name1[] = { 0x4840, 0x4216, 0x4327, 0x4824, 0 }; /* Media */
449 static const WCHAR t2_name2[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
450 static const WCHAR t2_name3[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
451 static const WCHAR t2_name4[] = { 0x4840, 0x4559, 0x44f2, 0x4568, 0x4737, 0 }; /* Property */
452 static const WCHAR t2_name5[] = { 0x4840, 0x4119, 0x41b7, 0x3e6b, 0x41a4, 0x412e, 0x422a, 0 }; /* PatchPackage */
453 static const WCHAR t2_name6[] = { 0x4840, 0x4452, 0x45f6, 0x43e4, 0x3baf, 0x423b, 0x4626,
454                                   0x4237, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* InstallExecuteSequence */
455 static const WCHAR t2_name7[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
456 static const WCHAR t2_name8[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
457 static const WCHAR t2_name9[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
458                                   0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
459                                   0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
460 static const WCHAR t2_name10[] = { 0x4840, 0x420f, 0x45e4, 0x4578, 0x3b28, 0x4432, 0x44b3,
461                                    0x4231, 0x45f1, 0x4836, 0 }; /* FeatureComponents */
462 static const WCHAR t2_name11[] = { 0x4840, 0x448c, 0x44f0, 0x4472, 0x4468, 0x4837, 0 }; /* Component */
463 static const WCHAR t2_name12[] = { 0x4840, 0x420f, 0x45e4, 0x4578, 0x4828, 0 }; /* Feature */
464 
465 static const WCHAR t2_data0[] = { /* File */
466     0x00c0, 0x0001, 0x9000, 0x83e8, 0x0801, 0x0002, 0x0003, 0x0002,
467     0x03e8, 0x8000, 0x0000, 0x0000, 0x9000, 0x83e9
468 };
469 static const WCHAR t2_data1[] = { /* Media */
470     0x0601, 0x8002, 0x03e9, 0x8000, 0x0000, 0x000d, 0x0000, 0x000e
471 };
472 static const WCHAR t2_data2[] = { /* _Columns */
473     0x0401, 0x000f, 0x0000, 0x0010, 0xad48, 0x0401, 0x000f, 0x0000, /* 0x0401 = add row (1), 4 shorts */
474     0x0011, 0xa502, 0x0401, 0x000f, 0x0000, 0x0012, 0x8104, 0x0401,
475     0x000f, 0x0000, 0x0013, 0x8502, 0x0401, 0x000f, 0x0000, 0x0014,
476     0x9900, 0x0401, 0x000f, 0x0000, 0x0015, 0x9d48, 0x0401, 0x0016,
477     0x0000, 0x0017, 0xad26, 0x0401, 0x0016, 0x0000, 0x0018, 0x8502,
478     0x0401, 0x001a, 0x0000, 0x001b, 0xad26, 0x0401, 0x001a, 0x0000,
479     0x0014, 0x8900
480 };
481 static const WCHAR t2_data3[] = { /* _Tables */
482     0x0101, 0x000f, 0x0101, 0x0016, 0x0101, 0x001a
483 };
484 static const WCHAR t2_data4[] = { /* Property */
485     0x0002, 0x0008, 0x0009, 0x0201, 0x000a, 0x000b
486 };
487 static const WCHAR t2_data5[] = { /* PatchPackage */
488     0x0201, 0x0019, 0x8002
489 };
490 static const WCHAR t2_data6[] = { /* InstallExecuteSequence */
491     0x0301, 0x000c, 0x0000, 0x87d1
492 };
493 static const char t2_data7[] = { /* _StringData */
494     "patch.txtfile.txtfile{327d9640-674f-4b9f-8b8a-547a0f6f8518}MSITESTDIRnewnew featurePATCHNEWSUMMARYSUBJECT"
495     "Installation DatabasePATCHNEWPACKAGECODE{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}PatchFiles#CAB_msitestprop"
496     "PatchFile_SequencePatchSizeAttributesHeaderStreamRef_PatchPackagePatchIdMedia_"
497     "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}MsiPatchHeadersStreamRef"
498 };
499 static const WCHAR t2_data8[] = { /* _StringPool */
500 /* len, refs */
501      0,  0,     /* string 0 '' */
502      9,  1,     /* string 1 'patch.txt' */
503      8,  3,     /* string 2 'file.txt' */
504      4,  3,     /* string 3 'file' */
505     38,  1,     /* string 4 '{327d9640-674f-4b9f-8b8a-547a0f6f8518}' */
506     10,  2,     /* string 5 'MSITESTDIR' */
507      3,  2,     /* string 6 'new' */
508     11,  1,     /* string 7 'new feature' */
509     22,  1,     /* string 8 'PATCHNEWSUMMARYSUBJECT' */
510     21,  1,     /* string 9 'Installation Database' */
511     19,  1,     /* string 10 'PATCHNEWPACKAGECODE' */
512     38,  1,     /* string 11 '{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}' */
513     10,  1,     /* string 12 'PatchFiles' */
514     12,  1,     /* string 13 '#CAB_msitest' */
515      4,  1,     /* string 14 'prop' */
516      5,  7,     /* string 15 'Patch' */
517      5,  1,     /* string 16 'File_' */
518      8,  1,     /* string 17 'Sequence' */
519      9,  1,     /* string 18 'PatchSize' */
520     10,  1,     /* string 19 'Attributes' */
521      6,  2,     /* string 20 'Header' */
522     10,  1,     /* string 21 'StreamRef_' */
523     12,  3,     /* string 22 'PatchPackage' */
524      7,  1,     /* string 23 'PatchId' */
525      6,  1,     /* string 24 'Media_' */
526     38,  1,     /* string 25 '{0F96CDC0-4CDF-4304-B283-7B9264889EF7}' */
527     15,  3,     /* string 26 'MsiPatchHeaders' */
528      9,  1      /* string 27 'StreamRef' */
529 };
530 static const char t2_data9[] = { /* SummaryInformation */
531     0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
532     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
533     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
534     0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
535     0x30, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00,
536     0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00,
537     0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x78,
538     0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
539     0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
540     0x00, 0x9c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa8, 0x00,
541     0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x09,
542     0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
543     0x4c, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00,
544     0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
545     0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
546     0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
547     0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
548     0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00,
549     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01,
550     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
551     0x06, 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00,
552     0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
553     0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b,
554     0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
555     0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
556     0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
557     0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
558     0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
559     0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
560     0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
561     0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
562     0x41, 0x32, 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45,
563     0x32, 0x43, 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30,
564     0x39, 0x2d, 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35,
565     0x46, 0x34, 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2d,
566     0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x27, 0x09
567 };
568 static const WCHAR t2_data10[] = { /* FeatureComponents */
569     0x0201, 0x0006, 0x0003
570 };
571 static const WCHAR t2_data11[] = { /* Component */
572     0x0601, 0x0003, 0x0004, 0x0005, 0x8000, 0x0000, 0x0002
573 };
574 static const WCHAR t2_data12[] = { /* Feature */
575     0x0801, 0x0006, 0x0000, 0x0000, 0x0007, 0x8001, 0x8001, 0x0005,
576     0x8000
577 };
578 
579 static const struct table_data table_transform2_data[] = {
580     { t2_name0, t2_data0, sizeof t2_data0 },
581     { t2_name1, t2_data1, sizeof t2_data1 },
582     { t2_name2, t2_data2, sizeof t2_data2 },
583     { t2_name3, t2_data3, sizeof t2_data3 },
584     { t2_name4, t2_data4, sizeof t2_data4 },
585     { t2_name5, t2_data5, sizeof t2_data5 },
586     { t2_name6, t2_data6, sizeof t2_data6 },
587     { t2_name7, t2_data7, sizeof t2_data7 - 1 },
588     { t2_name8, t2_data8, sizeof t2_data8 },
589     { t2_name9, t2_data9, sizeof t2_data9 },
590     { t2_name10, t2_data10, sizeof t2_data10 },
591     { t2_name11, t2_data11, sizeof t2_data11 },
592     { t2_name12, t2_data12, sizeof t2_data12 },
593 };
594 
write_tables(IStorage * stg,const struct table_data * tables,UINT num_tables)595 static void write_tables( IStorage *stg, const struct table_data *tables, UINT num_tables )
596 {
597     IStream *stm;
598     DWORD i, count;
599     HRESULT r;
600 
601     for (i = 0; i < num_tables; i++)
602     {
603         r = IStorage_CreateStream( stg, tables[i].name, STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
604         if (FAILED( r ))
605         {
606             ok( 0, "failed to create stream %#lx\n", r );
607             continue;
608         }
609 
610         r = IStream_Write( stm, tables[i].data, tables[i].size, &count );
611         if (FAILED( r ) || count != tables[i].size)
612             ok( 0, "failed to write stream\n" );
613         IStream_Release( stm );
614     }
615 }
616 
create_patch(const char * filename)617 static void create_patch( const char *filename )
618 {
619     IStorage *stg = NULL, *stg1 = NULL, *stg2 = NULL;
620     WCHAR *filenameW;
621     HRESULT r;
622     int len;
623     const DWORD mode = STGM_CREATE|STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE;
624 
625     const CLSID CLSID_MsiPatch = {0xc1086, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
626     const CLSID CLSID_MsiTransform = {0xc1082, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
627 
628     len = MultiByteToWideChar( CP_ACP, 0, filename, -1, NULL, 0 );
629     filenameW = malloc( len * sizeof(WCHAR) );
630     MultiByteToWideChar( CP_ACP, 0, filename, -1, filenameW, len );
631 
632     r = StgCreateDocfile( filenameW, mode, 0, &stg );
633     free( filenameW );
634     ok( r == S_OK, "failed to create storage %#lx\n", r );
635     if (!stg)
636         return;
637 
638     r = IStorage_SetClass( stg, &CLSID_MsiPatch );
639     ok( r == S_OK, "failed to set storage type %#lx\n", r );
640 
641     write_tables( stg, table_patch_data, ARRAY_SIZE( table_patch_data ));
642 
643     r = IStorage_CreateStorage( stg, p_name7, mode, 0, 0, &stg1 );
644     ok( r == S_OK, "failed to create substorage %#lx\n", r );
645 
646     r = IStorage_SetClass( stg1, &CLSID_MsiTransform );
647     ok( r == S_OK, "failed to set storage type %#lx\n", r );
648 
649     write_tables( stg1, table_transform1_data, ARRAY_SIZE( table_transform1_data ));
650     IStorage_Release( stg1 );
651 
652     r = IStorage_CreateStorage( stg, p_name8, mode, 0, 0, &stg2 );
653     ok( r == S_OK, "failed to create substorage %#lx\n", r );
654 
655     r = IStorage_SetClass( stg2, &CLSID_MsiTransform );
656     ok( r == S_OK, "failed to set storage type %#lx\n", r );
657 
658     write_tables( stg2, table_transform2_data, ARRAY_SIZE( table_transform2_data ));
659     IStorage_Release( stg2 );
660     IStorage_Release( stg );
661 }
662 
test_simple_patch(void)663 static void test_simple_patch( void )
664 {
665     UINT r;
666     DWORD size;
667     char path[MAX_PATH], install_source[MAX_PATH], buffer[32];
668     const char *query;
669     WCHAR pathW[MAX_PATH];
670     MSIHANDLE hpackage, hdb, hview, hrec;
671 
672     if (!is_process_elevated())
673     {
674         skip("process is limited\n");
675         return;
676     }
677 
678     CreateDirectoryA( "msitest", NULL );
679     create_file( "msitest\\patch.txt", 1000 );
680 
681     create_database_suminfo( msifile, tables, ARRAY_SIZE(tables) );
682     create_patch( mspfile );
683 
684     MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
685 
686     r = MsiInstallProductA( msifile, "DISABLE_FEATURE=1" );
687     if (r != ERROR_SUCCESS)
688     {
689         skip("Product installation failed with error code %u\n", r);
690         goto cleanup;
691     }
692 
693     size = get_pf_file_size( "msitest\\patch.txt" );
694     ok( size == 1000, "expected 1000, got %lu\n", size );
695 
696     size = sizeof(install_source);
697     r = MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
698                             "InstallSource", install_source, &size );
699     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
700 
701     strcpy( path, CURR_DIR );
702     strcat( path, "\\" );
703     strcat( path, msifile );
704 
705     r = MsiOpenPackageA( path, &hpackage );
706     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
707 
708     hdb = MsiGetActiveDatabase( hpackage );
709     ok( hdb, "failed to get database handle\n" );
710 
711     query = "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
712     r = MsiDatabaseOpenViewA( hdb, query, &hview );
713     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
714 
715     r = MsiViewExecute( hview, 0 );
716     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
717 
718     r = MsiViewFetch( hview, &hrec );
719     ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
720 
721     MsiCloseHandle( hrec );
722     MsiViewClose( hview );
723     MsiCloseHandle( hview );
724 
725     query = "SELECT * FROM `Property` WHERE `Property` = 'PATCHNEWSUMMARYSUBJECT' "
726             "AND `Value` = 'Installer Database'";
727     r = MsiDatabaseOpenViewA( hdb, query, &hview );
728     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
729 
730     r = MsiViewExecute( hview, 0 );
731     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
732 
733     r = MsiViewFetch( hview, &hrec );
734     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
735 
736     MsiCloseHandle( hrec );
737     MsiViewClose( hview );
738     MsiCloseHandle( hview );
739 
740     buffer[0] = 0;
741     size = sizeof(buffer);
742     r = MsiGetPropertyA( hpackage, "PATCHNEWSUMMARYSUBJECT", buffer, &size );
743     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
744     ok( !strcmp( buffer, "Installer Database" ), "expected \'Installer Database\', got \'%s\'\n", buffer );
745 
746     MsiCloseHandle( hdb );
747     MsiCloseHandle( hpackage );
748 
749     r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
750     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
751 
752     size = get_pf_file_size( "msitest\\patch.txt" );
753     ok( size == 1002, "expected 1002, got %lu\n", size );
754     size = get_pf_file_size( "msitest\\file.txt" );
755     ok( size == 1000, "expected 1000, got %lu\n", size );
756 
757     /* show that MsiOpenPackage applies registered patches */
758     r = MsiOpenPackageA( path, &hpackage );
759     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
760 
761     hdb = MsiGetActiveDatabase( hpackage );
762     ok( hdb, "failed to get database handle\n" );
763 
764     query = "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
765     r = MsiDatabaseOpenViewA( hdb, query, &hview );
766     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
767 
768     r = MsiViewExecute( hview, 0 );
769     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
770 
771     r = MsiViewFetch( hview, &hrec );
772     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
773 
774     MsiCloseHandle( hrec );
775     MsiViewClose( hview );
776     MsiCloseHandle( hview );
777 
778     query = "SELECT * FROM `Property` WHERE `Property` = 'PATCHNEWSUMMARYSUBJECT' "
779             "AND `Value` = 'Installation Database'";
780     r = MsiDatabaseOpenViewA( hdb, query, &hview );
781     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
782 
783     r = MsiViewExecute( hview, 0 );
784     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
785 
786     r = MsiViewFetch( hview, &hrec );
787     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
788 
789     MsiCloseHandle( hrec );
790     MsiViewClose( hview );
791     MsiCloseHandle( hview );
792 
793     buffer[0] = 0;
794     size = sizeof(buffer);
795     r = MsiGetPropertyA( hpackage, "PATCHNEWSUMMARYSUBJECT", buffer, &size );
796     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
797     ok( !strcmp( buffer, "Installation Database" ), "expected \'Installation Database\', got \'%s\'\n", buffer );
798 
799     MsiCloseHandle( hdb );
800     MsiCloseHandle( hpackage );
801 
802     /* show that patches are not committed to the local package database */
803     size = sizeof(path);
804     r = MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
805                             "LocalPackage", path, &size );
806     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
807 
808     MultiByteToWideChar( CP_ACP, 0, path, -1, pathW, MAX_PATH );
809     r = MsiOpenDatabaseW( pathW, MSIDBOPEN_READONLY, &hdb );
810     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
811 
812     r = MsiDatabaseOpenViewA( hdb, query, &hview );
813     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
814 
815     r = MsiViewExecute( hview, 0 );
816     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
817 
818     r = MsiViewFetch( hview, &hrec );
819     ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
820 
821     MsiCloseHandle( hrec );
822     MsiViewClose( hview );
823     MsiCloseHandle( hview );
824     MsiCloseHandle( hdb );
825 
826     size = sizeof(path);
827     r = MsiGetProductInfoA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
828                             "InstallSource", path, &size );
829     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
830     ok( !strcasecmp( path, install_source ), "wrong path %s\n", path );
831 
832     r = MsiInstallProductA( msifile, "REMOVE=ALL" );
833     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
834 
835     ok( !delete_pf( "msitest\\patch.txt", TRUE ), "file not removed\n" );
836     ok( !delete_pf( "msitest\\file.txt", TRUE ), "file not removed\n" );
837     ok( !delete_pf( "msitest", FALSE ), "directory not removed\n" );
838 
839 cleanup:
840     DeleteFileA( msifile );
841     DeleteFileA( mspfile );
842     DeleteFileA( "msitest\\patch.txt" );
843     RemoveDirectoryA( "msitest" );
844 }
845 
test_MsiOpenDatabase(void)846 static void test_MsiOpenDatabase( void )
847 {
848     UINT r;
849     MSIHANDLE hdb;
850 
851     r = MsiOpenDatabaseW( mspfileW, MSIDBOPEN_CREATE, &hdb );
852     ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
853 
854     r = MsiDatabaseCommit( hdb );
855     ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
856     MsiCloseHandle( hdb );
857 
858     r = MsiOpenDatabaseW( mspfileW, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
859     ok(r == ERROR_OPEN_FAILED, "expected ERROR_OPEN_FAILED, got %u\n", r);
860     DeleteFileA( mspfile );
861 
862     r = MsiOpenDatabaseW( mspfileW, MSIDBOPEN_CREATE + MSIDBOPEN_PATCHFILE, &hdb );
863     ok(r == ERROR_SUCCESS , "failed to open database %u\n", r);
864 
865     r = MsiDatabaseCommit( hdb );
866     ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
867     MsiCloseHandle( hdb );
868 
869     r = MsiOpenDatabaseW( mspfileW, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
870     ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
871     MsiCloseHandle( hdb );
872     DeleteFileA( mspfile );
873 
874     create_database_suminfo( msifile, tables, ARRAY_SIZE(tables) );
875     create_patch( mspfile );
876 
877     r = MsiOpenDatabaseW( msifileW, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
878     ok(r == ERROR_OPEN_FAILED, "failed to open database %u\n", r );
879 
880     r = MsiOpenDatabaseW( mspfileW, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
881     ok(r == ERROR_SUCCESS, "failed to open database %u\n", r );
882     MsiCloseHandle( hdb );
883 
884     DeleteFileA( msifile );
885     DeleteFileA( mspfile );
886 }
887 
find_entry(MSIHANDLE hdb,const char * table,const char * entry)888 static UINT find_entry( MSIHANDLE hdb, const char *table, const char *entry )
889 {
890     static const char fmt[] = "SELECT * FROM `%s` WHERE `Name` = '%s'";
891     char query[0x100];
892     UINT r;
893     MSIHANDLE hview, hrec;
894 
895     sprintf( query, fmt, table, entry );
896     r = MsiDatabaseOpenViewA( hdb, query, &hview );
897     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
898 
899     r = MsiViewExecute( hview, 0 );
900     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
901 
902     r = MsiViewFetch( hview, &hrec );
903     MsiViewClose( hview );
904     MsiCloseHandle( hview );
905     MsiCloseHandle( hrec );
906     return r;
907 }
908 
find_entryW(MSIHANDLE hdb,const WCHAR * table,const WCHAR * entry)909 static UINT find_entryW( MSIHANDLE hdb, const WCHAR *table, const WCHAR *entry )
910 {
911     WCHAR query[0x100];
912     MSIHANDLE hview, hrec;
913     UINT r;
914 
915     wsprintfW( query, L"SELECT * FROM `%s` WHERE `Name` = '%s'", table, entry );
916     r = MsiDatabaseOpenViewW( hdb, query, &hview );
917     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
918 
919     r = MsiViewExecute( hview, 0 );
920     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
921 
922     r = MsiViewFetch( hview, &hrec );
923     MsiViewClose( hview );
924     MsiCloseHandle( hview );
925     MsiCloseHandle( hrec );
926     return r;
927 }
928 
get_integer(MSIHANDLE hdb,UINT field,const char * query)929 static INT get_integer( MSIHANDLE hdb, UINT field, const char *query)
930 {
931     UINT r;
932     INT ret = -1;
933     MSIHANDLE hview, hrec;
934 
935     r = MsiDatabaseOpenViewA( hdb, query, &hview );
936     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
937 
938     r = MsiViewExecute( hview, 0 );
939     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
940 
941     r = MsiViewFetch( hview, &hrec );
942     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
943     if (r == ERROR_SUCCESS)
944     {
945         UINT r_tmp;
946         ret = MsiRecordGetInteger( hrec, field );
947         MsiCloseHandle( hrec );
948 
949         r_tmp = MsiViewFetch( hview, &hrec );
950         ok( r_tmp == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r);
951     }
952 
953     MsiViewClose( hview );
954     MsiCloseHandle( hview );
955     return ret;
956 }
957 
get_string(MSIHANDLE hdb,UINT field,const char * query)958 static char *get_string( MSIHANDLE hdb, UINT field, const char *query)
959 {
960     UINT r;
961     static char ret[MAX_PATH];
962     MSIHANDLE hview, hrec;
963 
964     ret[0] = '\0';
965 
966     r = MsiDatabaseOpenViewA( hdb, query, &hview );
967     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
968 
969     r = MsiViewExecute( hview, 0 );
970     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
971 
972     r = MsiViewFetch( hview, &hrec );
973     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
974     if (r == ERROR_SUCCESS)
975     {
976         DWORD size = MAX_PATH;
977         r = MsiRecordGetStringA( hrec, field, ret, &size );
978         ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
979         MsiCloseHandle( hrec );
980 
981         r = MsiViewFetch( hview, &hrec );
982         ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r);
983     }
984 
985     MsiViewClose( hview );
986     MsiCloseHandle( hview );
987     return ret;
988 }
989 
test_system_tables(void)990 static void test_system_tables( void )
991 {
992     static const char patchsource[] = "MSPSRC0F96CDC04CDF4304B2837B9264889EF7";
993     UINT r;
994     char *cr;
995     const char *query;
996     MSIHANDLE hproduct, hdb, hview, hrec;
997 
998     if (!is_process_elevated())
999     {
1000         skip("process is limited\n");
1001         return;
1002     }
1003 
1004     CreateDirectoryA( "msitest", NULL );
1005     create_file( "msitest\\patch.txt", 1000 );
1006 
1007     create_database_suminfo( msifile, tables, ARRAY_SIZE(tables) );
1008     create_patch( mspfile );
1009 
1010     MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
1011 
1012     r = MsiInstallProductA( msifile, "DISABLE_FEATURE=1" );
1013     if (r != ERROR_SUCCESS)
1014     {
1015         skip("Product installation failed with error code %d\n", r);
1016         goto cleanup;
1017     }
1018 
1019     r = MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct );
1020     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1021 
1022     hdb = MsiGetActiveDatabase( hproduct );
1023     ok( hdb, "failed to get database handle\n" );
1024 
1025     r = find_entry( hdb, "_Streams", "\5SummaryInformation" );
1026     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1027 
1028     query = "SELECT * FROM `_Storages`";
1029     r = MsiDatabaseOpenViewA( hdb, query, &hview );
1030     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1031 
1032     r = MsiViewExecute( hview, 0 );
1033     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1034 
1035     r = MsiViewFetch( hview, &hrec );
1036     ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
1037 
1038     r = find_entry( hdb, "_Tables", "Directory" );
1039     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1040 
1041     r = find_entry( hdb, "_Tables", "File" );
1042     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1043 
1044     r = find_entry( hdb, "_Tables", "Component" );
1045     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1046 
1047     r = find_entry( hdb, "_Tables", "Feature" );
1048     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1049 
1050     r = find_entry( hdb, "_Tables", "FeatureComponents" );
1051     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1052 
1053     r = find_entry( hdb, "_Tables", "Property" );
1054     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1055 
1056     r = find_entry( hdb, "_Tables", "InstallExecuteSequence" );
1057     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1058 
1059     r = find_entry( hdb, "_Tables", "Media" );
1060     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1061 
1062     r = get_integer( hdb, 1, "SELECT * FROM `Media` WHERE `VolumeLabel`=\'DISK1\'");
1063     ok( r == 1, "Got %u\n", r );
1064 
1065     r = find_entry( hdb, "_Tables", "_Property" );
1066     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1067 
1068     MsiCloseHandle( hrec );
1069     MsiViewClose( hview );
1070     MsiCloseHandle( hview );
1071     MsiCloseHandle( hdb );
1072     MsiCloseHandle( hproduct );
1073 
1074     r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
1075     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1076 
1077     r = MsiOpenProductA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}", &hproduct );
1078     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1079 
1080     hdb = MsiGetActiveDatabase( hproduct );
1081     ok( hdb, "failed to get database handle\n" );
1082 
1083     r = find_entry( hdb, "_Streams", "\5SummaryInformation" );
1084     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1085 
1086     r = find_entryW( hdb, L"_Streams", L"\x3a8c\x47cb\x45b0\x45ec\x45a8\x4837" );
1087     ok( r == ERROR_NO_MORE_ITEMS, "failed to find entry %u\n", r );
1088 
1089     query = "SELECT * FROM `_Storages`";
1090     r = MsiDatabaseOpenViewA( hdb, query, &hview );
1091     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1092 
1093     r = MsiViewExecute( hview, 0 );
1094     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1095 
1096     r = MsiViewFetch( hview, &hrec );
1097     ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
1098 
1099     r = find_entry( hdb, "_Tables", "Directory" );
1100     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1101 
1102     r = find_entry( hdb, "_Tables", "File" );
1103     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1104 
1105     r = find_entry( hdb, "_Tables", "Component" );
1106     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1107 
1108     r = find_entry( hdb, "_Tables", "Feature" );
1109     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1110 
1111     r = find_entry( hdb, "_Tables", "FeatureComponents" );
1112     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1113 
1114     r = find_entry( hdb, "_Tables", "Property" );
1115     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1116 
1117     r = find_entry( hdb, "_Tables", "InstallExecuteSequence" );
1118     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1119 
1120     r = find_entry( hdb, "_Tables", "Media" );
1121     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1122 
1123     r = find_entry( hdb, "_Tables", "_Property" );
1124     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1125 
1126     r = find_entry( hdb, "_Tables", "MsiPatchHeaders" );
1127     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1128 
1129     r = find_entry( hdb, "_Tables", "Patch" );
1130     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1131 
1132     r = find_entry( hdb, "_Tables", "PatchPackage" );
1133     ok( r == ERROR_SUCCESS, "failed to find entry %u\n", r );
1134 
1135     cr = get_string( hdb, 6, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL");
1136     todo_wine ok( !strcmp(cr, patchsource), "Expected \"%s\", got \"%s\"\n", patchsource, cr );
1137 
1138     r = get_integer( hdb, 1, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL");
1139     todo_wine ok( r == 100, "Got %u\n", r );
1140 
1141     r = get_integer( hdb, 2, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL");
1142     todo_wine ok( r == 10001, "Got %u\n", r );
1143 
1144     r = get_integer( hdb, 1, "SELECT * FROM `Media` WHERE `VolumeLabel`=\'DISK1\'");
1145     ok( r == 1, "Got %u\n", r );
1146 
1147     cr = get_string( hdb, 4, "SELECT * FROM `Media` WHERE `Source` IS NOT NULL");
1148     ok( !strcmp(cr, "#CAB_msitest"), "Expected \"#CAB_msitest\", got \"%s\"\n", cr );
1149 
1150     r = get_integer( hdb, 8, "SELECT * FROM `File` WHERE `File` = 'patch.txt'");
1151     ok( r == 10000, "Got %u\n", r );
1152 
1153     MsiCloseHandle( hrec );
1154     MsiViewClose( hview );
1155     MsiCloseHandle( hview );
1156     MsiCloseHandle( hdb );
1157     MsiCloseHandle( hproduct );
1158 
1159     r = MsiInstallProductA( msifile, "REMOVE=ALL" );
1160     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1161 
1162 cleanup:
1163     DeleteFileA( msifile );
1164     DeleteFileA( mspfile );
1165     DeleteFileA( "msitest\\patch.txt" );
1166     RemoveDirectoryA( "msitest" );
1167 }
1168 
test_patch_registration(void)1169 static void test_patch_registration( void )
1170 {
1171     UINT r;
1172     DWORD size;
1173     char buffer[MAX_PATH], patch_code[39];
1174 
1175     if (!is_process_elevated())
1176     {
1177         skip("process is limited\n");
1178         return;
1179     }
1180 
1181     CreateDirectoryA( "msitest", NULL );
1182     create_file( "msitest\\patch.txt", 1000 );
1183 
1184     create_database_suminfo( msifile, tables, ARRAY_SIZE(tables) );
1185     create_patch( mspfile );
1186 
1187     MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
1188 
1189     r = MsiInstallProductA( msifile, "DISABLE_FEATURE=1" );
1190     if (r != ERROR_SUCCESS)
1191     {
1192         skip("Product installation failed with error code %d\n", r);
1193         goto cleanup;
1194     }
1195 
1196     r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
1197     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1198 
1199     buffer[0] = 0;
1200     size = sizeof(buffer);
1201     r = MsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1202                             "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1203                              NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
1204                              INSTALLPROPERTY_LOCALPACKAGEA, buffer, &size );
1205     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1206     ok( buffer[0], "buffer empty\n" );
1207 
1208     buffer[0] = 0;
1209     size = sizeof(buffer);
1210     r = MsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1211                             "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1212                             NULL, MSIINSTALLCONTEXT_MACHINE,
1213                             INSTALLPROPERTY_LOCALPACKAGEA, buffer, &size );
1214     ok( r == ERROR_UNKNOWN_PRODUCT, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r );
1215 
1216     buffer[0] = 0;
1217     size = sizeof(buffer);
1218     r = MsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1219                             "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1220                              NULL, MSIINSTALLCONTEXT_USERMANAGED,
1221                              INSTALLPROPERTY_LOCALPACKAGEA, buffer, &size );
1222     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1223     ok( !buffer[0], "got %s\n", buffer );
1224 
1225     r = MsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1226                           NULL, MSIINSTALLCONTEXT_USERUNMANAGED, MSIPATCHSTATE_APPLIED,
1227                           0, patch_code, NULL, NULL, NULL, NULL );
1228     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1229     ok( !strcmp( patch_code, "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}" ), "wrong patch code\n" );
1230 
1231     r = MsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1232                           NULL, MSIINSTALLCONTEXT_MACHINE, MSIPATCHSTATE_APPLIED,
1233                           0, patch_code, NULL, NULL, NULL, NULL );
1234     ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
1235 
1236     r = MsiEnumPatchesExA( "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1237                           NULL, MSIINSTALLCONTEXT_USERMANAGED, MSIPATCHSTATE_APPLIED,
1238                           0, patch_code, NULL, NULL, NULL, NULL );
1239     ok( r == ERROR_NO_MORE_ITEMS, "expected ERROR_NO_MORE_ITEMS, got %u\n", r );
1240 
1241     r = MsiInstallProductA( msifile, "REMOVE=ALL" );
1242     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
1243 
1244     buffer[0] = 0;
1245     size = sizeof(buffer);
1246     r = MsiGetPatchInfoExA( "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}",
1247                             "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}",
1248                              NULL, MSIINSTALLCONTEXT_USERUNMANAGED,
1249                              INSTALLPROPERTY_LOCALPACKAGEA, buffer, &size );
1250     ok( r == ERROR_UNKNOWN_PRODUCT, "expected ERROR_UNKNOWN_PRODUCT, got %u\n", r );
1251 
1252 cleanup:
1253     DeleteFileA( msifile );
1254     DeleteFileA( mspfile );
1255     DeleteFileA( "msitest\\patch.txt" );
1256     RemoveDirectoryA( "msitest" );
1257 }
1258 
START_TEST(patch)1259 START_TEST(patch)
1260 {
1261     DWORD len;
1262     char temp_path[MAX_PATH], prev_path[MAX_PATH];
1263 
1264     if (!is_process_elevated()) restart_as_admin_elevated();
1265 
1266     GetCurrentDirectoryA( MAX_PATH, prev_path );
1267     GetTempPathA( MAX_PATH, temp_path );
1268     SetCurrentDirectoryA( temp_path );
1269 
1270     strcpy( CURR_DIR, temp_path );
1271     len = strlen( CURR_DIR );
1272 
1273     if (len && (CURR_DIR[len - 1] == '\\'))
1274         CURR_DIR[len - 1] = 0;
1275 
1276     get_program_files_dir( PROG_FILES_DIR, COMMON_FILES_DIR );
1277 
1278     test_simple_patch();
1279     test_MsiOpenDatabase();
1280     test_system_tables();
1281     test_patch_registration();
1282 
1283     SetCurrentDirectoryA( prev_path );
1284 }
1285