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
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(msiexec
);
32 static const char UsageStr
[] =
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"
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"
57 "NOTE: Product code on commandline unimplemented as of yet\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
)
67 ExitProcess(ExitCode
);
70 static BOOL
GetProductCode(LPCSTR str
, LPCSTR
*PackageName
, LPGUID
*ProductCode
)
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
);
87 HeapFree(GetProcessHeap(), 0, *ProductCode
);
95 static VOID
StringListAppend(LPSTR
*StringList
, LPCSTR StringAppend
)
97 LPSTR TempStr
= HeapReAlloc(GetProcessHeap(), 0, *StringList
, HeapSize(GetProcessHeap(), 0, *StringList
)+strlen(StringAppend
));
100 WINE_ERR("Out of memory!\n");
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
)
117 *DllHandle
= LoadLibraryExA(DllName
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
120 fprintf(stderr
, "Unable to load dll %s\n", DllName
);
123 proc
= (VOID
*) GetProcAddress(*DllHandle
, ProcName
);
126 fprintf(stderr
, "Dll %s does not implement function %s\n", DllName
, ProcName
);
127 FreeLibrary(*DllHandle
);
134 static void DllRegisterServer(LPCSTR DllName
)
137 DLLREGISTERSERVER pfDllRegisterServer
= NULL
;
138 HMODULE DllHandle
= NULL
;
140 pfDllRegisterServer
= LoadProc(DllName
, "DllRegisterServer", &DllHandle
);
142 hr
= pfDllRegisterServer();
145 fprintf(stderr
, "Failed to register dll %s\n", DllName
);
148 printf("Successfully registered dll %s\n", DllName
);
150 FreeLibrary(DllHandle
);
153 static void DllUnregisterServer(LPCSTR DllName
)
156 DLLUNREGISTERSERVER pfDllUnregisterServer
= NULL
;
157 HMODULE DllHandle
= NULL
;
159 pfDllUnregisterServer
= LoadProc(DllName
, "DllUnregisterServer", &DllHandle
);
161 hr
= pfDllUnregisterServer();
164 fprintf(stderr
, "Failed to unregister dll %s\n", DllName
);
167 printf("Successfully unregistered dll %s\n", DllName
);
169 FreeLibrary(DllHandle
);
172 int main(int argc
, char *argv
[])
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);
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
;
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)
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
;
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))
241 int len
= strlen(argv
[i
]);
242 FunctionRepair
= TRUE
;
243 for(j
= 2; j
< len
; j
++)
249 RepairMode
|= REINSTALLMODE_FILEMISSING
;
253 RepairMode
|= REINSTALLMODE_FILEOLDERVERSION
;
257 RepairMode
|= REINSTALLMODE_FILEEQUALVERSION
;
261 RepairMode
|= REINSTALLMODE_FILEEXACT
;
265 RepairMode
|= REINSTALLMODE_FILEVERIFY
;
269 RepairMode
|= REINSTALLMODE_FILEREPLACE
;
273 RepairMode
|= REINSTALLMODE_USERDATA
;
277 RepairMode
|= REINSTALLMODE_MACHINEDATA
;
281 RepairMode
|= REINSTALLMODE_SHORTCUT
;
285 RepairMode
|= REINSTALLMODE_PACKAGE
;
288 fprintf(stderr
, "Unknown option \"%c\" in Repair mode\n", argv
[i
][j
]);
294 RepairMode
= REINSTALLMODE_FILEMISSING
|
295 REINSTALLMODE_FILEEQUALVERSION
|
296 REINSTALLMODE_FILEVERIFY
|
297 REINSTALLMODE_MACHINEDATA
|
298 REINSTALLMODE_SHORTCUT
;
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
;
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))
319 int len
= strlen(argv
[i
]);
320 FunctionAdvertise
= TRUE
;
321 for(j
= 2; j
< len
; j
++)
327 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
331 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
334 fprintf(stderr
, "Unknown option \"%c\" in Advertise mode\n", argv
[i
][j
]);
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
;
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
;
361 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
362 PackageName
= argv
[i
];
364 else if(!strcasecmp(argv
[i
], "/t"))
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"))
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))
389 int len
= strlen(argv
[i
]);
390 for(j
= 2; j
< len
; j
++)
396 LogMode
|= INSTALLLOGMODE_INFO
;
400 LogMode
|= INSTALLLOGMODE_WARNING
;
404 LogMode
|= INSTALLLOGMODE_ERROR
;
408 LogMode
|= INSTALLLOGMODE_ACTIONSTART
;
412 LogMode
|= INSTALLLOGMODE_ACTIONDATA
;
416 LogMode
|= INSTALLLOGMODE_USER
;
420 LogMode
|= INSTALLLOGMODE_COMMONDATA
;
424 LogMode
|= INSTALLLOGMODE_FATALEXIT
;
428 LogMode
|= INSTALLLOGMODE_OUTOFDISKSPACE
;
432 LogMode
|= INSTALLLOGMODE_PROPERTYDUMP
;
436 LogMode
|= INSTALLLOGMODE_VERBOSE
;
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
;
456 LogAttributes
|= INSTALLLOGATTRIBUTES_APPEND
;
459 LogAttributes
|= INSTALLLOGATTRIBUTES_FLUSHEACHLINE
;
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
);
476 else if(!strcasecmp(argv
[i
], "/p"))
478 FunctionPatch
= TRUE
;
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
;
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
);
526 else if(!strcasecmp(argv
[i
], "/y"))
528 FunctionDllRegisterServer
= TRUE
;
532 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
535 else if(!strcasecmp(argv
[i
], "/z"))
537 FunctionDllUnregisterServer
= TRUE
;
541 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
544 else if(!strcasecmp(argv
[i
], "/h") || !strcasecmp(argv
[i
], "/?"))
548 else if(strchr(argv
[i
], '='))
550 StringListAppend(&Properties
, argv
[i
]);
551 StringListAppend(&Properties
, " ");
555 FunctionInstall
= TRUE
;
556 GotProductCode
= GetProductCode(argv
[i
], &PackageName
, &ProductCode
);
560 StringCompareRemoveLast(Properties
, ' ');
561 StringCompareRemoveLast(Transforms
, ';');
563 if(FunctionInstallAdmin
&& FunctionPatch
)
564 FunctionInstall
= FALSE
;
570 WINE_FIXME("Product code treatment not implemented yet\n");
575 if(MsiInstallProductA(PackageName
, Properties
) != ERROR_SUCCESS
)
577 fprintf(stderr
, "Installation of %s (%s) failed.\n", PackageName
, Properties
);
582 else if(FunctionRepair
)
586 WINE_FIXME("Product code treatment not implemented yet\n");
591 if(MsiReinstallProductA(PackageName
, RepairMode
) != ERROR_SUCCESS
)
593 fprintf(stderr
, "Repair of %s (0x%08lx) failed.\n", PackageName
, RepairMode
);
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
);
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
);
614 else if(FunctionDllRegisterServer
)
616 DllRegisterServer(DllName
);
618 else if(FunctionDllUnregisterServer
)
620 DllUnregisterServer(DllName
);