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