1 /*
2  * DLL for testing type 1 custom actions
3  *
4  * Copyright 2017 Zebediah Figura
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 #include <stdarg.h>
22 #include <stdio.h>
23 
24 #include <windef.h>
25 #include <winbase.h>
26 #include <winsvc.h>
27 #include <odbcinst.h>
28 #define COBJMACROS
29 #include <shlobj.h>
30 #include <msxml.h>
31 #include <msi.h>
32 #include <msiquery.h>
33 #include <msidefs.h>
34 
35 static int todo_level, todo_do_loop;
36 
37 static void WINAPIV ok_(MSIHANDLE hinst, int todo, const char *file, int line, int condition, const char *msg, ...)
38 {
39     static char buffer[2000];
40     MSIHANDLE record;
41     va_list valist;
42 
43     va_start(valist, msg);
44     vsprintf(buffer, msg, valist);
45     va_end(valist);
46 
47     record = MsiCreateRecord(5);
48     MsiRecordSetInteger(record, 1, todo);
49     MsiRecordSetStringA(record, 2, file);
50     MsiRecordSetInteger(record, 3, line);
51     MsiRecordSetInteger(record, 4, condition);
52     MsiRecordSetStringA(record, 5, buffer);
53     MsiProcessMessage(hinst, INSTALLMESSAGE_USER, record);
54     MsiCloseHandle(record);
55 }
56 
57 static void winetest_start_todo( int is_todo )
58 {
59     todo_level = (todo_level << 1) | (is_todo != 0);
60     todo_do_loop=1;
61 }
62 
63 static int winetest_loop_todo(void)
64 {
65     int do_loop=todo_do_loop;
66     todo_do_loop=0;
67     return do_loop;
68 }
69 
70 static void winetest_end_todo(void)
71 {
72     todo_level >>= 1;
73 }
74 
75 #define ok(hinst, condition, ...)   ok_(hinst, todo_level, __FILE__, __LINE__, condition, __VA_ARGS__)
76 #define todo_wine_if(is_todo) for (winetest_start_todo(is_todo); \
77                                    winetest_loop_todo(); \
78                                    winetest_end_todo())
79 #define todo_wine   todo_wine_if(1)
80 
81 static const char *dbgstr_w(WCHAR *str)
82 {
83     static char buffer[300], *p;
84 
85     if (!str) return "(null)";
86 
87     p = buffer;
88     *p++ = 'L';
89     *p++ = '"';
90     while ((*p++ = *str++));
91     *p++ = '"';
92     *p++ = 0;
93 
94     return buffer;
95 }
96 
97 static void check_prop(MSIHANDLE hinst, const char *prop, const char *expect)
98 {
99     char buffer[10] = "x";
100     DWORD sz = sizeof(buffer);
101     UINT r = MsiGetPropertyA(hinst, prop, buffer, &sz);
102     ok(hinst, !r, "'%s': got %u\n", prop, r);
103     ok(hinst, sz == strlen(buffer), "'%s': expected %u, got %u\n", prop, strlen(buffer), sz);
104     ok(hinst, !strcmp(buffer, expect), "expected '%s', got '%s'\n", expect, buffer);
105 }
106 
107 static void test_props(MSIHANDLE hinst)
108 {
109     char buffer[10];
110     WCHAR bufferW[10];
111     DWORD sz;
112     UINT r;
113 
114     /* test invalid values */
115     r = MsiGetPropertyA(hinst, NULL, NULL, NULL);
116     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
117 
118     r = MsiGetPropertyA(hinst, "boo", NULL, NULL);
119     ok(hinst, !r, "got %u\n", r);
120 
121     r = MsiGetPropertyA(hinst, "boo", buffer, NULL );
122     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
123 
124     sz = 0;
125     r = MsiGetPropertyA(hinst, "boo", NULL, &sz);
126     ok(hinst, !r, "got %u\n", r);
127     ok(hinst, sz == 0, "got size %u\n", sz);
128 
129     sz = 0;
130     strcpy(buffer,"x");
131     r = MsiGetPropertyA(hinst, "boo", buffer, &sz);
132     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
133     ok(hinst, !strcmp(buffer, "x"), "got \"%s\"\n", buffer);
134     ok(hinst, sz == 0, "got size %u\n", sz);
135 
136     sz = 1;
137     strcpy(buffer,"x");
138     r = MsiGetPropertyA(hinst, "boo", buffer, &sz);
139     ok(hinst, !r, "got %u\n", r);
140     ok(hinst, !buffer[0], "got \"%s\"\n", buffer);
141     ok(hinst, sz == 0, "got size %u\n", sz);
142 
143     /* set the property to something */
144     r = MsiSetPropertyA(hinst, NULL, NULL);
145     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
146 
147     r = MsiSetPropertyA(hinst, "", NULL);
148     ok(hinst, !r, "got %u\n", r);
149 
150     r = MsiSetPropertyA(hinst, "", "asdf");
151     ok(hinst, r == ERROR_FUNCTION_FAILED, "got %u\n", r);
152 
153     r = MsiSetPropertyA(hinst, "=", "asdf");
154     ok(hinst, !r, "got %u\n", r);
155     check_prop(hinst, "=", "asdf");
156 
157     r = MsiSetPropertyA(hinst, " ", "asdf");
158     ok(hinst, !r, "got %u\n", r);
159     check_prop(hinst, " ", "asdf");
160 
161     r = MsiSetPropertyA(hinst, "'", "asdf");
162     ok(hinst, !r, "got %u\n", r);
163     check_prop(hinst, "'", "asdf");
164 
165     r = MsiSetPropertyA(hinst, "boo", NULL);
166     ok(hinst, !r, "got %u\n", r);
167     check_prop(hinst, "boo", "");
168 
169     r = MsiSetPropertyA(hinst, "boo", "");
170     ok(hinst, !r, "got %u\n", r);
171     check_prop(hinst, "boo", "");
172 
173     r = MsiSetPropertyA(hinst, "boo", "xyz");
174     ok(hinst, !r, "got %u\n", r);
175     check_prop(hinst, "boo", "xyz");
176 
177     r = MsiGetPropertyA(hinst, "boo", NULL, NULL);
178     ok(hinst, !r, "got %u\n", r);
179 
180     r = MsiGetPropertyA(hinst, "boo", buffer, NULL );
181     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
182 
183     /* Returned size is in bytes, not chars, but only for custom actions.
184      * Seems to be a casualty of RPC... */
185 
186     sz = 0;
187     r = MsiGetPropertyA(hinst, "boo", NULL, &sz);
188     ok(hinst, !r, "got %u\n", r);
189     ok(hinst, sz == 6, "got size %u\n", sz);
190 
191     sz = 0;
192     strcpy(buffer,"q");
193     r = MsiGetPropertyA(hinst, "boo", buffer, &sz);
194     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
195     ok(hinst, !strcmp(buffer, "q"), "got \"%s\"\n", buffer);
196     ok(hinst, sz == 6, "got size %u\n", sz);
197 
198     sz = 1;
199     strcpy(buffer,"x");
200     r = MsiGetPropertyA(hinst, "boo", buffer, &sz);
201     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
202     ok(hinst, !buffer[0], "got \"%s\"\n", buffer);
203     ok(hinst, sz == 6, "got size %u\n", sz);
204 
205     sz = 3;
206     strcpy(buffer,"x");
207     r = MsiGetPropertyA(hinst, "boo", buffer, &sz);
208     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
209     ok(hinst, !strcmp(buffer, "xy"), "got \"%s\"\n", buffer);
210     ok(hinst, sz == 6, "got size %u\n", sz);
211 
212     sz = 4;
213     strcpy(buffer,"x");
214     r = MsiGetPropertyA(hinst, "boo", buffer, &sz);
215     ok(hinst, !r, "got %u\n", r);
216     ok(hinst, !strcmp(buffer, "xyz"), "got \"%s\"\n", buffer);
217     ok(hinst, sz == 3, "got size %u\n", sz);
218 
219     r = MsiGetPropertyW(hinst, L"boo", NULL, NULL);
220     ok(hinst, !r, "got %u\n", r);
221 
222     r = MsiGetPropertyW(hinst, L"boo", bufferW, NULL );
223     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
224 
225     sz = 0;
226     r = MsiGetPropertyW(hinst, L"boo", NULL, &sz);
227     ok(hinst, !r, "got %u\n", r);
228     ok(hinst, sz == 3, "got size %u\n", sz);
229 
230     sz = 0;
231     lstrcpyW(bufferW, L"boo");
232     r = MsiGetPropertyW(hinst, L"boo", bufferW, &sz);
233     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
234     ok(hinst, !lstrcmpW(bufferW, L"boo"), "got %s\n", dbgstr_w(bufferW));
235     ok(hinst, sz == 3, "got size %u\n", sz);
236 
237     sz = 1;
238     lstrcpyW(bufferW, L"boo");
239     r = MsiGetPropertyW(hinst, L"boo", bufferW, &sz);
240     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
241     ok(hinst, !bufferW[0], "got %s\n", dbgstr_w(bufferW));
242     ok(hinst, sz == 3, "got size %u\n", sz);
243 
244     sz = 3;
245     lstrcpyW(bufferW, L"boo");
246     r = MsiGetPropertyW(hinst, L"boo", bufferW, &sz);
247     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
248     ok(hinst, !lstrcmpW(bufferW, L"xy"), "got %s\n", dbgstr_w(bufferW));
249     ok(hinst, sz == 3, "got size %u\n", sz);
250 
251     sz = 4;
252     lstrcpyW(bufferW, L"boo");
253     r = MsiGetPropertyW(hinst, L"boo", bufferW, &sz);
254     ok(hinst, !r, "got %u\n", r);
255     ok(hinst, !lstrcmpW(bufferW, L"xyz"), "got %s\n", dbgstr_w(bufferW));
256     ok(hinst, sz == 3, "got size %u\n", sz);
257 
258     r = MsiSetPropertyA(hinst, "boo", NULL);
259     ok(hinst, !r, "got %u\n", r);
260     check_prop(hinst, "boo", "");
261 
262     sz = 0;
263     r = MsiGetPropertyA(hinst, "embednullprop", NULL, &sz);
264     ok(hinst, !r, "got %u\n", r);
265     ok(hinst, sz == 6, "got size %u\n", sz);
266 
267     sz = 4;
268     memset(buffer, 0xcc, sizeof(buffer));
269     r = MsiGetPropertyA(hinst, "embednullprop", buffer, &sz);
270     ok(hinst, !r, "got %u\n", r);
271     ok(hinst, sz == 3, "got size %u\n", sz);
272     ok(hinst, !memcmp(buffer, "a\0\0\0\xcc", 5), "wrong data\n");
273 }
274 
275 static void test_db(MSIHANDLE hinst)
276 {
277     static const UINT prop_type[20] = { VT_EMPTY, VT_EMPTY, VT_LPSTR, VT_EMPTY, VT_EMPTY,
278                                         VT_EMPTY, VT_EMPTY, VT_LPSTR, VT_EMPTY, VT_LPSTR,
279                                         VT_EMPTY, VT_EMPTY, VT_EMPTY, VT_EMPTY, VT_I4,
280                                         VT_I4, VT_EMPTY, VT_EMPTY, VT_EMPTY, VT_EMPTY };
281     MSIHANDLE hdb, hdb2, view, rec, rec2, suminfo;
282     char buffer[10];
283     DWORD sz;
284     UINT r, count, type, i;
285     INT int_value;
286     FILETIME ft;
287 
288     hdb = MsiGetActiveDatabase(hinst);
289     ok(hinst, hdb, "MsiGetActiveDatabase failed\n");
290 
291     r = MsiDatabaseIsTablePersistentA(hdb, "Test");
292     ok(hinst, r == MSICONDITION_TRUE, "got %u\n", r);
293 
294     r = MsiDatabaseOpenViewA(hdb, NULL, &view);
295     ok(hinst, r == ERROR_BAD_QUERY_SYNTAX, "got %u\n", r);
296 
297     r = MsiDatabaseOpenViewA(hdb, "SELECT * FROM `Test`", NULL);
298     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
299 
300     r = MsiDatabaseOpenViewA(hdb, "SELECT * FROM `Test`", &view);
301     ok(hinst, !r, "got %u\n", r);
302 
303     r = MsiViewGetColumnInfo(view, MSICOLINFO_NAMES, &rec2);
304     ok(hinst, !r, "got %u\n", r);
305 
306     sz = sizeof(buffer);
307     r = MsiRecordGetStringA(rec2, 1, buffer, &sz);
308     ok(hinst, !r, "got %u\n", r);
309     ok(hinst, sz == strlen(buffer), "got size %u\n", sz);
310     ok(hinst, !strcmp(buffer, "Name"), "got '%s'\n", buffer);
311 
312     /* Test MsiGetActiveDatabase + MsiDatabaseIsTablePersistent once again */
313     hdb2 = MsiGetActiveDatabase(hinst);
314     ok(hinst, hdb2, "MsiGetActiveDatabase failed\n");
315     ok(hinst, hdb2 != hdb, "db handles should be different\n");
316 
317     r = MsiDatabaseIsTablePersistentA(hdb2, "Test");
318     ok(hinst, r == MSICONDITION_TRUE, "got %u\n", r);
319 
320     r = MsiCloseHandle(hdb2);
321     ok(hinst, !r, "got %u\n", r);
322 
323     r = MsiCloseHandle(rec2);
324     ok(hinst, !r, "got %u\n", r);
325 
326     r = MsiViewExecute(view, 0);
327     ok(hinst, !r, "got %u\n", r);
328 
329     r = MsiViewFetch(view, &rec2);
330     ok(hinst, !r, "got %u\n", r);
331 
332     r = MsiRecordGetFieldCount(rec2);
333     ok(hinst, r == 3, "got %u\n", r);
334 
335     sz = sizeof(buffer);
336     r = MsiRecordGetStringA(rec2, 1, buffer, &sz);
337     ok(hinst, !r, "got %u\n", r);
338     ok(hinst, sz == strlen(buffer), "got size %u\n", sz);
339     ok(hinst, !strcmp(buffer, "one"), "got '%s'\n", buffer);
340 
341     r = MsiRecordGetInteger(rec2, 2);
342     ok(hinst, r == 1, "got %d\n", r);
343 
344     sz = sizeof(buffer);
345     r = MsiRecordReadStream(rec2, 3, buffer, &sz);
346     ok(hinst, !r, "got %u\n", r);
347     ok(hinst, !memcmp(buffer, "unus", 4), "wrong data\n");
348 
349     r = MsiCloseHandle(rec2);
350     ok(hinst, !r, "got %u\n", r);
351 
352     r = MsiViewFetch(view, &rec2);
353     ok(hinst, !r, "got %u\n", r);
354 
355     r = MsiRecordGetFieldCount(rec2);
356     ok(hinst, r == 3, "got %u\n", r);
357 
358     sz = sizeof(buffer);
359     r = MsiRecordGetStringA(rec2, 1, buffer, &sz);
360     ok(hinst, !r, "got %u\n", r);
361     ok(hinst, sz == strlen(buffer), "got size %u\n", sz);
362     ok(hinst, !strcmp(buffer, "two"), "got '%s'\n", buffer);
363 
364     r = MsiRecordGetInteger(rec2, 2);
365     ok(hinst, r == 2, "got %d\n", r);
366 
367     sz = sizeof(buffer);
368     r = MsiRecordReadStream(rec2, 3, buffer, &sz);
369     ok(hinst, !r, "got %u\n", r);
370     ok(hinst, !memcmp(buffer, "duo", 3), "wrong data\n");
371 
372     r = MsiViewModify(view, MSIMODIFY_REFRESH, 0);
373     ok(hinst, r == ERROR_INVALID_HANDLE, "got %u\n", r);
374 
375     r = MsiRecordSetStringA(rec2, 1, "three");
376     ok(hinst, !r, "got %u\n", r);
377 
378     r = MsiRecordSetInteger(rec2, 2, 3);
379     ok(hinst, !r, "got %u\n", r);
380 
381     r = MsiRecordSetInteger(rec2, 3, 3);
382     ok(hinst, !r, "got %u\n", r);
383 
384     r = MsiViewModify(view, MSIMODIFY_REFRESH, rec2);
385     ok(hinst, !r, "got %d\n", r);
386 
387     sz = sizeof(buffer);
388     r = MsiRecordGetStringA(rec2, 1, buffer, &sz);
389     ok(hinst, !r, "got %u\n", r);
390     ok(hinst, sz == strlen(buffer), "got size %u\n", sz);
391     ok(hinst, !strcmp(buffer, "two"), "got '%s'\n", buffer);
392 
393     r = MsiRecordGetInteger(rec2, 2);
394     ok(hinst, r == 2, "got %d\n", r);
395 
396     sz = sizeof(buffer);
397     r = MsiRecordReadStream(rec2, 3, buffer, &sz);
398     ok(hinst, !r, "got %u\n", r);
399     ok(hinst, !memcmp(buffer, "duo", 3), "wrong data\n");
400 
401     r = MsiCloseHandle(rec2);
402     ok(hinst, !r, "got %u\n", r);
403 
404     r = MsiViewFetch(view, &rec2);
405     ok(hinst, r == ERROR_NO_MORE_ITEMS, "got %u\n", r);
406     ok(hinst, !rec2, "got %u\n", rec2);
407 
408     r = MsiViewClose(view);
409     ok(hinst, !r, "got %u\n", r);
410 
411     r = MsiCloseHandle(view);
412     ok(hinst, !r, "got %u\n", r);
413 
414     r = MsiDatabaseOpenViewA(hdb, "SELECT * FROM `Test` WHERE `Name` = ?", &view);
415     ok(hinst, !r, "got %u\n", r);
416 
417     rec = MsiCreateRecord(1);
418     MsiRecordSetStringA(rec, 1, "one");
419 
420     r = MsiViewExecute(view, rec);
421     ok(hinst, !r, "got %u\n", r);
422 
423     r = MsiViewFetch(view, &rec2);
424     ok(hinst, !r, "got %u\n", r);
425 
426     r = MsiRecordGetInteger(rec2, 2);
427     ok(hinst, r == 1, "got %d\n", r);
428 
429     r = MsiCloseHandle(rec2);
430     ok(hinst, !r, "got %u\n", r);
431 
432     r = MsiViewFetch(view, &rec2);
433     ok(hinst, r == ERROR_NO_MORE_ITEMS, "got %u\n", r);
434     ok(hinst, !rec2, "got %u\n", rec2);
435 
436     r = MsiCloseHandle(rec);
437     ok(hinst, !r, "got %u\n", r);
438 
439     r = MsiCloseHandle(view);
440     ok(hinst, !r, "got %u\n", r);
441 
442     /* test MsiDatabaseGetPrimaryKeys() */
443     r = MsiDatabaseGetPrimaryKeysA(hdb, "Test", &rec);
444     ok(hinst, !r, "got %u\n", r);
445 
446     r = MsiRecordGetFieldCount(rec);
447     ok(hinst, r == 1, "got %d\n", r);
448 
449     sz = sizeof(buffer);
450     r = MsiRecordGetStringA(rec, 0, buffer, &sz);
451     ok(hinst, !r, "got %u\n", r);
452     ok(hinst, sz == strlen(buffer), "got size %u\n", sz);
453     ok(hinst, !strcmp(buffer, "Test"), "got '%s'\n", buffer);
454 
455     sz = sizeof(buffer);
456     r = MsiRecordGetStringA(rec, 1, buffer, &sz);
457     ok(hinst, !r, "got %u\n", r);
458     ok(hinst, sz == strlen(buffer), "got size %u\n", sz);
459     ok(hinst, !strcmp(buffer, "Name"), "got '%s'\n", buffer);
460 
461     r = MsiCloseHandle(rec);
462     ok(hinst, !r, "got %u\n", r);
463 
464     r = MsiGetSummaryInformationA(hdb, NULL, 1, NULL);
465     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
466 
467     r = MsiGetSummaryInformationA(hdb, NULL, 1, &suminfo);
468     ok(hinst, !r, "got %u\n", r);
469 
470     r = MsiSummaryInfoGetPropertyCount(suminfo, NULL);
471     ok(hinst, r == RPC_X_NULL_REF_POINTER, "got %u\n", r);
472 
473     count = 0xdeadbeef;
474     r = MsiSummaryInfoGetPropertyCount(suminfo, &count);
475     ok(hinst, !r, "got %u\n", r);
476     ok(hinst, count == 5, "got %u\n", count);
477 
478     r = MsiSummaryInfoGetPropertyA(suminfo, 0, NULL, NULL, NULL, NULL, NULL);
479     ok(hinst, r == RPC_X_NULL_REF_POINTER, "got %u\n", r);
480 
481     for (i = 0; i < 20; i++)
482     {
483         /* for some reason query for PID_TITLE leads to install failure under Windows */
484         if (i == PID_TITLE) continue;
485 
486         type = 0xdeadbeef;
487         int_value = 0xdeadbeef;
488         *buffer = 0;
489         sz = sizeof(buffer);
490         r = MsiSummaryInfoGetPropertyA(suminfo, i, &type, &int_value, &ft, buffer, &sz);
491         if (sz == sizeof(buffer) || i == PID_TEMPLATE)
492             ok(hinst, !r, "%u: got %u\n", i, r);
493         else
494             ok(hinst, r == ERROR_MORE_DATA, "%u: got %u\n", i, r);
495         ok(hinst, type == prop_type[i], "%u: expected %u, got %u\n", i, prop_type[i], type);
496         if (i == PID_PAGECOUNT)
497             ok(hinst, int_value == 100, "%u: got %u\n", i, int_value);
498         else
499             ok(hinst, int_value == 0, "%u: got %u\n", i, int_value);
500         if (i == PID_TEMPLATE)
501         {
502             ok(hinst, sz == 5, "%u: got %u\n", i, sz);
503             ok(hinst, !lstrcmpA(buffer, ";1033"), "%u: got %s\n", i, buffer);
504         }
505         else if (i == PID_REVNUMBER)
506         {
507             ok(hinst, sz == 76, "%u: got %u\n", i, sz);
508             ok(hinst, !lstrcmpA(buffer, "{004757CA"), "%u: got %s\n", i, buffer);
509         }
510         else
511         {
512             ok(hinst, sz == sizeof(buffer), "%u: got %u\n", i, sz);
513             ok(hinst, !*buffer, "%u: got %s\n", i, buffer);
514         }
515     }
516 
517     GetSystemTimeAsFileTime(&ft);
518 
519     for (i = 0; i < 20; i++)
520     {
521         r = MsiSummaryInfoSetPropertyA(suminfo, i, prop_type[i], 1252, &ft, "");
522         ok(hinst, r == ERROR_FUNCTION_FAILED, "%u: got %u\n", i, r);
523 
524         r = MsiSummaryInfoSetPropertyW(suminfo, i, prop_type[i], 1252, &ft, L"");
525         ok(hinst, r == ERROR_FUNCTION_FAILED, "%u: got %u\n", i, r);
526     }
527 
528     r = MsiCloseHandle(suminfo);
529     ok(hinst, !r, "got %u\n", r);
530 
531     r = MsiCloseHandle(hdb);
532     ok(hinst, !r, "got %u\n", r);
533 }
534 
535 static void test_doaction(MSIHANDLE hinst)
536 {
537     UINT r;
538 
539     r = MsiDoActionA(hinst, "nested51");
540     ok(hinst, !r, "got %u\n", r);
541     check_prop(hinst, "nested", "1");
542 
543     r = MsiDoActionA(hinst, "nested1");
544     ok(hinst, !r, "got %u\n", r);
545     check_prop(hinst, "nested", "2");
546 
547     r = MsiSequenceA(hinst, NULL, 0);
548     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
549 
550     r = MsiSequenceA(hinst, "TestSequence", 0);
551     ok(hinst, !r, "got %u\n", r);
552     check_prop(hinst, "nested", "1");
553 }
554 
555 UINT WINAPI nested(MSIHANDLE hinst)
556 {
557     MsiSetPropertyA(hinst, "nested", "2");
558 
559     return ERROR_SUCCESS;
560 }
561 
562 static void test_targetpath(MSIHANDLE hinst)
563 {
564     WCHAR bufferW[100];
565     char buffer[100];
566     DWORD sz, srcsz;
567     UINT r;
568 
569     /* test invalid values */
570     r = MsiGetTargetPathA(hinst, NULL, NULL, NULL);
571     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
572 
573     r = MsiGetTargetPathA(hinst, "TARGETDIR", NULL, NULL );
574     ok(hinst, !r, "got %u\n", r);
575 
576     r = MsiGetTargetPathA(hinst, "TARGETDIR", buffer, NULL );
577     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
578 
579     /* Returned size is in bytes, not chars, but only for custom actions.
580      * Seems to be a casualty of RPC... */
581 
582     sz = 0;
583     r = MsiGetTargetPathA(hinst, "TARGETDIR", NULL, &sz);
584     ok(hinst, !r, "got %u\n", r);
585     ok(hinst, sz == 6, "got size %u\n", sz);
586 
587     sz = 0;
588     strcpy(buffer,"q");
589     r = MsiGetTargetPathA(hinst, "TARGETDIR", buffer, &sz);
590     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
591     ok(hinst, !strcmp(buffer, "q"), "got \"%s\"\n", buffer);
592     ok(hinst, sz == 6, "got size %u\n", sz);
593 
594     sz = 1;
595     strcpy(buffer,"x");
596     r = MsiGetTargetPathA(hinst, "TARGETDIR", buffer, &sz);
597     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
598     ok(hinst, !buffer[0], "got \"%s\"\n", buffer);
599     ok(hinst, sz == 6, "got size %u\n", sz);
600 
601     sz = 3;
602     strcpy(buffer,"x");
603     r = MsiGetTargetPathA(hinst, "TARGETDIR", buffer, &sz);
604     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
605     ok(hinst, !strcmp(buffer, "C:"), "got \"%s\"\n", buffer);
606     ok(hinst, sz == 6, "got size %u\n", sz);
607 
608     sz = 4;
609     strcpy(buffer,"x");
610     r = MsiGetTargetPathA(hinst, "TARGETDIR", buffer, &sz);
611     ok(hinst, !r, "got %u\n", r);
612     ok(hinst, !strcmp(buffer, "C:\\"), "got \"%s\"\n", buffer);
613     ok(hinst, sz == 3, "got size %u\n", sz);
614 
615     sz = 0;
616     r = MsiGetTargetPathW(hinst, L"TARGETDIR", NULL, &sz);
617     ok(hinst, !r, "got %u\n", r);
618     ok(hinst, sz == 3, "got size %u\n", sz);
619 
620     sz = 0;
621     bufferW[0] = 'q';
622     r = MsiGetTargetPathW(hinst, L"TARGETDIR", bufferW, &sz);
623     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
624     ok(hinst, bufferW[0] == 'q', "got %s\n", dbgstr_w(bufferW));
625     ok(hinst, sz == 3, "got size %u\n", sz);
626 
627     sz = 1;
628     bufferW[0] = 'q';
629     r = MsiGetTargetPathW(hinst, L"TARGETDIR", bufferW, &sz);
630     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
631     ok(hinst, !bufferW[0], "got %s\n", dbgstr_w(bufferW));
632     ok(hinst, sz == 3, "got size %u\n", sz);
633 
634     sz = 3;
635     bufferW[0] = 'q';
636     r = MsiGetTargetPathW(hinst, L"TARGETDIR", bufferW, &sz);
637     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
638     ok(hinst, !lstrcmpW(bufferW, L"C:"), "got %s\n", dbgstr_w(bufferW));
639     ok(hinst, sz == 3, "got size %u\n", sz);
640 
641     sz = 4;
642     bufferW[0] = 'q';
643     r = MsiGetTargetPathW(hinst, L"TARGETDIR", bufferW, &sz);
644     ok(hinst, !r, "got %u\n", r);
645     ok(hinst, !lstrcmpW(bufferW, L"C:\\"), "got %s\n", dbgstr_w(bufferW));
646     ok(hinst, sz == 3, "got size %u\n", sz);
647 
648     r = MsiSetTargetPathA(hinst, NULL, "C:\\subdir");
649     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
650 
651     r = MsiSetTargetPathA(hinst, "MSITESTDIR", NULL);
652     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
653 
654     r = MsiSetTargetPathA(hinst, "MSITESTDIR", "C:\\subdir");
655     ok(hinst, !r, "got %u\n", r);
656 
657     sz = sizeof(buffer);
658     r = MsiGetTargetPathA(hinst, "MSITESTDIR", buffer, &sz);
659     ok(hinst, !r, "got %u\n", r);
660     ok(hinst, !strcmp(buffer, "C:\\subdir\\"), "got \"%s\"\n", buffer);
661 
662     r = MsiSetTargetPathA(hinst, "MSITESTDIR", "C:\\");
663 
664     /* test GetSourcePath() */
665 
666     r = MsiGetSourcePathA(hinst, NULL, NULL, NULL);
667     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
668 
669     r = MsiGetSourcePathA(hinst, "TARGETDIR", NULL, NULL );
670     ok(hinst, !r, "got %u\n", r);
671 
672     r = MsiGetSourcePathA(hinst, "TARGETDIR", buffer, NULL );
673     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
674 
675     /* Returned size is in bytes, not chars, but only for custom actions.
676      * Seems to be a casualty of RPC... */
677 
678     srcsz = 0;
679     MsiGetSourcePathW(hinst, L"TARGETDIR", NULL, &srcsz);
680 
681     sz = 0;
682     r = MsiGetSourcePathA(hinst, "TARGETDIR", NULL, &sz);
683     ok(hinst, !r, "got %u\n", r);
684     ok(hinst, sz == srcsz * 2, "got size %u\n", sz);
685 
686     sz = 0;
687     strcpy(buffer,"q");
688     r = MsiGetSourcePathA(hinst, "TARGETDIR", buffer, &sz);
689     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
690     ok(hinst, !strcmp(buffer, "q"), "got \"%s\"\n", buffer);
691     ok(hinst, sz == srcsz * 2, "got size %u\n", sz);
692 
693     sz = 1;
694     strcpy(buffer,"x");
695     r = MsiGetSourcePathA(hinst, "TARGETDIR", buffer, &sz);
696     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
697     ok(hinst, !buffer[0], "got \"%s\"\n", buffer);
698     ok(hinst, sz == srcsz * 2, "got size %u\n", sz);
699 
700     sz = srcsz;
701     strcpy(buffer,"x");
702     r = MsiGetSourcePathA(hinst, "TARGETDIR", buffer, &sz);
703     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
704     ok(hinst, strlen(buffer) == srcsz - 1, "wrong buffer length %d\n", strlen(buffer));
705     ok(hinst, sz == srcsz * 2, "got size %u\n", sz);
706 
707     sz = srcsz + 1;
708     strcpy(buffer,"x");
709     r = MsiGetSourcePathA(hinst, "TARGETDIR", buffer, &sz);
710     ok(hinst, !r, "got %u\n", r);
711     ok(hinst, strlen(buffer) == srcsz, "wrong buffer length %d\n", strlen(buffer));
712     ok(hinst, sz == srcsz, "got size %u\n", sz);
713 
714     sz = 0;
715     r = MsiGetSourcePathW(hinst, L"TARGETDIR", NULL, &sz);
716     ok(hinst, !r, "got %u\n", r);
717     ok(hinst, sz == srcsz, "got size %u\n", sz);
718 
719     sz = 0;
720     bufferW[0] = 'q';
721     r = MsiGetSourcePathW(hinst, L"TARGETDIR", bufferW, &sz);
722     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
723     ok(hinst, bufferW[0] == 'q', "got %s\n", dbgstr_w(bufferW));
724     ok(hinst, sz == srcsz, "got size %u\n", sz);
725 
726     sz = 1;
727     bufferW[0] = 'q';
728     r = MsiGetSourcePathW(hinst, L"TARGETDIR", bufferW, &sz);
729     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
730     ok(hinst, !bufferW[0], "got %s\n", dbgstr_w(bufferW));
731     ok(hinst, sz == srcsz, "got size %u\n", sz);
732 
733     sz = srcsz;
734     bufferW[0] = 'q';
735     r = MsiGetSourcePathW(hinst, L"TARGETDIR", bufferW, &sz);
736     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
737     ok(hinst, lstrlenW(bufferW) == srcsz - 1, "wrong buffer length %d\n", lstrlenW(bufferW));
738     ok(hinst, sz == srcsz, "got size %u\n", sz);
739 
740     sz = srcsz + 1;
741     bufferW[0] = 'q';
742     r = MsiGetSourcePathW(hinst, L"TARGETDIR", bufferW, &sz);
743     ok(hinst, !r, "got %u\n", r);
744     ok(hinst, lstrlenW(bufferW) == srcsz, "wrong buffer length %d\n", lstrlenW(bufferW));
745     ok(hinst, sz == srcsz, "got size %u\n", sz);
746 }
747 
748 static void test_misc(MSIHANDLE hinst)
749 {
750     MSICONDITION cond;
751     LANGID lang;
752     UINT r;
753 
754     r = MsiSetMode(hinst, MSIRUNMODE_REBOOTATEND, FALSE);
755     ok(hinst, !r, "got %u\n", r);
756 
757     lang = MsiGetLanguage(hinst);
758     ok(hinst, lang == 1033, "got %u\n", lang);
759 
760     check_prop(hinst, "INSTALLLEVEL", "3");
761     r = MsiSetInstallLevel(hinst, 123);
762     ok(hinst, !r, "got %u\n", r);
763     check_prop(hinst, "INSTALLLEVEL", "123");
764     MsiSetInstallLevel(hinst, 3);
765 
766     cond = MsiEvaluateConditionA(hinst, NULL);
767     ok(hinst, cond == MSICONDITION_NONE, "got %u\n", cond);
768     MsiSetPropertyA(hinst, "condprop", "1");
769     cond = MsiEvaluateConditionA(hinst, "condprop = 1");
770     ok(hinst, cond == MSICONDITION_TRUE, "got %u\n", cond);
771 }
772 
773 static void test_feature_states(MSIHANDLE hinst)
774 {
775     INSTALLSTATE state, action;
776     UINT r;
777 
778     /* test feature states */
779 
780     r = MsiGetFeatureStateA(hinst, NULL, &state, &action);
781     ok(hinst, r == ERROR_UNKNOWN_FEATURE, "got %u\n", r);
782 
783     r = MsiGetFeatureStateA(hinst, "fake", &state, &action);
784     ok(hinst, r == ERROR_UNKNOWN_FEATURE, "got %u\n", r);
785 
786     r = MsiGetFeatureStateA(hinst, "One", NULL, &action);
787     ok(hinst, r == RPC_X_NULL_REF_POINTER, "got %u\n", r);
788 
789     r = MsiGetFeatureStateA(hinst, "One", &state, NULL);
790     ok(hinst, r == RPC_X_NULL_REF_POINTER, "got %u\n", r);
791 
792     r = MsiGetFeatureStateA(hinst, "One", &state, &action);
793     ok(hinst, !r, "got %u\n", r);
794     ok(hinst, state == INSTALLSTATE_ABSENT, "got state %d\n", state);
795     ok(hinst, action == INSTALLSTATE_LOCAL, "got action %d\n", action);
796 
797     r = MsiSetFeatureStateA(hinst, NULL, INSTALLSTATE_ABSENT);
798     ok(hinst, r == ERROR_UNKNOWN_FEATURE, "got %u\n", r);
799 
800     r = MsiSetFeatureStateA(hinst, "One", INSTALLSTATE_ADVERTISED);
801     ok(hinst, !r, "got %u\n", r);
802 
803     r = MsiGetFeatureStateA(hinst, "One", &state, &action);
804     ok(hinst, !r, "got %u\n", r);
805     ok(hinst, action == INSTALLSTATE_ADVERTISED, "got action %d\n", action);
806 
807     r = MsiSetFeatureStateA(hinst, "One", INSTALLSTATE_LOCAL);
808     ok(hinst, !r, "got %u\n", r);
809 
810     r = MsiGetFeatureStateA(hinst, "One", &state, &action);
811     ok(hinst, !r, "got %u\n", r);
812     ok(hinst, action == INSTALLSTATE_LOCAL, "got action %d\n", action);
813 
814     /* test component states */
815 
816     r = MsiGetComponentStateA(hinst, NULL, &state, &action);
817     ok(hinst, r == ERROR_UNKNOWN_COMPONENT, "got %u\n", r);
818 
819     r = MsiGetComponentStateA(hinst, "fake", &state, &action);
820     ok(hinst, r == ERROR_UNKNOWN_COMPONENT, "got %u\n", r);
821 
822     r = MsiGetComponentStateA(hinst, "One", NULL, &action);
823     ok(hinst, r == RPC_X_NULL_REF_POINTER, "got %u\n", r);
824 
825     r = MsiGetComponentStateA(hinst, "One", &state, NULL);
826     ok(hinst, r == RPC_X_NULL_REF_POINTER, "got %u\n", r);
827 
828     r = MsiGetComponentStateA(hinst, "One", &state, &action);
829     ok(hinst, !r, "got %u\n", r);
830     ok(hinst, state == INSTALLSTATE_ABSENT, "got state %d\n", state);
831     ok(hinst, action == INSTALLSTATE_LOCAL, "got action %d\n", action);
832 
833     r = MsiGetComponentStateA(hinst, "dangler", &state, &action);
834     ok(hinst, !r, "got %u\n", r);
835     ok(hinst, state == INSTALLSTATE_ABSENT, "got state %d\n", state);
836     ok(hinst, action == INSTALLSTATE_UNKNOWN, "got action %d\n", action);
837 
838     r = MsiGetComponentStateA(hinst, "component", &state, &action);
839     ok(hinst, !r, "got %u\n", r);
840     ok(hinst, state == INSTALLSTATE_UNKNOWN, "got state %d\n", state);
841     ok(hinst, action == INSTALLSTATE_LOCAL, "got action %d\n", action);
842 
843     r = MsiSetComponentStateA(hinst, NULL, INSTALLSTATE_ABSENT);
844     ok(hinst, r == ERROR_UNKNOWN_COMPONENT, "got %u\n", r);
845 
846     r = MsiSetComponentStateA(hinst, "One", INSTALLSTATE_SOURCE);
847     ok(hinst, !r, "got %u\n", r);
848 
849     r = MsiGetComponentStateA(hinst, "One", &state, &action);
850     ok(hinst, !r, "got %u\n", r);
851     ok(hinst, state == INSTALLSTATE_ABSENT, "got state %d\n", state);
852     ok(hinst, action == INSTALLSTATE_SOURCE, "got action %d\n", action);
853 
854     r = MsiSetComponentStateA(hinst, "One", INSTALLSTATE_LOCAL);
855     ok(hinst, !r, "got %u\n", r);
856 
857     r = MsiGetComponentStateA(hinst, "One", &state, &action);
858     ok(hinst, !r, "got %u\n", r);
859     ok(hinst, state == INSTALLSTATE_ABSENT, "got state %d\n", state);
860     ok(hinst, action == INSTALLSTATE_LOCAL, "got action %d\n", action);
861 }
862 
863 static void test_format_record(MSIHANDLE hinst)
864 {
865     WCHAR bufferW[10];
866     char buffer[10];
867     MSIHANDLE rec;
868     DWORD sz;
869     UINT r;
870 
871     r = MsiFormatRecordA(hinst, 0, NULL, NULL);
872     ok(hinst, r == ERROR_INVALID_HANDLE, "got %u\n", r);
873 
874     rec = MsiCreateRecord(1);
875     MsiRecordSetStringA(rec, 0, "foo [1]");
876     MsiRecordSetInteger(rec, 1, 123);
877 
878     r = MsiFormatRecordA(hinst, rec, NULL, NULL);
879     ok(hinst, !r, "got %u\n", r);
880 
881     r = MsiFormatRecordA(hinst, rec, buffer, NULL);
882     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
883 
884     /* Returned size is in bytes, not chars, but only for custom actions. */
885 
886     sz = 0;
887     r = MsiFormatRecordA(hinst, rec, NULL, &sz);
888     ok(hinst, !r, "got %u\n", r);
889     ok(hinst, sz == 14, "got size %u\n", sz);
890 
891     sz = 0;
892     strcpy(buffer,"q");
893     r = MsiFormatRecordA(hinst, rec, buffer, &sz);
894     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
895     ok(hinst, !strcmp(buffer, "q"), "got \"%s\"\n", buffer);
896     ok(hinst, sz == 14, "got size %u\n", sz);
897 
898     sz = 1;
899     strcpy(buffer,"x");
900     r = MsiFormatRecordA(hinst, rec, buffer, &sz);
901     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
902     ok(hinst, !buffer[0], "got \"%s\"\n", buffer);
903     ok(hinst, sz == 14, "got size %u\n", sz);
904 
905     sz = 7;
906     strcpy(buffer,"x");
907     r = MsiFormatRecordA(hinst, rec, buffer, &sz);
908     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
909     ok(hinst, !strcmp(buffer, "foo 12"), "got \"%s\"\n", buffer);
910     ok(hinst, sz == 14, "got size %u\n", sz);
911 
912     sz = 8;
913     strcpy(buffer,"x");
914     r = MsiFormatRecordA(hinst, rec, buffer, &sz);
915     ok(hinst, !r, "got %u\n", r);
916     ok(hinst, !strcmp(buffer, "foo 123"), "got \"%s\"\n", buffer);
917     ok(hinst, sz == 7, "got size %u\n", sz);
918 
919     r = MsiFormatRecordW(hinst, rec, NULL, NULL);
920     ok(hinst, !r, "got %u\n", r);
921 
922     r = MsiFormatRecordW(hinst, rec, bufferW, NULL);
923     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
924 
925     sz = 0;
926     r = MsiFormatRecordW(hinst, rec, NULL, &sz);
927     ok(hinst, !r, "got %u\n", r);
928     ok(hinst, sz == 7, "got size %u\n", sz);
929 
930     sz = 0;
931     bufferW[0] = 'q';
932     r = MsiFormatRecordW(hinst, rec, bufferW, &sz);
933     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
934     ok(hinst, bufferW[0] == 'q', "got %s\n", dbgstr_w(bufferW));
935     ok(hinst, sz == 7, "got size %u\n", sz);
936 
937     sz = 1;
938     bufferW[0] = 'q';
939     r = MsiFormatRecordW(hinst, rec, bufferW, &sz);
940     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
941     ok(hinst, !bufferW[0], "got %s\n", dbgstr_w(bufferW));
942     ok(hinst, sz == 7, "got size %u\n", sz);
943 
944     sz = 7;
945     bufferW[0] = 'q';
946     r = MsiFormatRecordW(hinst, rec, bufferW, &sz);
947     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
948     ok(hinst, !lstrcmpW(bufferW, L"foo 12"), "got %s\n", dbgstr_w(bufferW));
949     ok(hinst, sz == 7, "got size %u\n", sz);
950 
951     sz = 8;
952     bufferW[0] = 'q';
953     r = MsiFormatRecordW(hinst, rec, bufferW, &sz);
954     ok(hinst, !r, "got %u\n", r);
955     ok(hinst, !lstrcmpW(bufferW, L"foo 123"), "got %s\n", dbgstr_w(bufferW));
956     ok(hinst, sz == 7, "got size %u\n", sz);
957 
958     /* check that properties work */
959     MsiSetPropertyA(hinst, "fmtprop", "foobar");
960     MsiRecordSetStringA(rec, 0, "[fmtprop]");
961     sz = sizeof(buffer);
962     r = MsiFormatRecordA(hinst, rec, buffer, &sz);
963     ok(hinst, !r, "got %u\n", r);
964     ok(hinst, !strcmp(buffer, "foobar"), "got \"%s\"\n", buffer);
965     ok(hinst, sz == 6, "got size %u\n", sz);
966 
967     MsiCloseHandle(rec);
968 }
969 
970 static void test_costs(MSIHANDLE hinst)
971 {
972     WCHAR bufferW[10];
973     char buffer[10];
974     int cost, temp;
975     DWORD sz;
976     UINT r;
977 
978     cost = 0xdead;
979     r = MsiGetFeatureCostA(hinst, NULL, MSICOSTTREE_CHILDREN, INSTALLSTATE_LOCAL, &cost);
980     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
981     todo_wine
982     ok(hinst, !cost, "got %d\n", cost);
983 
984     r = MsiGetFeatureCostA(hinst, "One", MSICOSTTREE_CHILDREN, INSTALLSTATE_LOCAL, NULL);
985     ok(hinst, r == RPC_X_NULL_REF_POINTER, "got %u\n", r);
986 
987     cost = 0xdead;
988     r = MsiGetFeatureCostA(hinst, "One", MSICOSTTREE_CHILDREN, INSTALLSTATE_LOCAL, &cost);
989     ok(hinst, !r, "got %u\n", r);
990     todo_wine
991     ok(hinst, cost == 8, "got %d\n", cost);
992 
993     sz = cost = temp = 0xdead;
994     r = MsiEnumComponentCostsA(hinst, "One", 0, INSTALLSTATE_LOCAL, NULL, &sz, &cost, &temp);
995     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
996     ok(hinst, sz == 0xdead, "got size %d\n", sz);
997     ok(hinst, cost == 0xdead, "got cost %d\n", cost);
998     ok(hinst, temp == 0xdead, "got temp %d\n", temp);
999 
1000     cost = temp = 0xdead;
1001     r = MsiEnumComponentCostsA(hinst, "One", 0, INSTALLSTATE_LOCAL, buffer, NULL, &cost, &temp);
1002     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
1003     ok(hinst, cost == 0xdead, "got cost %d\n", cost);
1004     ok(hinst, temp == 0xdead, "got temp %d\n", temp);
1005 
1006     sz = temp = 0xdead;
1007     r = MsiEnumComponentCostsA(hinst, "One", 0, INSTALLSTATE_LOCAL, buffer, &sz, NULL, &temp);
1008     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
1009     ok(hinst, sz == 0xdead, "got size %d\n", sz);
1010     ok(hinst, temp == 0xdead, "got temp %d\n", temp);
1011 
1012     sz = cost = 0xdead;
1013     r = MsiEnumComponentCostsA(hinst, "One", 0, INSTALLSTATE_LOCAL, buffer, &sz, &cost, NULL);
1014     ok(hinst, r == ERROR_INVALID_PARAMETER, "got %u\n", r);
1015     ok(hinst, sz == 0xdead, "got size %d\n", sz);
1016     ok(hinst, cost == 0xdead, "got cost %d\n", cost);
1017 
1018     cost = temp = 0xdead;
1019     sz = sizeof(buffer);
1020     r = MsiEnumComponentCostsA(hinst, NULL, 0, INSTALLSTATE_LOCAL, buffer, &sz, &cost, &temp);
1021     ok(hinst, !r, "got %u\n", r);
1022     ok(hinst, sz == 2, "got size %u\n", sz);
1023     ok(hinst, !strcmp(buffer, "C:"), "got '%s'\n", buffer);
1024     ok(hinst, !cost, "got cost %d\n", cost);
1025     ok(hinst, temp && temp != 0xdead, "got temp %d\n", temp);
1026 
1027     cost = temp = 0xdead;
1028     sz = sizeof(buffer);
1029     r = MsiEnumComponentCostsA(hinst, "One", 0, INSTALLSTATE_LOCAL, buffer, &sz, &cost, &temp);
1030     ok(hinst, !r, "got %u\n", r);
1031     ok(hinst, sz == 2, "got size %u\n", sz);
1032     ok(hinst, !strcmp(buffer, "C:"), "got '%s'\n", buffer);
1033     ok(hinst, cost == 8, "got cost %d\n", cost);
1034     ok(hinst, !temp, "got temp %d\n", temp);
1035 
1036     /* same string behaviour */
1037     cost = temp = 0xdead;
1038     sz = 0;
1039     strcpy(buffer,"q");
1040     r = MsiEnumComponentCostsA(hinst, "One", 0, INSTALLSTATE_LOCAL, buffer, &sz, &cost, &temp);
1041     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
1042     ok(hinst, !strcmp(buffer, "q"), "got \"%s\"\n", buffer);
1043     todo_wine
1044     ok(hinst, sz == 4, "got size %u\n", sz);
1045     ok(hinst, cost == 8, "got cost %d\n", cost);
1046     ok(hinst, !temp, "got temp %d\n", temp);
1047 
1048     sz = 1;
1049     strcpy(buffer,"x");
1050     r = MsiEnumComponentCostsA(hinst, "One", 0, INSTALLSTATE_LOCAL, buffer, &sz, &cost, &temp);
1051     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
1052     todo_wine {
1053     ok(hinst, !buffer[0], "got \"%s\"\n", buffer);
1054     ok(hinst, sz == 4, "got size %u\n", sz);
1055     }
1056 
1057     sz = 2;
1058     strcpy(buffer,"x");
1059     r = MsiEnumComponentCostsA(hinst, "One", 0, INSTALLSTATE_LOCAL, buffer, &sz, &cost, &temp);
1060     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
1061     todo_wine {
1062     ok(hinst, !strcmp(buffer, "C"), "got \"%s\"\n", buffer);
1063     ok(hinst, sz == 4, "got size %u\n", sz);
1064     }
1065 
1066     sz = 3;
1067     strcpy(buffer,"x");
1068     r = MsiEnumComponentCostsA(hinst, "One", 0, INSTALLSTATE_LOCAL, buffer, &sz, &cost, &temp);
1069     ok(hinst, !r, "got %u\n", r);
1070     ok(hinst, !strcmp(buffer, "C:"), "got \"%s\"\n", buffer);
1071     ok(hinst, sz == 2, "got size %u\n", sz);
1072 
1073     sz = 0;
1074     bufferW[0] = 'q';
1075     r = MsiEnumComponentCostsW(hinst, L"One", 0, INSTALLSTATE_LOCAL, bufferW, &sz, &cost, &temp);
1076     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
1077     ok(hinst, bufferW[0] == 'q', "got %s\n", dbgstr_w(bufferW));
1078     ok(hinst, sz == 2, "got size %u\n", sz);
1079 
1080     sz = 1;
1081     bufferW[0] = 'q';
1082     r = MsiEnumComponentCostsW(hinst, L"One", 0, INSTALLSTATE_LOCAL, bufferW, &sz, &cost, &temp);
1083     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
1084     ok(hinst, !bufferW[0], "got %s\n", dbgstr_w(bufferW));
1085     ok(hinst, sz == 2, "got size %u\n", sz);
1086 
1087     sz = 2;
1088     bufferW[0] = 'q';
1089     r = MsiEnumComponentCostsW(hinst, L"One", 0, INSTALLSTATE_LOCAL, bufferW, &sz, &cost, &temp);
1090     ok(hinst, r == ERROR_MORE_DATA, "got %u\n", r);
1091     ok(hinst, !lstrcmpW(bufferW, L"C"), "got %s\n", dbgstr_w(bufferW));
1092     ok(hinst, sz == 2, "got size %u\n", sz);
1093 
1094     sz = 3;
1095     bufferW[0] = 'q';
1096     r = MsiEnumComponentCostsW(hinst, L"One", 0, INSTALLSTATE_LOCAL, bufferW, &sz, &cost, &temp);
1097     ok(hinst, !r, "got %u\n", r);
1098     ok(hinst, !lstrcmpW(bufferW, L"C:"), "got %s\n", dbgstr_w(bufferW));
1099     ok(hinst, sz == 2, "got size %u\n", sz);
1100 }
1101 
1102 static void test_invalid_functions(MSIHANDLE hinst)
1103 {
1104     char path[MAX_PATH], package_name[20];
1105     MSIHANDLE db, preview, package;
1106     UINT r;
1107 
1108     r = MsiGetDatabaseState(hinst);
1109     ok(hinst, r == MSIDBSTATE_ERROR, "got %u\n", r);
1110 
1111     db = MsiGetActiveDatabase(hinst);
1112     ok(hinst, db, "MsiGetActiveDatabase failed\n");
1113 
1114     r = MsiDatabaseGenerateTransformA(db, db, "bogus.mst", 0, 0);
1115     todo_wine ok(hinst, r == ERROR_INVALID_HANDLE, "got %u\n", r);
1116 
1117     r = MsiDatabaseApplyTransformA(db, "bogus.mst", 0);
1118     ok(hinst, r == ERROR_INVALID_HANDLE, "got %u\n", r);
1119 
1120     r = MsiCreateTransformSummaryInfoA(db, db, "bogus.mst", 0, 0);
1121     todo_wine ok(hinst, r == ERROR_INSTALL_PACKAGE_OPEN_FAILED ||
1122                         r == ERROR_INSTALL_PACKAGE_INVALID /* winxp */,
1123                  "got %u\n", r);
1124 
1125     GetCurrentDirectoryA(sizeof(path), path);
1126     r = MsiDatabaseExportA(db, "Test", path, "bogus.idt");
1127     ok(hinst, r == ERROR_INVALID_HANDLE, "got %u\n", r);
1128 
1129     r = MsiDatabaseImportA(db, path, "bogus.idt");
1130     ok(hinst, r == ERROR_INVALID_HANDLE, "got %u\n", r);
1131 
1132     r = MsiDatabaseCommit(db);
1133     ok(hinst, r == ERROR_SUCCESS, "got %u\n", r);
1134 
1135     r = MsiDatabaseMergeA(db, db, "MergeErrors");
1136     ok(hinst, r == ERROR_INVALID_HANDLE, "got %u\n", r);
1137 
1138     r = MsiGetDatabaseState(db);
1139     ok(hinst, r == MSIDBSTATE_ERROR, "got %u\n", r);
1140 
1141     r = MsiEnableUIPreview(db, &preview);
1142     ok(hinst, r == ERROR_INVALID_HANDLE, "got %u\n", r);
1143 
1144     sprintf(package_name, "#%lu", db);
1145     r = MsiOpenPackageA(package_name, &package);
1146     ok(hinst, r == ERROR_INVALID_HANDLE, "got %u\n", r);
1147 
1148     MsiCloseHandle(db);
1149 }
1150 
1151 static void test_view_get_error(MSIHANDLE hinst)
1152 {
1153     MSIHANDLE db, view, rec;
1154     char buffer[5];
1155     MSIDBERROR err;
1156     DWORD sz;
1157     UINT r;
1158 
1159     db = MsiGetActiveDatabase(hinst);
1160     ok(hinst, db, "MsiGetActiveDatabase failed\n");
1161 
1162     r = MsiDatabaseOpenViewA(db, "SELECT * FROM `test2`", &view);
1163     ok(hinst, !r, "got %u\n", r);
1164 
1165     r = MsiViewExecute(view, 0);
1166     ok(hinst, !r, "got %u\n", r);
1167 
1168     sz = 0;
1169     err = MsiViewGetErrorA(0, NULL, &sz);
1170     todo_wine ok(hinst, err == MSIDBERROR_FUNCTIONERROR, "got %d\n", err);
1171     ok(hinst, sz == 0, "got size %u\n", sz);
1172 
1173     err = MsiViewGetErrorA(view, NULL, NULL);
1174     ok(hinst, err == MSIDBERROR_INVALIDARG, "got %d\n", err);
1175 
1176     sz = 0;
1177     err = MsiViewGetErrorA(view, NULL, &sz);
1178     ok(hinst, err == MSIDBERROR_FUNCTIONERROR, "got %d\n", err);
1179     ok(hinst, sz == 0, "got size %u\n", sz);
1180 
1181     sz = 0;
1182     strcpy(buffer, "x");
1183     err = MsiViewGetErrorA(view, buffer, &sz);
1184     ok(hinst, err == MSIDBERROR_FUNCTIONERROR, "got %d\n", err);
1185     ok(hinst, !strcmp(buffer, "x"), "got \"%s\"\n", buffer);
1186     ok(hinst, sz == 0, "got size %u\n", sz);
1187 
1188     sz = 1;
1189     strcpy(buffer, "x");
1190     err = MsiViewGetErrorA(view, buffer, &sz);
1191     ok(hinst, err == MSIDBERROR_NOERROR, "got %d\n", err);
1192     ok(hinst, !buffer[0], "got \"%s\"\n", buffer);
1193     ok(hinst, sz == 0, "got size %u\n", sz);
1194 
1195     rec = MsiCreateRecord(2);
1196     MsiRecordSetInteger(rec, 1, 1);
1197     MsiRecordSetInteger(rec, 2, 2);
1198     r = MsiViewModify(view, MSIMODIFY_VALIDATE_NEW, rec);
1199     ok(hinst, r == ERROR_INVALID_DATA, "got %u\n", r);
1200 
1201     sz = 2;
1202     strcpy(buffer, "x");
1203     err = MsiViewGetErrorA(view, buffer, &sz);
1204     ok(hinst, err == MSIDBERROR_DUPLICATEKEY, "got %d\n", err);
1205     ok(hinst, !strcmp(buffer, "A"), "got \"%s\"\n", buffer);
1206     ok(hinst, sz == 1, "got size %u\n", sz);
1207 
1208     sz = 2;
1209     strcpy(buffer, "x");
1210     err = MsiViewGetErrorA(view, buffer, &sz);
1211     todo_wine ok(hinst, err == MSIDBERROR_NOERROR, "got %d\n", err);
1212     todo_wine ok(hinst, !buffer[0], "got \"%s\"\n", buffer);
1213     todo_wine ok(hinst, sz == 0, "got size %u\n", sz);
1214 
1215     r = MsiViewModify(view, MSIMODIFY_VALIDATE_NEW, rec);
1216     ok(hinst, r == ERROR_INVALID_DATA, "got %u\n", r);
1217 
1218     sz = 1;
1219     strcpy(buffer, "x");
1220     err = MsiViewGetErrorA(view, buffer, &sz);
1221     ok(hinst, err == MSIDBERROR_MOREDATA, "got %d\n", err);
1222     ok(hinst, !buffer[0], "got \"%s\"\n", buffer);
1223     ok(hinst, sz == 1, "got size %u\n", sz);
1224 
1225     sz = 1;
1226     strcpy(buffer, "x");
1227     err = MsiViewGetErrorA(view, buffer, &sz);
1228     todo_wine ok(hinst, err == MSIDBERROR_NOERROR, "got %d\n", err);
1229     ok(hinst, !buffer[0], "got \"%s\"\n", buffer);
1230     todo_wine ok(hinst, sz == 0, "got size %u\n", sz);
1231 
1232     r = MsiViewModify(view, MSIMODIFY_VALIDATE_NEW, rec);
1233     ok(hinst, r == ERROR_INVALID_DATA, "got %u\n", r);
1234 
1235     sz = 0;
1236     strcpy(buffer, "x");
1237     err = MsiViewGetErrorA(view, buffer, &sz);
1238     ok(hinst, err == MSIDBERROR_FUNCTIONERROR, "got %d\n", err);
1239     ok(hinst, !strcmp(buffer, "x"), "got \"%s\"\n", buffer);
1240     ok(hinst, sz == 0, "got size %u\n", sz);
1241 
1242     sz = 0;
1243     strcpy(buffer, "x");
1244     err = MsiViewGetErrorA(view, buffer, &sz);
1245     ok(hinst, err == MSIDBERROR_FUNCTIONERROR, "got %d\n", err);
1246     ok(hinst, !strcmp(buffer, "x"), "got \"%s\"\n", buffer);
1247     ok(hinst, sz == 0, "got size %u\n", sz);
1248 
1249     MsiCloseHandle(rec);
1250     MsiCloseHandle(view);
1251     MsiCloseHandle(db);
1252 }
1253 
1254 /* Main test. Anything that doesn't depend on a specific install configuration
1255  * or have undesired side effects should go here. */
1256 UINT WINAPI main_test(MSIHANDLE hinst)
1257 {
1258     IUnknown *unk = NULL;
1259     HRESULT hr;
1260 
1261     /* Test for an MTA apartment */
1262     hr = CoCreateInstance(&CLSID_XMLDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void **)&unk);
1263     ok(hinst, hr == S_OK, "CoCreateInstance failed with %08x\n", hr);
1264 
1265     if (unk) IUnknown_Release(unk);
1266 
1267     /* but ours is uninitialized */
1268     hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
1269     ok(hinst, hr == S_OK, "got %#x\n", hr);
1270     CoUninitialize();
1271 
1272     test_props(hinst);
1273     test_db(hinst);
1274     test_doaction(hinst);
1275     test_targetpath(hinst);
1276     test_misc(hinst);
1277     test_feature_states(hinst);
1278     test_format_record(hinst);
1279     test_costs(hinst);
1280     test_invalid_functions(hinst);
1281     test_view_get_error(hinst);
1282 
1283     return ERROR_SUCCESS;
1284 }
1285 
1286 UINT WINAPI test_retval(MSIHANDLE hinst)
1287 {
1288     char prop[10];
1289     DWORD len = sizeof(prop);
1290     UINT retval;
1291 
1292     MsiGetPropertyA(hinst, "TEST_RETVAL", prop, &len);
1293     sscanf(prop, "%u", &retval);
1294     return retval;
1295 }
1296 
1297 static void append_file(MSIHANDLE hinst, const char *filename, const char *text)
1298 {
1299     DWORD size;
1300     HANDLE file = CreateFileA(filename, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
1301     ok(hinst, file != INVALID_HANDLE_VALUE, "CreateFile failed, error %u\n", GetLastError());
1302 
1303     SetFilePointer(file, 0, NULL, FILE_END);
1304     WriteFile(file, text, strlen(text), &size, NULL);
1305     CloseHandle(file);
1306 }
1307 
1308 UINT WINAPI da_immediate(MSIHANDLE hinst)
1309 {
1310     char prop[300];
1311     DWORD len = sizeof(prop);
1312 
1313     MsiGetPropertyA(hinst, "TESTPATH", prop, &len);
1314 
1315     append_file(hinst, prop, "one");
1316 
1317     ok(hinst, !MsiGetMode(hinst, MSIRUNMODE_SCHEDULED), "shouldn't be scheduled\n");
1318     ok(hinst, !MsiGetMode(hinst, MSIRUNMODE_ROLLBACK), "shouldn't be rollback\n");
1319     ok(hinst, !MsiGetMode(hinst, MSIRUNMODE_COMMIT), "shouldn't be commit\n");
1320 
1321     return ERROR_SUCCESS;
1322 }
1323 
1324 UINT WINAPI da_deferred(MSIHANDLE hinst)
1325 {
1326     char prop[300];
1327     DWORD len = sizeof(prop);
1328     LANGID lang;
1329     UINT r;
1330 
1331     /* Test that we were in fact deferred */
1332     r = MsiGetPropertyA(hinst, "CustomActionData", prop, &len);
1333     ok(hinst, r == ERROR_SUCCESS, "got %u\n", r);
1334     ok(hinst, prop[0], "CustomActionData was empty\n");
1335 
1336     append_file(hinst, prop, "two");
1337 
1338     /* Test available properties */
1339     len = sizeof(prop);
1340     r = MsiGetPropertyA(hinst, "ProductCode", prop, &len);
1341     ok(hinst, r == ERROR_SUCCESS, "got %u\n", r);
1342     ok(hinst, prop[0], "got %s\n", prop);
1343 
1344     len = sizeof(prop);
1345     r = MsiGetPropertyA(hinst, "UserSID", prop, &len);
1346     ok(hinst, r == ERROR_SUCCESS, "got %u\n", r);
1347     ok(hinst, prop[0], "got %s\n", prop);
1348 
1349     len = sizeof(prop);
1350     r = MsiGetPropertyA(hinst, "TESTPATH", prop, &len);
1351     ok(hinst, r == ERROR_SUCCESS, "got %u\n", r);
1352     todo_wine
1353     ok(hinst, !prop[0], "got %s\n", prop);
1354 
1355     /* Test modes */
1356     ok(hinst, MsiGetMode(hinst, MSIRUNMODE_SCHEDULED), "should be scheduled\n");
1357     ok(hinst, !MsiGetMode(hinst, MSIRUNMODE_ROLLBACK), "shouldn't be rollback\n");
1358     ok(hinst, !MsiGetMode(hinst, MSIRUNMODE_COMMIT), "shouldn't be commit\n");
1359 
1360     lang = MsiGetLanguage(hinst);
1361     ok(hinst, lang != ERROR_INVALID_HANDLE, "MsiGetLanguage failed\n");
1362 
1363     return ERROR_SUCCESS;
1364 }
1365 
1366 static int global_state;
1367 
1368 UINT WINAPI process1(MSIHANDLE hinst)
1369 {
1370     SetEnvironmentVariableA("MSI_PROCESS_TEST","1");
1371     global_state++;
1372     return ERROR_SUCCESS;
1373 }
1374 
1375 UINT WINAPI process2(MSIHANDLE hinst)
1376 {
1377     char env[2] = {0};
1378     DWORD r = GetEnvironmentVariableA("MSI_PROCESS_TEST", env, sizeof(env));
1379     ok(hinst, r == 1, "got %d, error %u\n", r, GetLastError());
1380     ok(hinst, !strcmp(env, "1"), "got %s\n", env);
1381     ok(hinst, !global_state, "got global_state %d\n", global_state);
1382     return ERROR_SUCCESS;
1383 }
1384 
1385 UINT WINAPI async1(MSIHANDLE hinst)
1386 {
1387     HANDLE event = CreateEventA(NULL, TRUE, FALSE, "wine_msi_async_test");
1388     HANDLE event2 = CreateEventA(NULL, TRUE, FALSE, "wine_msi_async_test2");
1389     DWORD r = WaitForSingleObject(event, 10000);
1390     ok(hinst, !r, "wait timed out\n");
1391     SetEvent(event2);
1392     return ERROR_SUCCESS;
1393 }
1394 
1395 UINT WINAPI async2(MSIHANDLE hinst)
1396 {
1397     HANDLE event = CreateEventA(NULL, TRUE, FALSE, "wine_msi_async_test");
1398     HANDLE event2 = CreateEventA(NULL, TRUE, FALSE, "wine_msi_async_test2");
1399     DWORD r;
1400     SetEvent(event);
1401     r = WaitForSingleObject(event2, 10000);
1402     ok(hinst, !r, "wait timed out\n");
1403     return ERROR_SUCCESS;
1404 }
1405 
1406 static BOOL pf_exists(const char *file)
1407 {
1408     char path[MAX_PATH];
1409 
1410     if (FAILED(SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILESX86, NULL, 0, path)))
1411         SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES, NULL, 0, path);
1412     strcat(path, "\\");
1413     strcat(path, file);
1414     return GetFileAttributesA(path) != INVALID_FILE_ATTRIBUTES;
1415 }
1416 
1417 UINT WINAPI cf_present(MSIHANDLE hinst)
1418 {
1419     ok(hinst, pf_exists("msitest\\first"), "folder absent\n");
1420     ok(hinst, pf_exists("msitest\\second"), "folder absent\n");
1421     ok(hinst, pf_exists("msitest\\third"), "folder absent\n");
1422     return ERROR_SUCCESS;
1423 }
1424 
1425 UINT WINAPI cf_absent(MSIHANDLE hinst)
1426 {
1427     ok(hinst, !pf_exists("msitest\\first"), "folder present\n");
1428     ok(hinst, !pf_exists("msitest\\second"), "folder present\n");
1429     ok(hinst, !pf_exists("msitest\\third"), "folder present\n");
1430     return ERROR_SUCCESS;
1431 }
1432 
1433 UINT WINAPI file_present(MSIHANDLE hinst)
1434 {
1435     ok(hinst, pf_exists("msitest\\first\\one.txt"), "file absent\n");
1436     ok(hinst, pf_exists("msitest\\second\\two.txt"), "file absent\n");
1437     return ERROR_SUCCESS;
1438 }
1439 
1440 UINT WINAPI file_absent(MSIHANDLE hinst)
1441 {
1442     ok(hinst, !pf_exists("msitest\\first\\one.txt"), "file present\n");
1443     ok(hinst, !pf_exists("msitest\\second\\two.txt"), "file present\n");
1444     return ERROR_SUCCESS;
1445 }
1446 
1447 UINT WINAPI crs_present(MSIHANDLE hinst)
1448 {
1449     ok(hinst, pf_exists("msitest\\shortcut.lnk"), "shortcut absent\n");
1450     return ERROR_SUCCESS;
1451 }
1452 
1453 UINT WINAPI crs_absent(MSIHANDLE hinst)
1454 {
1455     ok(hinst, !pf_exists("msitest\\shortcut.lnk"), "shortcut present\n");
1456     return ERROR_SUCCESS;
1457 }
1458 
1459 UINT WINAPI sds_present(MSIHANDLE hinst)
1460 {
1461     SC_HANDLE manager, service;
1462     manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1463     service = OpenServiceA(manager, "TestService3", GENERIC_ALL);
1464     ok(hinst, !!service, "service absent: %u\n", GetLastError());
1465     CloseServiceHandle(service);
1466     CloseServiceHandle(manager);
1467     return ERROR_SUCCESS;
1468 }
1469 
1470 UINT WINAPI sds_absent(MSIHANDLE hinst)
1471 {
1472     SC_HANDLE manager, service;
1473     manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1474     service = OpenServiceA(manager, "TestService3", GENERIC_ALL);
1475     ok(hinst, !service, "service present\n");
1476     if (service) CloseServiceHandle(service);
1477     CloseServiceHandle(manager);
1478     return ERROR_SUCCESS;
1479 }
1480 
1481 UINT WINAPI sis_present(MSIHANDLE hinst)
1482 {
1483     SC_HANDLE manager, service;
1484     manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1485     service = OpenServiceA(manager, "TestService", GENERIC_ALL);
1486     ok(hinst, !!service, "service absent: %u\n", GetLastError());
1487     CloseServiceHandle(service);
1488     CloseServiceHandle(manager);
1489     return ERROR_SUCCESS;
1490 }
1491 
1492 UINT WINAPI sis_absent(MSIHANDLE hinst)
1493 {
1494     SC_HANDLE manager, service;
1495     manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1496     service = OpenServiceA(manager, "TestService", GENERIC_ALL);
1497     ok(hinst, !service, "service present\n");
1498     if (service) CloseServiceHandle(service);
1499     CloseServiceHandle(manager);
1500     return ERROR_SUCCESS;
1501 }
1502 
1503 UINT WINAPI sss_started(MSIHANDLE hinst)
1504 {
1505     SC_HANDLE manager, service;
1506     SERVICE_STATUS status;
1507     BOOL ret;
1508 
1509     manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1510     service = OpenServiceA(manager, "Spooler", SC_MANAGER_ALL_ACCESS);
1511     ret = QueryServiceStatus(service, &status);
1512     ok(hinst, ret, "QueryServiceStatus failed: %u\n", GetLastError());
1513     ok(hinst, status.dwCurrentState == SERVICE_RUNNING, "got %u\n", status.dwCurrentState);
1514 
1515     CloseServiceHandle(service);
1516     CloseServiceHandle(manager);
1517     return ERROR_SUCCESS;
1518 }
1519 
1520 UINT WINAPI sss_stopped(MSIHANDLE hinst)
1521 {
1522     SC_HANDLE manager, service;
1523     SERVICE_STATUS status;
1524     BOOL ret;
1525 
1526     manager = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1527     service = OpenServiceA(manager, "Spooler", SC_MANAGER_ALL_ACCESS);
1528     ret = QueryServiceStatus(service, &status);
1529     ok(hinst, ret, "QueryServiceStatus failed: %u\n", GetLastError());
1530     ok(hinst, status.dwCurrentState == SERVICE_STOPPED, "got %u\n", status.dwCurrentState);
1531 
1532     CloseServiceHandle(service);
1533     CloseServiceHandle(manager);
1534     return ERROR_SUCCESS;
1535 }
1536 
1537 UINT WINAPI rd_present(MSIHANDLE hinst)
1538 {
1539     ok(hinst, pf_exists("msitest\\original2.txt"), "file absent\n");
1540     ok(hinst, pf_exists("msitest\\duplicate.txt"), "file absent\n");
1541     ok(hinst, !pf_exists("msitest\\original3.txt"), "file present\n");
1542     ok(hinst, !pf_exists("msitest\\duplicate2.txt"), "file present\n");
1543     return ERROR_SUCCESS;
1544 }
1545 
1546 UINT WINAPI rd_absent(MSIHANDLE hinst)
1547 {
1548     ok(hinst, !pf_exists("msitest\\original2.txt"), "file present\n");
1549     ok(hinst, !pf_exists("msitest\\duplicate.txt"), "file present\n");
1550     ok(hinst, !pf_exists("msitest\\original3.txt"), "file present\n");
1551     ok(hinst, !pf_exists("msitest\\duplicate2.txt"), "file present\n");
1552     return ERROR_SUCCESS;
1553 }
1554 
1555 UINT WINAPI odbc_present(MSIHANDLE hinst)
1556 {
1557     int gotdriver = 0, gotdriver2 = 0;
1558     char buffer[1000], *p;
1559     WORD len;
1560     BOOL r;
1561 
1562     buffer[0] = 0;
1563     len = sizeof(buffer);
1564     r = SQLGetInstalledDrivers(buffer, sizeof(buffer), &len);
1565     if (r) ok(hinst, len < sizeof(buffer), "buffer too small\n");
1566     for (p = buffer; *p; p += strlen(p) + 1)
1567     {
1568         if (!strcmp(p, "ODBC test driver"))
1569             gotdriver = 1;
1570         if (!strcmp(p, "ODBC test driver2"))
1571             gotdriver2 = 1;
1572     }
1573     ok(hinst, gotdriver, "driver absent\n");
1574     ok(hinst, gotdriver2, "driver 2 absent\n");
1575     return ERROR_SUCCESS;
1576 }
1577 
1578 UINT WINAPI odbc_absent(MSIHANDLE hinst)
1579 {
1580     int gotdriver = 0, gotdriver2 = 0;
1581     char buffer[1000], *p;
1582     WORD len;
1583     BOOL r;
1584 
1585     buffer[0] = 0;
1586     len = sizeof(buffer);
1587     r = SQLGetInstalledDrivers(buffer, sizeof(buffer), &len);
1588     if (r) ok(hinst, len < sizeof(buffer), "buffer too small\n");
1589     for (p = buffer; *p; p += strlen(p) + 1)
1590     {
1591         if (!strcmp(p, "ODBC test driver"))
1592             gotdriver = 1;
1593         if (!strcmp(p, "ODBC test driver2"))
1594             gotdriver2 = 1;
1595     }
1596     ok(hinst, !gotdriver, "driver present\n");
1597     ok(hinst, !gotdriver2, "driver 2 present\n");
1598     return ERROR_SUCCESS;
1599 }
1600 
1601 UINT WINAPI mov_present(MSIHANDLE hinst)
1602 {
1603     ok(hinst, pf_exists("msitest\\canada"), "file absent\n");
1604     ok(hinst, pf_exists("msitest\\dominica"), "file absent\n");
1605     return ERROR_SUCCESS;
1606 }
1607 
1608 UINT WINAPI mov_absent(MSIHANDLE hinst)
1609 {
1610     ok(hinst, !pf_exists("msitest\\canada"), "file present\n");
1611     ok(hinst, !pf_exists("msitest\\dominica"), "file present\n");
1612     return ERROR_SUCCESS;
1613 }
1614 
1615 static void check_reg_str(MSIHANDLE hinst, HKEY key, const char *name, const char *expect)
1616 {
1617     char value[300];
1618     DWORD sz;
1619     LONG res;
1620 
1621     sz = sizeof(value);
1622     res = RegQueryValueExA(key, name, NULL, NULL, (BYTE *)value, &sz);
1623     if (expect)
1624     {
1625         ok(hinst, !res, "failed to get value \"%s\": %d\n", name, res);
1626         ok(hinst, !strcmp(value, expect), "\"%s\": expected \"%s\", got \"%s\"\n",
1627             name, expect, value);
1628     }
1629     else
1630         ok(hinst, res == ERROR_FILE_NOT_FOUND, "\"%s\": expected missing, got %u\n",
1631             name, res);
1632 }
1633 
1634 static const char path_dotnet[] = "Software\\Microsoft\\Installer\\Assemblies\\Global";
1635 static const char name_dotnet[] = "Wine.Dotnet.Assembly,processorArchitecture=\"MSIL\","
1636     "publicKeyToken=\"abcdef0123456789\",version=\"1.0.0.0\",culture=\"neutral\"";
1637 
1638 UINT WINAPI pa_present(MSIHANDLE hinst)
1639 {
1640     HKEY key;
1641     LONG res;
1642 
1643     res = RegOpenKeyA(HKEY_CURRENT_USER, path_dotnet, &key);
1644     ok(hinst, !res, "got %d\n", res);
1645     check_reg_str(hinst, key, name_dotnet, "rcHQPHq?CA@Uv-XqMI1e>Z'q,T*76M@=YEg6My?~]");
1646     RegCloseKey(key);
1647 
1648     return ERROR_SUCCESS;
1649 }
1650 
1651 UINT WINAPI pa_absent(MSIHANDLE hinst)
1652 {
1653     HKEY key;
1654     LONG res;
1655 
1656     res = RegOpenKeyA(HKEY_CURRENT_USER, path_dotnet, &key);
1657     ok(hinst, !res || res == ERROR_FILE_NOT_FOUND, "got %d\n", res);
1658     if (!res)
1659     {
1660         check_reg_str(hinst, key, name_dotnet, NULL);
1661         RegCloseKey(key);
1662     }
1663     return ERROR_SUCCESS;
1664 }
1665 
1666 static const char ppc_key[] = "Software\\Microsoft\\Windows\\CurrentVersion\\"
1667     "Installer\\UserData\\S-1-5-18\\Components\\CBABC2FDCCB35E749A8944D8C1C098B5";
1668 
1669 UINT WINAPI ppc_present(MSIHANDLE hinst)
1670 {
1671     char expect[MAX_PATH];
1672     HKEY key;
1673     UINT r;
1674 
1675     r = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ppc_key, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &key);
1676     ok(hinst, !r, "got %u\n", r);
1677 
1678     if (FAILED(SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILESX86, NULL, 0, expect)))
1679         SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES, NULL, 0, expect);
1680     strcat(expect, "\\msitest\\maximus");
1681     check_reg_str(hinst, key, "84A88FD7F6998CE40A22FB59F6B9C2BB", expect);
1682 
1683     RegCloseKey(key);
1684     return ERROR_SUCCESS;
1685 }
1686 
1687 UINT WINAPI ppc_absent(MSIHANDLE hinst)
1688 {
1689     HKEY key;
1690     UINT r;
1691 
1692     r = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ppc_key, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &key);
1693     ok(hinst, r == ERROR_FILE_NOT_FOUND, "got %u\n", r);
1694     return ERROR_SUCCESS;
1695 }
1696 
1697 static const char pub_key[] = "Software\\Microsoft\\Installer\\Components\\0CBCFA296AC907244845745CEEB2F8AA";
1698 
1699 UINT WINAPI pub_present(MSIHANDLE hinst)
1700 {
1701     HKEY key;
1702     LONG res;
1703 
1704     res = RegOpenKeyA(HKEY_CURRENT_USER, pub_key, &key);
1705     ok(hinst, !res, "got %u\n", res);
1706     res = RegQueryValueExA(key, "english.txt", NULL, NULL, NULL, NULL);
1707     ok(hinst, !res, "got %u\n", res);
1708     RegCloseKey(key);
1709     return ERROR_SUCCESS;
1710 }
1711 
1712 UINT WINAPI pub_absent(MSIHANDLE hinst)
1713 {
1714     HKEY key;
1715     LONG res;
1716 
1717     res = RegOpenKeyA(HKEY_CURRENT_USER, pub_key, &key);
1718     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1719     return ERROR_SUCCESS;
1720 }
1721 
1722 static const char pf_classkey[] = "Installer\\Features\\84A88FD7F6998CE40A22FB59F6B9C2BB";
1723 static const char pf_userkey[] = "Software\\Microsoft\\Windows\\CurrentVersion\\"
1724     "Installer\\UserData\\S-1-5-18\\Products\\84A88FD7F6998CE40A22FB59F6B9C2BB\\Features";
1725 
1726 UINT WINAPI pf_present(MSIHANDLE hinst)
1727 {
1728     HKEY key;
1729     LONG res;
1730 
1731     res = RegOpenKeyExA(HKEY_CLASSES_ROOT, pf_classkey, 0, KEY_READ | KEY_WOW64_64KEY, &key);
1732     ok(hinst, !res, "got %u\n", res);
1733     check_reg_str(hinst, key, "feature", "");
1734     check_reg_str(hinst, key, "montecristo", "");
1735     RegCloseKey(key);
1736 
1737     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, pf_userkey, 0, KEY_READ | KEY_WOW64_64KEY, &key);
1738     ok(hinst, !res, "got %u\n", res);
1739     check_reg_str(hinst, key, "feature", "VGtfp^p+,?82@JU1j_KE");
1740     check_reg_str(hinst, key, "montecristo", "VGtfp^p+,?82@JU1j_KE");
1741     RegCloseKey(key);
1742 
1743     return ERROR_SUCCESS;
1744 }
1745 
1746 UINT WINAPI pf_absent(MSIHANDLE hinst)
1747 {
1748     HKEY key;
1749     LONG res;
1750 
1751     res = RegOpenKeyExA(HKEY_CLASSES_ROOT, pf_classkey, 0, KEY_READ | KEY_WOW64_64KEY, &key);
1752     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1753 
1754     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, pf_userkey, 0, KEY_READ | KEY_WOW64_64KEY, &key);
1755     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1756 
1757     return ERROR_SUCCESS;
1758 }
1759 
1760 static const char pp_prodkey[] = "Installer\\Products\\84A88FD7F6998CE40A22FB59F6B9C2BB";
1761 
1762 UINT WINAPI pp_present(MSIHANDLE hinst)
1763 {
1764     HKEY key;
1765     LONG res;
1766 
1767     res = RegOpenKeyExA(HKEY_CLASSES_ROOT, pp_prodkey, 0, KEY_READ | KEY_WOW64_64KEY, &key);
1768     ok(hinst, !res, "got %u\n", res);
1769     check_reg_str(hinst, key, "ProductName", "MSITEST");
1770     check_reg_str(hinst, key, "PackageCode", "AC75740029052C94DA02821EECD05F2F");
1771     check_reg_str(hinst, key, "Clients", ":");
1772 
1773     RegCloseKey(key);
1774     return ERROR_SUCCESS;
1775 }
1776 
1777 UINT WINAPI pp_absent(MSIHANDLE hinst)
1778 {
1779     HKEY key;
1780     LONG res;
1781 
1782     res = RegOpenKeyExA(HKEY_CLASSES_ROOT, pp_prodkey, 0, KEY_READ | KEY_WOW64_64KEY, &key);
1783     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1784 
1785     return ERROR_SUCCESS;
1786 }
1787 
1788 UINT WINAPI rci_present(MSIHANDLE hinst)
1789 {
1790     HKEY key;
1791     LONG res;
1792 
1793     res = RegOpenKeyExA(HKEY_CLASSES_ROOT, "CLSID\\{110913E7-86D1-4BF3-9922-BA103FCDDDFA}",
1794         0, KEY_READ | KEY_WOW64_32KEY, &key);
1795     ok(hinst, !res, "got %u\n", res);
1796     RegCloseKey(key);
1797 
1798     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "FileType\\{110913E7-86D1-4BF3-9922-BA103FCDDDFA}", &key);
1799     ok(hinst, !res, "got %u\n", res);
1800     RegCloseKey(key);
1801 
1802     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "AppID\\{CFCC3B38-E683-497D-9AB4-CB40AAFE307F}", &key);
1803     ok(hinst, !res, "got %u\n", res);
1804     RegCloseKey(key);
1805 
1806     return ERROR_SUCCESS;
1807 }
1808 
1809 UINT WINAPI rci_absent(MSIHANDLE hinst)
1810 {
1811     HKEY key;
1812     LONG res;
1813 
1814     res = RegOpenKeyExA(HKEY_CLASSES_ROOT, "CLSID\\{110913E7-86D1-4BF3-9922-BA103FCDDDFA}",
1815         0, KEY_READ | KEY_WOW64_32KEY, &key);
1816     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1817 
1818     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "FileType\\{110913E7-86D1-4BF3-9922-BA103FCDDDFA}", &key);
1819     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1820 
1821     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "AppID\\{CFCC3B38-E683-497D-9AB4-CB40AAFE307F}", &key);
1822     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1823 
1824     return ERROR_SUCCESS;
1825 }
1826 
1827 UINT WINAPI rei_present(MSIHANDLE hinst)
1828 {
1829     HKEY key;
1830     LONG res;
1831 
1832     res = RegOpenKeyA(HKEY_CLASSES_ROOT, ".extension", &key);
1833     ok(hinst, !res, "got %u\n", res);
1834     RegCloseKey(key);
1835 
1836     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Prog.Id.1\\shell\\Open\\command", &key);
1837     ok(hinst, !res, "got %u\n", res);
1838     RegCloseKey(key);
1839 
1840     return ERROR_SUCCESS;
1841 }
1842 
1843 UINT WINAPI rei_absent(MSIHANDLE hinst)
1844 {
1845     HKEY key;
1846     LONG res;
1847 
1848     res = RegOpenKeyA(HKEY_CLASSES_ROOT, ".extension", &key);
1849     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1850 
1851     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Prog.Id.1\\shell\\Open\\command", &key);
1852     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1853 
1854     return ERROR_SUCCESS;
1855 }
1856 
1857 static const char font_key[] = "Software\\Microsoft\\Windows NT\\CurrentVersion\\Fonts";
1858 
1859 UINT WINAPI font_present(MSIHANDLE hinst)
1860 {
1861     HKEY key;
1862     LONG res;
1863 
1864     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, font_key, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &key);
1865     ok(hinst, !res, "got %u\n", res);
1866     res = RegQueryValueExA(key, "msi test font", NULL, NULL, NULL, NULL);
1867     ok(hinst, !res, "got %u\n", res);
1868     RegCloseKey(key);
1869 
1870     return ERROR_SUCCESS;
1871 }
1872 
1873 UINT WINAPI font_absent(MSIHANDLE hinst)
1874 {
1875     HKEY key;
1876     LONG res;
1877 
1878     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, font_key, 0, KEY_QUERY_VALUE | KEY_WOW64_64KEY, &key);
1879     ok(hinst, !res, "got %u\n", res);
1880     check_reg_str(hinst, key, "msi test font", NULL);
1881     RegCloseKey(key);
1882 
1883     return ERROR_SUCCESS;
1884 }
1885 
1886 UINT WINAPI rmi_present(MSIHANDLE hinst)
1887 {
1888     HKEY key;
1889     LONG res;
1890 
1891     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "MIME\\Database\\Content Type\\mime/type", &key);
1892     ok(hinst, !res, "got %u\n", res);
1893 
1894     return ERROR_SUCCESS;
1895 }
1896 
1897 UINT WINAPI rmi_absent(MSIHANDLE hinst)
1898 {
1899     HKEY key;
1900     LONG res;
1901 
1902     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "MIME\\Database\\Content Type\\mime/type", &key);
1903     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1904 
1905     return ERROR_SUCCESS;
1906 }
1907 
1908 static const char rp_key[] = "Software\\Microsoft\\Windows\\CurrentVersion\\"
1909     "Uninstall\\{7DF88A48-996F-4EC8-A022-BF956F9B2CBB}";
1910 
1911 UINT WINAPI rp_present(MSIHANDLE hinst)
1912 {
1913     HKEY key;
1914     LONG res;
1915 
1916     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, rp_key, 0, KEY_READ | KEY_WOW64_32KEY, &key);
1917     ok(hinst, !res, "got %u\n", res);
1918     check_reg_str(hinst, key, "DisplayName", "MSITEST");
1919     RegCloseKey(key);
1920 
1921     return ERROR_SUCCESS;
1922 }
1923 
1924 UINT WINAPI rp_absent(MSIHANDLE hinst)
1925 {
1926     HKEY key;
1927     LONG res;
1928 
1929     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, rp_key, 0, KEY_READ | KEY_WOW64_32KEY, &key);
1930     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1931 
1932     return ERROR_SUCCESS;
1933 }
1934 
1935 UINT WINAPI rpi_present(MSIHANDLE hinst)
1936 {
1937     HKEY key;
1938     LONG res;
1939 
1940     res = RegOpenKeyExA(HKEY_CLASSES_ROOT, "CLSID\\{110913E7-86D1-4BF3-9922-BA103FCDDDFA}",
1941         0, KEY_READ | KEY_WOW64_32KEY, &key);
1942     ok(hinst, !res, "got %u\n", res);
1943     RegCloseKey(key);
1944 
1945     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Class.1", &key);
1946     ok(hinst, !res, "got %u\n", res);
1947     RegCloseKey(key);
1948 
1949     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Class", &key);
1950     ok(hinst, !res, "got %u\n", res);
1951     RegCloseKey(key);
1952 
1953     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Class.2", &key);
1954     ok(hinst, !res, "got %u\n", res);
1955     RegCloseKey(key);
1956 
1957     return ERROR_SUCCESS;
1958 }
1959 
1960 UINT WINAPI rpi_absent(MSIHANDLE hinst)
1961 {
1962     HKEY key;
1963     LONG res;
1964 
1965     res = RegOpenKeyExA(HKEY_CLASSES_ROOT, "CLSID\\{110913E7-86D1-4BF3-9922-BA103FCDDDFA}",
1966         0, KEY_READ | KEY_WOW64_32KEY, &key);
1967     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1968 
1969     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Class.1", &key);
1970     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1971 
1972     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Class", &key);
1973     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1974 
1975     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "Winetest.Class.2", &key);
1976     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
1977 
1978     return ERROR_SUCCESS;
1979 }
1980 
1981 static const CHAR ru_key[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Installer"
1982     "\\UserData\\S-1-5-18\\Products\\84A88FD7F6998CE40A22FB59F6B9C2BB\\InstallProperties";
1983 
1984 UINT WINAPI ru_present(MSIHANDLE hinst)
1985 {
1986     HKEY key;
1987     LONG res;
1988 
1989     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ru_key, 0, KEY_READ | KEY_WOW64_64KEY, &key);
1990     ok(hinst, !res, "got %u\n", res);
1991     check_reg_str(hinst, key, "ProductID", "none");
1992     RegCloseKey(key);
1993 
1994     return ERROR_SUCCESS;
1995 }
1996 
1997 UINT WINAPI ru_absent(MSIHANDLE hinst)
1998 {
1999     HKEY key;
2000     LONG res;
2001 
2002     res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, ru_key, 0, KEY_READ | KEY_WOW64_64KEY, &key);
2003     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
2004 
2005     return ERROR_SUCCESS;
2006 }
2007 
2008 static const GUID LIBID_register_test =
2009     {0xeac5166a, 0x9734, 0x4d91, {0x87,0x8f, 0x1d,0xd0,0x23,0x04,0xc6,0x6c}};
2010 
2011 UINT WINAPI tl_present(MSIHANDLE hinst)
2012 {
2013     ITypeLib *tlb;
2014     HRESULT hr;
2015 
2016     hr = LoadRegTypeLib(&LIBID_register_test, 7, 1, 0, &tlb);
2017     ok(hinst, hr == S_OK, "got %#x\n", hr);
2018     ITypeLib_Release(tlb);
2019 
2020     return ERROR_SUCCESS;
2021 }
2022 
2023 UINT WINAPI tl_absent(MSIHANDLE hinst)
2024 {
2025     ITypeLib *tlb;
2026     HRESULT hr;
2027 
2028     hr = LoadRegTypeLib(&LIBID_register_test, 7, 1, 0, &tlb);
2029     ok(hinst, hr == TYPE_E_LIBNOTREGISTERED, "got %#x\n", hr);
2030 
2031     return ERROR_SUCCESS;
2032 }
2033 
2034 UINT WINAPI sr_present(MSIHANDLE hinst)
2035 {
2036     HKEY key;
2037     LONG res;
2038 
2039     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "selfreg_test", &key);
2040     ok(hinst, !res, "got %u\n", res);
2041     RegCloseKey(key);
2042 
2043     return ERROR_SUCCESS;
2044 }
2045 
2046 UINT WINAPI sr_absent(MSIHANDLE hinst)
2047 {
2048     HKEY key;
2049     LONG res;
2050 
2051     res = RegOpenKeyA(HKEY_CLASSES_ROOT, "selfreg_test", &key);
2052     ok(hinst, res == ERROR_FILE_NOT_FOUND, "got %u\n", res);
2053 
2054     return ERROR_SUCCESS;
2055 }
2056 
2057 UINT WINAPI env_present(MSIHANDLE hinst)
2058 {
2059     HKEY key;
2060     LONG res;
2061 
2062     res = RegOpenKeyA(HKEY_CURRENT_USER, "Environment", &key);
2063     ok(hinst, !res, "got %u\n", res);
2064     check_reg_str(hinst, key, "MSITESTVAR3", "1");
2065     check_reg_str(hinst, key, "MSITESTVAR4", "1");
2066     RegCloseKey(key);
2067 
2068     return ERROR_SUCCESS;
2069 }
2070 
2071 UINT WINAPI env_absent(MSIHANDLE hinst)
2072 {
2073     HKEY key;
2074     LONG res;
2075 
2076     res = RegOpenKeyA(HKEY_CURRENT_USER, "Environment", &key);
2077     ok(hinst, !res, "got %u\n", res);
2078     check_reg_str(hinst, key, "MSITESTVAR3", NULL);
2079     check_reg_str(hinst, key, "MSITESTVAR4", NULL);
2080     RegCloseKey(key);
2081 
2082     return ERROR_SUCCESS;
2083 }
2084 
2085 UINT WINAPI ini_present(MSIHANDLE hinst)
2086 {
2087     char path[MAX_PATH], buf[10];
2088     DWORD len;
2089 
2090     if (FAILED(SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILESX86, NULL, 0, path)))
2091         SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES, NULL, 0, path);
2092     strcat(path, "\\msitest\\test.ini");
2093 
2094     len = GetPrivateProfileStringA("section1", "key1", NULL, buf, sizeof(buf), path);
2095     ok(hinst, len == 6, "got %u\n", len);
2096 
2097     return ERROR_SUCCESS;
2098 }
2099 
2100 UINT WINAPI ini_absent(MSIHANDLE hinst)
2101 {
2102     char path[MAX_PATH], buf[10];
2103     DWORD len;
2104 
2105     if (FAILED(SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILESX86, NULL, 0, path)))
2106         SHGetFolderPathA(NULL, CSIDL_PROGRAM_FILES, NULL, 0, path);
2107     strcat(path, "\\msitest\\test.ini");
2108 
2109     len = GetPrivateProfileStringA("section1", "key1", NULL, buf, sizeof(buf), path);
2110     ok(hinst, !len, "got %u\n", len);
2111 
2112     return ERROR_SUCCESS;
2113 }
2114 
2115 UINT WINAPI wrv_present(MSIHANDLE hinst)
2116 {
2117     HKEY key;
2118     LONG res;
2119 
2120     res = RegOpenKeyA(HKEY_CURRENT_USER, "msitest", &key);
2121     ok(hinst, !res, "got %u\n", res);
2122     check_reg_str(hinst, key, "sz", "string");
2123     RegCloseKey(key);
2124 
2125     return ERROR_SUCCESS;
2126 }
2127 
2128 UINT WINAPI wrv_absent(MSIHANDLE hinst)
2129 {
2130     HKEY key;
2131     LONG res;
2132 
2133     res = RegOpenKeyA(HKEY_CURRENT_USER, "msitest", &key);
2134     ok(hinst, !res, "got %u\n", res);
2135     check_reg_str(hinst, key, "sz", NULL);
2136     RegCloseKey(key);
2137 
2138     return ERROR_SUCCESS;
2139 }
2140