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