Don't hardcode c:\windows paths in msi.dll.
[wine/multimedia.git] / programs / msiexec / msiexec.c
blob24377bc97b400614194e7689c920785c583364f8
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;
183 BOOL GotProductCode = FALSE;
184 LPCSTR PackageName = NULL;
185 LPGUID ProductCode = HeapAlloc(GetProcessHeap(), 0, sizeof(GUID));
186 LPSTR Properties = HeapAlloc(GetProcessHeap(), 0, 1);
188 DWORD RepairMode = 0;
190 DWORD AdvertiseMode = 0;
191 LPSTR Transforms = HeapAlloc(GetProcessHeap(), 0, 1);
192 LANGID Language = 0;
194 DWORD LogMode = 0;
195 LPSTR LogFileName = NULL;
196 DWORD LogAttributes = 0;
198 LPSTR PatchFileName = NULL;
199 INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
201 INSTALLUILEVEL InstallUILevel = 0, retInstallUILevel;
203 LPSTR DllName = NULL;
205 Properties[0] = 0;
206 Transforms[0] = 0;
208 for(i = 1; i < argc; i++)
210 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
212 if(!strcasecmp(argv[i], "/i"))
214 FunctionInstall = TRUE;
215 i++;
216 if(i >= argc)
217 ShowUsage(1);
218 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
219 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
221 else if(!strcasecmp(argv[i], "/a"))
223 FunctionInstall = TRUE;
224 FunctionInstallAdmin = TRUE;
225 InstallType = INSTALLTYPE_NETWORK_IMAGE;
226 i++;
227 if(i >= argc)
228 ShowUsage(1);
229 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
230 PackageName = argv[i];
231 StringListAppend(&Properties, ActionAdmin);
233 else if(!strncasecmp(argv[i], "/f", 2))
235 int j;
236 int len = strlen(argv[i]);
237 FunctionRepair = TRUE;
238 for(j = 2; j < len; j++)
240 switch(argv[i][j])
242 case 'P':
243 case 'p':
244 RepairMode |= REINSTALLMODE_FILEMISSING;
245 break;
246 case 'O':
247 case 'o':
248 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
249 break;
250 case 'E':
251 case 'e':
252 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
253 break;
254 case 'D':
255 case 'd':
256 RepairMode |= REINSTALLMODE_FILEEXACT;
257 break;
258 case 'C':
259 case 'c':
260 RepairMode |= REINSTALLMODE_FILEVERIFY;
261 break;
262 case 'A':
263 case 'a':
264 RepairMode |= REINSTALLMODE_FILEREPLACE;
265 break;
266 case 'U':
267 case 'u':
268 RepairMode |= REINSTALLMODE_USERDATA;
269 break;
270 case 'M':
271 case 'm':
272 RepairMode |= REINSTALLMODE_MACHINEDATA;
273 break;
274 case 'S':
275 case 's':
276 RepairMode |= REINSTALLMODE_SHORTCUT;
277 break;
278 case 'V':
279 case 'v':
280 RepairMode |= REINSTALLMODE_PACKAGE;
281 break;
282 default:
283 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argv[i][j]);
284 break;
287 if(len == 2)
289 RepairMode = REINSTALLMODE_FILEMISSING |
290 REINSTALLMODE_FILEEQUALVERSION |
291 REINSTALLMODE_FILEVERIFY |
292 REINSTALLMODE_MACHINEDATA |
293 REINSTALLMODE_SHORTCUT;
295 i++;
296 if(i >= argc)
297 ShowUsage(1);
298 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
299 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
301 else if(!strcasecmp(argv[i], "/x"))
303 FunctionInstall = TRUE;
304 i++;
305 if(i >= argc)
306 ShowUsage(1);
307 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
308 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
309 StringListAppend(&Properties, RemoveAll);
311 else if(!strncasecmp(argv[i], "/j", 2))
313 int j;
314 int len = strlen(argv[i]);
315 FunctionAdvertise = TRUE;
316 for(j = 2; j < len; j++)
318 switch(argv[i][j])
320 case 'U':
321 case 'u':
322 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
323 break;
324 case 'M':
325 case 'm':
326 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
327 break;
328 default:
329 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argv[i][j]);
330 break;
333 i++;
334 if(i >= argc)
335 ShowUsage(1);
336 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
337 PackageName = argv[i];
339 else if(!strcasecmp(argv[i], "u"))
341 FunctionAdvertise = TRUE;
342 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
343 i++;
344 if(i >= argc)
345 ShowUsage(1);
346 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
347 PackageName = argv[i];
349 else if(!strcasecmp(argv[i], "m"))
351 FunctionAdvertise = TRUE;
352 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
353 i++;
354 if(i >= argc)
355 ShowUsage(1);
356 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
357 PackageName = argv[i];
359 else if(!strcasecmp(argv[i], "/t"))
361 i++;
362 if(i >= argc)
363 ShowUsage(1);
364 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
365 StringListAppend(&Transforms, argv[i]);
366 StringListAppend(&Transforms, ";");
368 else if(!strncasecmp(argv[i], "TRANSFORMS=", 11))
370 StringListAppend(&Transforms, argv[i]+11);
371 StringListAppend(&Transforms, ";");
373 else if(!strcasecmp(argv[i], "/g"))
375 i++;
376 if(i >= argc)
377 ShowUsage(1);
378 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
379 Language = strtol(argv[i], NULL, 0);
381 else if(!strncasecmp(argv[i], "/l", 2))
383 int j;
384 int len = strlen(argv[i]);
385 for(j = 2; j < len; j++)
387 switch(argv[i][j])
389 case 'I':
390 case 'i':
391 LogMode |= INSTALLLOGMODE_INFO;
392 break;
393 case 'W':
394 case 'w':
395 LogMode |= INSTALLLOGMODE_WARNING;
396 break;
397 case 'E':
398 case 'e':
399 LogMode |= INSTALLLOGMODE_ERROR;
400 break;
401 case 'A':
402 case 'a':
403 LogMode |= INSTALLLOGMODE_ACTIONSTART;
404 break;
405 case 'R':
406 case 'r':
407 LogMode |= INSTALLLOGMODE_ACTIONDATA;
408 break;
409 case 'U':
410 case 'u':
411 LogMode |= INSTALLLOGMODE_USER;
412 break;
413 case 'C':
414 case 'c':
415 LogMode |= INSTALLLOGMODE_COMMONDATA;
416 break;
417 case 'M':
418 case 'm':
419 LogMode |= INSTALLLOGMODE_FATALEXIT;
420 break;
421 case 'O':
422 case 'o':
423 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
424 break;
425 case 'P':
426 case 'p':
427 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
428 break;
429 case 'V':
430 case 'v':
431 LogMode |= INSTALLLOGMODE_VERBOSE;
432 break;
433 case '*':
434 LogMode = INSTALLLOGMODE_FATALEXIT |
435 INSTALLLOGMODE_ERROR |
436 INSTALLLOGMODE_WARNING |
437 INSTALLLOGMODE_USER |
438 INSTALLLOGMODE_INFO |
439 INSTALLLOGMODE_RESOLVESOURCE |
440 INSTALLLOGMODE_OUTOFDISKSPACE |
441 INSTALLLOGMODE_ACTIONSTART |
442 INSTALLLOGMODE_ACTIONDATA |
443 INSTALLLOGMODE_COMMONDATA |
444 INSTALLLOGMODE_PROPERTYDUMP |
445 INSTALLLOGMODE_PROGRESS |
446 INSTALLLOGMODE_INITIALIZE |
447 INSTALLLOGMODE_TERMINATE |
448 INSTALLLOGMODE_SHOWDIALOG;
449 break;
450 case '+':
451 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
452 break;
453 case '!':
454 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
455 break;
456 default:
457 break;
460 i++;
461 if(i >= argc)
462 ShowUsage(1);
463 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
464 LogFileName = argv[i];
465 if(MsiEnableLogA(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
467 fprintf(stderr, "Logging in %s (0x%08lx, %lu) failed\n", LogFileName, LogMode, LogAttributes);
468 ExitProcess(1);
471 else if(!strcasecmp(argv[i], "/p"))
473 FunctionPatch = TRUE;
474 i++;
475 if(i >= argc)
476 ShowUsage(1);
477 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
478 PatchFileName = argv[i];
480 else if(!strncasecmp(argv[i], "/q", 2))
482 if(strlen(argv[i]) == 2 || !strcasecmp(argv[i]+2, "n"))
484 InstallUILevel = INSTALLUILEVEL_NONE;
486 else if(!strcasecmp(argv[i]+2, "b"))
488 InstallUILevel = INSTALLUILEVEL_BASIC;
490 else if(!strcasecmp(argv[i]+2, "r"))
492 InstallUILevel = INSTALLUILEVEL_REDUCED;
494 else if(!strcasecmp(argv[i]+2, "f"))
496 InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
498 else if(!strcasecmp(argv[i]+2, "n+"))
500 InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
502 else if(!strcasecmp(argv[i]+2, "b+"))
504 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
506 else if(!strcasecmp(argv[i]+2, "b-"))
508 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
510 else
512 fprintf(stderr, "Unknown option \"%s\" for UI level\n", argv[i]+2);
514 retInstallUILevel = MsiSetInternalUI(InstallUILevel, NULL);
515 if(retInstallUILevel == INSTALLUILEVEL_NOCHANGE)
517 fprintf(stderr, "Setting the UI level to 0x%x failed.\n", InstallUILevel);
518 ExitProcess(1);
521 else if(!strcasecmp(argv[i], "/y"))
523 FunctionDllRegisterServer = TRUE;
524 i++;
525 if(i >= argc)
526 ShowUsage(1);
527 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
528 DllName = argv[i];
530 else if(!strcasecmp(argv[i], "/z"))
532 FunctionDllUnregisterServer = TRUE;
533 i++;
534 if(i >= argc)
535 ShowUsage(1);
536 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
537 DllName = argv[i];
539 else if(!strcasecmp(argv[i], "/h") || !strcasecmp(argv[i], "/?"))
541 ShowUsage(0);
543 else if(strchr(argv[i], '='))
545 StringListAppend(&Properties, argv[i]);
546 StringListAppend(&Properties, " ");
548 else
550 FunctionInstall = TRUE;
551 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
555 StringCompareRemoveLast(Properties, ' ');
556 StringCompareRemoveLast(Transforms, ';');
558 if(FunctionInstallAdmin && FunctionPatch)
559 FunctionInstall = FALSE;
561 if(FunctionInstall)
563 if(GotProductCode)
565 WINE_FIXME("Product code treatment not implemented yet\n");
566 ExitProcess(1);
568 else
570 if(MsiInstallProductA(PackageName, Properties) != ERROR_SUCCESS)
572 fprintf(stderr, "Installation of %s (%s) failed.\n", PackageName, Properties);
573 ExitProcess(1);
577 else if(FunctionRepair)
579 if(GotProductCode)
581 WINE_FIXME("Product code treatment not implemented yet\n");
582 ExitProcess(1);
584 else
586 if(MsiReinstallProductA(PackageName, RepairMode) != ERROR_SUCCESS)
588 fprintf(stderr, "Repair of %s (0x%08lx) failed.\n", PackageName, RepairMode);
589 ExitProcess(1);
593 else if(FunctionAdvertise)
595 if(MsiAdvertiseProductA(PackageName, (LPSTR) AdvertiseMode, Transforms, Language) != ERROR_SUCCESS)
597 fprintf(stderr, "Advertising of %s (%lu, %s, 0x%04x) failed.\n", PackageName, AdvertiseMode, Transforms, Language);
598 ExitProcess(1);
601 else if(FunctionPatch)
603 if(MsiApplyPatchA(PatchFileName, PackageName, InstallType, Properties) != ERROR_SUCCESS)
605 fprintf(stderr, "Patching with %s (%s, %d, %s)\n", PatchFileName, PackageName, InstallType, Properties);
606 ExitProcess(1);
609 else if(FunctionDllRegisterServer)
611 DllRegisterServer(DllName);
613 else if(FunctionDllUnregisterServer)
615 DllUnregisterServer(DllName);
617 else
618 ShowUsage(1);
620 return 0;