Implement BeginUpdateResource and UpdateResource.
[wine.git] / programs / msiexec / msiexec.c
blob5516eccd6c8de7545adf66e6326e794fb91abb8a
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(!strncasecmp(argv[i], "/i", 2))
214 char *argvi = argv[i];
215 FunctionInstall = TRUE;
216 if(strlen(argvi) > 2)
217 argvi += 2;
218 else {
219 i++;
220 if(i >= argc)
221 ShowUsage(1);
222 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
224 GotProductCode = GetProductCode(argvi, &PackageName, &ProductCode);
226 else if(!strcasecmp(argv[i], "/a"))
228 FunctionInstall = TRUE;
229 FunctionInstallAdmin = TRUE;
230 InstallType = INSTALLTYPE_NETWORK_IMAGE;
231 i++;
232 if(i >= argc)
233 ShowUsage(1);
234 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
235 PackageName = argv[i];
236 StringListAppend(&Properties, ActionAdmin);
238 else if(!strncasecmp(argv[i], "/f", 2))
240 int j;
241 int len = strlen(argv[i]);
242 FunctionRepair = TRUE;
243 for(j = 2; j < len; j++)
245 switch(argv[i][j])
247 case 'P':
248 case 'p':
249 RepairMode |= REINSTALLMODE_FILEMISSING;
250 break;
251 case 'O':
252 case 'o':
253 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
254 break;
255 case 'E':
256 case 'e':
257 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
258 break;
259 case 'D':
260 case 'd':
261 RepairMode |= REINSTALLMODE_FILEEXACT;
262 break;
263 case 'C':
264 case 'c':
265 RepairMode |= REINSTALLMODE_FILEVERIFY;
266 break;
267 case 'A':
268 case 'a':
269 RepairMode |= REINSTALLMODE_FILEREPLACE;
270 break;
271 case 'U':
272 case 'u':
273 RepairMode |= REINSTALLMODE_USERDATA;
274 break;
275 case 'M':
276 case 'm':
277 RepairMode |= REINSTALLMODE_MACHINEDATA;
278 break;
279 case 'S':
280 case 's':
281 RepairMode |= REINSTALLMODE_SHORTCUT;
282 break;
283 case 'V':
284 case 'v':
285 RepairMode |= REINSTALLMODE_PACKAGE;
286 break;
287 default:
288 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argv[i][j]);
289 break;
292 if(len == 2)
294 RepairMode = REINSTALLMODE_FILEMISSING |
295 REINSTALLMODE_FILEEQUALVERSION |
296 REINSTALLMODE_FILEVERIFY |
297 REINSTALLMODE_MACHINEDATA |
298 REINSTALLMODE_SHORTCUT;
300 i++;
301 if(i >= argc)
302 ShowUsage(1);
303 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
304 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
306 else if(!strcasecmp(argv[i], "/x"))
308 FunctionInstall = TRUE;
309 i++;
310 if(i >= argc)
311 ShowUsage(1);
312 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
313 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
314 StringListAppend(&Properties, RemoveAll);
316 else if(!strncasecmp(argv[i], "/j", 2))
318 int j;
319 int len = strlen(argv[i]);
320 FunctionAdvertise = TRUE;
321 for(j = 2; j < len; j++)
323 switch(argv[i][j])
325 case 'U':
326 case 'u':
327 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
328 break;
329 case 'M':
330 case 'm':
331 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
332 break;
333 default:
334 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argv[i][j]);
335 break;
338 i++;
339 if(i >= argc)
340 ShowUsage(1);
341 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
342 PackageName = argv[i];
344 else if(!strcasecmp(argv[i], "u"))
346 FunctionAdvertise = TRUE;
347 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
348 i++;
349 if(i >= argc)
350 ShowUsage(1);
351 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
352 PackageName = argv[i];
354 else if(!strcasecmp(argv[i], "m"))
356 FunctionAdvertise = TRUE;
357 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
358 i++;
359 if(i >= argc)
360 ShowUsage(1);
361 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
362 PackageName = argv[i];
364 else if(!strcasecmp(argv[i], "/t"))
366 i++;
367 if(i >= argc)
368 ShowUsage(1);
369 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
370 StringListAppend(&Transforms, argv[i]);
371 StringListAppend(&Transforms, ";");
373 else if(!strncasecmp(argv[i], "TRANSFORMS=", 11))
375 StringListAppend(&Transforms, argv[i]+11);
376 StringListAppend(&Transforms, ";");
378 else if(!strcasecmp(argv[i], "/g"))
380 i++;
381 if(i >= argc)
382 ShowUsage(1);
383 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
384 Language = strtol(argv[i], NULL, 0);
386 else if(!strncasecmp(argv[i], "/l", 2))
388 int j;
389 int len = strlen(argv[i]);
390 for(j = 2; j < len; j++)
392 switch(argv[i][j])
394 case 'I':
395 case 'i':
396 LogMode |= INSTALLLOGMODE_INFO;
397 break;
398 case 'W':
399 case 'w':
400 LogMode |= INSTALLLOGMODE_WARNING;
401 break;
402 case 'E':
403 case 'e':
404 LogMode |= INSTALLLOGMODE_ERROR;
405 break;
406 case 'A':
407 case 'a':
408 LogMode |= INSTALLLOGMODE_ACTIONSTART;
409 break;
410 case 'R':
411 case 'r':
412 LogMode |= INSTALLLOGMODE_ACTIONDATA;
413 break;
414 case 'U':
415 case 'u':
416 LogMode |= INSTALLLOGMODE_USER;
417 break;
418 case 'C':
419 case 'c':
420 LogMode |= INSTALLLOGMODE_COMMONDATA;
421 break;
422 case 'M':
423 case 'm':
424 LogMode |= INSTALLLOGMODE_FATALEXIT;
425 break;
426 case 'O':
427 case 'o':
428 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
429 break;
430 case 'P':
431 case 'p':
432 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
433 break;
434 case 'V':
435 case 'v':
436 LogMode |= INSTALLLOGMODE_VERBOSE;
437 break;
438 case '*':
439 LogMode = INSTALLLOGMODE_FATALEXIT |
440 INSTALLLOGMODE_ERROR |
441 INSTALLLOGMODE_WARNING |
442 INSTALLLOGMODE_USER |
443 INSTALLLOGMODE_INFO |
444 INSTALLLOGMODE_RESOLVESOURCE |
445 INSTALLLOGMODE_OUTOFDISKSPACE |
446 INSTALLLOGMODE_ACTIONSTART |
447 INSTALLLOGMODE_ACTIONDATA |
448 INSTALLLOGMODE_COMMONDATA |
449 INSTALLLOGMODE_PROPERTYDUMP |
450 INSTALLLOGMODE_PROGRESS |
451 INSTALLLOGMODE_INITIALIZE |
452 INSTALLLOGMODE_TERMINATE |
453 INSTALLLOGMODE_SHOWDIALOG;
454 break;
455 case '+':
456 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
457 break;
458 case '!':
459 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
460 break;
461 default:
462 break;
465 i++;
466 if(i >= argc)
467 ShowUsage(1);
468 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
469 LogFileName = argv[i];
470 if(MsiEnableLogA(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
472 fprintf(stderr, "Logging in %s (0x%08lx, %lu) failed\n", LogFileName, LogMode, LogAttributes);
473 ExitProcess(1);
476 else if(!strcasecmp(argv[i], "/p"))
478 FunctionPatch = TRUE;
479 i++;
480 if(i >= argc)
481 ShowUsage(1);
482 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
483 PatchFileName = argv[i];
485 else if(!strncasecmp(argv[i], "/q", 2))
487 if(strlen(argv[i]) == 2 || !strcasecmp(argv[i]+2, "n"))
489 InstallUILevel = INSTALLUILEVEL_NONE;
491 else if(!strcasecmp(argv[i]+2, "b"))
493 InstallUILevel = INSTALLUILEVEL_BASIC;
495 else if(!strcasecmp(argv[i]+2, "r"))
497 InstallUILevel = INSTALLUILEVEL_REDUCED;
499 else if(!strcasecmp(argv[i]+2, "f"))
501 InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
503 else if(!strcasecmp(argv[i]+2, "n+"))
505 InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
507 else if(!strcasecmp(argv[i]+2, "b+"))
509 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
511 else if(!strcasecmp(argv[i]+2, "b-"))
513 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
515 else
517 fprintf(stderr, "Unknown option \"%s\" for UI level\n", argv[i]+2);
519 retInstallUILevel = MsiSetInternalUI(InstallUILevel, NULL);
520 if(retInstallUILevel == INSTALLUILEVEL_NOCHANGE)
522 fprintf(stderr, "Setting the UI level to 0x%x failed.\n", InstallUILevel);
523 ExitProcess(1);
526 else if(!strcasecmp(argv[i], "/y"))
528 FunctionDllRegisterServer = TRUE;
529 i++;
530 if(i >= argc)
531 ShowUsage(1);
532 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
533 DllName = argv[i];
535 else if(!strcasecmp(argv[i], "/z"))
537 FunctionDllUnregisterServer = TRUE;
538 i++;
539 if(i >= argc)
540 ShowUsage(1);
541 WINE_TRACE("argv[%d] = %s\n", i, argv[i]);
542 DllName = argv[i];
544 else if(!strcasecmp(argv[i], "/h") || !strcasecmp(argv[i], "/?"))
546 ShowUsage(0);
548 else if(strchr(argv[i], '='))
550 StringListAppend(&Properties, argv[i]);
551 StringListAppend(&Properties, " ");
553 else
555 FunctionInstall = TRUE;
556 GotProductCode = GetProductCode(argv[i], &PackageName, &ProductCode);
560 StringCompareRemoveLast(Properties, ' ');
561 StringCompareRemoveLast(Transforms, ';');
563 if(FunctionInstallAdmin && FunctionPatch)
564 FunctionInstall = FALSE;
566 if(FunctionInstall)
568 if(GotProductCode)
570 WINE_FIXME("Product code treatment not implemented yet\n");
571 ExitProcess(1);
573 else
575 if(MsiInstallProductA(PackageName, Properties) != ERROR_SUCCESS)
577 fprintf(stderr, "Installation of %s (%s) failed.\n", PackageName, Properties);
578 ExitProcess(1);
582 else if(FunctionRepair)
584 if(GotProductCode)
586 WINE_FIXME("Product code treatment not implemented yet\n");
587 ExitProcess(1);
589 else
591 if(MsiReinstallProductA(PackageName, RepairMode) != ERROR_SUCCESS)
593 fprintf(stderr, "Repair of %s (0x%08lx) failed.\n", PackageName, RepairMode);
594 ExitProcess(1);
598 else if(FunctionAdvertise)
600 if(MsiAdvertiseProductA(PackageName, (LPSTR) AdvertiseMode, Transforms, Language) != ERROR_SUCCESS)
602 fprintf(stderr, "Advertising of %s (%lu, %s, 0x%04x) failed.\n", PackageName, AdvertiseMode, Transforms, Language);
603 ExitProcess(1);
606 else if(FunctionPatch)
608 if(MsiApplyPatchA(PatchFileName, PackageName, InstallType, Properties) != ERROR_SUCCESS)
610 fprintf(stderr, "Patching with %s (%s, %d, %s)\n", PatchFileName, PackageName, InstallType, Properties);
611 ExitProcess(1);
614 else if(FunctionDllRegisterServer)
616 DllRegisterServer(DllName);
618 else if(FunctionDllUnregisterServer)
620 DllUnregisterServer(DllName);
622 else
623 ShowUsage(1);
625 return 0;