msi/tests: Use a unique product code when testing MsiConfigureFeature parameter valid...
[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
35 static const char *msifile = "winetest-patch.msi";
36 static const char *mspfile = "winetest-patch.msp";
37
38 static char CURR_DIR[MAX_PATH];
39 static char PROG_FILES_DIR[MAX_PATH];
40 static char COMMON_FILES_DIR[MAX_PATH];
41
42 /* msi database data */
43
44 static const char property_dat[] =
45     "Property\tValue\n"
46     "s72\tl0\n"
47     "Property\tProperty\n"
48     "Manufacturer\tWineHQ\n"
49     "ProductCode\t{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}\n"
50     "UpgradeCode\t{A2E3D643-4E2C-477F-A309-F76F552D5F43}\n"
51     "ProductLanguage\t1033\n"
52     "ProductName\tmsitest\n"
53     "ProductVersion\t1.1.1\n";
54
55 static const char media_dat[] =
56     "DiskId\tLastSequence\tDiskPrompt\tCabinet\tVolumeLabel\tSource\n"
57     "i2\ti4\tL64\tS255\tS32\tS72\n"
58     "Media\tDiskId\n"
59     "1\t1\t\t\tDISK1\t\n";
60
61 static const char file_dat[] =
62     "File\tComponent_\tFileName\tFileSize\tVersion\tLanguage\tAttributes\tSequence\n"
63     "s72\ts72\tl255\ti4\tS72\tS20\tI2\ti2\n"
64     "File\tFile\n"
65     "patch.txt\tpatch\tpatch.txt\t1000\t\t\t0\t1\n";
66
67 static const char directory_dat[] =
68     "Directory\tDirectory_Parent\tDefaultDir\n"
69     "s72\tS72\tl255\n"
70     "Directory\tDirectory\n"
71     "MSITESTDIR\tProgramFilesFolder\tmsitest\n"
72     "ProgramFilesFolder\tTARGETDIR\t.\n"
73     "TARGETDIR\t\tSourceDir";
74
75 static const char component_dat[] =
76     "Component\tComponentId\tDirectory_\tAttributes\tCondition\tKeyPath\n"
77     "s72\tS38\ts72\ti2\tS255\tS72\n"
78     "Component\tComponent\n"
79     "patch\t{4B79D87E-6D28-4FD3-92D6-CD9B26AF64F1}\tMSITESTDIR\t0\t\tpatch.txt\n";
80
81 static const char feature_dat[] =
82     "Feature\tFeature_Parent\tTitle\tDescription\tDisplay\tLevel\tDirectory_\tAttributes\n"
83     "s38\tS38\tL64\tL255\tI2\ti2\tS72\ti2\n"
84     "Feature\tFeature\n"
85     "patch\t\t\tpatch feature\t1\t1\tMSITESTDIR\t0\n";
86
87 static const char feature_comp_dat[] =
88     "Feature_\tComponent_\n"
89     "s38\ts72\n"
90     "FeatureComponents\tFeature_\tComponent_\n"
91     "patch\tpatch\n";
92
93 static const char install_exec_seq_dat[] =
94     "Action\tCondition\tSequence\n"
95     "s72\tS255\tI2\n"
96     "InstallExecuteSequence\tAction\n"
97     "LaunchConditions\t\t100\n"
98     "CostInitialize\t\t800\n"
99     "FileCost\t\t900\n"
100     "CostFinalize\t\t1000\n"
101     "InstallValidate\t\t1400\n"
102     "InstallInitialize\t\t1500\n"
103     "ProcessComponents\t\t1600\n"
104     "RemoveFiles\t\t1700\n"
105     "InstallFiles\t\t2000\n"
106     "RegisterUser\t\t3000\n"
107     "RegisterProduct\t\t3100\n"
108     "PublishFeatures\t\t5100\n"
109     "PublishProduct\t\t5200\n"
110     "InstallFinalize\t\t6000\n";
111
112 struct msi_table
113 {
114     const char *filename;
115     const char *data;
116     int size;
117 };
118
119 #define ADD_TABLE( x ) { #x".idt", x##_dat, sizeof(x##_dat) }
120
121 static const struct msi_table tables[] =
122 {
123     ADD_TABLE( directory ),
124     ADD_TABLE( file ),
125     ADD_TABLE( component ),
126     ADD_TABLE( feature ),
127     ADD_TABLE( feature_comp ),
128     ADD_TABLE( property ),
129     ADD_TABLE( install_exec_seq ),
130     ADD_TABLE( media )
131 };
132
133 static void init_function_pointers( void )
134 {
135     HMODULE hmsi = GetModuleHandleA( "msi.dll" );
136
137 #define GET_PROC( mod, func ) \
138     p ## func = (void *)GetProcAddress( mod, #func ); \
139     if (!p ## func) \
140         trace( "GetProcAddress(%s) failed\n", #func );
141
142     GET_PROC( hmsi, MsiApplyPatchA );
143 #undef GET_PROC
144 }
145
146 static BOOL get_program_files_dir( char *buf, char *buf2 )
147 {
148     HKEY hkey;
149     DWORD type, size;
150
151     if (RegOpenKey( HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion", &hkey ))
152         return FALSE;
153
154     size = MAX_PATH;
155     if (RegQueryValueExA( hkey, "ProgramFilesDir", 0, &type, (LPBYTE)buf, &size ))
156     {
157         RegCloseKey( hkey );
158         return FALSE;
159     }
160     size = MAX_PATH;
161     if (RegQueryValueExA( hkey, "CommonFilesDir", 0, &type, (LPBYTE)buf2, &size ))
162     {
163         RegCloseKey( hkey );
164         return FALSE;
165     }
166     RegCloseKey( hkey );
167     return TRUE;
168 }
169
170 static void create_file_data( const char *filename, const char *data, DWORD size )
171 {
172     HANDLE file;
173     DWORD written;
174
175     file = CreateFileA( filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL );
176     if (file == INVALID_HANDLE_VALUE)
177         return;
178
179     WriteFile( file, data, strlen( data ), &written, NULL );
180     if (size)
181     {
182         SetFilePointer( file, size, NULL, FILE_BEGIN );
183         SetEndOfFile( file );
184     }
185     CloseHandle( file );
186 }
187
188 #define create_file( name, size ) create_file_data( name, name, size )
189
190 static BOOL delete_pf( const char *rel_path, BOOL is_file )
191 {
192     char path[MAX_PATH];
193
194     strcpy( path, PROG_FILES_DIR );
195     strcat( path, "\\" );
196     strcat( path, rel_path );
197
198     if (is_file)
199         return DeleteFileA( path );
200     else
201         return RemoveDirectoryA( path );
202 }
203
204 static DWORD get_pf_file_size( const char *filename )
205 {
206     char path[MAX_PATH];
207     HANDLE file;
208     DWORD size;
209
210     strcpy( path, PROG_FILES_DIR );
211     strcat( path, "\\");
212     strcat( path, filename );
213
214     file = CreateFileA( path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL );
215     if (file == INVALID_HANDLE_VALUE)
216         return INVALID_FILE_SIZE;
217
218     size = GetFileSize( file, NULL );
219     CloseHandle( file );
220     return size;
221 }
222
223 static void write_file( const char *filename, const char *data, DWORD data_size )
224 {
225     DWORD size;
226     HANDLE file = CreateFile( filename, GENERIC_WRITE, 0, NULL,
227                               CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
228     WriteFile( file, data, data_size, &size, NULL );
229     CloseHandle( file );
230 }
231
232 static void set_suminfo( const char *filename )
233 {
234     UINT r;
235     MSIHANDLE hsi, hdb;
236
237     r = MsiOpenDatabaseA( filename, MSIDBOPEN_DIRECT, &hdb );
238     ok( r == ERROR_SUCCESS, "failed to open database %u\n", r );
239
240     r = MsiGetSummaryInformation( hdb, NULL, 7, &hsi );
241     ok( r == ERROR_SUCCESS, "failed to open summaryinfo %u\n", r );
242
243     r = MsiSummaryInfoSetProperty( hsi, 2, VT_LPSTR, 0, NULL, "Installation Database" );
244     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
245
246     r = MsiSummaryInfoSetProperty( hsi, 3, VT_LPSTR, 0, NULL, "Installation Database" );
247     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
248
249     r = MsiSummaryInfoSetProperty( hsi, 4, VT_LPSTR, 0, NULL, "WineHQ" );
250     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
251
252     r = MsiSummaryInfoSetProperty( hsi, 7, VT_LPSTR, 0, NULL, ";1033" );
253     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
254
255     r = MsiSummaryInfoSetProperty( hsi, 9, VT_LPSTR, 0, NULL, "{913B8D18-FBB6-4CAC-A239-C74C11E3FA74}" );
256     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
257
258     r = MsiSummaryInfoSetProperty( hsi, 14, VT_I4, 100, NULL, NULL );
259     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
260
261     r = MsiSummaryInfoSetProperty( hsi, 15, VT_I4, 0, NULL, NULL );
262     ok( r == ERROR_SUCCESS, "failed to set summary info %u\n", r );
263
264     r = MsiSummaryInfoPersist( hsi );
265     ok( r == ERROR_SUCCESS, "failed to persist suminfo %u\n", r );
266
267     r = MsiCloseHandle( hsi );
268     ok( r == ERROR_SUCCESS, "failed to close suminfo %u\n", r );
269
270     r = MsiCloseHandle( hdb );
271     ok( r == ERROR_SUCCESS, "failed to close database %u\n", r );
272 }
273
274 static void create_database( const char *filename, const struct msi_table *tables, UINT num_tables )
275 {
276     MSIHANDLE hdb;
277     UINT r, i;
278
279     r = MsiOpenDatabaseA( filename, MSIDBOPEN_CREATE, &hdb );
280     ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
281
282     /* import the tables into the database */
283     for (i = 0; i < num_tables; i++)
284     {
285         const struct msi_table *table = &tables[i];
286
287         write_file( table->filename, table->data, (table->size - 1) * sizeof(char) );
288
289         r = MsiDatabaseImportA( hdb, CURR_DIR, table->filename );
290         ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
291
292         DeleteFileA( table->filename );
293     }
294
295     r = MsiDatabaseCommit( hdb );
296     ok(r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r);
297
298     MsiCloseHandle( hdb );
299     set_suminfo( filename );
300 }
301
302 /* data for generating a patch */
303
304 /* table names - encoded as in an msi database file */
305 static const WCHAR p_name0[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
306 static const WCHAR p_name1[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
307 static const WCHAR p_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
308 static const WCHAR p_name3[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
309 static const WCHAR p_name4[] = { 0x3a8c, 0x47cb, 0x45b0, 0x45ec, 0x45a8, 0x4837, 0}; /* CAB_msitest */
310 static const WCHAR p_name5[] = { 0x4840, 0x4596, 0x3e6c, 0x45e4, 0x42e6, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* MsiPatchSequence */
311 static const WCHAR p_name6[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
312                                  0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
313                                  0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
314 /* substorage names */
315 static const WCHAR p_name7[] = { 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075, 0x0070,
316                                  0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* targetToupgraded */
317 static const WCHAR p_name8[] = { 0x0023, 0x0074, 0x0061, 0x0072, 0x0067, 0x0065, 0x0074, 0x0054, 0x006f, 0x0075,
318                                  0x0070, 0x0067, 0x0072, 0x0061, 0x0064, 0x0065, 0x0064, 0 }; /* #targetToupgraded */
319
320 /* data in each table */
321 static const WCHAR p_data0[] = { /* _Columns */
322     0x0001, 0x0001, 0x0001, 0x0001, 0x8001, 0x8002, 0x8003, 0x8004,
323     0x0002, 0x0003, 0x0004, 0x0005, 0xad00, 0xbd26, 0x8d00, 0x9502
324 };
325 static const WCHAR p_data1[] = { /* _Tables */
326     0x0001
327 };
328 static const char p_data2[] = { /* _StringData */
329     "MsiPatchSequencePatchFamilyProductCodeSequenceAttributes1.1.19388.37230913B8D18FBB64CACA239C74C11E3FA74"
330 };
331 static const WCHAR p_data3[] = { /* _StringPool */
332 /* len, refs */
333      0,  0,     /* string 0 '' */
334     16,  5,     /* string 1 'MsiPatchSequence' */
335     11,  1,     /* string 2 'PatchFamily' */
336     11,  1,     /* string 3 'ProductCode' */
337      8,  1,     /* string 4 'Sequence' */
338     10,  1,     /* string 5 'Attributes' */
339     15,  1,     /* string 6 '1.1.19388.37230' */
340     32,  1,     /* string 7 '913B8D18FBB64CACA239C74C11E3FA74' */
341 };
342 static const char p_data4[] = { /* CAB_msitest */
343     0x4d, 0x53, 0x43, 0x46, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x00,
344     0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00,
345     0x00, 0x00, 0x03, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9e,
346     0x03, 0x00, 0x00, 0x46, 0x00, 0x00, 0x00, 0x01, 0x00, 0x03, 0x12,
347     0xea, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
348     0x3c, 0xd4, 0x80, 0x20, 0x00, 0x70, 0x61, 0x74, 0x63, 0x68, 0x2e,
349     0x74, 0x78, 0x74, 0x00, 0x0b, 0x3c, 0xd6, 0xc1, 0x4a, 0x00, 0xea,
350     0x03, 0x5b, 0x80, 0x80, 0x8d, 0x00, 0x10, 0xa1, 0x3e, 0x00, 0x00,
351     0x00, 0x00, 0x03, 0x00, 0x40, 0x30, 0x0c, 0x43, 0xf8, 0xb4, 0x85,
352     0x4d, 0x96, 0x08, 0x0a, 0x92, 0xf0, 0x52, 0xfb, 0xbb, 0x82, 0xf9,
353     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x0e, 0x31, 0x7d,
354     0x56, 0xdf, 0xf7, 0x48, 0x7c, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
355     0x00, 0x00, 0x41, 0x80, 0xdf, 0xf7, 0xd8, 0x72, 0xbf, 0xb9, 0x63,
356     0x91, 0x0e, 0x57, 0x1f, 0xfa, 0x1a, 0x66, 0x54, 0x55
357 };
358 static const WCHAR p_data5[] = { /* MsiPatchSequence */
359     0x0007, 0x0000, 0x0006, 0x8000
360 };
361 static const char p_data6[] = { /* SummaryInformation */
362     0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
363     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
364     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
365     0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
366     0x30, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00,
367     0x00, 0x09, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x07, 0x00,
368     0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x90,
369     0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00,
370     0x0f, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
371     0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x30, 0x46, 0x39, 0x36, 0x43,
372     0x44, 0x43, 0x30, 0x2d, 0x34, 0x43, 0x44, 0x46, 0x2d, 0x34, 0x33,
373     0x30, 0x34, 0x2d, 0x42, 0x32, 0x38, 0x33, 0x2d, 0x37, 0x42, 0x39,
374     0x32, 0x36, 0x34, 0x38, 0x38, 0x39, 0x45, 0x46, 0x37, 0x7d, 0x00,
375     0x00, 0x1e, 0x00, 0x00, 0x00, 0x27, 0x00, 0x00, 0x00, 0x7b, 0x39,
376     0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42,
377     0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39,
378     0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41,
379     0x37, 0x34, 0x7d, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x25, 0x00,
380     0x00, 0x00, 0x3a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f,
381     0x75, 0x70, 0x67, 0x72, 0x61, 0x64, 0x65, 0x64, 0x3b, 0x3a, 0x23,
382     0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x54, 0x6f, 0x75, 0x70, 0x67,
383     0x72, 0x61, 0x64, 0x65, 0x64, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00,
384     0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x50, 0x61, 0x74, 0x63, 0x68,
385     0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x00,
386     0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
387 };
388
389 struct table_data {
390     LPCWSTR name;
391     const void *data;
392     DWORD size;
393 };
394
395 static const struct table_data table_patch_data[] = {
396     { p_name0, p_data0, sizeof p_data0 },
397     { p_name1, p_data1, sizeof p_data1 },
398     { p_name2, p_data2, sizeof p_data2 - 1 },
399     { p_name3, p_data3, sizeof p_data3 },
400     { p_name4, p_data4, sizeof p_data4 },
401     { p_name5, p_data5, sizeof p_data5 },
402     { p_name6, p_data6, sizeof p_data6 }
403 };
404
405 #define NUM_PATCH_TABLES (sizeof table_patch_data/sizeof table_patch_data[0])
406
407 static const WCHAR t1_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
408 static const WCHAR t1_name1[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
409 static const WCHAR t1_name2[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
410 static const WCHAR t1_name3[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
411                                   0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
412                                   0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
413
414 static const WCHAR t1_data0[] = { /* File */
415     0x0008, 0x0001, 0x03ea, 0x8000
416 };
417 static const char t1_data1[] = { /* _StringData */
418     "patch.txt"
419 };
420 static const WCHAR t1_data2[] = { /* _StringPool */
421 /* len, refs */
422      0,  0,     /* string 0 '' */
423      9,  1,     /* string 1 'patch.txt' */
424 };
425 static const char t1_data3[] = { /* SummaryInformation */
426     0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
427     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
428     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
429     0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
430     0x30, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00,
431     0x00, 0x02, 0x00, 0x00, 0x00, 0x68, 0x00, 0x00, 0x00, 0x03, 0x00,
432     0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xa8,
433     0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00,
434     0x06, 0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
435     0x00, 0xd0, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xdc, 0x00,
436     0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0x08,
437     0x00, 0x00, 0x00, 0xf4, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
438     0x04, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x8c, 0x01, 0x00,
439     0x00, 0x10, 0x00, 0x00, 0x00, 0x94, 0x01, 0x00, 0x00, 0x1e, 0x00,
440     0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61,
441     0x6c, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74,
442     0x61, 0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
443     0x00, 0x16, 0x00, 0x00, 0x00, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c,
444     0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x44, 0x61, 0x74, 0x61,
445     0x62, 0x61, 0x73, 0x65, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
446     0x07, 0x00, 0x00, 0x00, 0x57, 0x69, 0x6e, 0x65, 0x48, 0x51, 0x00,
447     0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
448     0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
449     0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
450     0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
451     0x00, 0x1e, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x3b, 0x31,
452     0x30, 0x33, 0x33, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x06,
453     0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00, 0x00,
454     0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b, 0x39, 0x31,
455     0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
456     0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
457     0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
458     0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x39, 0x31,
459     0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42, 0x42, 0x36,
460     0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33, 0x39, 0x2d,
461     0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46, 0x41, 0x37,
462     0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b, 0x41, 0x32,
463     0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45, 0x32, 0x43,
464     0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30, 0x39, 0x2d,
465     0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35, 0x46, 0x34,
466     0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00,
467     0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x22, 0x09
468 };
469
470 static const struct table_data table_transform1_data[] = {
471     { t1_name0, t1_data0, sizeof t1_data0 },
472     { t1_name1, t1_data1, sizeof t1_data1 - 1 },
473     { t1_name2, t1_data2, sizeof t1_data2 },
474     { t1_name3, t1_data3, sizeof t1_data3 }
475 };
476
477 #define NUM_TRANSFORM1_TABLES (sizeof table_transform1_data/sizeof table_transform1_data[0])
478
479 static const WCHAR t2_name0[] = { 0x4840, 0x430f, 0x422f, 0 }; /* File */
480 static const WCHAR t2_name1[] = { 0x4840, 0x4216, 0x4327, 0x4824, 0 }; /* Media */
481 static const WCHAR t2_name2[] = { 0x4840, 0x3b3f, 0x43f2, 0x4438, 0x45b1, 0 }; /* _Columns */
482 static const WCHAR t2_name3[] = { 0x4840, 0x3f7f, 0x4164, 0x422f, 0x4836, 0 }; /* _Tables */
483 static const WCHAR t2_name4[] = { 0x4840, 0x4559, 0x44f2, 0x4568, 0x4737, 0 }; /* Property */
484 static const WCHAR t2_name5[] = { 0x4840, 0x4119, 0x41b7, 0x3e6b, 0x41a4, 0x412e, 0x422a, 0 }; /* PatchPackage */
485 static const WCHAR t2_name6[] = { 0x4840, 0x4452, 0x45f6, 0x43e4, 0x3baf, 0x423b, 0x4626,
486                                   0x4237, 0x421c, 0x4634, 0x4468, 0x4226, 0 }; /* InstallExecuteSequence */
487 static const WCHAR t2_name7[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3b6a, 0x45e4, 0x4824, 0 }; /* _StringData */
488 static const WCHAR t2_name8[] = { 0x4840, 0x3f3f, 0x4577, 0x446c, 0x3e6a, 0x44b2, 0x482f, 0 }; /* _StringPool */
489 static const WCHAR t2_name9[] = { 0x0005, 0x0053, 0x0075, 0x006d, 0x006d, 0x0061, 0x0072,
490                                   0x0079, 0x0049, 0x006e, 0x0066, 0x006f, 0x0072, 0x006d,
491                                   0x0061, 0x0074, 0x0069, 0x006f, 0x006e, 0 }; /* SummaryInformation */
492
493 static const WCHAR t2_data0[] = { /* File */
494     0x00c0, 0x0001, 0x9000, 0x83e8
495 };
496 static const WCHAR t2_data1[] = { /* Media */
497     0x0601, 0x8002, 0x03e9, 0x8000, 0x0000, 0x0007, 0x0000, 0x0008
498 };
499 static const WCHAR t2_data2[] = { /* _Columns */
500     0x0401, 0x0009, 0x0000, 0x000a, 0xad48, 0x0401, 0x0009, 0x0000, /* 0x0401 = add row (1), 4 shorts */
501     0x000b, 0xa502, 0x0401, 0x0009, 0x0000, 0x000c, 0x8104, 0x0401,
502     0x0009, 0x0000, 0x000d, 0x8502, 0x0401, 0x0009, 0x0000, 0x000e,
503     0x9900, 0x0401, 0x0009, 0x0000, 0x000f, 0x9d48, 0x0401, 0x0010,
504     0x0000, 0x0011, 0xad26, 0x0401, 0x0010, 0x0000, 0x0012, 0x8502,
505     0x0401, 0x0014, 0x0000, 0x0015, 0xad26, 0x0401, 0x0014, 0x0000,
506     0x000e, 0x8900
507 };
508 static const WCHAR t2_data3[] = { /* _Tables */
509     0x0101, 0x0009, 0x0101, 0x0010, 0x0101, 0x0014
510 };
511 static const WCHAR t2_data4[] = { /* Property */
512     0x0201, 0x0002, 0x0003, 0x0201, 0x0004, 0x0005
513 };
514 static const WCHAR t2_data5[] = { /* PatchPackage */
515     0x0201, 0x0013, 0x8002
516 };
517 static const WCHAR t2_data6[] = { /* InstallExecuteSequence */
518     0x0301, 0x0006, 0x0000, 0x87d1
519 };
520 static const char t2_data7[] = { /* _StringData */
521     "patch.txtPATCHNEWSUMMARYSUBJECTInstallation DatabasePATCHNEWPACKAGECODE{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}"
522     "PatchFiles#CAB_msitestpropPatchFile_SequencePatchSizeAttributesHeaderStreamRef_PatchPackagePatchIdMedia_"
523     "{0F96CDC0-4CDF-4304-B283-7B9264889EF7}MsiPatchHeadersStreamRef"
524 };
525 static const WCHAR t2_data8[] = { /* _StringPool */
526 /* len, refs */
527      0,  0,     /* string 0 '' */
528      9,  1,     /* string 1 'patch.txt' */
529     22,  1,     /* string 2 'PATCHNEWSUMMARYSUBJECT' */
530     21,  1,     /* string 3 'Installation Database' */
531     19,  1,     /* string 4 'PATCHNEWPACKAGECODE' */
532     38,  1,     /* string 5 '{42A14A82-12F8-4E6D-970E-1B4EE7BE28B0}' */
533     10,  1,     /* string 6 'PatchFiles' */
534     12,  1,     /* string 7 '#CAB_msitest' */
535      4,  1,     /* string 8 'prop' */
536      5,  7,     /* string 9 'Patch' */
537      5,  1,     /* string 10 'File_' */
538      8,  1,     /* string 11 'Sequence' */
539      9,  1,     /* string 12 'PatchSize' */
540     10,  1,     /* string 13 'Attributes' */
541      6,  2,     /* string 14 'Header' */
542     10,  1,     /* string 15 'StreamRef_' */
543     12,  3,     /* string 16 'PatchPackage' */
544      7,  1,     /* string 17 'PatchId' */
545      6,  1,     /* string 18 'Media_' */
546     38,  1,     /* string 19 '{0F96CDC0-4CDF-4304-B283-7B9264889EF7}' */
547     15,  3,     /* string 20 'MsiPatchHeaders' */
548      9,  1      /* string 21 'StreamRef' */
549 };
550 static const char t2_data9[] = { /* SummaryInformation */
551     0xfe, 0xff, 0x00, 0x00, 0x05, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,
552     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
553     0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xe0, 0x85, 0x9f, 0xf2, 0xf9,
554     0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9,
555     0x30, 0x00, 0x00, 0x00, 0x5c, 0x01, 0x00, 0x00, 0x0b, 0x00, 0x00,
556     0x00, 0x02, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, 0x00, 0x03, 0x00,
557     0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x78,
558     0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x84, 0x00, 0x00, 0x00,
559     0x06, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00,
560     0x00, 0x9c, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0xa8, 0x00,
561     0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x09,
562     0x00, 0x00, 0x00, 0xc4, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
563     0x4c, 0x01, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x54, 0x01, 0x00,
564     0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
565     0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
566     0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
567     0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
568     0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00,
569     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x01,
570     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00,
571     0x06, 0x00, 0x00, 0x00, 0x3b, 0x31, 0x30, 0x33, 0x33, 0x00, 0x00,
572     0x00, 0x1e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
573     0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x7b,
574     0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
575     0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
576     0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
577     0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
578     0x39, 0x31, 0x33, 0x42, 0x38, 0x44, 0x31, 0x38, 0x2d, 0x46, 0x42,
579     0x42, 0x36, 0x2d, 0x34, 0x43, 0x41, 0x43, 0x2d, 0x41, 0x32, 0x33,
580     0x39, 0x2d, 0x43, 0x37, 0x34, 0x43, 0x31, 0x31, 0x45, 0x33, 0x46,
581     0x41, 0x37, 0x34, 0x7d, 0x31, 0x2e, 0x31, 0x2e, 0x31, 0x3b, 0x7b,
582     0x41, 0x32, 0x45, 0x33, 0x44, 0x36, 0x34, 0x33, 0x2d, 0x34, 0x45,
583     0x32, 0x43, 0x2d, 0x34, 0x37, 0x37, 0x46, 0x2d, 0x41, 0x33, 0x30,
584     0x39, 0x2d, 0x46, 0x37, 0x36, 0x46, 0x35, 0x35, 0x32, 0x44, 0x35,
585     0x46, 0x34, 0x33, 0x7d, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x2d,
586     0x01, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x17, 0x00, 0x27, 0x09
587 };
588
589 static const struct table_data table_transform2_data[] = {
590     { t2_name0, t2_data0, sizeof t2_data0 },
591     { t2_name1, t2_data1, sizeof t2_data1 },
592     { t2_name2, t2_data2, sizeof t2_data2 },
593     { t2_name3, t2_data3, sizeof t2_data3 },
594     { t2_name4, t2_data4, sizeof t2_data4 },
595     { t2_name5, t2_data5, sizeof t2_data5 },
596     { t2_name6, t2_data6, sizeof t2_data6 },
597     { t2_name7, t2_data7, sizeof t2_data7 - 1 },
598     { t2_name8, t2_data8, sizeof t2_data8 },
599     { t2_name9, t2_data9, sizeof t2_data9 }
600 };
601
602 #define NUM_TRANSFORM2_TABLES (sizeof table_transform2_data/sizeof table_transform2_data[0])
603
604 static void write_tables( IStorage *stg, const struct table_data *tables, UINT num_tables )
605 {
606     IStream *stm;
607     DWORD i, count;
608     HRESULT r;
609
610     for (i = 0; i < num_tables; i++)
611     {
612         r = IStorage_CreateStream( stg, tables[i].name, STGM_WRITE|STGM_SHARE_EXCLUSIVE, 0, 0, &stm );
613         if (FAILED( r ))
614         {
615             ok( 0, "failed to create stream 0x%08x\n", r );
616             continue;
617         }
618
619         r = IStream_Write( stm, tables[i].data, tables[i].size, &count );
620         if (FAILED( r ) || count != tables[i].size)
621             ok( 0, "failed to write stream\n" );
622         IStream_Release( stm );
623     }
624 }
625
626 static void create_patch( const char *filename )
627 {
628     IStorage *stg = NULL, *stg1 = NULL, *stg2 = NULL;
629     WCHAR *filenameW;
630     HRESULT r;
631     int len;
632     const DWORD mode = STGM_CREATE|STGM_READWRITE|STGM_DIRECT|STGM_SHARE_EXCLUSIVE;
633
634     const CLSID CLSID_MsiPatch = {0xc1086, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
635     const CLSID CLSID_MsiTransform = {0xc1082, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46}};
636
637     len = MultiByteToWideChar( CP_ACP, 0, filename, -1, NULL, 0 );
638     filenameW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
639     MultiByteToWideChar( CP_ACP, 0, filename, -1, filenameW, len );
640
641     r = StgCreateDocfile( filenameW, mode, 0, &stg );
642     HeapFree( GetProcessHeap(), 0, filenameW );
643     ok( r == S_OK, "failed to create storage 0x%08x\n", r );
644     if (!stg)
645         return;
646
647     r = IStorage_SetClass( stg, &CLSID_MsiPatch );
648     ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
649
650     write_tables( stg, table_patch_data, NUM_PATCH_TABLES );
651
652     r = IStorage_CreateStorage( stg, p_name7, mode, 0, 0, &stg1 );
653     ok( r == S_OK, "failed to create substorage 0x%08x\n", r );
654
655     r = IStorage_SetClass( stg1, &CLSID_MsiTransform );
656     ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
657
658     write_tables( stg1, table_transform1_data, NUM_TRANSFORM1_TABLES );
659     IStorage_Release( stg1 );
660
661     r = IStorage_CreateStorage( stg, p_name8, mode, 0, 0, &stg2 );
662     ok( r == S_OK, "failed to create substorage 0x%08x\n", r );
663
664     r = IStorage_SetClass( stg2, &CLSID_MsiTransform );
665     ok( r == S_OK, "failed to set storage type 0x%08x\n", r );
666
667     write_tables( stg2, table_transform2_data, NUM_TRANSFORM2_TABLES );
668     IStorage_Release( stg2 );
669     IStorage_Release( stg );
670 }
671
672 static void test_simple_patch( void )
673 {
674     UINT r;
675     DWORD size;
676     char path[MAX_PATH];
677     const char *query;
678     MSIHANDLE hpackage, hdb, hview, hrec;
679
680     if (!pMsiApplyPatchA)
681     {
682         win_skip("MsiApplyPatchA is not available\n");
683         return;
684     }
685
686     CreateDirectoryA( "msitest", NULL );
687     create_file( "msitest\\patch.txt", 1000 );
688
689     create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
690     create_patch( mspfile );
691
692     MsiSetInternalUI( INSTALLUILEVEL_NONE, NULL );
693
694     r = MsiInstallProductA( msifile, NULL );
695     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
696
697     size = get_pf_file_size( "msitest\\patch.txt" );
698     ok( size == 1000, "expected 1000, got %u\n", size );
699
700     r = MsiApplyPatchA( mspfile, NULL, INSTALLTYPE_DEFAULT, NULL );
701     ok( r == ERROR_SUCCESS || broken( r == ERROR_PATCH_PACKAGE_INVALID ), /* version 2.0 */
702         "expected ERROR_SUCCESS, got %u\n", r );
703
704     if (r == ERROR_PATCH_PACKAGE_INVALID)
705     {
706         win_skip("Windows Installer < 3.0 detected\n");
707         return;
708     }
709
710     size = get_pf_file_size( "msitest\\patch.txt" );
711     ok( size == 1002, "expected 1002, got %u\n", size );
712
713     strcpy( path, CURR_DIR );
714     strcat( path, "\\" );
715     strcat( path, msifile );
716
717     r = MsiOpenPackageA( path, &hpackage );
718     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
719
720     hdb = MsiGetActiveDatabase( hpackage );
721     ok( hdb, "failed to get database handle\n" );
722
723     query = "SELECT * FROM `Property` where `Property` = 'PATCHNEWPACKAGECODE'";
724     r = MsiDatabaseOpenView( hdb, query, &hview );
725     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
726
727     r = MsiViewExecute( hview, 0 );
728     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
729
730     r = MsiViewFetch( hview, &hrec );
731     todo_wine ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
732
733     MsiCloseHandle( hrec );
734     MsiViewClose( hview );
735     MsiCloseHandle( hview );
736     MsiCloseHandle( hdb );
737     MsiCloseHandle( hpackage );
738
739     r = MsiInstallProductA( msifile, "REMOVE=ALL" );
740     ok( r == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %u\n", r );
741
742     ok( !delete_pf( "msitest\\patch.txt", TRUE ), "file not removed\n" );
743     ok( !delete_pf( "msitest", FALSE ), "directory not removed\n" );
744
745     DeleteFileA( msifile );
746     DeleteFileA( mspfile );
747     RemoveDirectoryA( "msitest" );
748 }
749
750 static void test_MsiOpenDatabase( void )
751 {
752     UINT r;
753     MSIHANDLE hdb;
754
755     r = MsiOpenDatabase( mspfile, MSIDBOPEN_CREATE, &hdb );
756     ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
757
758     r = MsiDatabaseCommit( hdb );
759     ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
760     MsiCloseHandle( hdb );
761
762     r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
763     ok(r == ERROR_OPEN_FAILED, "expected ERROR_OPEN_FAILED, got %u\n", r);
764     DeleteFileA( mspfile );
765
766     r = MsiOpenDatabase( mspfile, MSIDBOPEN_CREATE + MSIDBOPEN_PATCHFILE, &hdb );
767     ok(r == ERROR_SUCCESS , "failed to open database %u\n", r);
768
769     r = MsiDatabaseCommit( hdb );
770     ok(r == ERROR_SUCCESS, "failed to commit database %u\n", r);
771     MsiCloseHandle( hdb );
772
773     r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
774     ok(r == ERROR_SUCCESS, "failed to open database %u\n", r);
775     MsiCloseHandle( hdb );
776     DeleteFileA( mspfile );
777
778     create_database( msifile, tables, sizeof(tables) / sizeof(struct msi_table) );
779     create_patch( mspfile );
780
781     r = MsiOpenDatabase( msifile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
782     ok(r == ERROR_OPEN_FAILED, "failed to open database %u\n", r );
783
784     r = MsiOpenDatabase( mspfile, MSIDBOPEN_READONLY + MSIDBOPEN_PATCHFILE, &hdb );
785     ok(r == ERROR_SUCCESS, "failed to open database %u\n", r );
786     MsiCloseHandle( hdb );
787
788     DeleteFileA( msifile );
789     DeleteFileA( mspfile );
790 }
791
792 START_TEST(patch)
793 {
794     DWORD len;
795     char temp_path[MAX_PATH], prev_path[MAX_PATH];
796
797     init_function_pointers();
798
799     GetCurrentDirectoryA( MAX_PATH, prev_path );
800     GetTempPath( MAX_PATH, temp_path );
801     SetCurrentDirectoryA( temp_path );
802
803     strcpy( CURR_DIR, temp_path );
804     len = strlen( CURR_DIR );
805
806     if (len && (CURR_DIR[len - 1] == '\\'))
807         CURR_DIR[len - 1] = 0;
808
809     get_program_files_dir( PROG_FILES_DIR, COMMON_FILES_DIR );
810
811     test_simple_patch();
812     test_MsiOpenDatabase();
813
814     SetCurrentDirectoryA( prev_path );
815 }