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