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