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
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(msiexec
);
35 typedef HRESULT (WINAPI
*DLLREGISTERSERVER
)(void);
36 typedef HRESULT (WINAPI
*DLLUNREGISTERSERVER
)(void);
38 DWORD
DoService(void);
42 struct string_list
*next
;
46 static const WCHAR ActionAdmin
[] = {
47 'A','C','T','I','O','N','=','A','D','M','I','N',0 };
48 static const WCHAR RemoveAll
[] = {
49 'R','E','M','O','V','E','=','A','L','L',0 };
51 static const WCHAR InstallRunOnce
[] = {
52 'S','o','f','t','w','a','r','e','\\',
53 'M','i','c','r','o','s','o','f','t','\\',
54 'W','i','n','d','o','w','s','\\',
55 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
56 'I','n','s','t','a','l','l','e','r','\\',
57 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
59 static void ShowUsage(int ExitCode
)
61 WCHAR msiexec_version
[40];
62 WCHAR filename
[MAX_PATH
];
65 HMODULE hmsi
= GetModuleHandleA("msi.dll");
69 /* MsiGetFileVersion need the full path */
71 res
= GetModuleFileNameW(hmsi
, filename
, sizeof(filename
) / sizeof(filename
[0]));
73 WINE_ERR("GetModuleFileName failed: %d\n", GetLastError());
75 len
= sizeof(msiexec_version
) / sizeof(msiexec_version
[0]);
77 res
= MsiGetFileVersionW(filename
, msiexec_version
, &len
, NULL
, NULL
);
79 WINE_ERR("MsiGetFileVersion failed with %d\n", res
);
81 /* Return the length of the resource.
82 No typo: The LPWSTR parameter must be a LPWSTR * for this mode */
83 len
= LoadStringW(hmsi
, 10, (LPWSTR
) &msi_res
, 0);
85 msi_res
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
86 msiexec_help
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) + sizeof(msiexec_version
));
87 if (msi_res
&& msiexec_help
) {
89 LoadStringW(hmsi
, 10, msi_res
, len
+ 1);
91 sprintfW(msiexec_help
, msi_res
, msiexec_version
);
92 MsiMessageBoxW(0, msiexec_help
, NULL
, 0, GetUserDefaultLangID(), 0);
94 HeapFree(GetProcessHeap(), 0, msi_res
);
95 HeapFree(GetProcessHeap(), 0, msiexec_help
);
96 ExitProcess(ExitCode
);
99 static BOOL
IsProductCode(LPWSTR str
)
103 if(lstrlenW(str
) != 38)
105 return ( (CLSIDFromString(str
, &ProductCode
) == NOERROR
) );
109 static VOID
StringListAppend(struct string_list
**list
, LPCWSTR str
)
111 struct string_list
*entry
;
113 entry
= HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(struct string_list
, str
[lstrlenW(str
) + 1]));
116 WINE_ERR("Out of memory!\n");
119 lstrcpyW(entry
->str
, str
);
123 * Ignoring o(n^2) time complexity to add n strings for simplicity,
124 * add the string to the end of the list to preserve the order.
127 list
= &(*list
)->next
;
131 static LPWSTR
build_properties(struct string_list
*property_list
)
133 struct string_list
*list
;
134 LPWSTR ret
, p
, value
;
141 /* count the space we need */
143 for(list
= property_list
; list
; list
= list
->next
)
144 len
+= lstrlenW(list
->str
) + 3;
146 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
148 /* add a space before each string, and quote the value */
150 for(list
= property_list
; list
; list
= list
->next
)
152 value
= strchrW(list
->str
,'=');
155 len
= value
- list
->str
;
157 memcpy(p
, list
->str
, len
* sizeof(WCHAR
));
161 /* check if the value contains spaces and maybe quote it */
163 needs_quote
= strchrW(value
,' ') ? 1 : 0;
166 len
= lstrlenW(value
);
167 memcpy(p
, value
, len
* sizeof(WCHAR
));
174 WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret
) );
179 static LPWSTR
build_transforms(struct string_list
*transform_list
)
181 struct string_list
*list
;
185 /* count the space we need */
187 for(list
= transform_list
; list
; list
= list
->next
)
188 len
+= lstrlenW(list
->str
) + 1;
190 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
192 /* add all the transforms with a semicolon between each one */
194 for(list
= transform_list
; list
; list
= list
->next
)
196 len
= lstrlenW(list
->str
);
197 lstrcpynW(p
, list
->str
, len
);
207 static DWORD
msi_atou(LPCWSTR str
)
210 while(*str
>= '0' && *str
<= '9')
219 /* str1 is the same as str2, ignoring case */
220 static BOOL
msi_strequal(LPCWSTR str1
, LPCSTR str2
)
225 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
228 if( lstrlenW(str1
) != (len
-1) )
230 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
231 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
232 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
, strW
, len
);
233 HeapFree(GetProcessHeap(), 0, strW
);
234 return (ret
== CSTR_EQUAL
);
237 /* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
238 static BOOL
msi_option_equal(LPCWSTR str1
, LPCSTR str2
)
240 if (str1
[0] != '/' && str1
[0] != '-')
243 /* skip over the hyphen or slash */
244 return msi_strequal(str1
+ 1, str2
);
247 /* str2 is at the beginning of str1, ignoring case */
248 static BOOL
msi_strprefix(LPCWSTR str1
, LPCSTR str2
)
253 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
256 if( lstrlenW(str1
) < (len
-1) )
258 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
259 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
260 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
-1, strW
, len
-1);
261 HeapFree(GetProcessHeap(), 0, strW
);
262 return (ret
== CSTR_EQUAL
);
265 /* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
266 static BOOL
msi_option_prefix(LPCWSTR str1
, LPCSTR str2
)
268 if (str1
[0] != '/' && str1
[0] != '-')
271 /* skip over the hyphen or slash */
272 return msi_strprefix(str1
+ 1, str2
);
275 static VOID
*LoadProc(LPCWSTR DllName
, LPCSTR ProcName
, HMODULE
* DllHandle
)
279 *DllHandle
= LoadLibraryExW(DllName
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
282 fprintf(stderr
, "Unable to load dll %s\n", wine_dbgstr_w(DllName
));
285 proc
= (VOID
*) GetProcAddress(*DllHandle
, ProcName
);
288 fprintf(stderr
, "Dll %s does not implement function %s\n",
289 wine_dbgstr_w(DllName
), ProcName
);
290 FreeLibrary(*DllHandle
);
297 static DWORD
DoDllRegisterServer(LPCWSTR DllName
)
300 DLLREGISTERSERVER pfDllRegisterServer
= NULL
;
301 HMODULE DllHandle
= NULL
;
303 pfDllRegisterServer
= LoadProc(DllName
, "DllRegisterServer", &DllHandle
);
305 hr
= pfDllRegisterServer();
308 fprintf(stderr
, "Failed to register dll %s\n", wine_dbgstr_w(DllName
));
311 printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName
));
313 FreeLibrary(DllHandle
);
317 static DWORD
DoDllUnregisterServer(LPCWSTR DllName
)
320 DLLUNREGISTERSERVER pfDllUnregisterServer
= NULL
;
321 HMODULE DllHandle
= NULL
;
323 pfDllUnregisterServer
= LoadProc(DllName
, "DllUnregisterServer", &DllHandle
);
325 hr
= pfDllUnregisterServer();
328 fprintf(stderr
, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName
));
331 printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName
));
333 FreeLibrary(DllHandle
);
337 static DWORD
DoRegServer(void)
339 static const WCHAR msiserverW
[] = {'M','S','I','S','e','r','v','e','r',0};
340 static const WCHAR msiexecW
[] = {'\\','m','s','i','e','x','e','c',' ','/','V',0};
341 SC_HANDLE scm
, service
;
342 WCHAR path
[MAX_PATH
+12];
345 if (!(scm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, SC_MANAGER_CREATE_SERVICE
)))
347 fprintf(stderr
, "Failed to open the service control manager.\n");
350 len
= GetSystemDirectoryW(path
, MAX_PATH
);
351 lstrcpyW(path
+ len
, msiexecW
);
352 if ((service
= CreateServiceW(scm
, msiserverW
, msiserverW
, GENERIC_ALL
,
353 SERVICE_WIN32_SHARE_PROCESS
, SERVICE_DEMAND_START
,
354 SERVICE_ERROR_NORMAL
, path
, NULL
, NULL
, NULL
, NULL
, NULL
)))
356 CloseServiceHandle(service
);
358 else if (GetLastError() != ERROR_SERVICE_EXISTS
)
360 fprintf(stderr
, "Failed to create MSI service\n");
363 CloseServiceHandle(scm
);
367 static DWORD
DoUnregServer(void)
369 static const WCHAR msiserverW
[] = {'M','S','I','S','e','r','v','e','r',0};
370 SC_HANDLE scm
, service
;
373 if (!(scm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, SC_MANAGER_CONNECT
)))
375 fprintf(stderr
, "Failed to open service control manager\n");
378 if ((service
= OpenServiceW(scm
, msiserverW
, DELETE
)))
380 if (!DeleteService(service
))
382 fprintf(stderr
, "Failed to delete MSI service\n");
385 CloseServiceHandle(service
);
387 else if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST
)
389 fprintf(stderr
, "Failed to open MSI service\n");
392 CloseServiceHandle(scm
);
396 static INT
DoEmbedding( LPWSTR key
)
398 printf("Remote custom actions are not supported yet\n");
403 * state machine to break up the command line properly
413 static int chomp( const WCHAR
*in
, WCHAR
*out
)
415 enum chomp_state state
= CS_TOKEN
;
420 for (p
= in
; *p
; p
++)
448 state
= CS_WHITESPACE
;
452 if (p
> in
&& p
[-1] == '"')
472 if (!ignore
&& out
) *out
++ = *p
;
478 static void process_args( WCHAR
*cmdline
, int *pargc
, WCHAR
***pargv
)
486 count
= chomp( cmdline
, NULL
);
487 if (!(p
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW(cmdline
) + count
+ 1) * sizeof(WCHAR
) )))
490 count
= chomp( cmdline
, p
);
491 if (!(argv
= HeapAlloc( GetProcessHeap(), 0, (count
+ 1) * sizeof(WCHAR
*) )))
493 HeapFree( GetProcessHeap(), 0, p
);
496 for (i
= 0; i
< count
; i
++)
499 p
+= lstrlenW( p
) + 1;
507 static BOOL
process_args_from_reg( const WCHAR
*ident
, int *pargc
, WCHAR
***pargv
)
511 DWORD sz
= 0, type
= 0;
515 r
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, InstallRunOnce
, &hkey
);
516 if(r
!= ERROR_SUCCESS
)
518 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, 0, &sz
);
519 if(r
== ERROR_SUCCESS
&& type
== REG_SZ
)
521 int len
= lstrlenW( *pargv
[0] );
522 if (!(buf
= HeapAlloc( GetProcessHeap(), 0, sz
+ (len
+ 1) * sizeof(WCHAR
) )))
527 memcpy( buf
, *pargv
[0], len
* sizeof(WCHAR
) );
529 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, (LPBYTE
)(buf
+ len
), &sz
);
530 if( r
== ERROR_SUCCESS
)
532 process_args(buf
, pargc
, pargv
);
535 HeapFree(GetProcessHeap(), 0, buf
);
541 int WINAPI
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPSTR lpCmdLine
, int nCmdShow
)
544 BOOL FunctionInstall
= FALSE
;
545 BOOL FunctionInstallAdmin
= FALSE
;
546 BOOL FunctionRepair
= FALSE
;
547 BOOL FunctionAdvertise
= FALSE
;
548 BOOL FunctionPatch
= FALSE
;
549 BOOL FunctionDllRegisterServer
= FALSE
;
550 BOOL FunctionDllUnregisterServer
= FALSE
;
551 BOOL FunctionRegServer
= FALSE
;
552 BOOL FunctionUnregServer
= FALSE
;
553 BOOL FunctionServer
= FALSE
;
554 BOOL FunctionUnknown
= FALSE
;
556 LPWSTR PackageName
= NULL
;
557 LPWSTR Properties
= NULL
;
558 struct string_list
*property_list
= NULL
;
560 DWORD RepairMode
= 0;
562 DWORD_PTR AdvertiseMode
= 0;
563 struct string_list
*transform_list
= NULL
;
567 LPWSTR LogFileName
= NULL
;
568 DWORD LogAttributes
= 0;
570 LPWSTR PatchFileName
= NULL
;
571 INSTALLTYPE InstallType
= INSTALLTYPE_DEFAULT
;
573 INSTALLUILEVEL InstallUILevel
= INSTALLUILEVEL_FULL
;
575 LPWSTR DllName
= NULL
;
578 LPWSTR
*argvW
= NULL
;
580 /* parse the command line */
581 process_args( GetCommandLineW(), &argc
, &argvW
);
584 * If the args begin with /@ IDENT then we need to load the real
585 * command line out of the RunOnceEntries key in the registry.
586 * We do that before starting to process the real commandline,
587 * then overwrite the commandline again.
589 if(argc
>1 && msi_option_equal(argvW
[1], "@"))
591 if(!process_args_from_reg( argvW
[2], &argc
, &argvW
))
595 if (argc
== 3 && msi_option_equal(argvW
[1], "Embedding"))
596 return DoEmbedding( argvW
[2] );
598 for(i
= 1; i
< argc
; i
++)
600 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
602 if (msi_option_equal(argvW
[i
], "regserver"))
604 FunctionRegServer
= TRUE
;
606 else if (msi_option_equal(argvW
[i
], "unregserver") || msi_option_equal(argvW
[i
], "unregister")
607 || msi_option_equal(argvW
[i
], "unreg"))
609 FunctionUnregServer
= TRUE
;
611 else if(msi_option_prefix(argvW
[i
], "i") || msi_option_prefix(argvW
[i
], "package"))
613 LPWSTR argvWi
= argvW
[i
];
614 int argLen
= (msi_option_prefix(argvW
[i
], "i") ? 2 : 8);
615 FunctionInstall
= TRUE
;
616 if(lstrlenW(argvW
[i
]) > argLen
)
623 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
626 PackageName
= argvWi
;
628 else if(msi_option_equal(argvW
[i
], "a"))
630 FunctionInstall
= TRUE
;
631 FunctionInstallAdmin
= TRUE
;
632 InstallType
= INSTALLTYPE_NETWORK_IMAGE
;
636 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
637 PackageName
= argvW
[i
];
638 StringListAppend(&property_list
, ActionAdmin
);
639 WINE_FIXME("Administrative installs are not currently supported\n");
641 else if(msi_option_prefix(argvW
[i
], "f"))
644 int len
= lstrlenW(argvW
[i
]);
645 FunctionRepair
= TRUE
;
646 for(j
= 2; j
< len
; j
++)
652 RepairMode
|= REINSTALLMODE_FILEMISSING
;
656 RepairMode
|= REINSTALLMODE_FILEOLDERVERSION
;
660 RepairMode
|= REINSTALLMODE_FILEEQUALVERSION
;
664 RepairMode
|= REINSTALLMODE_FILEEXACT
;
668 RepairMode
|= REINSTALLMODE_FILEVERIFY
;
672 RepairMode
|= REINSTALLMODE_FILEREPLACE
;
676 RepairMode
|= REINSTALLMODE_USERDATA
;
680 RepairMode
|= REINSTALLMODE_MACHINEDATA
;
684 RepairMode
|= REINSTALLMODE_SHORTCUT
;
688 RepairMode
|= REINSTALLMODE_PACKAGE
;
691 fprintf(stderr
, "Unknown option \"%c\" in Repair mode\n", argvW
[i
][j
]);
697 RepairMode
= REINSTALLMODE_FILEMISSING
|
698 REINSTALLMODE_FILEEQUALVERSION
|
699 REINSTALLMODE_FILEVERIFY
|
700 REINSTALLMODE_MACHINEDATA
|
701 REINSTALLMODE_SHORTCUT
;
706 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
707 PackageName
= argvW
[i
];
709 else if(msi_option_prefix(argvW
[i
], "x") || msi_option_equal(argvW
[i
], "uninstall"))
711 FunctionInstall
= TRUE
;
712 if(msi_option_prefix(argvW
[i
], "x")) PackageName
= argvW
[i
]+2;
713 if(!PackageName
|| !PackageName
[0])
718 PackageName
= argvW
[i
];
720 WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName
));
721 StringListAppend(&property_list
, RemoveAll
);
723 else if(msi_option_prefix(argvW
[i
], "j"))
726 int len
= lstrlenW(argvW
[i
]);
727 FunctionAdvertise
= TRUE
;
728 for(j
= 2; j
< len
; j
++)
734 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
738 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
741 fprintf(stderr
, "Unknown option \"%c\" in Advertise mode\n", argvW
[i
][j
]);
748 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
749 PackageName
= argvW
[i
];
751 else if(msi_strequal(argvW
[i
], "u"))
753 FunctionAdvertise
= TRUE
;
754 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
758 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
759 PackageName
= argvW
[i
];
761 else if(msi_strequal(argvW
[i
], "m"))
763 FunctionAdvertise
= TRUE
;
764 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
768 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
769 PackageName
= argvW
[i
];
771 else if(msi_option_equal(argvW
[i
], "t"))
776 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
777 StringListAppend(&transform_list
, argvW
[i
]);
779 else if(msi_option_equal(argvW
[i
], "g"))
784 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
785 Language
= msi_atou(argvW
[i
]);
787 else if(msi_option_prefix(argvW
[i
], "l"))
790 int len
= lstrlenW(argvW
[i
]);
791 for(j
= 2; j
< len
; j
++)
797 LogMode
|= INSTALLLOGMODE_INFO
;
801 LogMode
|= INSTALLLOGMODE_WARNING
;
805 LogMode
|= INSTALLLOGMODE_ERROR
;
809 LogMode
|= INSTALLLOGMODE_ACTIONSTART
;
813 LogMode
|= INSTALLLOGMODE_ACTIONDATA
;
817 LogMode
|= INSTALLLOGMODE_USER
;
821 LogMode
|= INSTALLLOGMODE_COMMONDATA
;
825 LogMode
|= INSTALLLOGMODE_FATALEXIT
;
829 LogMode
|= INSTALLLOGMODE_OUTOFDISKSPACE
;
833 LogMode
|= INSTALLLOGMODE_PROPERTYDUMP
;
837 LogMode
|= INSTALLLOGMODE_VERBOSE
;
840 LogMode
= INSTALLLOGMODE_FATALEXIT
|
841 INSTALLLOGMODE_ERROR
|
842 INSTALLLOGMODE_WARNING
|
843 INSTALLLOGMODE_USER
|
844 INSTALLLOGMODE_INFO
|
845 INSTALLLOGMODE_RESOLVESOURCE
|
846 INSTALLLOGMODE_OUTOFDISKSPACE
|
847 INSTALLLOGMODE_ACTIONSTART
|
848 INSTALLLOGMODE_ACTIONDATA
|
849 INSTALLLOGMODE_COMMONDATA
|
850 INSTALLLOGMODE_PROPERTYDUMP
|
851 INSTALLLOGMODE_PROGRESS
|
852 INSTALLLOGMODE_INITIALIZE
|
853 INSTALLLOGMODE_TERMINATE
|
854 INSTALLLOGMODE_SHOWDIALOG
;
857 LogAttributes
|= INSTALLLOGATTRIBUTES_APPEND
;
860 LogAttributes
|= INSTALLLOGATTRIBUTES_FLUSHEACHLINE
;
869 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
870 LogFileName
= argvW
[i
];
871 if(MsiEnableLogW(LogMode
, LogFileName
, LogAttributes
) != ERROR_SUCCESS
)
873 fprintf(stderr
, "Logging in %s (0x%08x, %u) failed\n",
874 wine_dbgstr_w(LogFileName
), LogMode
, LogAttributes
);
878 else if(msi_option_equal(argvW
[i
], "p") || msi_option_equal(argvW
[i
], "update"))
880 FunctionPatch
= TRUE
;
884 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
885 PatchFileName
= argvW
[i
];
887 else if(msi_option_prefix(argvW
[i
], "q"))
889 if(lstrlenW(argvW
[i
]) == 2 || msi_strequal(argvW
[i
]+2, "n") ||
890 msi_strequal(argvW
[i
] + 2, "uiet"))
892 InstallUILevel
= INSTALLUILEVEL_NONE
;
894 else if(msi_strequal(argvW
[i
]+2, "r"))
896 InstallUILevel
= INSTALLUILEVEL_REDUCED
;
898 else if(msi_strequal(argvW
[i
]+2, "f"))
900 InstallUILevel
= INSTALLUILEVEL_FULL
|INSTALLUILEVEL_ENDDIALOG
;
902 else if(msi_strequal(argvW
[i
]+2, "n+"))
904 InstallUILevel
= INSTALLUILEVEL_NONE
|INSTALLUILEVEL_ENDDIALOG
;
906 else if(msi_strprefix(argvW
[i
]+2, "b"))
908 const WCHAR
*ptr
= argvW
[i
] + 3;
910 InstallUILevel
= INSTALLUILEVEL_BASIC
;
914 if (msi_strprefix(ptr
, "+"))
915 InstallUILevel
|= INSTALLUILEVEL_ENDDIALOG
;
916 if (msi_strprefix(ptr
, "-"))
917 InstallUILevel
|= INSTALLUILEVEL_PROGRESSONLY
;
918 if (msi_strprefix(ptr
, "!"))
920 WINE_FIXME("Unhandled modifier: !\n");
921 InstallUILevel
|= INSTALLUILEVEL_HIDECANCEL
;
928 fprintf(stderr
, "Unknown option \"%s\" for UI level\n",
929 wine_dbgstr_w(argvW
[i
]+2));
932 else if(msi_option_equal(argvW
[i
], "passive"))
934 static const WCHAR rebootpromptW
[] =
935 {'R','E','B','O','O','T','P','R','O','M','P','T','=','"','S','"',0};
937 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_PROGRESSONLY
|INSTALLUILEVEL_HIDECANCEL
;
938 StringListAppend(&property_list
, rebootpromptW
);
940 else if(msi_option_equal(argvW
[i
], "y"))
942 FunctionDllRegisterServer
= TRUE
;
946 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
949 else if(msi_option_equal(argvW
[i
], "z"))
951 FunctionDllUnregisterServer
= TRUE
;
955 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
958 else if(msi_option_equal(argvW
[i
], "help") || msi_option_equal(argvW
[i
], "?"))
962 else if(msi_option_equal(argvW
[i
], "m"))
964 FunctionUnknown
= TRUE
;
965 WINE_FIXME("Unknown parameter /m\n");
967 else if(msi_option_equal(argvW
[i
], "D"))
969 FunctionUnknown
= TRUE
;
970 WINE_FIXME("Unknown parameter /D\n");
972 else if (msi_option_equal(argvW
[i
], "V"))
974 FunctionServer
= TRUE
;
977 StringListAppend(&property_list
, argvW
[i
]);
981 MsiSetInternalUI(InstallUILevel
, NULL
);
983 Properties
= build_properties( property_list
);
985 if(FunctionInstallAdmin
&& FunctionPatch
)
986 FunctionInstall
= FALSE
;
991 if(IsProductCode(PackageName
))
992 ReturnCode
= MsiConfigureProductExW(PackageName
, 0, INSTALLSTATE_DEFAULT
, Properties
);
994 ReturnCode
= MsiInstallProductW(PackageName
, Properties
);
996 else if(FunctionRepair
)
998 if(IsProductCode(PackageName
))
999 WINE_FIXME("Product code treatment not implemented yet\n");
1001 ReturnCode
= MsiReinstallProductW(PackageName
, RepairMode
);
1003 else if(FunctionAdvertise
)
1005 LPWSTR Transforms
= build_transforms( property_list
);
1006 ReturnCode
= MsiAdvertiseProductW(PackageName
, (LPWSTR
) AdvertiseMode
, Transforms
, Language
);
1008 else if(FunctionPatch
)
1010 ReturnCode
= MsiApplyPatchW(PatchFileName
, PackageName
, InstallType
, Properties
);
1012 else if(FunctionDllRegisterServer
)
1014 ReturnCode
= DoDllRegisterServer(DllName
);
1016 else if(FunctionDllUnregisterServer
)
1018 ReturnCode
= DoDllUnregisterServer(DllName
);
1020 else if (FunctionRegServer
)
1022 ReturnCode
= DoRegServer();
1024 else if (FunctionUnregServer
)
1026 ReturnCode
= DoUnregServer();
1028 else if (FunctionServer
)
1030 ReturnCode
= DoService();
1032 else if (FunctionUnknown
)
1034 WINE_FIXME( "Unknown function, ignoring\n" );