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);
37 DWORD
DoService(void);
41 struct string_list
*next
;
45 static const WCHAR ActionAdmin
[] = {
46 'A','C','T','I','O','N','=','A','D','M','I','N',0 };
47 static const WCHAR RemoveAll
[] = {
48 'R','E','M','O','V','E','=','A','L','L',0 };
50 static const WCHAR InstallRunOnce
[] = {
51 'S','o','f','t','w','a','r','e','\\',
52 'M','i','c','r','o','s','o','f','t','\\',
53 'W','i','n','d','o','w','s','\\',
54 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
55 'I','n','s','t','a','l','l','e','r','\\',
56 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
58 static void ShowUsage(int ExitCode
)
60 WCHAR msiexec_version
[40];
61 WCHAR filename
[MAX_PATH
];
64 HMODULE hmsi
= GetModuleHandleA("msi.dll");
68 /* MsiGetFileVersion need the full path */
70 res
= GetModuleFileNameW(hmsi
, filename
, sizeof(filename
) / sizeof(filename
[0]));
72 WINE_ERR("GetModuleFileName failed: %d\n", GetLastError());
74 len
= sizeof(msiexec_version
) / sizeof(msiexec_version
[0]);
76 res
= MsiGetFileVersionW(filename
, msiexec_version
, &len
, NULL
, NULL
);
78 WINE_ERR("MsiGetFileVersion failed with %d\n", res
);
80 /* Return the length of the resource.
81 No typo: The LPWSTR parameter must be a LPWSTR * for this mode */
82 len
= LoadStringW(hmsi
, 10, (LPWSTR
) &msi_res
, 0);
84 msi_res
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
85 msiexec_help
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) + sizeof(msiexec_version
));
86 if (msi_res
&& msiexec_help
) {
88 LoadStringW(hmsi
, 10, msi_res
, len
+ 1);
90 sprintfW(msiexec_help
, msi_res
, msiexec_version
);
91 MsiMessageBoxW(0, msiexec_help
, NULL
, 0, GetUserDefaultLangID(), 0);
93 HeapFree(GetProcessHeap(), 0, msi_res
);
94 HeapFree(GetProcessHeap(), 0, msiexec_help
);
95 ExitProcess(ExitCode
);
98 static BOOL
IsProductCode(LPWSTR str
)
102 if(lstrlenW(str
) != 38)
104 return ( (CLSIDFromString(str
, &ProductCode
) == NOERROR
) );
108 static VOID
StringListAppend(struct string_list
**list
, LPCWSTR str
)
110 struct string_list
*entry
;
113 size
= sizeof *entry
+ lstrlenW(str
) * sizeof (WCHAR
);
114 entry
= HeapAlloc(GetProcessHeap(), 0, size
);
117 WINE_ERR("Out of memory!\n");
120 lstrcpyW(entry
->str
, str
);
124 * Ignoring o(n^2) time complexity to add n strings for simplicity,
125 * add the string to the end of the list to preserve the order.
128 list
= &(*list
)->next
;
132 static LPWSTR
build_properties(struct string_list
*property_list
)
134 struct string_list
*list
;
135 LPWSTR ret
, p
, value
;
142 /* count the space we need */
144 for(list
= property_list
; list
; list
= list
->next
)
145 len
+= lstrlenW(list
->str
) + 3;
147 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
149 /* add a space before each string, and quote the value */
151 for(list
= property_list
; list
; list
= list
->next
)
153 value
= strchrW(list
->str
,'=');
156 len
= value
- list
->str
;
158 memcpy(p
, list
->str
, len
* sizeof(WCHAR
));
162 /* check if the value contains spaces and maybe quote it */
164 needs_quote
= strchrW(value
,' ') ? 1 : 0;
167 len
= lstrlenW(value
);
168 memcpy(p
, value
, len
* sizeof(WCHAR
));
175 WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret
) );
180 static LPWSTR
build_transforms(struct string_list
*transform_list
)
182 struct string_list
*list
;
186 /* count the space we need */
188 for(list
= transform_list
; list
; list
= list
->next
)
189 len
+= lstrlenW(list
->str
) + 1;
191 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
193 /* add all the transforms with a semicolon between each one */
195 for(list
= transform_list
; list
; list
= list
->next
)
197 len
= lstrlenW(list
->str
);
198 lstrcpynW(p
, list
->str
, len
);
208 static DWORD
msi_atou(LPCWSTR str
)
211 while(*str
>= '0' && *str
<= '9')
220 static LPWSTR
msi_strdup(LPCWSTR str
)
222 DWORD len
= lstrlenW(str
)+1;
223 LPWSTR ret
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
228 /* str1 is the same as str2, ignoring case */
229 static BOOL
msi_strequal(LPCWSTR str1
, LPCSTR str2
)
234 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
237 if( lstrlenW(str1
) != (len
-1) )
239 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
240 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
241 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
, strW
, len
);
242 HeapFree(GetProcessHeap(), 0, strW
);
243 return (ret
== CSTR_EQUAL
);
246 /* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
247 static BOOL
msi_option_equal(LPCWSTR str1
, LPCSTR str2
)
249 if (str1
[0] != '/' && str1
[0] != '-')
252 /* skip over the hyphen or slash */
253 return msi_strequal(str1
+ 1, str2
);
256 /* str2 is at the beginning of str1, ignoring case */
257 static BOOL
msi_strprefix(LPCWSTR str1
, LPCSTR str2
)
262 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
265 if( lstrlenW(str1
) < (len
-1) )
267 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
268 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
269 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
-1, strW
, len
-1);
270 HeapFree(GetProcessHeap(), 0, strW
);
271 return (ret
== CSTR_EQUAL
);
274 /* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
275 static BOOL
msi_option_prefix(LPCWSTR str1
, LPCSTR str2
)
277 if (str1
[0] != '/' && str1
[0] != '-')
280 /* skip over the hyphen or slash */
281 return msi_strprefix(str1
+ 1, str2
);
284 static VOID
*LoadProc(LPCWSTR DllName
, LPCSTR ProcName
, HMODULE
* DllHandle
)
288 *DllHandle
= LoadLibraryExW(DllName
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
291 fprintf(stderr
, "Unable to load dll %s\n", wine_dbgstr_w(DllName
));
294 proc
= (VOID
*) GetProcAddress(*DllHandle
, ProcName
);
297 fprintf(stderr
, "Dll %s does not implement function %s\n",
298 wine_dbgstr_w(DllName
), ProcName
);
299 FreeLibrary(*DllHandle
);
306 static DWORD
DoDllRegisterServer(LPCWSTR DllName
)
309 DLLREGISTERSERVER pfDllRegisterServer
= NULL
;
310 HMODULE DllHandle
= NULL
;
312 pfDllRegisterServer
= LoadProc(DllName
, "DllRegisterServer", &DllHandle
);
314 hr
= pfDllRegisterServer();
317 fprintf(stderr
, "Failed to register dll %s\n", wine_dbgstr_w(DllName
));
320 printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName
));
322 FreeLibrary(DllHandle
);
326 static DWORD
DoDllUnregisterServer(LPCWSTR DllName
)
329 DLLUNREGISTERSERVER pfDllUnregisterServer
= NULL
;
330 HMODULE DllHandle
= NULL
;
332 pfDllUnregisterServer
= LoadProc(DllName
, "DllUnregisterServer", &DllHandle
);
334 hr
= pfDllUnregisterServer();
337 fprintf(stderr
, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName
));
340 printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName
));
342 FreeLibrary(DllHandle
);
346 static DWORD
DoRegServer(void)
348 SC_HANDLE scm
, service
;
349 CHAR path
[MAX_PATH
+12];
352 scm
= OpenSCManagerA(NULL
, SERVICES_ACTIVE_DATABASEA
, SC_MANAGER_CREATE_SERVICE
);
355 fprintf(stderr
, "Failed to open the service control manager.\n");
359 GetSystemDirectoryA(path
, MAX_PATH
);
360 lstrcatA(path
, "\\msiexec.exe /V");
362 service
= CreateServiceA(scm
, "MSIServer", "MSIServer", GENERIC_ALL
,
363 SERVICE_WIN32_SHARE_PROCESS
, SERVICE_DEMAND_START
,
364 SERVICE_ERROR_NORMAL
, path
, NULL
, NULL
,
367 if (service
) CloseServiceHandle(service
);
368 else if (GetLastError() != ERROR_SERVICE_EXISTS
)
370 fprintf(stderr
, "Failed to create MSI service\n");
373 CloseServiceHandle(scm
);
377 static INT
DoEmbedding( LPWSTR key
)
379 printf("Remote custom actions are not supported yet\n");
384 * state machine to break up the command line properly
394 static int chomp( WCHAR
*str
)
396 enum chomp_state state
= cs_token
;
398 int count
= 1, ignore
;
400 for( p
= str
, out
= str
; *p
; p
++ )
428 state
= cs_whitespace
;
456 static void process_args( WCHAR
*cmdline
, int *pargc
, WCHAR
***pargv
)
458 WCHAR
**argv
, *p
= msi_strdup(cmdline
);
462 argv
= HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR
*)*(n
+1));
466 p
+= lstrlenW(p
) + 1;
474 static BOOL
process_args_from_reg( LPWSTR ident
, int *pargc
, WCHAR
***pargv
)
477 HKEY hkey
= 0, hkeyArgs
= 0;
478 DWORD sz
= 0, type
= 0;
482 r
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, InstallRunOnce
, &hkey
);
483 if(r
!= ERROR_SUCCESS
)
485 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, 0, &sz
);
486 if(r
== ERROR_SUCCESS
&& type
== REG_SZ
)
488 buf
= HeapAlloc(GetProcessHeap(), 0, sz
);
489 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, (LPBYTE
)buf
, &sz
);
490 if( r
== ERROR_SUCCESS
)
492 process_args(buf
, pargc
, pargv
);
495 HeapFree(GetProcessHeap(), 0, buf
);
497 RegCloseKey(hkeyArgs
);
501 int WINAPI
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPSTR lpCmdLine
, int nCmdShow
)
504 BOOL FunctionInstall
= FALSE
;
505 BOOL FunctionInstallAdmin
= FALSE
;
506 BOOL FunctionRepair
= FALSE
;
507 BOOL FunctionAdvertise
= FALSE
;
508 BOOL FunctionPatch
= FALSE
;
509 BOOL FunctionDllRegisterServer
= FALSE
;
510 BOOL FunctionDllUnregisterServer
= FALSE
;
511 BOOL FunctionRegServer
= FALSE
;
512 BOOL FunctionUnregServer
= FALSE
;
513 BOOL FunctionServer
= FALSE
;
514 BOOL FunctionUnknown
= FALSE
;
516 LPWSTR PackageName
= NULL
;
517 LPWSTR Properties
= NULL
;
518 struct string_list
*property_list
= NULL
;
520 DWORD RepairMode
= 0;
522 DWORD_PTR AdvertiseMode
= 0;
523 struct string_list
*transform_list
= NULL
;
527 LPWSTR LogFileName
= NULL
;
528 DWORD LogAttributes
= 0;
530 LPWSTR PatchFileName
= NULL
;
531 INSTALLTYPE InstallType
= INSTALLTYPE_DEFAULT
;
533 INSTALLUILEVEL InstallUILevel
= INSTALLUILEVEL_FULL
;
535 LPWSTR DllName
= NULL
;
538 LPWSTR
*argvW
= NULL
;
540 /* parse the command line */
541 process_args( GetCommandLineW(), &argc
, &argvW
);
544 * If the args begin with /@ IDENT then we need to load the real
545 * command line out of the RunOnceEntries key in the registry.
546 * We do that before starting to process the real commandline,
547 * then overwrite the commandline again.
549 if(argc
>1 && msi_option_equal(argvW
[1], "@"))
551 if(!process_args_from_reg( argvW
[2], &argc
, &argvW
))
555 if (argc
== 3 && msi_option_equal(argvW
[1], "Embedding"))
556 return DoEmbedding( argvW
[2] );
558 for(i
= 1; i
< argc
; i
++)
560 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
562 if (msi_option_equal(argvW
[i
], "regserver"))
564 FunctionRegServer
= TRUE
;
566 else if (msi_option_equal(argvW
[i
], "unregserver") || msi_option_equal(argvW
[i
], "unregister")
567 || msi_option_equal(argvW
[i
], "unreg"))
569 FunctionUnregServer
= TRUE
;
571 else if(msi_option_prefix(argvW
[i
], "i") || msi_option_prefix(argvW
[i
], "package"))
573 LPWSTR argvWi
= argvW
[i
];
574 int argLen
= (msi_option_prefix(argvW
[i
], "i") ? 2 : 8);
575 FunctionInstall
= TRUE
;
576 if(lstrlenW(argvW
[i
]) > argLen
)
583 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
586 PackageName
= argvWi
;
588 else if(msi_option_equal(argvW
[i
], "a"))
590 FunctionInstall
= TRUE
;
591 FunctionInstallAdmin
= TRUE
;
592 InstallType
= INSTALLTYPE_NETWORK_IMAGE
;
596 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
597 PackageName
= argvW
[i
];
598 StringListAppend(&property_list
, ActionAdmin
);
600 else if(msi_option_prefix(argvW
[i
], "f"))
603 int len
= lstrlenW(argvW
[i
]);
604 FunctionRepair
= TRUE
;
605 for(j
= 2; j
< len
; j
++)
611 RepairMode
|= REINSTALLMODE_FILEMISSING
;
615 RepairMode
|= REINSTALLMODE_FILEOLDERVERSION
;
619 RepairMode
|= REINSTALLMODE_FILEEQUALVERSION
;
623 RepairMode
|= REINSTALLMODE_FILEEXACT
;
627 RepairMode
|= REINSTALLMODE_FILEVERIFY
;
631 RepairMode
|= REINSTALLMODE_FILEREPLACE
;
635 RepairMode
|= REINSTALLMODE_USERDATA
;
639 RepairMode
|= REINSTALLMODE_MACHINEDATA
;
643 RepairMode
|= REINSTALLMODE_SHORTCUT
;
647 RepairMode
|= REINSTALLMODE_PACKAGE
;
650 fprintf(stderr
, "Unknown option \"%c\" in Repair mode\n", argvW
[i
][j
]);
656 RepairMode
= REINSTALLMODE_FILEMISSING
|
657 REINSTALLMODE_FILEEQUALVERSION
|
658 REINSTALLMODE_FILEVERIFY
|
659 REINSTALLMODE_MACHINEDATA
|
660 REINSTALLMODE_SHORTCUT
;
665 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
666 PackageName
= argvW
[i
];
668 else if(msi_option_prefix(argvW
[i
], "x") || msi_option_equal(argvW
[i
], "uninstall"))
670 FunctionInstall
= TRUE
;
671 if(msi_option_prefix(argvW
[i
], "x")) PackageName
= argvW
[i
]+2;
672 if(!PackageName
|| !PackageName
[0])
677 PackageName
= argvW
[i
];
679 WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName
));
680 StringListAppend(&property_list
, RemoveAll
);
682 else if(msi_option_prefix(argvW
[i
], "j"))
685 int len
= lstrlenW(argvW
[i
]);
686 FunctionAdvertise
= TRUE
;
687 for(j
= 2; j
< len
; j
++)
693 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
697 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
700 fprintf(stderr
, "Unknown option \"%c\" in Advertise mode\n", argvW
[i
][j
]);
707 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
708 PackageName
= argvW
[i
];
710 else if(msi_strequal(argvW
[i
], "u"))
712 FunctionAdvertise
= TRUE
;
713 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
717 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
718 PackageName
= argvW
[i
];
720 else if(msi_strequal(argvW
[i
], "m"))
722 FunctionAdvertise
= TRUE
;
723 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
727 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
728 PackageName
= argvW
[i
];
730 else if(msi_option_equal(argvW
[i
], "t"))
735 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
736 StringListAppend(&transform_list
, argvW
[i
]);
738 else if(msi_option_equal(argvW
[i
], "g"))
743 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
744 Language
= msi_atou(argvW
[i
]);
746 else if(msi_option_prefix(argvW
[i
], "l"))
749 int len
= lstrlenW(argvW
[i
]);
750 for(j
= 2; j
< len
; j
++)
756 LogMode
|= INSTALLLOGMODE_INFO
;
760 LogMode
|= INSTALLLOGMODE_WARNING
;
764 LogMode
|= INSTALLLOGMODE_ERROR
;
768 LogMode
|= INSTALLLOGMODE_ACTIONSTART
;
772 LogMode
|= INSTALLLOGMODE_ACTIONDATA
;
776 LogMode
|= INSTALLLOGMODE_USER
;
780 LogMode
|= INSTALLLOGMODE_COMMONDATA
;
784 LogMode
|= INSTALLLOGMODE_FATALEXIT
;
788 LogMode
|= INSTALLLOGMODE_OUTOFDISKSPACE
;
792 LogMode
|= INSTALLLOGMODE_PROPERTYDUMP
;
796 LogMode
|= INSTALLLOGMODE_VERBOSE
;
799 LogMode
= INSTALLLOGMODE_FATALEXIT
|
800 INSTALLLOGMODE_ERROR
|
801 INSTALLLOGMODE_WARNING
|
802 INSTALLLOGMODE_USER
|
803 INSTALLLOGMODE_INFO
|
804 INSTALLLOGMODE_RESOLVESOURCE
|
805 INSTALLLOGMODE_OUTOFDISKSPACE
|
806 INSTALLLOGMODE_ACTIONSTART
|
807 INSTALLLOGMODE_ACTIONDATA
|
808 INSTALLLOGMODE_COMMONDATA
|
809 INSTALLLOGMODE_PROPERTYDUMP
|
810 INSTALLLOGMODE_PROGRESS
|
811 INSTALLLOGMODE_INITIALIZE
|
812 INSTALLLOGMODE_TERMINATE
|
813 INSTALLLOGMODE_SHOWDIALOG
;
816 LogAttributes
|= INSTALLLOGATTRIBUTES_APPEND
;
819 LogAttributes
|= INSTALLLOGATTRIBUTES_FLUSHEACHLINE
;
828 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
829 LogFileName
= argvW
[i
];
830 if(MsiEnableLogW(LogMode
, LogFileName
, LogAttributes
) != ERROR_SUCCESS
)
832 fprintf(stderr
, "Logging in %s (0x%08x, %u) failed\n",
833 wine_dbgstr_w(LogFileName
), LogMode
, LogAttributes
);
837 else if(msi_option_equal(argvW
[i
], "p"))
839 FunctionPatch
= TRUE
;
843 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
844 PatchFileName
= argvW
[i
];
846 else if(msi_option_prefix(argvW
[i
], "q"))
848 if(lstrlenW(argvW
[i
]) == 2 || msi_strequal(argvW
[i
]+2, "n") ||
849 msi_strequal(argvW
[i
] + 2, "uiet"))
851 InstallUILevel
= INSTALLUILEVEL_NONE
;
853 else if(msi_strequal(argvW
[i
]+2, "b"))
855 InstallUILevel
= INSTALLUILEVEL_BASIC
;
857 else if(msi_strequal(argvW
[i
]+2, "r"))
859 InstallUILevel
= INSTALLUILEVEL_REDUCED
;
861 else if(msi_strequal(argvW
[i
]+2, "f"))
863 InstallUILevel
= INSTALLUILEVEL_FULL
|INSTALLUILEVEL_ENDDIALOG
;
865 else if(msi_strequal(argvW
[i
]+2, "n+"))
867 InstallUILevel
= INSTALLUILEVEL_NONE
|INSTALLUILEVEL_ENDDIALOG
;
869 else if(msi_strequal(argvW
[i
]+2, "b+"))
871 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
873 else if(msi_strequal(argvW
[i
]+2, "b-"))
875 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_PROGRESSONLY
;
877 else if(msi_strequal(argvW
[i
]+2, "b+!"))
879 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
880 WINE_FIXME("Unknown modifier: !\n");
884 fprintf(stderr
, "Unknown option \"%s\" for UI level\n",
885 wine_dbgstr_w(argvW
[i
]+2));
888 else if(msi_option_equal(argvW
[i
], "y"))
890 FunctionDllRegisterServer
= TRUE
;
894 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
897 else if(msi_option_equal(argvW
[i
], "z"))
899 FunctionDllUnregisterServer
= TRUE
;
903 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
906 else if(msi_option_equal(argvW
[i
], "help") || msi_option_equal(argvW
[i
], "?"))
910 else if(msi_option_equal(argvW
[i
], "m"))
912 FunctionUnknown
= TRUE
;
913 WINE_FIXME("Unknown parameter /m\n");
915 else if(msi_option_equal(argvW
[i
], "D"))
917 FunctionUnknown
= TRUE
;
918 WINE_FIXME("Unknown parameter /D\n");
920 else if (msi_option_equal(argvW
[i
], "V"))
922 FunctionServer
= TRUE
;
925 StringListAppend(&property_list
, argvW
[i
]);
929 MsiSetInternalUI(InstallUILevel
, NULL
);
931 Properties
= build_properties( property_list
);
933 if(FunctionInstallAdmin
&& FunctionPatch
)
934 FunctionInstall
= FALSE
;
939 if(IsProductCode(PackageName
))
940 ReturnCode
= MsiConfigureProductExW(PackageName
, 0, INSTALLSTATE_DEFAULT
, Properties
);
942 ReturnCode
= MsiInstallProductW(PackageName
, Properties
);
944 else if(FunctionRepair
)
946 if(IsProductCode(PackageName
))
947 WINE_FIXME("Product code treatment not implemented yet\n");
949 ReturnCode
= MsiReinstallProductW(PackageName
, RepairMode
);
951 else if(FunctionAdvertise
)
953 LPWSTR Transforms
= build_transforms( property_list
);
954 ReturnCode
= MsiAdvertiseProductW(PackageName
, (LPWSTR
) AdvertiseMode
, Transforms
, Language
);
956 else if(FunctionPatch
)
958 ReturnCode
= MsiApplyPatchW(PatchFileName
, PackageName
, InstallType
, Properties
);
960 else if(FunctionDllRegisterServer
)
962 ReturnCode
= DoDllRegisterServer(DllName
);
964 else if(FunctionDllUnregisterServer
)
966 ReturnCode
= DoDllUnregisterServer(DllName
);
968 else if (FunctionRegServer
)
970 ReturnCode
= DoRegServer();
972 else if (FunctionUnregServer
)
974 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
976 else if (FunctionServer
)
978 ReturnCode
= DoService();
980 else if (FunctionUnknown
)
982 WINE_FIXME( "Unknown function, ignoring\n" );