2 * DLL for testing type 1 custom actions
4 * Copyright 2017 Zebediah Figura
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.
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.
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
31 static void ok_(MSIHANDLE hinst
, int todo
, const char *file
, int line
, int condition
, const char *msg
, ...)
33 static char buffer
[2000];
37 va_start(valist
, msg
);
38 vsprintf(buffer
, msg
, valist
);
41 record
= MsiCreateRecord(5);
42 MsiRecordSetInteger(record
, 1, todo
);
43 MsiRecordSetStringA(record
, 2, file
);
44 MsiRecordSetInteger(record
, 3, line
);
45 MsiRecordSetInteger(record
, 4, condition
);
46 MsiRecordSetStringA(record
, 5, buffer
);
47 MsiProcessMessage(hinst
, INSTALLMESSAGE_USER
, record
);
48 MsiCloseHandle(record
);
50 #define ok(hinst, condition, ...) ok_(hinst, 0, __FILE__, __LINE__, condition, __VA_ARGS__)
51 #define todo_wine_ok(hinst, condition, ...) ok_(hinst, 1, __FILE__, __LINE__, condition, __VA_ARGS__)
53 static const char *dbgstr_w(WCHAR
*str
)
55 static char buffer
[300], *p
;
57 if (!str
) return "(null)";
62 while ((*p
++ = *str
++));
69 static void check_prop(MSIHANDLE hinst
, const char *prop
, const char *expect
)
71 char buffer
[10] = "x";
72 DWORD sz
= sizeof(buffer
);
73 UINT r
= MsiGetPropertyA(hinst
, prop
, buffer
, &sz
);
74 ok(hinst
, !r
, "'%s': got %u\n", prop
, r
);
75 ok(hinst
, sz
== strlen(buffer
), "'%s': expected %u, got %u\n", prop
, strlen(buffer
), sz
);
76 ok(hinst
, !strcmp(buffer
, expect
), "expected '%s', got '%s'\n", expect
, buffer
);
79 static void test_props(MSIHANDLE hinst
)
81 static const WCHAR booW
[] = {'b','o','o',0};
82 static const WCHAR xyzW
[] = {'x','y','z',0};
83 static const WCHAR xyW
[] = {'x','y',0};
89 /* test invalid values */
90 r
= MsiGetPropertyA(hinst
, NULL
, NULL
, NULL
);
91 ok(hinst
, r
== ERROR_INVALID_PARAMETER
, "got %u\n", r
);
93 r
= MsiGetPropertyA(hinst
, "boo", NULL
, NULL
);
94 ok(hinst
, !r
, "got %u\n", r
);
96 r
= MsiGetPropertyA(hinst
, "boo", buffer
, NULL
);
97 ok(hinst
, r
== ERROR_INVALID_PARAMETER
, "got %u\n", r
);
100 r
= MsiGetPropertyA(hinst
, "boo", NULL
, &sz
);
101 ok(hinst
, !r
, "got %u\n", r
);
102 ok(hinst
, sz
== 0, "got size %u\n", sz
);
106 r
= MsiGetPropertyA(hinst
, "boo", buffer
, &sz
);
107 ok(hinst
, r
== ERROR_MORE_DATA
, "got %u\n", r
);
108 ok(hinst
, !strcmp(buffer
, "x"), "got \"%s\"\n", buffer
);
109 ok(hinst
, sz
== 0, "got size %u\n", sz
);
113 r
= MsiGetPropertyA(hinst
, "boo", buffer
, &sz
);
114 ok(hinst
, !r
, "got %u\n", r
);
115 ok(hinst
, !buffer
[0], "got \"%s\"\n", buffer
);
116 ok(hinst
, sz
== 0, "got size %u\n", sz
);
118 /* set the property to something */
119 r
= MsiSetPropertyA(hinst
, NULL
, NULL
);
120 ok(hinst
, r
== ERROR_INVALID_PARAMETER
, "got %u\n", r
);
122 r
= MsiSetPropertyA(hinst
, "", NULL
);
123 ok(hinst
, !r
, "got %u\n", r
);
125 r
= MsiSetPropertyA(hinst
, "", "asdf");
126 ok(hinst
, r
== ERROR_FUNCTION_FAILED
, "got %u\n", r
);
128 r
= MsiSetPropertyA(hinst
, "=", "asdf");
129 ok(hinst
, !r
, "got %u\n", r
);
130 check_prop(hinst
, "=", "asdf");
132 r
= MsiSetPropertyA(hinst
, " ", "asdf");
133 ok(hinst
, !r
, "got %u\n", r
);
134 check_prop(hinst
, " ", "asdf");
136 r
= MsiSetPropertyA(hinst
, "'", "asdf");
137 ok(hinst
, !r
, "got %u\n", r
);
138 check_prop(hinst
, "'", "asdf");
140 r
= MsiSetPropertyA(hinst
, "boo", NULL
);
141 ok(hinst
, !r
, "got %u\n", r
);
142 check_prop(hinst
, "boo", "");
144 r
= MsiSetPropertyA(hinst
, "boo", "");
145 ok(hinst
, !r
, "got %u\n", r
);
146 check_prop(hinst
, "boo", "");
148 r
= MsiSetPropertyA(hinst
, "boo", "xyz");
149 ok(hinst
, !r
, "got %u\n", r
);
150 check_prop(hinst
, "boo", "xyz");
152 r
= MsiGetPropertyA(hinst
, "boo", NULL
, NULL
);
153 ok(hinst
, !r
, "got %u\n", r
);
155 r
= MsiGetPropertyA(hinst
, "boo", buffer
, NULL
);
156 ok(hinst
, r
== ERROR_INVALID_PARAMETER
, "got %u\n", r
);
158 /* Returned size is in bytes, not chars, but only for custom actions.
159 * Seems to be a casualty of RPC... */
162 r
= MsiGetPropertyA(hinst
, "boo", NULL
, &sz
);
163 ok(hinst
, !r
, "got %u\n", r
);
164 ok(hinst
, sz
== 6, "got size %u\n", sz
);
168 r
= MsiGetPropertyA(hinst
, "boo", buffer
, &sz
);
169 ok(hinst
, r
== ERROR_MORE_DATA
, "got %u\n", r
);
170 ok(hinst
, !strcmp(buffer
, "q"), "got \"%s\"\n", buffer
);
171 todo_wine_ok(hinst
, sz
== 6, "got size %u\n", sz
);
175 r
= MsiGetPropertyA(hinst
, "boo", buffer
, &sz
);
176 ok(hinst
, r
== ERROR_MORE_DATA
, "got %u\n", r
);
177 ok(hinst
, !buffer
[0], "got \"%s\"\n", buffer
);
178 todo_wine_ok(hinst
, sz
== 6, "got size %u\n", sz
);
182 r
= MsiGetPropertyA(hinst
, "boo", buffer
, &sz
);
183 ok(hinst
, r
== ERROR_MORE_DATA
, "got %u\n", r
);
184 ok(hinst
, !strcmp(buffer
, "xy"), "got \"%s\"\n", buffer
);
185 todo_wine_ok(hinst
, sz
== 6, "got size %u\n", sz
);
189 r
= MsiGetPropertyA(hinst
, "boo", buffer
, &sz
);
190 ok(hinst
, !r
, "got %u\n", r
);
191 ok(hinst
, !strcmp(buffer
, "xyz"), "got \"%s\"\n", buffer
);
192 ok(hinst
, sz
== 3, "got size %u\n", sz
);
195 r
= MsiGetPropertyW(hinst
, booW
, NULL
, &sz
);
196 ok(hinst
, !r
, "got %u\n", r
);
197 ok(hinst
, sz
== 3, "got size %u\n", sz
);
200 lstrcpyW(bufferW
, booW
);
201 r
= MsiGetPropertyW(hinst
, booW
, bufferW
, &sz
);
202 ok(hinst
, r
== ERROR_MORE_DATA
, "got %u\n", r
);
203 ok(hinst
, !lstrcmpW(bufferW
, booW
), "got %s\n", dbgstr_w(bufferW
));
204 ok(hinst
, sz
== 3, "got size %u\n", sz
);
207 lstrcpyW(bufferW
, booW
);
208 r
= MsiGetPropertyW(hinst
, booW
, bufferW
, &sz
);
209 ok(hinst
, r
== ERROR_MORE_DATA
, "got %u\n", r
);
210 ok(hinst
, !bufferW
[0], "got %s\n", dbgstr_w(bufferW
));
211 ok(hinst
, sz
== 3, "got size %u\n", sz
);
214 lstrcpyW(bufferW
, booW
);
215 r
= MsiGetPropertyW(hinst
, booW
, bufferW
, &sz
);
216 ok(hinst
, r
== ERROR_MORE_DATA
, "got %u\n", r
);
217 ok(hinst
, !lstrcmpW(bufferW
, xyW
), "got %s\n", dbgstr_w(bufferW
));
218 ok(hinst
, sz
== 3, "got size %u\n", sz
);
221 lstrcpyW(bufferW
, booW
);
222 r
= MsiGetPropertyW(hinst
, booW
, bufferW
, &sz
);
223 ok(hinst
, !r
, "got %u\n", r
);
224 ok(hinst
, !lstrcmpW(bufferW
, xyzW
), "got %s\n", dbgstr_w(bufferW
));
225 ok(hinst
, sz
== 3, "got size %u\n", sz
);
227 r
= MsiSetPropertyA(hinst
, "boo", NULL
);
228 ok(hinst
, !r
, "got %u\n", r
);
229 check_prop(hinst
, "boo", "");
232 r
= MsiGetPropertyA(hinst
, "embednullprop", NULL
, &sz
);
233 ok(hinst
, !r
, "got %u\n", r
);
234 ok(hinst
, sz
== 6, "got size %u\n", sz
);
237 memset(buffer
, 0xcc, sizeof(buffer
));
238 r
= MsiGetPropertyA(hinst
, "embednullprop", buffer
, &sz
);
239 ok(hinst
, !r
, "got %u\n", r
);
240 ok(hinst
, sz
== 3, "got size %u\n", sz
);
241 ok(hinst
, !memcmp(buffer
, "a\0\0\0\xcc", 5), "wrong data\n");
244 static void test_db(MSIHANDLE hinst
)
246 MSIHANDLE hdb
, view
, rec
, rec2
, suminfo
;
251 hdb
= MsiGetActiveDatabase(hinst
);
252 ok(hinst
, hdb
, "MsiGetActiveDatabase failed\n");
254 r
= MsiDatabaseIsTablePersistentA(hdb
, "Test");
255 ok(hinst
, r
== MSICONDITION_TRUE
, "got %u\n", r
);
257 r
= MsiDatabaseOpenViewA(hdb
, NULL
, &view
);
258 ok(hinst
, r
== ERROR_BAD_QUERY_SYNTAX
, "got %u\n", r
);
260 r
= MsiDatabaseOpenViewA(hdb
, "SELECT * FROM `Test`", NULL
);
261 ok(hinst
, r
== ERROR_INVALID_PARAMETER
, "got %u\n", r
);
263 r
= MsiDatabaseOpenViewA(hdb
, "SELECT * FROM `Test`", &view
);
264 ok(hinst
, !r
, "got %u\n", r
);
266 r
= MsiViewGetColumnInfo(view
, MSICOLINFO_NAMES
, &rec2
);
267 ok(hinst
, !r
, "got %u\n", r
);
270 r
= MsiRecordGetStringA(rec2
, 1, buffer
, &sz
);
271 ok(hinst
, !r
, "got %u\n", r
);
272 ok(hinst
, sz
== strlen(buffer
), "got size %u\n", sz
);
273 ok(hinst
, !strcmp(buffer
, "Name"), "got '%s'\n", buffer
);
275 r
= MsiCloseHandle(rec2
);
276 ok(hinst
, !r
, "got %u\n", r
);
278 r
= MsiViewExecute(view
, 0);
279 ok(hinst
, !r
, "got %u\n", r
);
281 r
= MsiViewFetch(view
, &rec2
);
282 ok(hinst
, !r
, "got %u\n", r
);
284 r
= MsiRecordGetFieldCount(rec2
);
285 ok(hinst
, r
== 3, "got %u\n", r
);
288 r
= MsiRecordGetStringA(rec2
, 1, buffer
, &sz
);
289 ok(hinst
, !r
, "got %u\n", r
);
290 ok(hinst
, sz
== strlen(buffer
), "got size %u\n", sz
);
291 ok(hinst
, !strcmp(buffer
, "one"), "got '%s'\n", buffer
);
293 r
= MsiRecordGetInteger(rec2
, 2);
294 ok(hinst
, r
== 1, "got %d\n", r
);
297 r
= MsiRecordReadStream(rec2
, 3, buffer
, &sz
);
298 ok(hinst
, !r
, "got %u\n", r
);
299 ok(hinst
, !memcmp(buffer
, "unus", 4), "wrong data\n");
301 r
= MsiCloseHandle(rec2
);
302 ok(hinst
, !r
, "got %u\n", r
);
304 r
= MsiViewFetch(view
, &rec2
);
305 ok(hinst
, !r
, "got %u\n", r
);
307 r
= MsiRecordGetFieldCount(rec2
);
308 ok(hinst
, r
== 3, "got %u\n", r
);
311 r
= MsiRecordGetStringA(rec2
, 1, buffer
, &sz
);
312 ok(hinst
, !r
, "got %u\n", r
);
313 ok(hinst
, sz
== strlen(buffer
), "got size %u\n", sz
);
314 ok(hinst
, !strcmp(buffer
, "two"), "got '%s'\n", buffer
);
316 r
= MsiRecordGetInteger(rec2
, 2);
317 ok(hinst
, r
== 2, "got %d\n", r
);
320 r
= MsiRecordReadStream(rec2
, 3, buffer
, &sz
);
321 ok(hinst
, !r
, "got %u\n", r
);
322 ok(hinst
, !memcmp(buffer
, "duo", 3), "wrong data\n");
324 r
= MsiViewModify(view
, MSIMODIFY_REFRESH
, 0);
325 ok(hinst
, r
== ERROR_INVALID_HANDLE
, "got %u\n", r
);
327 r
= MsiRecordSetStringA(rec2
, 1, "three");
328 ok(hinst
, !r
, "got %u\n", r
);
330 r
= MsiRecordSetInteger(rec2
, 2, 3);
331 ok(hinst
, !r
, "got %u\n", r
);
333 r
= MsiRecordSetInteger(rec2
, 3, 3);
334 ok(hinst
, !r
, "got %u\n", r
);
336 r
= MsiViewModify(view
, MSIMODIFY_REFRESH
, rec2
);
337 ok(hinst
, !r
, "got %d\n", r
);
340 r
= MsiRecordGetStringA(rec2
, 1, buffer
, &sz
);
341 ok(hinst
, !r
, "got %u\n", r
);
342 ok(hinst
, sz
== strlen(buffer
), "got size %u\n", sz
);
343 ok(hinst
, !strcmp(buffer
, "two"), "got '%s'\n", buffer
);
345 r
= MsiRecordGetInteger(rec2
, 2);
346 ok(hinst
, r
== 2, "got %d\n", r
);
349 r
= MsiRecordReadStream(rec2
, 3, buffer
, &sz
);
350 ok(hinst
, !r
, "got %u\n", r
);
351 ok(hinst
, !memcmp(buffer
, "duo", 3), "wrong data\n");
353 r
= MsiCloseHandle(rec2
);
354 ok(hinst
, !r
, "got %u\n", r
);
356 r
= MsiViewFetch(view
, &rec2
);
357 ok(hinst
, r
== ERROR_NO_MORE_ITEMS
, "got %u\n", r
);
358 ok(hinst
, !rec2
, "got %u\n", rec2
);
360 r
= MsiViewClose(view
);
361 ok(hinst
, !r
, "got %u\n", r
);
363 r
= MsiCloseHandle(view
);
364 ok(hinst
, !r
, "got %u\n", r
);
366 r
= MsiDatabaseOpenViewA(hdb
, "SELECT * FROM `Test` WHERE `Name` = ?", &view
);
367 ok(hinst
, !r
, "got %u\n", r
);
369 rec
= MsiCreateRecord(1);
370 MsiRecordSetStringA(rec
, 1, "one");
372 r
= MsiViewExecute(view
, rec
);
373 ok(hinst
, !r
, "got %u\n", r
);
375 r
= MsiViewFetch(view
, &rec2
);
376 ok(hinst
, !r
, "got %u\n", r
);
378 r
= MsiRecordGetInteger(rec2
, 2);
379 ok(hinst
, r
== 1, "got %d\n", r
);
381 r
= MsiCloseHandle(rec2
);
382 ok(hinst
, !r
, "got %u\n", r
);
384 r
= MsiViewFetch(view
, &rec2
);
385 ok(hinst
, r
== ERROR_NO_MORE_ITEMS
, "got %u\n", r
);
386 ok(hinst
, !rec2
, "got %u\n", rec2
);
388 r
= MsiCloseHandle(rec
);
389 ok(hinst
, !r
, "got %u\n", r
);
391 r
= MsiCloseHandle(view
);
392 ok(hinst
, !r
, "got %u\n", r
);
394 /* test MsiDatabaseGetPrimaryKeys() */
395 r
= MsiDatabaseGetPrimaryKeysA(hdb
, "Test", &rec
);
396 ok(hinst
, !r
, "got %u\n", r
);
398 r
= MsiRecordGetFieldCount(rec
);
399 ok(hinst
, r
== 1, "got %d\n", r
);
402 r
= MsiRecordGetStringA(rec
, 0, buffer
, &sz
);
403 ok(hinst
, !r
, "got %u\n", r
);
404 ok(hinst
, sz
== strlen(buffer
), "got size %u\n", sz
);
405 ok(hinst
, !strcmp(buffer
, "Test"), "got '%s'\n", buffer
);
408 r
= MsiRecordGetStringA(rec
, 1, buffer
, &sz
);
409 ok(hinst
, !r
, "got %u\n", r
);
410 ok(hinst
, sz
== strlen(buffer
), "got size %u\n", sz
);
411 ok(hinst
, !strcmp(buffer
, "Name"), "got '%s'\n", buffer
);
413 r
= MsiCloseHandle(rec
);
414 ok(hinst
, !r
, "got %u\n", r
);
416 r
= MsiGetSummaryInformationA(hdb
, NULL
, 1, NULL
);
417 ok(hinst
, r
== ERROR_INVALID_PARAMETER
, "got %u\n", r
);
419 r
= MsiGetSummaryInformationA(hdb
, NULL
, 1, &suminfo
);
420 ok(hinst
, !r
, "got %u\n", r
);
422 r
= MsiCloseHandle(suminfo
);
423 ok(hinst
, !r
, "got %u\n", r
);
425 r
= MsiCloseHandle(hdb
);
426 ok(hinst
, !r
, "got %u\n", r
);
429 /* Main test. Anything that doesn't depend on a specific install configuration
430 * or have undesired side effects should go here. */
431 UINT WINAPI
main_test(MSIHANDLE hinst
)
434 IUnknown
*unk
= NULL
;
437 /* Test for an MTA apartment */
438 hr
= CoCreateInstance(&CLSID_XMLDocument
, NULL
, CLSCTX_INPROC_SERVER
, &IID_IUnknown
, (void **)&unk
);
439 todo_wine_ok(hinst
, hr
== S_OK
, "CoCreateInstance failed with %08x\n", hr
);
441 if (unk
) IUnknown_Release(unk
);
443 /* but ours is uninitialized */
444 hr
= CoInitializeEx(NULL
, COINIT_APARTMENTTHREADED
);
445 ok(hinst
, hr
== S_OK
, "got %#x\n", hr
);
448 /* Test MsiGetDatabaseState() */
449 res
= MsiGetDatabaseState(hinst
);
450 todo_wine_ok(hinst
, res
== MSIDBSTATE_ERROR
, "expected MSIDBSTATE_ERROR, got %u\n", res
);
455 return ERROR_SUCCESS
;
458 UINT WINAPI
test_retval(MSIHANDLE hinst
)
461 DWORD len
= sizeof(prop
);
464 MsiGetPropertyA(hinst
, "TEST_RETVAL", prop
, &len
);
465 sscanf(prop
, "%u", &retval
);
469 static void append_file(MSIHANDLE hinst
, const char *filename
, const char *text
)
472 HANDLE file
= CreateFileA(filename
, GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
473 ok(hinst
, file
!= INVALID_HANDLE_VALUE
, "CreateFile failed, error %u\n", GetLastError());
475 SetFilePointer(file
, 0, NULL
, FILE_END
);
476 WriteFile(file
, text
, strlen(text
), &size
, NULL
);
480 UINT WINAPI
da_immediate(MSIHANDLE hinst
)
483 DWORD len
= sizeof(prop
);
485 MsiGetPropertyA(hinst
, "TESTPATH", prop
, &len
);
487 append_file(hinst
, prop
, "one");
489 ok(hinst
, !MsiGetMode(hinst
, MSIRUNMODE_SCHEDULED
), "shouldn't be scheduled\n");
490 ok(hinst
, !MsiGetMode(hinst
, MSIRUNMODE_ROLLBACK
), "shouldn't be rollback\n");
491 ok(hinst
, !MsiGetMode(hinst
, MSIRUNMODE_COMMIT
), "shouldn't be commit\n");
493 return ERROR_SUCCESS
;
496 UINT WINAPI
da_deferred(MSIHANDLE hinst
)
499 DWORD len
= sizeof(prop
);
503 /* Test that we were in fact deferred */
504 r
= MsiGetPropertyA(hinst
, "CustomActionData", prop
, &len
);
505 ok(hinst
, r
== ERROR_SUCCESS
, "got %u\n", r
);
506 ok(hinst
, prop
[0], "CustomActionData was empty\n");
508 append_file(hinst
, prop
, "two");
510 /* Test available properties */
512 r
= MsiGetPropertyA(hinst
, "ProductCode", prop
, &len
);
513 ok(hinst
, r
== ERROR_SUCCESS
, "got %u\n", r
);
514 ok(hinst
, prop
[0], "got %s\n", prop
);
517 r
= MsiGetPropertyA(hinst
, "UserSID", prop
, &len
);
518 ok(hinst
, r
== ERROR_SUCCESS
, "got %u\n", r
);
519 ok(hinst
, prop
[0], "got %s\n", prop
);
522 r
= MsiGetPropertyA(hinst
, "TESTPATH", prop
, &len
);
523 ok(hinst
, r
== ERROR_SUCCESS
, "got %u\n", r
);
524 todo_wine_ok(hinst
, !prop
[0], "got %s\n", prop
);
527 ok(hinst
, MsiGetMode(hinst
, MSIRUNMODE_SCHEDULED
), "should be scheduled\n");
528 ok(hinst
, !MsiGetMode(hinst
, MSIRUNMODE_ROLLBACK
), "shouldn't be rollback\n");
529 ok(hinst
, !MsiGetMode(hinst
, MSIRUNMODE_COMMIT
), "shouldn't be commit\n");
531 lang
= MsiGetLanguage(hinst
);
532 ok(hinst
, lang
!= ERROR_INVALID_HANDLE
, "MsiGetLanguage failed\n");
534 return ERROR_SUCCESS
;