2 * msiexec.exe implementation
4 * Copyright 2004 Vincent Béron
5 * Copyright 2005 Mike McCormack
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #define WIN32_LEAN_AND_MEAN
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(msiexec);
34 typedef HRESULT (WINAPI *DLLREGISTERSERVER)(void);
35 typedef HRESULT (WINAPI *DLLUNREGISTERSERVER)(void);
39 struct string_list *next;
43 static const char UsageStr[] =
45 " Install a product:\n"
46 " msiexec {package|productcode} [property]\n"
47 " msiexec /i {package|productcode} [property]\n"
48 " msiexec /a package [property]\n"
49 " Repair an installation:\n"
50 " msiexec /f[p|o|e|d|c|a|u|m|s|v] {package|productcode}\n"
51 " Uninstall a product:\n"
52 " msiexec /x {package|productcode} [property]\n"
53 " Advertise a product:\n"
54 " msiexec /j[u|m] package [/t transform] [/g languageid]\n"
55 " msiexec {u|m} package [/t transform] [/g languageid]\n"
57 " msiexec /p patchpackage [property]\n"
58 " msiexec /p patchpackage /a package [property]\n"
59 " Modifiers for above operations:\n"
60 " msiexec /l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] logfile\n"
61 " msiexec /q{|n|b|r|f|n+|b+|b-}\n"
62 " Register a module:\n"
63 " msiexec /y module\n"
64 " Unregister a module:\n"
65 " msiexec /z module\n"
66 " Display usage and copyright:\n"
68 "NOTE: Product code on commandline unimplemented as of yet\n"
70 "Copyright 2004 Vincent Béron\n";
72 static const WCHAR ActionAdmin[] = {
73 'A','C','T','I','O','N','=','A','D','M','I','N',0 };
74 static const WCHAR RemoveAll[] = {
75 'R','E','M','O','V','E','=','A','L','L',0 };
77 static const WCHAR InstallRunOnce[] = {
78 'S','o','f','t','w','a','r','e','\\',
79 'M','i','c','r','o','s','o','f','t','\\',
80 'W','i','n','d','o','w','s','\\',
81 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
82 'I','n','s','t','a','l','l','e','r','\\',
83 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
85 static void ShowUsage(int ExitCode)
88 ExitProcess(ExitCode);
91 static BOOL IsProductCode(LPWSTR str)
95 if(lstrlenW(str) != 38)
97 return ( (CLSIDFromString(str, &ProductCode) == NOERROR) );
101 static VOID StringListAppend(struct string_list **list, LPCWSTR str)
103 struct string_list *entry;
106 size = sizeof *entry + lstrlenW(str) * sizeof (WCHAR);
107 entry = HeapAlloc(GetProcessHeap(), 0, size);
110 WINE_ERR("Out of memory!\n");
113 lstrcpyW(entry->str, str);
117 * Ignoring o(n^2) time complexity to add n strings for simplicity,
118 * add the string to the end of the list to preserve the order.
121 list = &(*list)->next;
125 static LPWSTR build_properties(struct string_list *property_list)
127 struct string_list *list;
128 LPWSTR ret, p, value;
135 /* count the space we need */
137 for(list = property_list; list; list = list->next)
138 len += lstrlenW(list->str) + 3;
140 ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
142 /* add a space before each string, and quote the value */
144 for(list = property_list; list; list = list->next)
146 value = strchrW(list->str,'=');
149 len = value - list->str;
151 memcpy(p, list->str, len * sizeof(WCHAR));
155 /* check if the value contains spaces and maybe quote it */
157 needs_quote = strchrW(value,' ') ? 1 : 0;
160 len = lstrlenW(value);
161 memcpy(p, value, len * sizeof(WCHAR));
168 WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret) );
173 static LPWSTR build_transforms(struct string_list *transform_list)
175 struct string_list *list;
179 /* count the space we need */
181 for(list = transform_list; list; list = list->next)
182 len += lstrlenW(list->str) + 1;
184 ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
186 /* add all the transforms with a semicolon between each one */
188 for(list = transform_list; list; list = list->next)
190 len = lstrlenW(list->str);
191 lstrcpynW(p, list->str, len );
201 static DWORD msi_atou(LPCWSTR str)
204 while(*str >= '0' && *str <= '9')
213 static LPWSTR msi_strdup(LPCWSTR str)
215 DWORD len = lstrlenW(str)+1;
216 LPWSTR ret = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
221 /* str1 is the same as str2, ignoring case */
222 static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2)
227 len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
230 if( lstrlenW(str1) != (len-1) )
232 strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
233 MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
234 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len, strW, len);
235 HeapFree(GetProcessHeap(), 0, strW);
236 return (ret != CSTR_EQUAL);
239 /* str2 is at the beginning of str1, ignoring case */
240 static BOOL msi_strprefix(LPCWSTR str1, LPCSTR str2)
245 len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
248 if( lstrlenW(str1) < (len-1) )
250 strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
251 MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
252 ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len-1, strW, len-1);
253 HeapFree(GetProcessHeap(), 0, strW);
254 return (ret != CSTR_EQUAL);
257 static VOID *LoadProc(LPCWSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
261 *DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
264 fprintf(stderr, "Unable to load dll %s\n", wine_dbgstr_w(DllName));
267 proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
270 fprintf(stderr, "Dll %s does not implement function %s\n",
271 wine_dbgstr_w(DllName), ProcName);
272 FreeLibrary(*DllHandle);
279 static DWORD DoDllRegisterServer(LPCWSTR DllName)
282 DLLREGISTERSERVER pfDllRegisterServer = NULL;
283 HMODULE DllHandle = NULL;
285 pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);
287 hr = pfDllRegisterServer();
290 fprintf(stderr, "Failed to register dll %s\n", wine_dbgstr_w(DllName));
293 printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName));
295 FreeLibrary(DllHandle);
299 static DWORD DoDllUnregisterServer(LPCWSTR DllName)
302 DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
303 HMODULE DllHandle = NULL;
305 pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);
307 hr = pfDllUnregisterServer();
310 fprintf(stderr, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName));
313 printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName));
315 FreeLibrary(DllHandle);
320 * state machine to break up the command line properly
330 static int chomp( WCHAR *str )
332 enum chomp_state state = cs_whitespace;
334 int count = 0, ignore;
336 for( p = str, out = str; *p; p++ )
364 state = cs_whitespace;
392 static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
394 WCHAR **argv, *p = msi_strdup(cmdline);
398 argv = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR*)*(n+1));
402 p += lstrlenW(p) + 1;
410 static BOOL process_args_from_reg( LPWSTR ident, int *pargc, WCHAR ***pargv )
413 HKEY hkey = 0, hkeyArgs = 0;
414 DWORD sz = 0, type = 0;
418 r = RegOpenKeyW(HKEY_LOCAL_MACHINE, InstallRunOnce, &hkey);
419 if(r != ERROR_SUCCESS)
421 r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
422 if(r == ERROR_SUCCESS && type == REG_SZ)
424 buf = HeapAlloc(GetProcessHeap(), 0, sz);
425 r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)buf, &sz);
426 if( r == ERROR_SUCCESS )
428 process_args(buf, pargc, pargv);
432 RegCloseKey(hkeyArgs);
436 int main(int argc, char **argv)
439 BOOL FunctionInstall = FALSE;
440 BOOL FunctionInstallAdmin = FALSE;
441 BOOL FunctionRepair = FALSE;
442 BOOL FunctionAdvertise = FALSE;
443 BOOL FunctionPatch = FALSE;
444 BOOL FunctionDllRegisterServer = FALSE;
445 BOOL FunctionDllUnregisterServer = FALSE;
446 BOOL FunctionRegServer = FALSE;
447 BOOL FunctionUnregServer = FALSE;
448 BOOL FunctionUnknown = FALSE;
450 LPWSTR PackageName = NULL;
451 LPWSTR Properties = NULL;
452 struct string_list *property_list = NULL;
454 DWORD RepairMode = 0;
456 DWORD AdvertiseMode = 0;
457 struct string_list *transform_list = NULL;
461 LPWSTR LogFileName = NULL;
462 DWORD LogAttributes = 0;
464 LPWSTR PatchFileName = NULL;
465 INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
467 INSTALLUILEVEL InstallUILevel = INSTALLUILEVEL_FULL;
469 LPWSTR DllName = NULL;
471 LPWSTR *argvW = NULL;
473 /* overwrite the command line */
474 process_args( GetCommandLineW(), &argc, &argvW );
477 * If the args begin with /@ IDENT then we need to load the real
478 * command line out of the RunOnceEntries key in the registry.
479 * We do that before starting to process the real commandline,
480 * then overwrite the commandline again.
482 if(!msi_strequal(argvW[1], "/@"))
484 if(!process_args_from_reg( argvW[2], &argc, &argvW ))
488 for(i = 1; i < argc; i++)
490 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
492 if (!msi_strequal(argvW[i], "/regserver"))
494 FunctionRegServer = TRUE;
496 else if (!msi_strequal(argvW[i], "/unregserver") || !msi_strequal(argvW[i], "/unregister"))
498 FunctionUnregServer = TRUE;
500 else if(!msi_strprefix(argvW[i], "/i"))
502 LPWSTR argvWi = argvW[i];
503 FunctionInstall = TRUE;
504 if(lstrlenW(argvWi) > 2)
511 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
514 PackageName = argvWi;
516 else if(!msi_strequal(argvW[i], "/a"))
518 FunctionInstall = TRUE;
519 FunctionInstallAdmin = TRUE;
520 InstallType = INSTALLTYPE_NETWORK_IMAGE;
524 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
525 PackageName = argvW[i];
526 StringListAppend(&property_list, ActionAdmin);
528 else if(!msi_strprefix(argvW[i], "/f"))
531 int len = lstrlenW(argvW[i]);
532 FunctionRepair = TRUE;
533 for(j = 2; j < len; j++)
539 RepairMode |= REINSTALLMODE_FILEMISSING;
543 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
547 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
551 RepairMode |= REINSTALLMODE_FILEEXACT;
555 RepairMode |= REINSTALLMODE_FILEVERIFY;
559 RepairMode |= REINSTALLMODE_FILEREPLACE;
563 RepairMode |= REINSTALLMODE_USERDATA;
567 RepairMode |= REINSTALLMODE_MACHINEDATA;
571 RepairMode |= REINSTALLMODE_SHORTCUT;
575 RepairMode |= REINSTALLMODE_PACKAGE;
578 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argvW[i][j]);
584 RepairMode = REINSTALLMODE_FILEMISSING |
585 REINSTALLMODE_FILEEQUALVERSION |
586 REINSTALLMODE_FILEVERIFY |
587 REINSTALLMODE_MACHINEDATA |
588 REINSTALLMODE_SHORTCUT;
593 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
594 PackageName = argvW[i];
596 else if(!msi_strequal(argvW[i], "/x"))
598 FunctionInstall = TRUE;
602 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
603 PackageName = argvW[i];
604 StringListAppend(&property_list, RemoveAll);
606 else if(!msi_strprefix(argvW[i], "/j"))
609 int len = lstrlenW(argvW[i]);
610 FunctionAdvertise = TRUE;
611 for(j = 2; j < len; j++)
617 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
621 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
624 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argvW[i][j]);
631 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
632 PackageName = argvW[i];
634 else if(!msi_strequal(argvW[i], "u"))
636 FunctionAdvertise = TRUE;
637 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
641 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
642 PackageName = argvW[i];
644 else if(!msi_strequal(argvW[i], "m"))
646 FunctionAdvertise = TRUE;
647 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
651 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
652 PackageName = argvW[i];
654 else if(!msi_strequal(argvW[i], "/t"))
659 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
660 StringListAppend(&transform_list, argvW[i]);
662 else if(!msi_strequal(argvW[i], "/g"))
667 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
668 Language = msi_atou(argvW[i]);
670 else if(!msi_strprefix(argvW[i], "/l"))
673 int len = lstrlenW(argvW[i]);
674 for(j = 2; j < len; j++)
680 LogMode |= INSTALLLOGMODE_INFO;
684 LogMode |= INSTALLLOGMODE_WARNING;
688 LogMode |= INSTALLLOGMODE_ERROR;
692 LogMode |= INSTALLLOGMODE_ACTIONSTART;
696 LogMode |= INSTALLLOGMODE_ACTIONDATA;
700 LogMode |= INSTALLLOGMODE_USER;
704 LogMode |= INSTALLLOGMODE_COMMONDATA;
708 LogMode |= INSTALLLOGMODE_FATALEXIT;
712 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
716 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
720 LogMode |= INSTALLLOGMODE_VERBOSE;
723 LogMode = INSTALLLOGMODE_FATALEXIT |
724 INSTALLLOGMODE_ERROR |
725 INSTALLLOGMODE_WARNING |
726 INSTALLLOGMODE_USER |
727 INSTALLLOGMODE_INFO |
728 INSTALLLOGMODE_RESOLVESOURCE |
729 INSTALLLOGMODE_OUTOFDISKSPACE |
730 INSTALLLOGMODE_ACTIONSTART |
731 INSTALLLOGMODE_ACTIONDATA |
732 INSTALLLOGMODE_COMMONDATA |
733 INSTALLLOGMODE_PROPERTYDUMP |
734 INSTALLLOGMODE_PROGRESS |
735 INSTALLLOGMODE_INITIALIZE |
736 INSTALLLOGMODE_TERMINATE |
737 INSTALLLOGMODE_SHOWDIALOG;
740 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
743 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
752 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
753 LogFileName = argvW[i];
754 if(MsiEnableLogW(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
756 fprintf(stderr, "Logging in %s (0x%08lx, %lu) failed\n",
757 wine_dbgstr_w(LogFileName), LogMode, LogAttributes);
761 else if(!msi_strequal(argvW[i], "/p"))
763 FunctionPatch = TRUE;
767 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
768 PatchFileName = argvW[i];
770 else if(!msi_strprefix(argvW[i], "/q"))
772 if(lstrlenW(argvW[i]) == 2 || !msi_strequal(argvW[i]+2, "n"))
774 InstallUILevel = INSTALLUILEVEL_NONE;
776 else if(!msi_strequal(argvW[i]+2, "b"))
778 InstallUILevel = INSTALLUILEVEL_BASIC;
780 else if(!msi_strequal(argvW[i]+2, "r"))
782 InstallUILevel = INSTALLUILEVEL_REDUCED;
784 else if(!msi_strequal(argvW[i]+2, "f"))
786 InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
788 else if(!msi_strequal(argvW[i]+2, "n+"))
790 InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
792 else if(!msi_strequal(argvW[i]+2, "b+"))
794 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
796 else if(!msi_strequal(argvW[i]+2, "b-"))
798 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY;
800 else if(!msi_strequal(argvW[i]+2, "b+!"))
802 InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG;
803 WINE_FIXME("Unknown modifier: !\n");
807 fprintf(stderr, "Unknown option \"%s\" for UI level\n",
808 wine_dbgstr_w(argvW[i]+2));
811 else if(!msi_strequal(argvW[i], "/y"))
813 FunctionDllRegisterServer = TRUE;
817 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
820 else if(!msi_strequal(argvW[i], "/z"))
822 FunctionDllUnregisterServer = TRUE;
826 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
829 else if(!msi_strequal(argvW[i], "/h") || !msi_strequal(argvW[i], "/?"))
833 else if(!msi_strequal(argvW[i], "/m"))
835 FunctionUnknown = TRUE;
836 WINE_FIXME("Unknown parameter /m\n");
838 else if(!msi_strequal(argvW[i], "/D"))
840 FunctionUnknown = TRUE;
841 WINE_FIXME("Unknown parameter /D\n");
843 else if(strchrW(argvW[i], '='))
845 StringListAppend(&property_list, argvW[i]);
849 FunctionInstall = TRUE;
850 PackageName = argvW[i];
855 MsiSetInternalUI(InstallUILevel, NULL);
857 Properties = build_properties( property_list );
859 if(FunctionInstallAdmin && FunctionPatch)
860 FunctionInstall = FALSE;
865 if(IsProductCode(PackageName))
866 ReturnCode = MsiConfigureProductExW(PackageName, 0, INSTALLSTATE_DEFAULT, Properties);
868 ReturnCode = MsiInstallProductW(PackageName, Properties);
870 else if(FunctionRepair)
872 if(IsProductCode(PackageName))
873 WINE_FIXME("Product code treatment not implemented yet\n");
875 ReturnCode = MsiReinstallProductW(PackageName, RepairMode);
877 else if(FunctionAdvertise)
879 LPWSTR Transforms = build_transforms( property_list );
880 ReturnCode = MsiAdvertiseProductW(PackageName, (LPWSTR) AdvertiseMode, Transforms, Language);
882 else if(FunctionPatch)
884 ReturnCode = MsiApplyPatchW(PatchFileName, PackageName, InstallType, Properties);
886 else if(FunctionDllRegisterServer)
888 ReturnCode = DoDllRegisterServer(DllName);
890 else if(FunctionDllUnregisterServer)
892 ReturnCode = DoDllUnregisterServer(DllName);
894 else if (FunctionRegServer)
896 WINE_FIXME( "/regserver not implemented yet, ignoring\n" );
898 else if (FunctionUnregServer)
900 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
902 else if (FunctionUnknown)
904 WINE_FIXME( "Unknown function, ignoring\n" );