Janitorial: C booleans must not be compared against TRUE.
[wine/multimedia.git] / programs / msiexec / msiexec.c
bloba918468063763e2dc6effa4e9ceae2fd2c60faab
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 INT MSIEXEC_lstrncmpiA(LPCSTR str1, LPCSTR str2, INT size)
115 INT ret;
117 if ((str1 == NULL) && (str2 == NULL)) return 0;
118 if (str1 == NULL) return -1;
119 if (str2 == NULL) return 1;
121 ret = CompareStringA(GetThreadLocale(), NORM_IGNORECASE, str1, size, str2, -1);
122 if (ret) ret -= 2;
124 return ret;
127 static VOID *LoadProc(LPCSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
129 VOID* (*proc)(void);
131 *DllHandle = LoadLibraryExA(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
132 if(!*DllHandle)
134 fprintf(stderr, "Unable to load dll %s\n", DllName);
135 ExitProcess(1);
137 proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
138 if(!proc)
140 fprintf(stderr, "Dll %s does not implement function %s\n", DllName, ProcName);
141 FreeLibrary(*DllHandle);
142 ExitProcess(1);
145 return proc;
148 static void DllRegisterServer(LPCSTR DllName)
150 HRESULT hr;
151 DLLREGISTERSERVER pfDllRegisterServer = NULL;
152 HMODULE DllHandle = NULL;
154 pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);
156 hr = pfDllRegisterServer();
157 if(FAILED(hr))
159 fprintf(stderr, "Failed to register dll %s\n", DllName);
160 ExitProcess(1);
162 printf("Successfully registered dll %s\n", DllName);
163 if(DllHandle)
164 FreeLibrary(DllHandle);
167 static void DllUnregisterServer(LPCSTR DllName)
169 HRESULT hr;
170 DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
171 HMODULE DllHandle = NULL;
173 pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);
175 hr = pfDllUnregisterServer();
176 if(FAILED(hr))
178 fprintf(stderr, "Failed to unregister dll %s\n", DllName);
179 ExitProcess(1);
181 printf("Successfully unregistered dll %s\n", DllName);
182 if(DllHandle)
183 FreeLibrary(DllHandle);
186 int main(int argc, char *argv[])
188 int i;
189 BOOL FunctionInstall = FALSE;
190 BOOL FunctionInstallAdmin = FALSE;
191 BOOL FunctionRepair = FALSE;
192 BOOL FunctionAdvertise = FALSE;
193 BOOL FunctionPatch = FALSE;
194 BOOL FunctionDllRegisterServer = FALSE;
195 BOOL FunctionDllUnregisterServer = FALSE;
196 BOOL FunctionRegServer = FALSE;
197 BOOL FunctionUnregServer = FALSE;
198 BOOL FunctionUnknown = FALSE;
200 BOOL GotProductCode = FALSE;
201 LPCSTR PackageName = NULL;
202 LPGUID ProductCode = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
203 LPSTR Properties = HeapAlloc(GetProcessHeap(), 0, 1);
205 DWORD RepairMode = 0;
207 DWORD AdvertiseMode = 0;
208 LPSTR Transforms = HeapAlloc(GetProcessHeap(), 0, 1);
209 LANGID Language = 0;
211 DWORD LogMode = 0;
212 LPSTR LogFileName = NULL;
213 DWORD LogAttributes = 0;
215 LPSTR PatchFileName = NULL;
216 INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
218 INSTALLUILEVEL InstallUILevel = 0, retInstallUILevel;
220 LPSTR DllName = NULL;
222 Properties[0] = 0;
223 Transforms[0] = 0;
225 for(i = 1; i < argc; i++)
227 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
229 if (!lstrcmpiA(argv[i], "/regserver"))
231 FunctionRegServer = TRUE;
233 else if (!lstrcmpiA(argv[i], "/unregserver") || !lstrcmpiA(argv[i], "/unregister"))
235 FunctionUnregServer = TRUE;
237 else if(!MSIEXEC_lstrncmpiA(argv[i], "/i", 2))
239 char *argvi = argv[i];
240 FunctionInstall = TRUE;
241 if(strlen(argvi) > 2)
242 argvi += 2;
243 else {
244 i++;
245 if(i >= argc)
246 ShowUsage(1);
247 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
248 argvi = argv[i];
250 GotProductCode = GetProductCode(argvi, &PackageName, &ProductCode);
252 else if(!lstrcmpiA(argv[i], "/a"))
254 FunctionInstall = TRUE;
255 FunctionInstallAdmin = TRUE;
256 InstallType = INSTALLTYPE_NETWORK_IMAGE;
257 i++;
258 if(i >= argc)
259 ShowUsage(1);
260 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
261 PackageName = argv[i];
262 StringListAppend(&Properties, ActionAdmin);
264 else if(!MSIEXEC_lstrncmpiA(argv[i], "/f", 2))
266 int j;
267 int len = strlen(argv[i]);
268 FunctionRepair = TRUE;
269 for(j = 2; j < len; j++)
271 switch(argv[i][j])
273 case 'P':
274 case 'p':
275 RepairMode |= REINSTALLMODE_FILEMISSING;
276 break;
277 case 'O':
278 case 'o':
279 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
280 break;
281 case 'E':
282 case 'e':
283 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
284 break;
285 case 'D':
286 case 'd':
287 RepairMode |= REINSTALLMODE_FILEEXACT;
288 break;
289 case 'C':
290 case 'c':
291 RepairMode |= REINSTALLMODE_FILEVERIFY;
292 break;
293 case 'A':
294 case 'a':
295 RepairMode |= REINSTALLMODE_FILEREPLACE;
296 break;
297 case 'U':
298 case 'u':
299 RepairMode |= REINSTALLMODE_USERDATA;
300 break;
301 case 'M':
302 case 'm':
303 RepairMode |= REINSTALLMODE_MACHINEDATA;
304 break;
305 case 'S':
306 case 's':
307 RepairMode |= REINSTALLMODE_SHORTCUT;
308 break;
309 case 'V':
310 case 'v':
311 RepairMode |= REINSTALLMODE_PACKAGE;
312 break;
313 default:
314 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argv[i][j]);
315 break;
318 if(len == 2)
320 RepairMode = REINSTALLMODE_FILEMISSING |
321 REINSTALLMODE_FILEEQUALVERSION |
322 REINSTALLMODE_FILEVERIFY |
323 REINSTALLMODE_MACHINEDATA |
324 REINSTALLMODE_SHORTCUT;
326 i++;
327 if(i >= argc)
328 ShowUsage(1);
329 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
330 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
332 else if(!lstrcmpiA(argv[i], "/x"))
334 FunctionInstall = TRUE;
335 i++;
336 if(i >= argc)
337 ShowUsage(1);
338 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
339 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
340 StringListAppend(&Properties, RemoveAll);
342 else if(!MSIEXEC_lstrncmpiA(argv[i], "/j", 2))
344 int j;
345 int len = strlen(argv[i]);
346 FunctionAdvertise = TRUE;
347 for(j = 2; j < len; j++)
349 switch(argv[i][j])
351 case 'U':
352 case 'u':
353 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
354 break;
355 case 'M':
356 case 'm':
357 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
358 break;
359 default:
360 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argv[i][j]);
361 break;
364 i++;
365 if(i >= argc)
366 ShowUsage(1);
367 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
368 PackageName = argv[i];
370 else if(!lstrcmpiA(argv[i], "u"))
372 FunctionAdvertise = TRUE;
373 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
374 i++;
375 if(i >= argc)
376 ShowUsage(1);
377 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
378 PackageName = argv[i];
380 else if(!lstrcmpiA(argv[i], "m"))
382 FunctionAdvertise = TRUE;
383 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
384 i++;
385 if(i >= argc)
386 ShowUsage(1);
387 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
388 PackageName = argv[i];
390 else if(!lstrcmpiA(argv[i], "/t"))
392 i++;
393 if(i >= argc)
394 ShowUsage(1);
395 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
396 StringListAppend(&Transforms, argv[i]);
397 StringListAppend(&Transforms, ";");
399 else if(!MSIEXEC_lstrncmpiA(argv[i], "TRANSFORMS=", 11))
401 StringListAppend(&Transforms, argv[i]+11);
402 StringListAppend(&Transforms, ";");
404 else if(!lstrcmpiA(argv[i], "/g"))
406 i++;
407 if(i >= argc)
408 ShowUsage(1);
409 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
410 Language = strtol(argv[i], NULL, 0);
412 else if(!MSIEXEC_lstrncmpiA(argv[i], "/l", 2))
414 int j;
415 int len = strlen(argv[i]);
416 for(j = 2; j < len; j++)
418 switch(argv[i][j])
420 case 'I':
421 case 'i':
422 LogMode |= INSTALLLOGMODE_INFO;
423 break;
424 case 'W':
425 case 'w':
426 LogMode |= INSTALLLOGMODE_WARNING;
427 break;
428 case 'E':
429 case 'e':
430 LogMode |= INSTALLLOGMODE_ERROR;
431 break;
432 case 'A':
433 case 'a':
434 LogMode |= INSTALLLOGMODE_ACTIONSTART;
435 break;
436 case 'R':
437 case 'r':
438 LogMode |= INSTALLLOGMODE_ACTIONDATA;
439 break;
440 case 'U':
441 case 'u':
442 LogMode |= INSTALLLOGMODE_USER;
443 break;
444 case 'C':
445 case 'c':
446 LogMode |= INSTALLLOGMODE_COMMONDATA;
447 break;
448 case 'M':
449 case 'm':
450 LogMode |= INSTALLLOGMODE_FATALEXIT;
451 break;
452 case 'O':
453 case 'o':
454 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
455 break;
456 case 'P':
457 case 'p':
458 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
459 break;
460 case 'V':
461 case 'v':
462 LogMode |= INSTALLLOGMODE_VERBOSE;
463 break;
464 case '*':
465 LogMode = INSTALLLOGMODE_FATALEXIT |
466 INSTALLLOGMODE_ERROR |
467 INSTALLLOGMODE_WARNING |
468 INSTALLLOGMODE_USER |
469 INSTALLLOGMODE_INFO |
470 INSTALLLOGMODE_RESOLVESOURCE |
471 INSTALLLOGMODE_OUTOFDISKSPACE |
472 INSTALLLOGMODE_ACTIONSTART |
473 INSTALLLOGMODE_ACTIONDATA |
474 INSTALLLOGMODE_COMMONDATA |
475 INSTALLLOGMODE_PROPERTYDUMP |
476 INSTALLLOGMODE_PROGRESS |
477 INSTALLLOGMODE_INITIALIZE |
478 INSTALLLOGMODE_TERMINATE |
479 INSTALLLOGMODE_SHOWDIALOG;
480 break;
481 case '+':
482 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
483 break;
484 case '!':
485 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
486 break;
487 default:
488 break;
491 i++;
492 if(i >= argc)
493 ShowUsage(1);
494 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
495 LogFileName = argv[i];
496 if(MsiEnableLogA(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
498 fprintf(stderr, "Logging in %s (0x%08lx, %lu) failed\n", LogFileName, LogMode, LogAttributes);
499 ExitProcess(1);
502 else if(!lstrcmpiA(argv[i], "/p"))
504 FunctionPatch = TRUE;
505 i++;
506 if(i >= argc)
507 ShowUsage(1);
508 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
509 PatchFileName = argv[i];
511 else if(!MSIEXEC_lstrncmpiA(argv[i], "/q", 2))
513 if(strlen(argv[i]) == 2 || !lstrcmpiA(argv[i]+2, "n"))
515 InstallUILevel = INSTALLUILEVEL_NONE;
517 else if(!lstrcmpiA(argv[i]+2, "b"))
519 InstallUILevel = INSTALLUILEVEL_BASIC;
521 else if(!lstrcmpiA(argv[i]+2, "r"))
523 InstallUILevel = INSTALLUILEVEL_REDUCED;
525 else if(!lstrcmpiA(argv[i]+2, "f"))
527 InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
529 else if(!lstrcmpiA(argv[i]+2, "n+"))
531 InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
533 else if(!lstrcmpiA(argv[i]+2, "b+"))
535 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
537 else if(!lstrcmpiA(argv[i]+2, "b-"))
539 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
541 else if(!lstrcmpiA(argv[i]+2, "b+!"))
543 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
544 WINE_FIXME("Unknown modifier: !\n");
546 else
548 fprintf(stderr, "Unknown option \"%s\" for UI level\n", argv[i]+2);
550 retInstallUILevel = MsiSetInternalUI(InstallUILevel, NULL);
551 if(retInstallUILevel == INSTALLUILEVEL_NOCHANGE)
553 fprintf(stderr, "Setting the UI level to 0x%x failed.\n", InstallUILevel);
554 ExitProcess(1);
557 else if(!lstrcmpiA(argv[i], "/y"))
559 FunctionDllRegisterServer = TRUE;
560 i++;
561 if(i >= argc)
562 ShowUsage(1);
563 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
564 DllName = argv[i];
566 else if(!lstrcmpiA(argv[i], "/z"))
568 FunctionDllUnregisterServer = TRUE;
569 i++;
570 if(i >= argc)
571 ShowUsage(1);
572 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
573 DllName = argv[i];
575 else if(!lstrcmpiA(argv[i], "/h") || !lstrcmpiA(argv[i], "/?"))
577 ShowUsage(0);
579 else if(!lstrcmpiA(argv[i], "/m"))
581 FunctionUnknown = TRUE;
582 WINE_FIXME("Unknown parameter /m\n");
584 else if(!lstrcmpiA(argv[i], "/D"))
586 FunctionUnknown = TRUE;
587 WINE_FIXME("Unknown parameter /D\n");
589 else if(strchr(argv[i], '='))
591 StringListAppend(&Properties, argv[i]);
592 StringListAppend(&Properties, " ");
594 else
596 FunctionInstall = TRUE;
597 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
601 StringCompareRemoveLast(Properties, ' ');
602 StringCompareRemoveLast(Transforms, ';');
604 if(FunctionInstallAdmin && FunctionPatch)
605 FunctionInstall = FALSE;
607 if(FunctionInstall)
609 if(GotProductCode)
611 WINE_FIXME("Product code treatment not implemented yet\n");
612 ExitProcess(1);
614 else
616 if(MsiInstallProductA(PackageName, Properties) != ERROR_SUCCESS)
618 fprintf(stderr, "Installation of %s (%s) failed.\n", PackageName, Properties);
619 ExitProcess(1);
623 else if(FunctionRepair)
625 if(GotProductCode)
627 WINE_FIXME("Product code treatment not implemented yet\n");
628 ExitProcess(1);
630 else
632 if(MsiReinstallProductA(PackageName, RepairMode) != ERROR_SUCCESS)
634 fprintf(stderr, "Repair of %s (0x%08lx) failed.\n", PackageName, RepairMode);
635 ExitProcess(1);
639 else if(FunctionAdvertise)
641 if(MsiAdvertiseProductA(PackageName, (LPSTR) AdvertiseMode, Transforms, Language) != ERROR_SUCCESS)
643 fprintf(stderr, "Advertising of %s (%lu, %s, 0x%04x) failed.\n", PackageName, AdvertiseMode, Transforms, Language);
644 ExitProcess(1);
647 else if(FunctionPatch)
649 if(MsiApplyPatchA(PatchFileName, PackageName, InstallType, Properties) != ERROR_SUCCESS)
651 fprintf(stderr, "Patching with %s (%s, %d, %s)\n", PatchFileName, PackageName, InstallType, Properties);
652 ExitProcess(1);
655 else if(FunctionDllRegisterServer)
657 DllRegisterServer(DllName);
659 else if(FunctionDllUnregisterServer)
661 DllUnregisterServer(DllName);
663 else if (FunctionRegServer)
665 WINE_FIXME( "/regserver not implemented yet, ignoring\n" );
667 else if (FunctionUnregServer)
669 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
671 else if (FunctionUnknown)
673 WINE_FIXME( "Unknown function, ignoring\n" );
675 else
676 ShowUsage(1);
678 return 0;