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
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(msiexec
);
36 struct string_list
*next
;
40 static const char UsageStr
[] =
42 " Install a product:\n"
43 " msiexec {package|productcode} [property]\n"
44 " msiexec /i {package|productcode} [property]\n"
45 " msiexec /a package [property]\n"
46 " Repair an installation:\n"
47 " msiexec /f[p|o|e|d|c|a|u|m|s|v] {package|productcode}\n"
48 " Uninstall a product:\n"
49 " msiexec /x {package|productcode} [property]\n"
50 " Advertise a product:\n"
51 " msiexec /j[u|m] package [/t transform] [/g languageid]\n"
52 " msiexec {u|m} package [/t transform] [/g languageid]\n"
54 " msiexec /p patchpackage [property]\n"
55 " msiexec /p patchpackage /a package [property]\n"
56 " Modifiers for above operations:\n"
57 " msiexec /l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] logfile\n"
58 " msiexec /q{|n|b|r|f|n+|b+|b-}\n"
59 " Register a module:\n"
60 " msiexec /y module\n"
61 " Unregister a module:\n"
62 " msiexec /z module\n"
63 " Display usage and copyright:\n"
65 "NOTE: Product code on commandline unimplemented as of yet\n"
67 "Copyright 2004 Vincent Béron\n";
69 static const WCHAR ActionAdmin
[] = {
70 'A','C','T','I','O','N','=','A','D','M','I','N',0 };
71 static const WCHAR RemoveAll
[] = {
72 'R','E','M','O','V','E','=','A','L','L',0 };
74 static const WCHAR InstallRunOnce
[] = {
75 'S','o','f','t','w','a','r','e','\\',
76 'M','i','c','r','o','s','o','f','t','\\',
77 'W','i','n','d','o','w','s','\\',
78 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
79 'I','n','s','t','a','l','l','e','r','\\',
80 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
82 static void ShowUsage(int ExitCode
)
85 ExitProcess(ExitCode
);
88 static BOOL
IsProductCode(LPWSTR str
)
92 if(lstrlenW(str
) != 38)
94 return ( (CLSIDFromString(str
, &ProductCode
) == NOERROR
) );
98 static VOID
StringListAppend(struct string_list
**list
, LPCWSTR str
)
100 struct string_list
*entry
;
103 size
= sizeof *entry
+ lstrlenW(str
) * sizeof (WCHAR
);
104 entry
= HeapAlloc(GetProcessHeap(), 0, size
);
107 WINE_ERR("Out of memory!\n");
110 lstrcpyW(entry
->str
, str
);
114 * Ignoring o(n^2) time complexity to add n strings for simplicity,
115 * add the string to the end of the list to preserve the order.
118 list
= &(*list
)->next
;
122 static LPWSTR
build_properties(struct string_list
*property_list
)
124 struct string_list
*list
;
125 LPWSTR ret
, p
, value
;
132 /* count the space we need */
134 for(list
= property_list
; list
; list
= list
->next
)
135 len
+= lstrlenW(list
->str
) + 3;
137 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
139 /* add a space before each string, and quote the value */
141 for(list
= property_list
; list
; list
= list
->next
)
143 value
= strchrW(list
->str
,'=');
146 len
= value
- list
->str
;
148 memcpy(p
, list
->str
, len
* sizeof(WCHAR
));
152 /* check if the value contains spaces and maybe quote it */
154 needs_quote
= strchrW(value
,' ') ? 1 : 0;
157 len
= lstrlenW(value
);
158 memcpy(p
, value
, len
* sizeof(WCHAR
));
165 WINE_TRACE("properties -> %S\n", ret
);
170 static LPWSTR
build_transforms(struct string_list
*transform_list
)
172 struct string_list
*list
;
176 /* count the space we need */
178 for(list
= transform_list
; list
; list
= list
->next
)
179 len
+= lstrlenW(list
->str
) + 1;
181 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
183 /* add all the transforms with a semicolon between each one */
185 for(list
= transform_list
; list
; list
= list
->next
)
187 len
= lstrlenW(list
->str
);
188 lstrcpynW(p
, list
->str
, len
);
198 static DWORD
msi_atou(LPCWSTR str
)
201 while(*str
>= '0' && *str
<= '9')
210 static LPWSTR
msi_strdup(LPCWSTR str
)
212 DWORD len
= lstrlenW(str
)+1;
213 LPWSTR ret
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
218 /* str1 is the same as str2, ignoring case */
219 static BOOL
msi_strequal(LPCWSTR str1
, LPCSTR str2
)
224 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
227 if( lstrlenW(str1
) != (len
-1) )
229 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
230 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
231 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
, strW
, len
);
232 HeapFree(GetProcessHeap(), 0, strW
);
233 return (ret
!= CSTR_EQUAL
);
236 /* str2 is at the beginning of str1, ignoring case */
237 static BOOL
msi_strprefix(LPCWSTR str1
, LPCSTR str2
)
242 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
245 if( lstrlenW(str1
) < (len
-1) )
247 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
248 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
249 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
-1, strW
, len
-1);
250 HeapFree(GetProcessHeap(), 0, strW
);
251 return (ret
!= CSTR_EQUAL
);
254 static VOID
*LoadProc(LPCWSTR DllName
, LPCSTR ProcName
, HMODULE
* DllHandle
)
258 *DllHandle
= LoadLibraryExW(DllName
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
261 fprintf(stderr
, "Unable to load dll %S\n", DllName
);
264 proc
= (VOID
*) GetProcAddress(*DllHandle
, ProcName
);
267 fprintf(stderr
, "Dll %S does not implement function %s\n",
269 FreeLibrary(*DllHandle
);
276 static DWORD
DoDllRegisterServer(LPCWSTR DllName
)
279 DLLREGISTERSERVER pfDllRegisterServer
= NULL
;
280 HMODULE DllHandle
= NULL
;
282 pfDllRegisterServer
= LoadProc(DllName
, "DllRegisterServer", &DllHandle
);
284 hr
= pfDllRegisterServer();
287 fprintf(stderr
, "Failed to register dll %S\n", DllName
);
290 printf("Successfully registered dll %S\n", DllName
);
292 FreeLibrary(DllHandle
);
296 static DWORD
DoDllUnregisterServer(LPCWSTR DllName
)
299 DLLUNREGISTERSERVER pfDllUnregisterServer
= NULL
;
300 HMODULE DllHandle
= NULL
;
302 pfDllUnregisterServer
= LoadProc(DllName
, "DllUnregisterServer", &DllHandle
);
304 hr
= pfDllUnregisterServer();
307 fprintf(stderr
, "Failed to unregister dll %S\n", DllName
);
310 printf("Successfully unregistered dll %S\n", DllName
);
312 FreeLibrary(DllHandle
);
317 * state machine to break up the command line properly
327 static int chomp( WCHAR
*str
)
329 enum chomp_state state
= cs_whitespace
;
331 int count
= 0, ignore
;
333 for( p
= str
, out
= str
; *p
; p
++ )
361 state
= cs_whitespace
;
389 void process_args( WCHAR
*cmdline
, int *pargc
, WCHAR
***pargv
)
391 WCHAR
**argv
, *p
= msi_strdup(cmdline
);
395 argv
= HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR
*)*(n
+1));
399 p
+= lstrlenW(p
) + 1;
407 BOOL
process_args_from_reg( LPWSTR ident
, int *pargc
, WCHAR
***pargv
)
410 HKEY hkey
= 0, hkeyArgs
= 0;
411 DWORD sz
= 0, type
= 0;
415 r
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, InstallRunOnce
, &hkey
);
416 if(r
!= ERROR_SUCCESS
)
418 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, 0, &sz
);
419 if(r
== ERROR_SUCCESS
&& type
== REG_SZ
)
421 buf
= HeapAlloc(GetProcessHeap(), 0, sz
);
422 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, (LPBYTE
)buf
, &sz
);
423 if( r
== ERROR_SUCCESS
)
425 process_args(buf
, pargc
, pargv
);
429 RegCloseKey(hkeyArgs
);
433 int main(int argc
, char **argv
)
436 BOOL FunctionInstall
= FALSE
;
437 BOOL FunctionInstallAdmin
= FALSE
;
438 BOOL FunctionRepair
= FALSE
;
439 BOOL FunctionAdvertise
= FALSE
;
440 BOOL FunctionPatch
= FALSE
;
441 BOOL FunctionDllRegisterServer
= FALSE
;
442 BOOL FunctionDllUnregisterServer
= FALSE
;
443 BOOL FunctionRegServer
= FALSE
;
444 BOOL FunctionUnregServer
= FALSE
;
445 BOOL FunctionUnknown
= FALSE
;
447 LPWSTR PackageName
= NULL
;
448 LPWSTR Properties
= NULL
;
449 struct string_list
*property_list
= NULL
;
451 DWORD RepairMode
= 0;
453 DWORD AdvertiseMode
= 0;
454 LPWSTR Transforms
= NULL
;
455 struct string_list
*transform_list
= NULL
;
459 LPWSTR LogFileName
= NULL
;
460 DWORD LogAttributes
= 0;
462 LPWSTR PatchFileName
= NULL
;
463 INSTALLTYPE InstallType
= INSTALLTYPE_DEFAULT
;
465 INSTALLUILEVEL InstallUILevel
= INSTALLUILEVEL_FULL
;
467 LPWSTR DllName
= NULL
;
469 LPWSTR
*argvW
= NULL
;
471 /* overwrite the command line */
472 process_args( GetCommandLineW(), &argc
, &argvW
);
475 * If the args begin with /@ IDENT then we need to load the real
476 * command line out of the RunOnceEntries key in the registry.
477 * We do that before starting to process the real commandline,
478 * then overwrite the commandline again.
480 if(!msi_strequal(argvW
[1], "/@"))
482 if(!process_args_from_reg( argvW
[2], &argc
, &argvW
))
486 for(i
= 1; i
< argc
; i
++)
488 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
490 if (!msi_strequal(argvW
[i
], "/regserver"))
492 FunctionRegServer
= TRUE
;
494 else if (!msi_strequal(argvW
[i
], "/unregserver") || !msi_strequal(argvW
[i
], "/unregister"))
496 FunctionUnregServer
= TRUE
;
498 else if(!msi_strprefix(argvW
[i
], "/i"))
500 LPWSTR argvWi
= argvW
[i
];
501 FunctionInstall
= TRUE
;
502 if(lstrlenW(argvWi
) > 2)
509 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
512 PackageName
= argvWi
;
514 else if(!msi_strequal(argvW
[i
], "/a"))
516 FunctionInstall
= TRUE
;
517 FunctionInstallAdmin
= TRUE
;
518 InstallType
= INSTALLTYPE_NETWORK_IMAGE
;
522 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
523 PackageName
= argvW
[i
];
524 StringListAppend(&property_list
, ActionAdmin
);
526 else if(!msi_strprefix(argvW
[i
], "/f"))
529 int len
= lstrlenW(argvW
[i
]);
530 FunctionRepair
= TRUE
;
531 for(j
= 2; j
< len
; j
++)
537 RepairMode
|= REINSTALLMODE_FILEMISSING
;
541 RepairMode
|= REINSTALLMODE_FILEOLDERVERSION
;
545 RepairMode
|= REINSTALLMODE_FILEEQUALVERSION
;
549 RepairMode
|= REINSTALLMODE_FILEEXACT
;
553 RepairMode
|= REINSTALLMODE_FILEVERIFY
;
557 RepairMode
|= REINSTALLMODE_FILEREPLACE
;
561 RepairMode
|= REINSTALLMODE_USERDATA
;
565 RepairMode
|= REINSTALLMODE_MACHINEDATA
;
569 RepairMode
|= REINSTALLMODE_SHORTCUT
;
573 RepairMode
|= REINSTALLMODE_PACKAGE
;
576 fprintf(stderr
, "Unknown option \"%c\" in Repair mode\n", argvW
[i
][j
]);
582 RepairMode
= REINSTALLMODE_FILEMISSING
|
583 REINSTALLMODE_FILEEQUALVERSION
|
584 REINSTALLMODE_FILEVERIFY
|
585 REINSTALLMODE_MACHINEDATA
|
586 REINSTALLMODE_SHORTCUT
;
591 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
592 PackageName
= argvW
[i
];
594 else if(!msi_strequal(argvW
[i
], "/x"))
596 FunctionInstall
= TRUE
;
600 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
601 PackageName
= argvW
[i
];
602 StringListAppend(&property_list
, RemoveAll
);
604 else if(!msi_strprefix(argvW
[i
], "/j"))
607 int len
= lstrlenW(argvW
[i
]);
608 FunctionAdvertise
= TRUE
;
609 for(j
= 2; j
< len
; j
++)
615 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
619 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
622 fprintf(stderr
, "Unknown option \"%c\" in Advertise mode\n", argvW
[i
][j
]);
629 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
630 PackageName
= argvW
[i
];
632 else if(!msi_strequal(argvW
[i
], "u"))
634 FunctionAdvertise
= TRUE
;
635 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
639 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
640 PackageName
= argvW
[i
];
642 else if(!msi_strequal(argvW
[i
], "m"))
644 FunctionAdvertise
= TRUE
;
645 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
649 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
650 PackageName
= argvW
[i
];
652 else if(!msi_strequal(argvW
[i
], "/t"))
657 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
658 StringListAppend(&transform_list
, argvW
[i
]);
660 else if(!msi_strprefix(argvW
[i
], "TRANSFORMS="))
662 StringListAppend(&transform_list
, argvW
[i
]+11);
664 else if(!msi_strequal(argvW
[i
], "/g"))
669 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
670 Language
= msi_atou(argvW
[i
]);
672 else if(!msi_strprefix(argvW
[i
], "/l"))
675 int len
= lstrlenW(argvW
[i
]);
676 for(j
= 2; j
< len
; j
++)
682 LogMode
|= INSTALLLOGMODE_INFO
;
686 LogMode
|= INSTALLLOGMODE_WARNING
;
690 LogMode
|= INSTALLLOGMODE_ERROR
;
694 LogMode
|= INSTALLLOGMODE_ACTIONSTART
;
698 LogMode
|= INSTALLLOGMODE_ACTIONDATA
;
702 LogMode
|= INSTALLLOGMODE_USER
;
706 LogMode
|= INSTALLLOGMODE_COMMONDATA
;
710 LogMode
|= INSTALLLOGMODE_FATALEXIT
;
714 LogMode
|= INSTALLLOGMODE_OUTOFDISKSPACE
;
718 LogMode
|= INSTALLLOGMODE_PROPERTYDUMP
;
722 LogMode
|= INSTALLLOGMODE_VERBOSE
;
725 LogMode
= INSTALLLOGMODE_FATALEXIT
|
726 INSTALLLOGMODE_ERROR
|
727 INSTALLLOGMODE_WARNING
|
728 INSTALLLOGMODE_USER
|
729 INSTALLLOGMODE_INFO
|
730 INSTALLLOGMODE_RESOLVESOURCE
|
731 INSTALLLOGMODE_OUTOFDISKSPACE
|
732 INSTALLLOGMODE_ACTIONSTART
|
733 INSTALLLOGMODE_ACTIONDATA
|
734 INSTALLLOGMODE_COMMONDATA
|
735 INSTALLLOGMODE_PROPERTYDUMP
|
736 INSTALLLOGMODE_PROGRESS
|
737 INSTALLLOGMODE_INITIALIZE
|
738 INSTALLLOGMODE_TERMINATE
|
739 INSTALLLOGMODE_SHOWDIALOG
;
742 LogAttributes
|= INSTALLLOGATTRIBUTES_APPEND
;
745 LogAttributes
|= INSTALLLOGATTRIBUTES_FLUSHEACHLINE
;
754 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
755 LogFileName
= argvW
[i
];
756 if(MsiEnableLogW(LogMode
, LogFileName
, LogAttributes
) != ERROR_SUCCESS
)
758 fprintf(stderr
, "Logging in %S (0x%08lx, %lu) failed\n",
759 LogFileName
, LogMode
, LogAttributes
);
763 else if(!msi_strequal(argvW
[i
], "/p"))
765 FunctionPatch
= TRUE
;
769 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
770 PatchFileName
= argvW
[i
];
772 else if(!msi_strprefix(argvW
[i
], "/q"))
774 if(lstrlenW(argvW
[i
]) == 2 || !msi_strequal(argvW
[i
]+2, "n"))
776 InstallUILevel
= INSTALLUILEVEL_NONE
;
778 else if(!msi_strequal(argvW
[i
]+2, "b"))
780 InstallUILevel
= INSTALLUILEVEL_BASIC
;
782 else if(!msi_strequal(argvW
[i
]+2, "r"))
784 InstallUILevel
= INSTALLUILEVEL_REDUCED
;
786 else if(!msi_strequal(argvW
[i
]+2, "f"))
788 InstallUILevel
= INSTALLUILEVEL_FULL
|INSTALLUILEVEL_ENDDIALOG
;
790 else if(!msi_strequal(argvW
[i
]+2, "n+"))
792 InstallUILevel
= INSTALLUILEVEL_NONE
|INSTALLUILEVEL_ENDDIALOG
;
794 else if(!msi_strequal(argvW
[i
]+2, "b+"))
796 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
798 else if(!msi_strequal(argvW
[i
]+2, "b-"))
800 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_PROGRESSONLY
;
802 else if(!msi_strequal(argvW
[i
]+2, "b+!"))
804 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
805 WINE_FIXME("Unknown modifier: !\n");
809 fprintf(stderr
, "Unknown option \"%S\" for UI level\n",
813 else if(!msi_strequal(argvW
[i
], "/y"))
815 FunctionDllRegisterServer
= TRUE
;
819 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
822 else if(!msi_strequal(argvW
[i
], "/z"))
824 FunctionDllUnregisterServer
= TRUE
;
828 WINE_TRACE("argvW[%d] = %S\n", i
, argvW
[i
]);
831 else if(!msi_strequal(argvW
[i
], "/h") || !msi_strequal(argvW
[i
], "/?"))
835 else if(!msi_strequal(argvW
[i
], "/m"))
837 FunctionUnknown
= TRUE
;
838 WINE_FIXME("Unknown parameter /m\n");
840 else if(!msi_strequal(argvW
[i
], "/D"))
842 FunctionUnknown
= TRUE
;
843 WINE_FIXME("Unknown parameter /D\n");
845 else if(strchrW(argvW
[i
], '='))
847 StringListAppend(&property_list
, argvW
[i
]);
851 FunctionInstall
= TRUE
;
852 PackageName
= argvW
[i
];
857 MsiSetInternalUI(InstallUILevel
, NULL
);
859 Properties
= build_properties( property_list
);
860 Transforms
= build_transforms( transform_list
);
862 if(FunctionInstallAdmin
&& FunctionPatch
)
863 FunctionInstall
= FALSE
;
868 if(IsProductCode(PackageName
))
869 ReturnCode
= MsiConfigureProductExW(PackageName
, 0, INSTALLSTATE_DEFAULT
, Properties
);
871 ReturnCode
= MsiInstallProductW(PackageName
, Properties
);
873 else if(FunctionRepair
)
875 if(IsProductCode(PackageName
))
876 WINE_FIXME("Product code treatment not implemented yet\n");
878 ReturnCode
= MsiReinstallProductW(PackageName
, RepairMode
);
880 else if(FunctionAdvertise
)
882 ReturnCode
= MsiAdvertiseProductW(PackageName
, (LPWSTR
) AdvertiseMode
, Transforms
, Language
);
884 else if(FunctionPatch
)
886 ReturnCode
= MsiApplyPatchW(PatchFileName
, PackageName
, InstallType
, Properties
);
888 else if(FunctionDllRegisterServer
)
890 ReturnCode
= DoDllRegisterServer(DllName
);
892 else if(FunctionDllUnregisterServer
)
894 ReturnCode
= DoDllUnregisterServer(DllName
);
896 else if (FunctionRegServer
)
898 WINE_FIXME( "/regserver not implemented yet, ignoring\n" );
900 else if (FunctionUnregServer
)
902 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
904 else if (FunctionUnknown
)
906 WINE_FIXME( "Unknown function, ignoring\n" );