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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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_PTR 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");
844 StringListAppend(&property_list
, argvW
[i
]);
848 MsiSetInternalUI(InstallUILevel
, NULL
);
850 Properties
= build_properties( property_list
);
852 if(FunctionInstallAdmin
&& FunctionPatch
)
853 FunctionInstall
= FALSE
;
858 if(IsProductCode(PackageName
))
859 ReturnCode
= MsiConfigureProductExW(PackageName
, 0, INSTALLSTATE_DEFAULT
, Properties
);
861 ReturnCode
= MsiInstallProductW(PackageName
, Properties
);
863 else if(FunctionRepair
)
865 if(IsProductCode(PackageName
))
866 WINE_FIXME("Product code treatment not implemented yet\n");
868 ReturnCode
= MsiReinstallProductW(PackageName
, RepairMode
);
870 else if(FunctionAdvertise
)
872 LPWSTR Transforms
= build_transforms( property_list
);
873 ReturnCode
= MsiAdvertiseProductW(PackageName
, (LPWSTR
) AdvertiseMode
, Transforms
, Language
);
875 else if(FunctionPatch
)
877 ReturnCode
= MsiApplyPatchW(PatchFileName
, PackageName
, InstallType
, Properties
);
879 else if(FunctionDllRegisterServer
)
881 ReturnCode
= DoDllRegisterServer(DllName
);
883 else if(FunctionDllUnregisterServer
)
885 ReturnCode
= DoDllUnregisterServer(DllName
);
887 else if (FunctionRegServer
)
889 WINE_FIXME( "/regserver not implemented yet, ignoring\n" );
891 else if (FunctionUnregServer
)
893 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
895 else if (FunctionUnknown
)
897 WINE_FIXME( "Unknown function, ignoring\n" );