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 INT
MSIEXEC_lstrncmpiA(LPCSTR str1
, LPCSTR str2
, INT size
)
117 if ((str1
== NULL
) && (str2
== NULL
)) return 0;
118 if (str1
== NULL
) return -1;
119 if (str2
== NULL
) return 1;
121 ret
= CompareStringA(GetThreadLocale(), NORM_IGNORECASE
, str1
, size
, str2
, -1);
127 static VOID
*LoadProc(LPCSTR DllName
, LPCSTR ProcName
, HMODULE
* DllHandle
)
131 *DllHandle
= LoadLibraryExA(DllName
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
134 fprintf(stderr
, "Unable to load dll %s\n", DllName
);
137 proc
= (VOID
*) GetProcAddress(*DllHandle
, ProcName
);
140 fprintf(stderr
, "Dll %s does not implement function %s\n", DllName
, ProcName
);
141 FreeLibrary(*DllHandle
);
148 static void DllRegisterServer(LPCSTR DllName
)
151 DLLREGISTERSERVER pfDllRegisterServer
= NULL
;
152 HMODULE DllHandle
= NULL
;
154 pfDllRegisterServer
= LoadProc(DllName
, "DllRegisterServer", &DllHandle
);
156 hr
= pfDllRegisterServer();
159 fprintf(stderr
, "Failed to register dll %s\n", DllName
);
162 printf("Successfully registered dll %s\n", DllName
);
164 FreeLibrary(DllHandle
);
167 static void DllUnregisterServer(LPCSTR DllName
)
170 DLLUNREGISTERSERVER pfDllUnregisterServer
= NULL
;
171 HMODULE DllHandle
= NULL
;
173 pfDllUnregisterServer
= LoadProc(DllName
, "DllUnregisterServer", &DllHandle
);
175 hr
= pfDllUnregisterServer();
178 fprintf(stderr
, "Failed to unregister dll %s\n", DllName
);
181 printf("Successfully unregistered dll %s\n", DllName
);
183 FreeLibrary(DllHandle
);
186 int main(int argc
, char *argv
[])
189 BOOL FunctionInstall
= FALSE
;
190 BOOL FunctionInstallAdmin
= FALSE
;
191 BOOL FunctionRepair
= FALSE
;
192 BOOL FunctionAdvertise
= FALSE
;
193 BOOL FunctionPatch
= FALSE
;
194 BOOL FunctionDllRegisterServer
= FALSE
;
195 BOOL FunctionDllUnregisterServer
= FALSE
;
196 BOOL FunctionRegServer
= FALSE
;
197 BOOL FunctionUnregServer
= FALSE
;
198 BOOL FunctionUnknown
= FALSE
;
200 BOOL GotProductCode
= FALSE
;
201 LPCSTR PackageName
= NULL
;
202 LPGUID ProductCode
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
203 LPSTR Properties
= HeapAlloc(GetProcessHeap(), 0, 1);
205 DWORD RepairMode
= 0;
207 DWORD AdvertiseMode
= 0;
208 LPSTR Transforms
= HeapAlloc(GetProcessHeap(), 0, 1);
212 LPSTR LogFileName
= NULL
;
213 DWORD LogAttributes
= 0;
215 LPSTR PatchFileName
= NULL
;
216 INSTALLTYPE InstallType
= INSTALLTYPE_DEFAULT
;
218 INSTALLUILEVEL InstallUILevel
= 0, retInstallUILevel
;
220 LPSTR DllName
= NULL
;
225 for(i
= 1; i
< argc
; i
++)
227 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
229 if (!lstrcmpiA(argv
[i
], "/regserver"))
231 FunctionRegServer
= TRUE
;
233 else if (!lstrcmpiA(argv
[i
], "/unregserver") || !lstrcmpiA(argv
[i
], "/unregister"))
235 FunctionUnregServer
= TRUE
;
237 else if(!MSIEXEC_lstrncmpiA(argv
[i
], "/i", 2))
239 char *argvi
= argv
[i
];
240 FunctionInstall
= TRUE
;
241 if(strlen(argvi
) > 2)
247 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
250 GotProductCode
= GetProductCode(argvi
, &PackageName
, &ProductCode
);
252 else if(!lstrcmpiA(argv
[i
], "/a"))
254 FunctionInstall
= TRUE
;
255 FunctionInstallAdmin
= TRUE
;
256 InstallType
= INSTALLTYPE_NETWORK_IMAGE
;
260 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
261 PackageName
= argv
[i
];
262 StringListAppend(&Properties
, ActionAdmin
);
264 else if(!MSIEXEC_lstrncmpiA(argv
[i
], "/f", 2))
267 int len
= strlen(argv
[i
]);
268 FunctionRepair
= TRUE
;
269 for(j
= 2; j
< len
; j
++)
275 RepairMode
|= REINSTALLMODE_FILEMISSING
;
279 RepairMode
|= REINSTALLMODE_FILEOLDERVERSION
;
283 RepairMode
|= REINSTALLMODE_FILEEQUALVERSION
;
287 RepairMode
|= REINSTALLMODE_FILEEXACT
;
291 RepairMode
|= REINSTALLMODE_FILEVERIFY
;
295 RepairMode
|= REINSTALLMODE_FILEREPLACE
;
299 RepairMode
|= REINSTALLMODE_USERDATA
;
303 RepairMode
|= REINSTALLMODE_MACHINEDATA
;
307 RepairMode
|= REINSTALLMODE_SHORTCUT
;
311 RepairMode
|= REINSTALLMODE_PACKAGE
;
314 fprintf(stderr
, "Unknown option \"%c\" in Repair mode\n", argv
[i
][j
]);
320 RepairMode
= REINSTALLMODE_FILEMISSING
|
321 REINSTALLMODE_FILEEQUALVERSION
|
322 REINSTALLMODE_FILEVERIFY
|
323 REINSTALLMODE_MACHINEDATA
|
324 REINSTALLMODE_SHORTCUT
;
329 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
330 GotProductCode
= GetProductCode(argv
[i
], &PackageName
, &ProductCode
);
332 else if(!lstrcmpiA(argv
[i
], "/x"))
334 FunctionInstall
= TRUE
;
338 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
339 GotProductCode
= GetProductCode(argv
[i
], &PackageName
, &ProductCode
);
340 StringListAppend(&Properties
, RemoveAll
);
342 else if(!MSIEXEC_lstrncmpiA(argv
[i
], "/j", 2))
345 int len
= strlen(argv
[i
]);
346 FunctionAdvertise
= TRUE
;
347 for(j
= 2; j
< len
; j
++)
353 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
357 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
360 fprintf(stderr
, "Unknown option \"%c\" in Advertise mode\n", argv
[i
][j
]);
367 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
368 PackageName
= argv
[i
];
370 else if(!lstrcmpiA(argv
[i
], "u"))
372 FunctionAdvertise
= TRUE
;
373 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
377 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
378 PackageName
= argv
[i
];
380 else if(!lstrcmpiA(argv
[i
], "m"))
382 FunctionAdvertise
= TRUE
;
383 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
387 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
388 PackageName
= argv
[i
];
390 else if(!lstrcmpiA(argv
[i
], "/t"))
395 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
396 StringListAppend(&Transforms
, argv
[i
]);
397 StringListAppend(&Transforms
, ";");
399 else if(!MSIEXEC_lstrncmpiA(argv
[i
], "TRANSFORMS=", 11))
401 StringListAppend(&Transforms
, argv
[i
]+11);
402 StringListAppend(&Transforms
, ";");
404 else if(!lstrcmpiA(argv
[i
], "/g"))
409 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
410 Language
= strtol(argv
[i
], NULL
, 0);
412 else if(!MSIEXEC_lstrncmpiA(argv
[i
], "/l", 2))
415 int len
= strlen(argv
[i
]);
416 for(j
= 2; j
< len
; j
++)
422 LogMode
|= INSTALLLOGMODE_INFO
;
426 LogMode
|= INSTALLLOGMODE_WARNING
;
430 LogMode
|= INSTALLLOGMODE_ERROR
;
434 LogMode
|= INSTALLLOGMODE_ACTIONSTART
;
438 LogMode
|= INSTALLLOGMODE_ACTIONDATA
;
442 LogMode
|= INSTALLLOGMODE_USER
;
446 LogMode
|= INSTALLLOGMODE_COMMONDATA
;
450 LogMode
|= INSTALLLOGMODE_FATALEXIT
;
454 LogMode
|= INSTALLLOGMODE_OUTOFDISKSPACE
;
458 LogMode
|= INSTALLLOGMODE_PROPERTYDUMP
;
462 LogMode
|= INSTALLLOGMODE_VERBOSE
;
465 LogMode
= INSTALLLOGMODE_FATALEXIT
|
466 INSTALLLOGMODE_ERROR
|
467 INSTALLLOGMODE_WARNING
|
468 INSTALLLOGMODE_USER
|
469 INSTALLLOGMODE_INFO
|
470 INSTALLLOGMODE_RESOLVESOURCE
|
471 INSTALLLOGMODE_OUTOFDISKSPACE
|
472 INSTALLLOGMODE_ACTIONSTART
|
473 INSTALLLOGMODE_ACTIONDATA
|
474 INSTALLLOGMODE_COMMONDATA
|
475 INSTALLLOGMODE_PROPERTYDUMP
|
476 INSTALLLOGMODE_PROGRESS
|
477 INSTALLLOGMODE_INITIALIZE
|
478 INSTALLLOGMODE_TERMINATE
|
479 INSTALLLOGMODE_SHOWDIALOG
;
482 LogAttributes
|= INSTALLLOGATTRIBUTES_APPEND
;
485 LogAttributes
|= INSTALLLOGATTRIBUTES_FLUSHEACHLINE
;
494 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
495 LogFileName
= argv
[i
];
496 if(MsiEnableLogA(LogMode
, LogFileName
, LogAttributes
) != ERROR_SUCCESS
)
498 fprintf(stderr
, "Logging in %s (0x%08lx, %lu) failed\n", LogFileName
, LogMode
, LogAttributes
);
502 else if(!lstrcmpiA(argv
[i
], "/p"))
504 FunctionPatch
= TRUE
;
508 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
509 PatchFileName
= argv
[i
];
511 else if(!MSIEXEC_lstrncmpiA(argv
[i
], "/q", 2))
513 if(strlen(argv
[i
]) == 2 || !lstrcmpiA(argv
[i
]+2, "n"))
515 InstallUILevel
= INSTALLUILEVEL_NONE
;
517 else if(!lstrcmpiA(argv
[i
]+2, "b"))
519 InstallUILevel
= INSTALLUILEVEL_BASIC
;
521 else if(!lstrcmpiA(argv
[i
]+2, "r"))
523 InstallUILevel
= INSTALLUILEVEL_REDUCED
;
525 else if(!lstrcmpiA(argv
[i
]+2, "f"))
527 InstallUILevel
= INSTALLUILEVEL_FULL
|INSTALLUILEVEL_ENDDIALOG
;
529 else if(!lstrcmpiA(argv
[i
]+2, "n+"))
531 InstallUILevel
= INSTALLUILEVEL_NONE
|INSTALLUILEVEL_ENDDIALOG
;
533 else if(!lstrcmpiA(argv
[i
]+2, "b+"))
535 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
537 else if(!lstrcmpiA(argv
[i
]+2, "b-"))
539 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_PROGRESSONLY
;
541 else if(!lstrcmpiA(argv
[i
]+2, "b+!"))
543 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
544 WINE_FIXME("Unknown modifier: !\n");
548 fprintf(stderr
, "Unknown option \"%s\" for UI level\n", argv
[i
]+2);
550 retInstallUILevel
= MsiSetInternalUI(InstallUILevel
, NULL
);
551 if(retInstallUILevel
== INSTALLUILEVEL_NOCHANGE
)
553 fprintf(stderr
, "Setting the UI level to 0x%x failed.\n", InstallUILevel
);
557 else if(!lstrcmpiA(argv
[i
], "/y"))
559 FunctionDllRegisterServer
= TRUE
;
563 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
566 else if(!lstrcmpiA(argv
[i
], "/z"))
568 FunctionDllUnregisterServer
= TRUE
;
572 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
575 else if(!lstrcmpiA(argv
[i
], "/h") || !lstrcmpiA(argv
[i
], "/?"))
579 else if(!lstrcmpiA(argv
[i
], "/m"))
581 FunctionUnknown
= TRUE
;
582 WINE_FIXME("Unknown parameter /m\n");
584 else if(!lstrcmpiA(argv
[i
], "/D"))
586 FunctionUnknown
= TRUE
;
587 WINE_FIXME("Unknown parameter /D\n");
589 else if(strchr(argv
[i
], '='))
591 StringListAppend(&Properties
, argv
[i
]);
592 StringListAppend(&Properties
, " ");
596 FunctionInstall
= TRUE
;
597 GotProductCode
= GetProductCode(argv
[i
], &PackageName
, &ProductCode
);
601 StringCompareRemoveLast(Properties
, ' ');
602 StringCompareRemoveLast(Transforms
, ';');
604 if(FunctionInstallAdmin
&& FunctionPatch
)
605 FunctionInstall
= FALSE
;
611 WINE_FIXME("Product code treatment not implemented yet\n");
616 if(MsiInstallProductA(PackageName
, Properties
) != ERROR_SUCCESS
)
618 fprintf(stderr
, "Installation of %s (%s) failed.\n", PackageName
, Properties
);
623 else if(FunctionRepair
)
627 WINE_FIXME("Product code treatment not implemented yet\n");
632 if(MsiReinstallProductA(PackageName
, RepairMode
) != ERROR_SUCCESS
)
634 fprintf(stderr
, "Repair of %s (0x%08lx) failed.\n", PackageName
, RepairMode
);
639 else if(FunctionAdvertise
)
641 if(MsiAdvertiseProductA(PackageName
, (LPSTR
) AdvertiseMode
, Transforms
, Language
) != ERROR_SUCCESS
)
643 fprintf(stderr
, "Advertising of %s (%lu, %s, 0x%04x) failed.\n", PackageName
, AdvertiseMode
, Transforms
, Language
);
647 else if(FunctionPatch
)
649 if(MsiApplyPatchA(PatchFileName
, PackageName
, InstallType
, Properties
) != ERROR_SUCCESS
)
651 fprintf(stderr
, "Patching with %s (%s, %d, %s)\n", PatchFileName
, PackageName
, InstallType
, Properties
);
655 else if(FunctionDllRegisterServer
)
657 DllRegisterServer(DllName
);
659 else if(FunctionDllUnregisterServer
)
661 DllUnregisterServer(DllName
);
663 else if (FunctionRegServer
)
665 WINE_FIXME( "/regserver not implemented yet, ignoring\n" );
667 else if (FunctionUnregServer
)
669 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
671 else if (FunctionUnknown
)
673 WINE_FIXME( "Unknown function, ignoring\n" );