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
;
182 BOOL FunctionRegServer
= FALSE
;
183 BOOL FunctionUnregServer
= FALSE
;
184 BOOL FunctionUnknown
= FALSE
;
186 BOOL GotProductCode
= FALSE
;
187 LPCSTR PackageName
= NULL
;
188 LPGUID ProductCode
= HeapAlloc(GetProcessHeap(), 0, sizeof(GUID
));
189 LPSTR Properties
= HeapAlloc(GetProcessHeap(), 0, 1);
191 DWORD RepairMode
= 0;
193 DWORD AdvertiseMode
= 0;
194 LPSTR Transforms
= HeapAlloc(GetProcessHeap(), 0, 1);
198 LPSTR LogFileName
= NULL
;
199 DWORD LogAttributes
= 0;
201 LPSTR PatchFileName
= NULL
;
202 INSTALLTYPE InstallType
= INSTALLTYPE_DEFAULT
;
204 INSTALLUILEVEL InstallUILevel
= 0, retInstallUILevel
;
206 LPSTR DllName
= NULL
;
211 for(i
= 1; i
< argc
; i
++)
213 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
215 if (!strcasecmp(argv
[i
], "/regserver"))
217 FunctionRegServer
= TRUE
;
219 else if (!strcasecmp(argv
[i
], "/unregserver") || !strcasecmp(argv
[i
], "/unregister"))
221 FunctionUnregServer
= TRUE
;
223 else if(!strncasecmp(argv
[i
], "/i", 2))
225 char *argvi
= argv
[i
];
226 FunctionInstall
= TRUE
;
227 if(strlen(argvi
) > 2)
233 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
236 GotProductCode
= GetProductCode(argvi
, &PackageName
, &ProductCode
);
238 else if(!strcasecmp(argv
[i
], "/a"))
240 FunctionInstall
= TRUE
;
241 FunctionInstallAdmin
= TRUE
;
242 InstallType
= INSTALLTYPE_NETWORK_IMAGE
;
246 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
247 PackageName
= argv
[i
];
248 StringListAppend(&Properties
, ActionAdmin
);
250 else if(!strncasecmp(argv
[i
], "/f", 2))
253 int len
= strlen(argv
[i
]);
254 FunctionRepair
= TRUE
;
255 for(j
= 2; j
< len
; j
++)
261 RepairMode
|= REINSTALLMODE_FILEMISSING
;
265 RepairMode
|= REINSTALLMODE_FILEOLDERVERSION
;
269 RepairMode
|= REINSTALLMODE_FILEEQUALVERSION
;
273 RepairMode
|= REINSTALLMODE_FILEEXACT
;
277 RepairMode
|= REINSTALLMODE_FILEVERIFY
;
281 RepairMode
|= REINSTALLMODE_FILEREPLACE
;
285 RepairMode
|= REINSTALLMODE_USERDATA
;
289 RepairMode
|= REINSTALLMODE_MACHINEDATA
;
293 RepairMode
|= REINSTALLMODE_SHORTCUT
;
297 RepairMode
|= REINSTALLMODE_PACKAGE
;
300 fprintf(stderr
, "Unknown option \"%c\" in Repair mode\n", argv
[i
][j
]);
306 RepairMode
= REINSTALLMODE_FILEMISSING
|
307 REINSTALLMODE_FILEEQUALVERSION
|
308 REINSTALLMODE_FILEVERIFY
|
309 REINSTALLMODE_MACHINEDATA
|
310 REINSTALLMODE_SHORTCUT
;
315 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
316 GotProductCode
= GetProductCode(argv
[i
], &PackageName
, &ProductCode
);
318 else if(!strcasecmp(argv
[i
], "/x"))
320 FunctionInstall
= TRUE
;
324 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
325 GotProductCode
= GetProductCode(argv
[i
], &PackageName
, &ProductCode
);
326 StringListAppend(&Properties
, RemoveAll
);
328 else if(!strncasecmp(argv
[i
], "/j", 2))
331 int len
= strlen(argv
[i
]);
332 FunctionAdvertise
= TRUE
;
333 for(j
= 2; j
< len
; j
++)
339 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
343 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
346 fprintf(stderr
, "Unknown option \"%c\" in Advertise mode\n", argv
[i
][j
]);
353 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
354 PackageName
= argv
[i
];
356 else if(!strcasecmp(argv
[i
], "u"))
358 FunctionAdvertise
= TRUE
;
359 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
363 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
364 PackageName
= argv
[i
];
366 else if(!strcasecmp(argv
[i
], "m"))
368 FunctionAdvertise
= TRUE
;
369 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
373 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
374 PackageName
= argv
[i
];
376 else if(!strcasecmp(argv
[i
], "/t"))
381 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
382 StringListAppend(&Transforms
, argv
[i
]);
383 StringListAppend(&Transforms
, ";");
385 else if(!strncasecmp(argv
[i
], "TRANSFORMS=", 11))
387 StringListAppend(&Transforms
, argv
[i
]+11);
388 StringListAppend(&Transforms
, ";");
390 else if(!strcasecmp(argv
[i
], "/g"))
395 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
396 Language
= strtol(argv
[i
], NULL
, 0);
398 else if(!strncasecmp(argv
[i
], "/l", 2))
401 int len
= strlen(argv
[i
]);
402 for(j
= 2; j
< len
; j
++)
408 LogMode
|= INSTALLLOGMODE_INFO
;
412 LogMode
|= INSTALLLOGMODE_WARNING
;
416 LogMode
|= INSTALLLOGMODE_ERROR
;
420 LogMode
|= INSTALLLOGMODE_ACTIONSTART
;
424 LogMode
|= INSTALLLOGMODE_ACTIONDATA
;
428 LogMode
|= INSTALLLOGMODE_USER
;
432 LogMode
|= INSTALLLOGMODE_COMMONDATA
;
436 LogMode
|= INSTALLLOGMODE_FATALEXIT
;
440 LogMode
|= INSTALLLOGMODE_OUTOFDISKSPACE
;
444 LogMode
|= INSTALLLOGMODE_PROPERTYDUMP
;
448 LogMode
|= INSTALLLOGMODE_VERBOSE
;
451 LogMode
= INSTALLLOGMODE_FATALEXIT
|
452 INSTALLLOGMODE_ERROR
|
453 INSTALLLOGMODE_WARNING
|
454 INSTALLLOGMODE_USER
|
455 INSTALLLOGMODE_INFO
|
456 INSTALLLOGMODE_RESOLVESOURCE
|
457 INSTALLLOGMODE_OUTOFDISKSPACE
|
458 INSTALLLOGMODE_ACTIONSTART
|
459 INSTALLLOGMODE_ACTIONDATA
|
460 INSTALLLOGMODE_COMMONDATA
|
461 INSTALLLOGMODE_PROPERTYDUMP
|
462 INSTALLLOGMODE_PROGRESS
|
463 INSTALLLOGMODE_INITIALIZE
|
464 INSTALLLOGMODE_TERMINATE
|
465 INSTALLLOGMODE_SHOWDIALOG
;
468 LogAttributes
|= INSTALLLOGATTRIBUTES_APPEND
;
471 LogAttributes
|= INSTALLLOGATTRIBUTES_FLUSHEACHLINE
;
480 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
481 LogFileName
= argv
[i
];
482 if(MsiEnableLogA(LogMode
, LogFileName
, LogAttributes
) != ERROR_SUCCESS
)
484 fprintf(stderr
, "Logging in %s (0x%08lx, %lu) failed\n", LogFileName
, LogMode
, LogAttributes
);
488 else if(!strcasecmp(argv
[i
], "/p"))
490 FunctionPatch
= TRUE
;
494 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
495 PatchFileName
= argv
[i
];
497 else if(!strncasecmp(argv
[i
], "/q", 2))
499 if(strlen(argv
[i
]) == 2 || !strcasecmp(argv
[i
]+2, "n"))
501 InstallUILevel
= INSTALLUILEVEL_NONE
;
503 else if(!strcasecmp(argv
[i
]+2, "b"))
505 InstallUILevel
= INSTALLUILEVEL_BASIC
;
507 else if(!strcasecmp(argv
[i
]+2, "r"))
509 InstallUILevel
= INSTALLUILEVEL_REDUCED
;
511 else if(!strcasecmp(argv
[i
]+2, "f"))
513 InstallUILevel
= INSTALLUILEVEL_FULL
|INSTALLUILEVEL_ENDDIALOG
;
515 else if(!strcasecmp(argv
[i
]+2, "n+"))
517 InstallUILevel
= INSTALLUILEVEL_NONE
|INSTALLUILEVEL_ENDDIALOG
;
519 else if(!strcasecmp(argv
[i
]+2, "b+"))
521 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
523 else if(!strcasecmp(argv
[i
]+2, "b-"))
525 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_PROGRESSONLY
;
527 else if(!strcasecmp(argv
[i
]+2, "b+!"))
529 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
530 WINE_FIXME("Unknown modifier: !\n");
534 fprintf(stderr
, "Unknown option \"%s\" for UI level\n", argv
[i
]+2);
536 retInstallUILevel
= MsiSetInternalUI(InstallUILevel
, NULL
);
537 if(retInstallUILevel
== INSTALLUILEVEL_NOCHANGE
)
539 fprintf(stderr
, "Setting the UI level to 0x%x failed.\n", InstallUILevel
);
543 else if(!strcasecmp(argv
[i
], "/y"))
545 FunctionDllRegisterServer
= TRUE
;
549 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
552 else if(!strcasecmp(argv
[i
], "/z"))
554 FunctionDllUnregisterServer
= TRUE
;
558 WINE_TRACE("argv[%d] = %s\n", i
, argv
[i
]);
561 else if(!strcasecmp(argv
[i
], "/h") || !strcasecmp(argv
[i
], "/?"))
565 else if(!strcasecmp(argv
[i
], "/m"))
567 FunctionUnknown
= TRUE
;
568 WINE_FIXME("Unknown parameter /m\n");
570 else if(!strcasecmp(argv
[i
], "/D"))
572 FunctionUnknown
= TRUE
;
573 WINE_FIXME("Unknown parameter /D\n");
575 else if(strchr(argv
[i
], '='))
577 StringListAppend(&Properties
, argv
[i
]);
578 StringListAppend(&Properties
, " ");
582 FunctionInstall
= TRUE
;
583 GotProductCode
= GetProductCode(argv
[i
], &PackageName
, &ProductCode
);
587 StringCompareRemoveLast(Properties
, ' ');
588 StringCompareRemoveLast(Transforms
, ';');
590 if(FunctionInstallAdmin
&& FunctionPatch
)
591 FunctionInstall
= FALSE
;
597 WINE_FIXME("Product code treatment not implemented yet\n");
602 if(MsiInstallProductA(PackageName
, Properties
) != ERROR_SUCCESS
)
604 fprintf(stderr
, "Installation of %s (%s) failed.\n", PackageName
, Properties
);
609 else if(FunctionRepair
)
613 WINE_FIXME("Product code treatment not implemented yet\n");
618 if(MsiReinstallProductA(PackageName
, RepairMode
) != ERROR_SUCCESS
)
620 fprintf(stderr
, "Repair of %s (0x%08lx) failed.\n", PackageName
, RepairMode
);
625 else if(FunctionAdvertise
)
627 if(MsiAdvertiseProductA(PackageName
, (LPSTR
) AdvertiseMode
, Transforms
, Language
) != ERROR_SUCCESS
)
629 fprintf(stderr
, "Advertising of %s (%lu, %s, 0x%04x) failed.\n", PackageName
, AdvertiseMode
, Transforms
, Language
);
633 else if(FunctionPatch
)
635 if(MsiApplyPatchA(PatchFileName
, PackageName
, InstallType
, Properties
) != ERROR_SUCCESS
)
637 fprintf(stderr
, "Patching with %s (%s, %d, %s)\n", PatchFileName
, PackageName
, InstallType
, Properties
);
641 else if(FunctionDllRegisterServer
)
643 DllRegisterServer(DllName
);
645 else if(FunctionDllUnregisterServer
)
647 DllUnregisterServer(DllName
);
649 else if (FunctionRegServer
)
651 WINE_FIXME( "/regserver not implemented yet, ignoring\n" );
653 else if (FunctionUnregServer
)
655 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
657 else if (FunctionUnknown
)
659 WINE_FIXME( "Unknown function, ignoring\n" );