Don't consider no driver a test failure.
[wine/testsucceed.git] / programs / msiexec / msiexec.c
blob78cac5e2b37f7a8b3b3e02bde4af16bb431b138f
1 /*
2 * msiexec.exe implementation
4 * Copyright 2004 Vincent Béron
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include <windows.h>
22 #include <msi.h>
23 #include <objbase.h>
24 #include <stdio.h>
26 #include "msiexec.h"
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(msiexec);
32 static const char UsageStr[] =
33 "Usage:\n"
34 " Install a product:\n"
35 " msiexec {package|productcode} [property]\n"
36 " msiexec /i {package|productcode} [property]\n"
37 " msiexec /a package [property]\n"
38 " Repair an installation:\n"
39 " msiexec /f[p|o|e|d|c|a|u|m|s|v] {package|productcode}\n"
40 " Uninstall a product:\n"
41 " msiexec /x {package|productcode} [property]\n"
42 " Advertise a product:\n"
43 " msiexec /j[u|m] package [/t transform] [/g languageid]\n"
44 " msiexec {u|m} package [/t transform] [/g languageid]\n"
45 " Apply a patch:\n"
46 " msiexec /p patchpackage [property]\n"
47 " msiexec /p patchpackage /a package [property]\n"
48 " Modifiers for above operations:\n"
49 " msiexec /l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] logfile\n"
50 " msiexec /q{|n|b|r|f|n+|b+|b-}\n"
51 " Register a module:\n"
52 " msiexec /y module\n"
53 " Unregister a module:\n"
54 " msiexec /z module\n"
55 " Display usage and copyright:\n"
56 " msiexec {/h|/?}\n"
57 "NOTE: Product code on commandline unimplemented as of yet\n"
58 "\n"
59 "Copyright 2004 Vincent Béron\n";
61 static const char ActionAdmin[] = "ACTION=ADMIN ";
62 static const char RemoveAll[] = "REMOVE=ALL ";
64 static void ShowUsage(int ExitCode)
66 printf(UsageStr);
67 ExitProcess(ExitCode);
70 static BOOL GetProductCode(LPCSTR str, LPCSTR *PackageName, LPGUID *ProductCode)
72 BOOL ret = FALSE;
73 int len = 0;
74 LPWSTR wstr = NULL;
76 if(strlen(str) == 38)
78 len = MultiByteToWideChar(CP_ACP, 0, str, -1, wstr, 0);
79 wstr = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
80 ret = (CLSIDFromString(wstr, *ProductCode) == NOERROR);
81 HeapFree(GetProcessHeap(), 0, wstr);
82 wstr = NULL;
85 if(!ret)
87 HeapFree(GetProcessHeap(), 0, *ProductCode);
88 *ProductCode = NULL;
89 *PackageName = str;
92 return ret;
95 static VOID StringListAppend(LPSTR *StringList, LPCSTR StringAppend)
97 LPSTR TempStr = HeapReAlloc(GetProcessHeap(), 0, *StringList, HeapSize(GetProcessHeap(), 0, *StringList)+strlen(StringAppend));
98 if(!TempStr)
100 WINE_ERR("Out of memory!\n");
101 ExitProcess(1);
103 *StringList = TempStr;
104 strcat(*StringList, StringAppend);
107 static VOID StringCompareRemoveLast(LPSTR String, CHAR character)
109 int len = strlen(String);
110 if(len && String[len-1] == character) String[len-1] = 0;
113 static VOID *LoadProc(LPCSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
115 VOID* (*proc)(void);
117 *DllHandle = LoadLibraryExA(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
118 if(!*DllHandle)
120 fprintf(stderr, "Unable to load dll %s\n", DllName);
121 ExitProcess(1);
123 proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
124 if(!proc)
126 fprintf(stderr, "Dll %s does not implement function %s\n", DllName, ProcName);
127 FreeLibrary(*DllHandle);
128 ExitProcess(1);
131 return proc;
134 static void DllRegisterServer(LPCSTR DllName)
136 HRESULT hr;
137 DLLREGISTERSERVER pfDllRegisterServer = NULL;
138 HMODULE DllHandle = NULL;
140 pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);
142 hr = pfDllRegisterServer();
143 if(FAILED(hr))
145 fprintf(stderr, "Failed to register dll %s\n", DllName);
146 ExitProcess(1);
148 printf("Successfully registered dll %s\n", DllName);
149 if(DllHandle)
150 FreeLibrary(DllHandle);
153 static void DllUnregisterServer(LPCSTR DllName)
155 HRESULT hr;
156 DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
157 HMODULE DllHandle = NULL;
159 pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);
161 hr = pfDllUnregisterServer();
162 if(FAILED(hr))
164 fprintf(stderr, "Failed to unregister dll %s\n", DllName);
165 ExitProcess(1);
167 printf("Successfully unregistered dll %s\n", DllName);
168 if(DllHandle)
169 FreeLibrary(DllHandle);
172 int main(int argc, char *argv[])
174 int i;
175 BOOL FunctionInstall = FALSE;
176 BOOL FunctionInstallAdmin = FALSE;
177 BOOL FunctionRepair = FALSE;
178 BOOL FunctionAdvertise = FALSE;
179 BOOL FunctionPatch = FALSE;
180 BOOL FunctionDllRegisterServer = FALSE;
181 BOOL FunctionDllUnregisterServer = FALSE;
182 BOOL FunctionRegServer = FALSE;
183 BOOL FunctionUnregServer = FALSE;
185 BOOL GotProductCode = FALSE;
186 LPCSTR PackageName = NULL;
187 LPGUID ProductCode = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
188 LPSTR Properties = HeapAlloc(GetProcessHeap(), 0, 1);
190 DWORD RepairMode = 0;
192 DWORD AdvertiseMode = 0;
193 LPSTR Transforms = HeapAlloc(GetProcessHeap(), 0, 1);
194 LANGID Language = 0;
196 DWORD LogMode = 0;
197 LPSTR LogFileName = NULL;
198 DWORD LogAttributes = 0;
200 LPSTR PatchFileName = NULL;
201 INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
203 INSTALLUILEVEL InstallUILevel = 0, retInstallUILevel;
205 LPSTR DllName = NULL;
207 Properties[0] = 0;
208 Transforms[0] = 0;
210 for(i = 1; i < argc; i++)
212 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
214 if (!strcasecmp(argv[i], "/regserver"))
216 FunctionRegServer = TRUE;
218 else if (!strcasecmp(argv[i], "/unregserver") || !strcasecmp(argv[i], "/unregister"))
220 FunctionUnregServer = TRUE;
222 else if(!strncasecmp(argv[i], "/i", 2))
224 char *argvi = argv[i];
225 FunctionInstall = TRUE;
226 if(strlen(argvi) > 2)
227 argvi += 2;
228 else {
229 i++;
230 if(i >= argc)
231 ShowUsage(1);
232 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
233 argvi = argv[i];
235 GotProductCode = GetProductCode(argvi, &PackageName, &ProductCode);
237 else if(!strcasecmp(argv[i], "/a"))
239 FunctionInstall = TRUE;
240 FunctionInstallAdmin = TRUE;
241 InstallType = INSTALLTYPE_NETWORK_IMAGE;
242 i++;
243 if(i >= argc)
244 ShowUsage(1);
245 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
246 PackageName = argv[i];
247 StringListAppend(&Properties, ActionAdmin);
249 else if(!strncasecmp(argv[i], "/f", 2))
251 int j;
252 int len = strlen(argv[i]);
253 FunctionRepair = TRUE;
254 for(j = 2; j < len; j++)
256 switch(argv[i][j])
258 case 'P':
259 case 'p':
260 RepairMode |= REINSTALLMODE_FILEMISSING;
261 break;
262 case 'O':
263 case 'o':
264 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
265 break;
266 case 'E':
267 case 'e':
268 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
269 break;
270 case 'D':
271 case 'd':
272 RepairMode |= REINSTALLMODE_FILEEXACT;
273 break;
274 case 'C':
275 case 'c':
276 RepairMode |= REINSTALLMODE_FILEVERIFY;
277 break;
278 case 'A':
279 case 'a':
280 RepairMode |= REINSTALLMODE_FILEREPLACE;
281 break;
282 case 'U':
283 case 'u':
284 RepairMode |= REINSTALLMODE_USERDATA;
285 break;
286 case 'M':
287 case 'm':
288 RepairMode |= REINSTALLMODE_MACHINEDATA;
289 break;
290 case 'S':
291 case 's':
292 RepairMode |= REINSTALLMODE_SHORTCUT;
293 break;
294 case 'V':
295 case 'v':
296 RepairMode |= REINSTALLMODE_PACKAGE;
297 break;
298 default:
299 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argv[i][j]);
300 break;
303 if(len == 2)
305 RepairMode = REINSTALLMODE_FILEMISSING |
306 REINSTALLMODE_FILEEQUALVERSION |
307 REINSTALLMODE_FILEVERIFY |
308 REINSTALLMODE_MACHINEDATA |
309 REINSTALLMODE_SHORTCUT;
311 i++;
312 if(i >= argc)
313 ShowUsage(1);
314 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
315 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
317 else if(!strcasecmp(argv[i], "/x"))
319 FunctionInstall = TRUE;
320 i++;
321 if(i >= argc)
322 ShowUsage(1);
323 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
324 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
325 StringListAppend(&Properties, RemoveAll);
327 else if(!strncasecmp(argv[i], "/j", 2))
329 int j;
330 int len = strlen(argv[i]);
331 FunctionAdvertise = TRUE;
332 for(j = 2; j < len; j++)
334 switch(argv[i][j])
336 case 'U':
337 case 'u':
338 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
339 break;
340 case 'M':
341 case 'm':
342 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
343 break;
344 default:
345 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argv[i][j]);
346 break;
349 i++;
350 if(i >= argc)
351 ShowUsage(1);
352 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
353 PackageName = argv[i];
355 else if(!strcasecmp(argv[i], "u"))
357 FunctionAdvertise = TRUE;
358 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
359 i++;
360 if(i >= argc)
361 ShowUsage(1);
362 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
363 PackageName = argv[i];
365 else if(!strcasecmp(argv[i], "m"))
367 FunctionAdvertise = TRUE;
368 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
369 i++;
370 if(i >= argc)
371 ShowUsage(1);
372 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
373 PackageName = argv[i];
375 else if(!strcasecmp(argv[i], "/t"))
377 i++;
378 if(i >= argc)
379 ShowUsage(1);
380 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
381 StringListAppend(&Transforms, argv[i]);
382 StringListAppend(&Transforms, ";");
384 else if(!strncasecmp(argv[i], "TRANSFORMS=", 11))
386 StringListAppend(&Transforms, argv[i]+11);
387 StringListAppend(&Transforms, ";");
389 else if(!strcasecmp(argv[i], "/g"))
391 i++;
392 if(i >= argc)
393 ShowUsage(1);
394 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
395 Language = strtol(argv[i], NULL, 0);
397 else if(!strncasecmp(argv[i], "/l", 2))
399 int j;
400 int len = strlen(argv[i]);
401 for(j = 2; j < len; j++)
403 switch(argv[i][j])
405 case 'I':
406 case 'i':
407 LogMode |= INSTALLLOGMODE_INFO;
408 break;
409 case 'W':
410 case 'w':
411 LogMode |= INSTALLLOGMODE_WARNING;
412 break;
413 case 'E':
414 case 'e':
415 LogMode |= INSTALLLOGMODE_ERROR;
416 break;
417 case 'A':
418 case 'a':
419 LogMode |= INSTALLLOGMODE_ACTIONSTART;
420 break;
421 case 'R':
422 case 'r':
423 LogMode |= INSTALLLOGMODE_ACTIONDATA;
424 break;
425 case 'U':
426 case 'u':
427 LogMode |= INSTALLLOGMODE_USER;
428 break;
429 case 'C':
430 case 'c':
431 LogMode |= INSTALLLOGMODE_COMMONDATA;
432 break;
433 case 'M':
434 case 'm':
435 LogMode |= INSTALLLOGMODE_FATALEXIT;
436 break;
437 case 'O':
438 case 'o':
439 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
440 break;
441 case 'P':
442 case 'p':
443 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
444 break;
445 case 'V':
446 case 'v':
447 LogMode |= INSTALLLOGMODE_VERBOSE;
448 break;
449 case '*':
450 LogMode = INSTALLLOGMODE_FATALEXIT |
451 INSTALLLOGMODE_ERROR |
452 INSTALLLOGMODE_WARNING |
453 INSTALLLOGMODE_USER |
454 INSTALLLOGMODE_INFO |
455 INSTALLLOGMODE_RESOLVESOURCE |
456 INSTALLLOGMODE_OUTOFDISKSPACE |
457 INSTALLLOGMODE_ACTIONSTART |
458 INSTALLLOGMODE_ACTIONDATA |
459 INSTALLLOGMODE_COMMONDATA |
460 INSTALLLOGMODE_PROPERTYDUMP |
461 INSTALLLOGMODE_PROGRESS |
462 INSTALLLOGMODE_INITIALIZE |
463 INSTALLLOGMODE_TERMINATE |
464 INSTALLLOGMODE_SHOWDIALOG;
465 break;
466 case '+':
467 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
468 break;
469 case '!':
470 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
471 break;
472 default:
473 break;
476 i++;
477 if(i >= argc)
478 ShowUsage(1);
479 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
480 LogFileName = argv[i];
481 if(MsiEnableLogA(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
483 fprintf(stderr, "Logging in %s (0x%08lx, %lu) failed\n", LogFileName, LogMode, LogAttributes);
484 ExitProcess(1);
487 else if(!strcasecmp(argv[i], "/p"))
489 FunctionPatch = TRUE;
490 i++;
491 if(i >= argc)
492 ShowUsage(1);
493 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
494 PatchFileName = argv[i];
496 else if(!strncasecmp(argv[i], "/q", 2))
498 if(strlen(argv[i]) == 2 || !strcasecmp(argv[i]+2, "n"))
500 InstallUILevel = INSTALLUILEVEL_NONE;
502 else if(!strcasecmp(argv[i]+2, "b"))
504 InstallUILevel = INSTALLUILEVEL_BASIC;
506 else if(!strcasecmp(argv[i]+2, "r"))
508 InstallUILevel = INSTALLUILEVEL_REDUCED;
510 else if(!strcasecmp(argv[i]+2, "f"))
512 InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
514 else if(!strcasecmp(argv[i]+2, "n+"))
516 InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
518 else if(!strcasecmp(argv[i]+2, "b+"))
520 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
522 else if(!strcasecmp(argv[i]+2, "b-"))
524 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
526 else
528 fprintf(stderr, "Unknown option \"%s\" for UI level\n", argv[i]+2);
530 retInstallUILevel = MsiSetInternalUI(InstallUILevel, NULL);
531 if(retInstallUILevel == INSTALLUILEVEL_NOCHANGE)
533 fprintf(stderr, "Setting the UI level to 0x%x failed.\n", InstallUILevel);
534 ExitProcess(1);
537 else if(!strcasecmp(argv[i], "/y"))
539 FunctionDllRegisterServer = TRUE;
540 i++;
541 if(i >= argc)
542 ShowUsage(1);
543 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
544 DllName = argv[i];
546 else if(!strcasecmp(argv[i], "/z"))
548 FunctionDllUnregisterServer = TRUE;
549 i++;
550 if(i >= argc)
551 ShowUsage(1);
552 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
553 DllName = argv[i];
555 else if(!strcasecmp(argv[i], "/h") || !strcasecmp(argv[i], "/?"))
557 ShowUsage(0);
559 else if(strchr(argv[i], '='))
561 StringListAppend(&Properties, argv[i]);
562 StringListAppend(&Properties, " ");
564 else
566 FunctionInstall = TRUE;
567 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
571 StringCompareRemoveLast(Properties, ' ');
572 StringCompareRemoveLast(Transforms, ';');
574 if(FunctionInstallAdmin && FunctionPatch)
575 FunctionInstall = FALSE;
577 if(FunctionInstall)
579 if(GotProductCode)
581 WINE_FIXME("Product code treatment not implemented yet\n");
582 ExitProcess(1);
584 else
586 if(MsiInstallProductA(PackageName, Properties) != ERROR_SUCCESS)
588 fprintf(stderr, "Installation of %s (%s) failed.\n", PackageName, Properties);
589 ExitProcess(1);
593 else if(FunctionRepair)
595 if(GotProductCode)
597 WINE_FIXME("Product code treatment not implemented yet\n");
598 ExitProcess(1);
600 else
602 if(MsiReinstallProductA(PackageName, RepairMode) != ERROR_SUCCESS)
604 fprintf(stderr, "Repair of %s (0x%08lx) failed.\n", PackageName, RepairMode);
605 ExitProcess(1);
609 else if(FunctionAdvertise)
611 if(MsiAdvertiseProductA(PackageName, (LPSTR) AdvertiseMode, Transforms, Language) != ERROR_SUCCESS)
613 fprintf(stderr, "Advertising of %s (%lu, %s, 0x%04x) failed.\n", PackageName, AdvertiseMode, Transforms, Language);
614 ExitProcess(1);
617 else if(FunctionPatch)
619 if(MsiApplyPatchA(PatchFileName, PackageName, InstallType, Properties) != ERROR_SUCCESS)
621 fprintf(stderr, "Patching with %s (%s, %d, %s)\n", PatchFileName, PackageName, InstallType, Properties);
622 ExitProcess(1);
625 else if(FunctionDllRegisterServer)
627 DllRegisterServer(DllName);
629 else if(FunctionDllUnregisterServer)
631 DllUnregisterServer(DllName);
633 else if (FunctionRegServer)
635 WINE_FIXME( "/regserver not implemented yet, ignoring\n" );
637 else if (FunctionUnregServer)
639 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
641 else
642 ShowUsage(1);
644 return 0;