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