Handle unknown parameters more gracefully.
[wine.git] / programs / msiexec / msiexec.c
blob6b6d9f08a675e42a199ae9d16267ab7201042a94
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;
184 BOOL FunctionUnknown = FALSE;
186 BOOL GotProductCode = FALSE;
187 LPCSTR PackageName = NULL;
188 LPGUID ProductCode = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
189 LPSTR Properties = HeapAlloc(GetProcessHeap(), 0, 1);
191 DWORD RepairMode = 0;
193 DWORD AdvertiseMode = 0;
194 LPSTR Transforms = HeapAlloc(GetProcessHeap(), 0, 1);
195 LANGID Language = 0;
197 DWORD LogMode = 0;
198 LPSTR LogFileName = NULL;
199 DWORD LogAttributes = 0;
201 LPSTR PatchFileName = NULL;
202 INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
204 INSTALLUILEVEL InstallUILevel = 0, retInstallUILevel;
206 LPSTR DllName = NULL;
208 Properties[0] = 0;
209 Transforms[0] = 0;
211 for(i = 1; i < argc; i++)
213 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
215 if (!strcasecmp(argv[i], "/regserver"))
217 FunctionRegServer = TRUE;
219 else if (!strcasecmp(argv[i], "/unregserver") || !strcasecmp(argv[i], "/unregister"))
221 FunctionUnregServer = TRUE;
223 else if(!strncasecmp(argv[i], "/i", 2))
225 char *argvi = argv[i];
226 FunctionInstall = TRUE;
227 if(strlen(argvi) > 2)
228 argvi += 2;
229 else {
230 i++;
231 if(i >= argc)
232 ShowUsage(1);
233 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
234 argvi = argv[i];
236 GotProductCode = GetProductCode(argvi, &PackageName, &ProductCode);
238 else if(!strcasecmp(argv[i], "/a"))
240 FunctionInstall = TRUE;
241 FunctionInstallAdmin = TRUE;
242 InstallType = INSTALLTYPE_NETWORK_IMAGE;
243 i++;
244 if(i >= argc)
245 ShowUsage(1);
246 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
247 PackageName = argv[i];
248 StringListAppend(&Properties, ActionAdmin);
250 else if(!strncasecmp(argv[i], "/f", 2))
252 int j;
253 int len = strlen(argv[i]);
254 FunctionRepair = TRUE;
255 for(j = 2; j < len; j++)
257 switch(argv[i][j])
259 case 'P':
260 case 'p':
261 RepairMode |= REINSTALLMODE_FILEMISSING;
262 break;
263 case 'O':
264 case 'o':
265 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
266 break;
267 case 'E':
268 case 'e':
269 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
270 break;
271 case 'D':
272 case 'd':
273 RepairMode |= REINSTALLMODE_FILEEXACT;
274 break;
275 case 'C':
276 case 'c':
277 RepairMode |= REINSTALLMODE_FILEVERIFY;
278 break;
279 case 'A':
280 case 'a':
281 RepairMode |= REINSTALLMODE_FILEREPLACE;
282 break;
283 case 'U':
284 case 'u':
285 RepairMode |= REINSTALLMODE_USERDATA;
286 break;
287 case 'M':
288 case 'm':
289 RepairMode |= REINSTALLMODE_MACHINEDATA;
290 break;
291 case 'S':
292 case 's':
293 RepairMode |= REINSTALLMODE_SHORTCUT;
294 break;
295 case 'V':
296 case 'v':
297 RepairMode |= REINSTALLMODE_PACKAGE;
298 break;
299 default:
300 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argv[i][j]);
301 break;
304 if(len == 2)
306 RepairMode = REINSTALLMODE_FILEMISSING |
307 REINSTALLMODE_FILEEQUALVERSION |
308 REINSTALLMODE_FILEVERIFY |
309 REINSTALLMODE_MACHINEDATA |
310 REINSTALLMODE_SHORTCUT;
312 i++;
313 if(i >= argc)
314 ShowUsage(1);
315 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
316 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
318 else if(!strcasecmp(argv[i], "/x"))
320 FunctionInstall = TRUE;
321 i++;
322 if(i >= argc)
323 ShowUsage(1);
324 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
325 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
326 StringListAppend(&Properties, RemoveAll);
328 else if(!strncasecmp(argv[i], "/j", 2))
330 int j;
331 int len = strlen(argv[i]);
332 FunctionAdvertise = TRUE;
333 for(j = 2; j < len; j++)
335 switch(argv[i][j])
337 case 'U':
338 case 'u':
339 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
340 break;
341 case 'M':
342 case 'm':
343 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
344 break;
345 default:
346 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argv[i][j]);
347 break;
350 i++;
351 if(i >= argc)
352 ShowUsage(1);
353 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
354 PackageName = argv[i];
356 else if(!strcasecmp(argv[i], "u"))
358 FunctionAdvertise = TRUE;
359 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
360 i++;
361 if(i >= argc)
362 ShowUsage(1);
363 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
364 PackageName = argv[i];
366 else if(!strcasecmp(argv[i], "m"))
368 FunctionAdvertise = TRUE;
369 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
370 i++;
371 if(i >= argc)
372 ShowUsage(1);
373 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
374 PackageName = argv[i];
376 else if(!strcasecmp(argv[i], "/t"))
378 i++;
379 if(i >= argc)
380 ShowUsage(1);
381 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
382 StringListAppend(&Transforms, argv[i]);
383 StringListAppend(&Transforms, ";");
385 else if(!strncasecmp(argv[i], "TRANSFORMS=", 11))
387 StringListAppend(&Transforms, argv[i]+11);
388 StringListAppend(&Transforms, ";");
390 else if(!strcasecmp(argv[i], "/g"))
392 i++;
393 if(i >= argc)
394 ShowUsage(1);
395 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
396 Language = strtol(argv[i], NULL, 0);
398 else if(!strncasecmp(argv[i], "/l", 2))
400 int j;
401 int len = strlen(argv[i]);
402 for(j = 2; j < len; j++)
404 switch(argv[i][j])
406 case 'I':
407 case 'i':
408 LogMode |= INSTALLLOGMODE_INFO;
409 break;
410 case 'W':
411 case 'w':
412 LogMode |= INSTALLLOGMODE_WARNING;
413 break;
414 case 'E':
415 case 'e':
416 LogMode |= INSTALLLOGMODE_ERROR;
417 break;
418 case 'A':
419 case 'a':
420 LogMode |= INSTALLLOGMODE_ACTIONSTART;
421 break;
422 case 'R':
423 case 'r':
424 LogMode |= INSTALLLOGMODE_ACTIONDATA;
425 break;
426 case 'U':
427 case 'u':
428 LogMode |= INSTALLLOGMODE_USER;
429 break;
430 case 'C':
431 case 'c':
432 LogMode |= INSTALLLOGMODE_COMMONDATA;
433 break;
434 case 'M':
435 case 'm':
436 LogMode |= INSTALLLOGMODE_FATALEXIT;
437 break;
438 case 'O':
439 case 'o':
440 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
441 break;
442 case 'P':
443 case 'p':
444 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
445 break;
446 case 'V':
447 case 'v':
448 LogMode |= INSTALLLOGMODE_VERBOSE;
449 break;
450 case '*':
451 LogMode = INSTALLLOGMODE_FATALEXIT |
452 INSTALLLOGMODE_ERROR |
453 INSTALLLOGMODE_WARNING |
454 INSTALLLOGMODE_USER |
455 INSTALLLOGMODE_INFO |
456 INSTALLLOGMODE_RESOLVESOURCE |
457 INSTALLLOGMODE_OUTOFDISKSPACE |
458 INSTALLLOGMODE_ACTIONSTART |
459 INSTALLLOGMODE_ACTIONDATA |
460 INSTALLLOGMODE_COMMONDATA |
461 INSTALLLOGMODE_PROPERTYDUMP |
462 INSTALLLOGMODE_PROGRESS |
463 INSTALLLOGMODE_INITIALIZE |
464 INSTALLLOGMODE_TERMINATE |
465 INSTALLLOGMODE_SHOWDIALOG;
466 break;
467 case '+':
468 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
469 break;
470 case '!':
471 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
472 break;
473 default:
474 break;
477 i++;
478 if(i >= argc)
479 ShowUsage(1);
480 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
481 LogFileName = argv[i];
482 if(MsiEnableLogA(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
484 fprintf(stderr, "Logging in %s (0x%08lx, %lu) failed\n", LogFileName, LogMode, LogAttributes);
485 ExitProcess(1);
488 else if(!strcasecmp(argv[i], "/p"))
490 FunctionPatch = TRUE;
491 i++;
492 if(i >= argc)
493 ShowUsage(1);
494 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
495 PatchFileName = argv[i];
497 else if(!strncasecmp(argv[i], "/q", 2))
499 if(strlen(argv[i]) == 2 || !strcasecmp(argv[i]+2, "n"))
501 InstallUILevel = INSTALLUILEVEL_NONE;
503 else if(!strcasecmp(argv[i]+2, "b"))
505 InstallUILevel = INSTALLUILEVEL_BASIC;
507 else if(!strcasecmp(argv[i]+2, "r"))
509 InstallUILevel = INSTALLUILEVEL_REDUCED;
511 else if(!strcasecmp(argv[i]+2, "f"))
513 InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
515 else if(!strcasecmp(argv[i]+2, "n+"))
517 InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
519 else if(!strcasecmp(argv[i]+2, "b+"))
521 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
523 else if(!strcasecmp(argv[i]+2, "b-"))
525 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
527 else if(!strcasecmp(argv[i]+2, "b+!"))
529 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
530 WINE_FIXME("Unknown modifier: !\n");
532 else
534 fprintf(stderr, "Unknown option \"%s\" for UI level\n", argv[i]+2);
536 retInstallUILevel = MsiSetInternalUI(InstallUILevel, NULL);
537 if(retInstallUILevel == INSTALLUILEVEL_NOCHANGE)
539 fprintf(stderr, "Setting the UI level to 0x%x failed.\n", InstallUILevel);
540 ExitProcess(1);
543 else if(!strcasecmp(argv[i], "/y"))
545 FunctionDllRegisterServer = TRUE;
546 i++;
547 if(i >= argc)
548 ShowUsage(1);
549 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
550 DllName = argv[i];
552 else if(!strcasecmp(argv[i], "/z"))
554 FunctionDllUnregisterServer = TRUE;
555 i++;
556 if(i >= argc)
557 ShowUsage(1);
558 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
559 DllName = argv[i];
561 else if(!strcasecmp(argv[i], "/h") || !strcasecmp(argv[i], "/?"))
563 ShowUsage(0);
565 else if(!strcasecmp(argv[i], "/m"))
567 FunctionUnknown = TRUE;
568 WINE_FIXME("Unknown parameter /m\n");
570 else if(!strcasecmp(argv[i], "/D"))
572 FunctionUnknown = TRUE;
573 WINE_FIXME("Unknown parameter /D\n");
575 else if(strchr(argv[i], '='))
577 StringListAppend(&Properties, argv[i]);
578 StringListAppend(&Properties, " ");
580 else
582 FunctionInstall = TRUE;
583 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
587 StringCompareRemoveLast(Properties, ' ');
588 StringCompareRemoveLast(Transforms, ';');
590 if(FunctionInstallAdmin && FunctionPatch)
591 FunctionInstall = FALSE;
593 if(FunctionInstall)
595 if(GotProductCode)
597 WINE_FIXME("Product code treatment not implemented yet\n");
598 ExitProcess(1);
600 else
602 if(MsiInstallProductA(PackageName, Properties) != ERROR_SUCCESS)
604 fprintf(stderr, "Installation of %s (%s) failed.\n", PackageName, Properties);
605 ExitProcess(1);
609 else if(FunctionRepair)
611 if(GotProductCode)
613 WINE_FIXME("Product code treatment not implemented yet\n");
614 ExitProcess(1);
616 else
618 if(MsiReinstallProductA(PackageName, RepairMode) != ERROR_SUCCESS)
620 fprintf(stderr, "Repair of %s (0x%08lx) failed.\n", PackageName, RepairMode);
621 ExitProcess(1);
625 else if(FunctionAdvertise)
627 if(MsiAdvertiseProductA(PackageName, (LPSTR) AdvertiseMode, Transforms, Language) != ERROR_SUCCESS)
629 fprintf(stderr, "Advertising of %s (%lu, %s, 0x%04x) failed.\n", PackageName, AdvertiseMode, Transforms, Language);
630 ExitProcess(1);
633 else if(FunctionPatch)
635 if(MsiApplyPatchA(PatchFileName, PackageName, InstallType, Properties) != ERROR_SUCCESS)
637 fprintf(stderr, "Patching with %s (%s, %d, %s)\n", PatchFileName, PackageName, InstallType, Properties);
638 ExitProcess(1);
641 else if(FunctionDllRegisterServer)
643 DllRegisterServer(DllName);
645 else if(FunctionDllUnregisterServer)
647 DllUnregisterServer(DllName);
649 else if (FunctionRegServer)
651 WINE_FIXME( "/regserver not implemented yet, ignoring\n" );
653 else if (FunctionUnregServer)
655 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
657 else if (FunctionUnknown)
659 WINE_FIXME( "Unknown function, ignoring\n" );
661 else
662 ShowUsage(1);
664 return 0;