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/heap.h"
32 #include "wine/unicode.h"
35 DEFINE_GUID(GUID_NULL
,0,0,0,0,0,0,0,0,0,0,0);
37 WINE_DEFAULT_DEBUG_CHANNEL(msiexec
);
39 typedef HRESULT (WINAPI
*DLLREGISTERSERVER
)(void);
40 typedef HRESULT (WINAPI
*DLLUNREGISTERSERVER
)(void);
42 DWORD
DoService(void);
46 struct string_list
*next
;
50 static const WCHAR ActionAdmin
[] = {
51 'A','C','T','I','O','N','=','A','D','M','I','N',0 };
52 static const WCHAR RemoveAll
[] = {
53 'R','E','M','O','V','E','=','A','L','L',0 };
55 static const WCHAR InstallRunOnce
[] = {
56 'S','o','f','t','w','a','r','e','\\',
57 'M','i','c','r','o','s','o','f','t','\\',
58 'W','i','n','d','o','w','s','\\',
59 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
60 'I','n','s','t','a','l','l','e','r','\\',
61 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
63 static void ShowUsage(int ExitCode
)
65 WCHAR msiexec_version
[40];
66 WCHAR filename
[MAX_PATH
];
69 HMODULE hmsi
= GetModuleHandleA("msi.dll");
73 /* MsiGetFileVersion need the full path */
75 res
= GetModuleFileNameW(hmsi
, filename
, ARRAY_SIZE(filename
));
77 WINE_ERR("GetModuleFileName failed: %d\n", GetLastError());
79 len
= ARRAY_SIZE(msiexec_version
);
81 res
= MsiGetFileVersionW(filename
, msiexec_version
, &len
, NULL
, NULL
);
83 WINE_ERR("MsiGetFileVersion failed with %d\n", res
);
85 /* Return the length of the resource.
86 No typo: The LPWSTR parameter must be a LPWSTR * for this mode */
87 len
= LoadStringW(hmsi
, 10, (LPWSTR
) &msi_res
, 0);
89 msi_res
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
90 msiexec_help
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) + sizeof(msiexec_version
));
91 if (msi_res
&& msiexec_help
) {
93 LoadStringW(hmsi
, 10, msi_res
, len
+ 1);
95 sprintfW(msiexec_help
, msi_res
, msiexec_version
);
96 MsiMessageBoxW(0, msiexec_help
, NULL
, 0, GetUserDefaultLangID(), 0);
98 HeapFree(GetProcessHeap(), 0, msi_res
);
99 HeapFree(GetProcessHeap(), 0, msiexec_help
);
100 ExitProcess(ExitCode
);
103 static BOOL
IsProductCode(LPWSTR str
)
107 if(lstrlenW(str
) != 38)
109 return ( (CLSIDFromString(str
, &ProductCode
) == NOERROR
) );
113 static VOID
StringListAppend(struct string_list
**list
, LPCWSTR str
)
115 struct string_list
*entry
;
117 entry
= HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(struct string_list
, str
[lstrlenW(str
) + 1]));
120 WINE_ERR("Out of memory!\n");
123 lstrcpyW(entry
->str
, str
);
127 * Ignoring o(n^2) time complexity to add n strings for simplicity,
128 * add the string to the end of the list to preserve the order.
131 list
= &(*list
)->next
;
135 static LPWSTR
build_properties(struct string_list
*property_list
)
137 struct string_list
*list
;
138 LPWSTR ret
, p
, value
;
145 /* count the space we need */
147 for(list
= property_list
; list
; list
= list
->next
)
148 len
+= lstrlenW(list
->str
) + 3;
150 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
152 /* add a space before each string, and quote the value */
154 for(list
= property_list
; list
; list
= list
->next
)
156 value
= strchrW(list
->str
,'=');
159 len
= value
- list
->str
;
161 memcpy(p
, list
->str
, len
* sizeof(WCHAR
));
165 /* check if the value contains spaces and maybe quote it */
167 needs_quote
= strchrW(value
,' ') ? 1 : 0;
170 len
= lstrlenW(value
);
171 memcpy(p
, value
, len
* sizeof(WCHAR
));
178 WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret
) );
183 static LPWSTR
build_transforms(struct string_list
*transform_list
)
185 struct string_list
*list
;
189 /* count the space we need */
191 for(list
= transform_list
; list
; list
= list
->next
)
192 len
+= lstrlenW(list
->str
) + 1;
194 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
196 /* add all the transforms with a semicolon between each one */
198 for(list
= transform_list
; list
; list
= list
->next
)
200 len
= lstrlenW(list
->str
);
201 lstrcpynW(p
, list
->str
, len
);
211 static DWORD
msi_atou(LPCWSTR str
)
214 while(*str
>= '0' && *str
<= '9')
223 /* str1 is the same as str2, ignoring case */
224 static BOOL
msi_strequal(LPCWSTR str1
, LPCSTR str2
)
229 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
232 if( lstrlenW(str1
) != (len
-1) )
234 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
235 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
236 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
, strW
, len
);
237 HeapFree(GetProcessHeap(), 0, strW
);
238 return (ret
== CSTR_EQUAL
);
241 /* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
242 static BOOL
msi_option_equal(LPCWSTR str1
, LPCSTR str2
)
244 if (str1
[0] != '/' && str1
[0] != '-')
247 /* skip over the hyphen or slash */
248 return msi_strequal(str1
+ 1, str2
);
251 /* str2 is at the beginning of str1, ignoring case */
252 static BOOL
msi_strprefix(LPCWSTR str1
, LPCSTR str2
)
257 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
260 if( lstrlenW(str1
) < (len
-1) )
262 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
263 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
264 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
-1, strW
, len
-1);
265 HeapFree(GetProcessHeap(), 0, strW
);
266 return (ret
== CSTR_EQUAL
);
269 /* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
270 static BOOL
msi_option_prefix(LPCWSTR str1
, LPCSTR str2
)
272 if (str1
[0] != '/' && str1
[0] != '-')
275 /* skip over the hyphen or slash */
276 return msi_strprefix(str1
+ 1, str2
);
279 static VOID
*LoadProc(LPCWSTR DllName
, LPCSTR ProcName
, HMODULE
* DllHandle
)
283 *DllHandle
= LoadLibraryExW(DllName
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
286 fprintf(stderr
, "Unable to load dll %s\n", wine_dbgstr_w(DllName
));
289 proc
= (VOID
*) GetProcAddress(*DllHandle
, ProcName
);
292 fprintf(stderr
, "Dll %s does not implement function %s\n",
293 wine_dbgstr_w(DllName
), ProcName
);
294 FreeLibrary(*DllHandle
);
301 static DWORD
DoDllRegisterServer(LPCWSTR DllName
)
304 DLLREGISTERSERVER pfDllRegisterServer
= NULL
;
305 HMODULE DllHandle
= NULL
;
307 pfDllRegisterServer
= LoadProc(DllName
, "DllRegisterServer", &DllHandle
);
309 hr
= pfDllRegisterServer();
312 fprintf(stderr
, "Failed to register dll %s\n", wine_dbgstr_w(DllName
));
315 printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName
));
317 FreeLibrary(DllHandle
);
321 static DWORD
DoDllUnregisterServer(LPCWSTR DllName
)
324 DLLUNREGISTERSERVER pfDllUnregisterServer
= NULL
;
325 HMODULE DllHandle
= NULL
;
327 pfDllUnregisterServer
= LoadProc(DllName
, "DllUnregisterServer", &DllHandle
);
329 hr
= pfDllUnregisterServer();
332 fprintf(stderr
, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName
));
335 printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName
));
337 FreeLibrary(DllHandle
);
341 static DWORD
DoRegServer(void)
343 static const WCHAR msiserverW
[] = {'M','S','I','S','e','r','v','e','r',0};
344 static const WCHAR msiexecW
[] = {'\\','m','s','i','e','x','e','c',' ','/','V',0};
345 SC_HANDLE scm
, service
;
346 WCHAR path
[MAX_PATH
+12];
349 if (!(scm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, SC_MANAGER_CREATE_SERVICE
)))
351 fprintf(stderr
, "Failed to open the service control manager.\n");
354 len
= GetSystemDirectoryW(path
, MAX_PATH
);
355 lstrcpyW(path
+ len
, msiexecW
);
356 if ((service
= CreateServiceW(scm
, msiserverW
, msiserverW
, GENERIC_ALL
,
357 SERVICE_WIN32_SHARE_PROCESS
, SERVICE_DEMAND_START
,
358 SERVICE_ERROR_NORMAL
, path
, NULL
, NULL
, NULL
, NULL
, NULL
)))
360 CloseServiceHandle(service
);
362 else if (GetLastError() != ERROR_SERVICE_EXISTS
)
364 fprintf(stderr
, "Failed to create MSI service\n");
367 CloseServiceHandle(scm
);
371 static DWORD
DoUnregServer(void)
373 static const WCHAR msiserverW
[] = {'M','S','I','S','e','r','v','e','r',0};
374 SC_HANDLE scm
, service
;
377 if (!(scm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, SC_MANAGER_CONNECT
)))
379 fprintf(stderr
, "Failed to open service control manager\n");
382 if ((service
= OpenServiceW(scm
, msiserverW
, DELETE
)))
384 if (!DeleteService(service
))
386 fprintf(stderr
, "Failed to delete MSI service\n");
389 CloseServiceHandle(service
);
391 else if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST
)
393 fprintf(stderr
, "Failed to open MSI service\n");
396 CloseServiceHandle(scm
);
400 extern UINT CDECL
__wine_msi_call_dll_function(GUID
*guid
);
402 static DWORD CALLBACK
custom_action_thread(void *arg
)
404 GUID guid
= *(GUID
*)arg
;
406 return __wine_msi_call_dll_function(&guid
);
409 static int custom_action_server(const WCHAR
*arg
)
411 static const WCHAR pipe_name
[] = {'\\','\\','.','\\','p','i','p','e','\\','m','s','i','c','a','_','%','x','_','%','d',0};
412 DWORD client_pid
= atoiW(arg
);
413 GUID guid
, *thread_guid
;
420 TRACE("%s\n", debugstr_w(arg
));
424 ERR("Invalid parameter %s\n", debugstr_w(arg
));
428 sprintfW(buffer
, pipe_name
, client_pid
, sizeof(void *) * 8);
429 pipe
= CreateFileW(buffer
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
430 if (pipe
== INVALID_HANDLE_VALUE
)
432 ERR("Failed to create custom action server pipe: %u\n", GetLastError());
433 return GetLastError();
436 /* We need this to unmarshal streams, and some apps expect it to be present. */
437 CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
439 while (ReadFile(pipe
, &guid
, sizeof(guid
), &size
, NULL
) && size
== sizeof(guid
))
441 if (IsEqualGUID(&guid
, &GUID_NULL
))
443 /* package closed; time to shut down */
448 thread_guid
= heap_alloc(sizeof(GUID
));
449 memcpy(thread_guid
, &guid
, sizeof(GUID
));
450 thread
= CreateThread(NULL
, 0, custom_action_thread
, thread_guid
, 0, NULL
);
452 /* give the thread handle to the client to wait on, since we might have
453 * to run a nested action and can't block during this one */
454 thread64
= (DWORD_PTR
)thread
;
455 if (!WriteFile(pipe
, &thread64
, sizeof(thread64
), &size
, NULL
) || size
!= sizeof(thread64
))
457 ERR("Failed to write to custom action server pipe: %u\n", GetLastError());
459 return GetLastError();
462 ERR("Failed to read from custom action server pipe: %u\n", GetLastError());
464 return GetLastError();
468 * state machine to break up the command line properly
478 static int chomp( const WCHAR
*in
, WCHAR
*out
)
480 enum chomp_state state
= CS_TOKEN
;
485 for (p
= in
; *p
; p
++)
513 state
= CS_WHITESPACE
;
517 if (p
> in
&& p
[-1] == '"')
537 if (!ignore
&& out
) *out
++ = *p
;
543 static void process_args( WCHAR
*cmdline
, int *pargc
, WCHAR
***pargv
)
551 count
= chomp( cmdline
, NULL
);
552 if (!(p
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW(cmdline
) + count
+ 1) * sizeof(WCHAR
) )))
555 count
= chomp( cmdline
, p
);
556 if (!(argv
= HeapAlloc( GetProcessHeap(), 0, (count
+ 1) * sizeof(WCHAR
*) )))
558 HeapFree( GetProcessHeap(), 0, p
);
561 for (i
= 0; i
< count
; i
++)
564 p
+= lstrlenW( p
) + 1;
572 static BOOL
process_args_from_reg( const WCHAR
*ident
, int *pargc
, WCHAR
***pargv
)
576 DWORD sz
= 0, type
= 0;
580 r
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, InstallRunOnce
, &hkey
);
581 if(r
!= ERROR_SUCCESS
)
583 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, 0, &sz
);
584 if(r
== ERROR_SUCCESS
&& type
== REG_SZ
)
586 int len
= lstrlenW( *pargv
[0] );
587 if (!(buf
= HeapAlloc( GetProcessHeap(), 0, sz
+ (len
+ 1) * sizeof(WCHAR
) )))
592 memcpy( buf
, *pargv
[0], len
* sizeof(WCHAR
) );
594 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, (LPBYTE
)(buf
+ len
), &sz
);
595 if( r
== ERROR_SUCCESS
)
597 process_args(buf
, pargc
, pargv
);
600 HeapFree(GetProcessHeap(), 0, buf
);
606 int WINAPI
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPSTR lpCmdLine
, int nCmdShow
)
609 BOOL FunctionInstall
= FALSE
;
610 BOOL FunctionInstallAdmin
= FALSE
;
611 BOOL FunctionRepair
= FALSE
;
612 BOOL FunctionAdvertise
= FALSE
;
613 BOOL FunctionPatch
= FALSE
;
614 BOOL FunctionDllRegisterServer
= FALSE
;
615 BOOL FunctionDllUnregisterServer
= FALSE
;
616 BOOL FunctionRegServer
= FALSE
;
617 BOOL FunctionUnregServer
= FALSE
;
618 BOOL FunctionServer
= FALSE
;
619 BOOL FunctionUnknown
= FALSE
;
621 LPWSTR PackageName
= NULL
;
622 LPWSTR Properties
= NULL
;
623 struct string_list
*property_list
= NULL
;
625 DWORD RepairMode
= 0;
627 DWORD_PTR AdvertiseMode
= 0;
628 struct string_list
*transform_list
= NULL
;
632 LPWSTR LogFileName
= NULL
;
633 DWORD LogAttributes
= 0;
635 LPWSTR PatchFileName
= NULL
;
636 INSTALLTYPE InstallType
= INSTALLTYPE_DEFAULT
;
638 INSTALLUILEVEL InstallUILevel
= INSTALLUILEVEL_FULL
;
640 LPWSTR DllName
= NULL
;
643 LPWSTR
*argvW
= NULL
;
645 /* parse the command line */
646 process_args( GetCommandLineW(), &argc
, &argvW
);
649 * If the args begin with /@ IDENT then we need to load the real
650 * command line out of the RunOnceEntries key in the registry.
651 * We do that before starting to process the real commandline,
652 * then overwrite the commandline again.
654 if(argc
>1 && msi_option_equal(argvW
[1], "@"))
656 if(!process_args_from_reg( argvW
[2], &argc
, &argvW
))
660 if (argc
== 3 && msi_option_equal(argvW
[1], "Embedding"))
661 return custom_action_server(argvW
[2]);
663 for(i
= 1; i
< argc
; i
++)
665 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
667 if (msi_option_equal(argvW
[i
], "regserver"))
669 FunctionRegServer
= TRUE
;
671 else if (msi_option_equal(argvW
[i
], "unregserver") || msi_option_equal(argvW
[i
], "unregister")
672 || msi_option_equal(argvW
[i
], "unreg"))
674 FunctionUnregServer
= TRUE
;
676 else if(msi_option_prefix(argvW
[i
], "i") || msi_option_prefix(argvW
[i
], "package"))
678 LPWSTR argvWi
= argvW
[i
];
679 int argLen
= (msi_option_prefix(argvW
[i
], "i") ? 2 : 8);
680 FunctionInstall
= TRUE
;
681 if(lstrlenW(argvW
[i
]) > argLen
)
688 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
691 PackageName
= argvWi
;
693 else if(msi_option_equal(argvW
[i
], "a"))
695 FunctionInstall
= TRUE
;
696 FunctionInstallAdmin
= TRUE
;
697 InstallType
= INSTALLTYPE_NETWORK_IMAGE
;
701 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
702 PackageName
= argvW
[i
];
703 StringListAppend(&property_list
, ActionAdmin
);
704 WINE_FIXME("Administrative installs are not currently supported\n");
706 else if(msi_option_prefix(argvW
[i
], "f"))
709 int len
= lstrlenW(argvW
[i
]);
710 FunctionRepair
= TRUE
;
711 for(j
= 2; j
< len
; j
++)
717 RepairMode
|= REINSTALLMODE_FILEMISSING
;
721 RepairMode
|= REINSTALLMODE_FILEOLDERVERSION
;
725 RepairMode
|= REINSTALLMODE_FILEEQUALVERSION
;
729 RepairMode
|= REINSTALLMODE_FILEEXACT
;
733 RepairMode
|= REINSTALLMODE_FILEVERIFY
;
737 RepairMode
|= REINSTALLMODE_FILEREPLACE
;
741 RepairMode
|= REINSTALLMODE_USERDATA
;
745 RepairMode
|= REINSTALLMODE_MACHINEDATA
;
749 RepairMode
|= REINSTALLMODE_SHORTCUT
;
753 RepairMode
|= REINSTALLMODE_PACKAGE
;
756 fprintf(stderr
, "Unknown option \"%c\" in Repair mode\n", argvW
[i
][j
]);
762 RepairMode
= REINSTALLMODE_FILEMISSING
|
763 REINSTALLMODE_FILEEQUALVERSION
|
764 REINSTALLMODE_FILEVERIFY
|
765 REINSTALLMODE_MACHINEDATA
|
766 REINSTALLMODE_SHORTCUT
;
771 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
772 PackageName
= argvW
[i
];
774 else if(msi_option_prefix(argvW
[i
], "x") || msi_option_equal(argvW
[i
], "uninstall"))
776 FunctionInstall
= TRUE
;
777 if(msi_option_prefix(argvW
[i
], "x")) PackageName
= argvW
[i
]+2;
778 if(!PackageName
|| !PackageName
[0])
783 PackageName
= argvW
[i
];
785 WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName
));
786 StringListAppend(&property_list
, RemoveAll
);
788 else if(msi_option_prefix(argvW
[i
], "j"))
791 int len
= lstrlenW(argvW
[i
]);
792 FunctionAdvertise
= TRUE
;
793 for(j
= 2; j
< len
; j
++)
799 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
803 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
806 fprintf(stderr
, "Unknown option \"%c\" in Advertise mode\n", argvW
[i
][j
]);
813 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
814 PackageName
= argvW
[i
];
816 else if(msi_strequal(argvW
[i
], "u"))
818 FunctionAdvertise
= TRUE
;
819 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
823 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
824 PackageName
= argvW
[i
];
826 else if(msi_strequal(argvW
[i
], "m"))
828 FunctionAdvertise
= TRUE
;
829 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
833 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
834 PackageName
= argvW
[i
];
836 else if(msi_option_equal(argvW
[i
], "t"))
841 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
842 StringListAppend(&transform_list
, argvW
[i
]);
844 else if(msi_option_equal(argvW
[i
], "g"))
849 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
850 Language
= msi_atou(argvW
[i
]);
852 else if(msi_option_prefix(argvW
[i
], "l"))
855 int len
= lstrlenW(argvW
[i
]);
856 for(j
= 2; j
< len
; j
++)
862 LogMode
|= INSTALLLOGMODE_INFO
;
866 LogMode
|= INSTALLLOGMODE_WARNING
;
870 LogMode
|= INSTALLLOGMODE_ERROR
;
874 LogMode
|= INSTALLLOGMODE_ACTIONSTART
;
878 LogMode
|= INSTALLLOGMODE_ACTIONDATA
;
882 LogMode
|= INSTALLLOGMODE_USER
;
886 LogMode
|= INSTALLLOGMODE_COMMONDATA
;
890 LogMode
|= INSTALLLOGMODE_FATALEXIT
;
894 LogMode
|= INSTALLLOGMODE_OUTOFDISKSPACE
;
898 LogMode
|= INSTALLLOGMODE_PROPERTYDUMP
;
902 LogMode
|= INSTALLLOGMODE_VERBOSE
;
905 LogMode
= INSTALLLOGMODE_FATALEXIT
|
906 INSTALLLOGMODE_ERROR
|
907 INSTALLLOGMODE_WARNING
|
908 INSTALLLOGMODE_USER
|
909 INSTALLLOGMODE_INFO
|
910 INSTALLLOGMODE_RESOLVESOURCE
|
911 INSTALLLOGMODE_OUTOFDISKSPACE
|
912 INSTALLLOGMODE_ACTIONSTART
|
913 INSTALLLOGMODE_ACTIONDATA
|
914 INSTALLLOGMODE_COMMONDATA
|
915 INSTALLLOGMODE_PROPERTYDUMP
|
916 INSTALLLOGMODE_PROGRESS
|
917 INSTALLLOGMODE_INITIALIZE
|
918 INSTALLLOGMODE_TERMINATE
|
919 INSTALLLOGMODE_SHOWDIALOG
;
922 LogAttributes
|= INSTALLLOGATTRIBUTES_APPEND
;
925 LogAttributes
|= INSTALLLOGATTRIBUTES_FLUSHEACHLINE
;
934 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
935 LogFileName
= argvW
[i
];
936 if(MsiEnableLogW(LogMode
, LogFileName
, LogAttributes
) != ERROR_SUCCESS
)
938 fprintf(stderr
, "Logging in %s (0x%08x, %u) failed\n",
939 wine_dbgstr_w(LogFileName
), LogMode
, LogAttributes
);
943 else if(msi_option_equal(argvW
[i
], "p") || msi_option_equal(argvW
[i
], "update"))
945 FunctionPatch
= TRUE
;
949 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
950 PatchFileName
= argvW
[i
];
952 else if(msi_option_prefix(argvW
[i
], "q"))
954 if(lstrlenW(argvW
[i
]) == 2 || msi_strequal(argvW
[i
]+2, "n") ||
955 msi_strequal(argvW
[i
] + 2, "uiet"))
957 InstallUILevel
= INSTALLUILEVEL_NONE
;
959 else if(msi_strequal(argvW
[i
]+2, "r"))
961 InstallUILevel
= INSTALLUILEVEL_REDUCED
;
963 else if(msi_strequal(argvW
[i
]+2, "f"))
965 InstallUILevel
= INSTALLUILEVEL_FULL
|INSTALLUILEVEL_ENDDIALOG
;
967 else if(msi_strequal(argvW
[i
]+2, "n+"))
969 InstallUILevel
= INSTALLUILEVEL_NONE
|INSTALLUILEVEL_ENDDIALOG
;
971 else if(msi_strprefix(argvW
[i
]+2, "b"))
973 const WCHAR
*ptr
= argvW
[i
] + 3;
975 InstallUILevel
= INSTALLUILEVEL_BASIC
;
979 if (msi_strprefix(ptr
, "+"))
980 InstallUILevel
|= INSTALLUILEVEL_ENDDIALOG
;
981 if (msi_strprefix(ptr
, "-"))
982 InstallUILevel
|= INSTALLUILEVEL_PROGRESSONLY
;
983 if (msi_strprefix(ptr
, "!"))
985 WINE_FIXME("Unhandled modifier: !\n");
986 InstallUILevel
|= INSTALLUILEVEL_HIDECANCEL
;
993 fprintf(stderr
, "Unknown option \"%s\" for UI level\n",
994 wine_dbgstr_w(argvW
[i
]+2));
997 else if(msi_option_equal(argvW
[i
], "passive"))
999 static const WCHAR rebootpromptW
[] =
1000 {'R','E','B','O','O','T','P','R','O','M','P','T','=','"','S','"',0};
1002 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_PROGRESSONLY
|INSTALLUILEVEL_HIDECANCEL
;
1003 StringListAppend(&property_list
, rebootpromptW
);
1005 else if(msi_option_equal(argvW
[i
], "y"))
1007 FunctionDllRegisterServer
= TRUE
;
1011 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
1014 else if(msi_option_equal(argvW
[i
], "z"))
1016 FunctionDllUnregisterServer
= TRUE
;
1020 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
1023 else if(msi_option_equal(argvW
[i
], "help") || msi_option_equal(argvW
[i
], "?"))
1027 else if(msi_option_equal(argvW
[i
], "m"))
1029 FunctionUnknown
= TRUE
;
1030 WINE_FIXME("Unknown parameter /m\n");
1032 else if(msi_option_equal(argvW
[i
], "D"))
1034 FunctionUnknown
= TRUE
;
1035 WINE_FIXME("Unknown parameter /D\n");
1037 else if (msi_option_equal(argvW
[i
], "V"))
1039 FunctionServer
= TRUE
;
1042 StringListAppend(&property_list
, argvW
[i
]);
1046 MsiSetInternalUI(InstallUILevel
, NULL
);
1048 Properties
= build_properties( property_list
);
1050 if(FunctionInstallAdmin
&& FunctionPatch
)
1051 FunctionInstall
= FALSE
;
1056 if(IsProductCode(PackageName
))
1057 ReturnCode
= MsiConfigureProductExW(PackageName
, 0, INSTALLSTATE_DEFAULT
, Properties
);
1059 ReturnCode
= MsiInstallProductW(PackageName
, Properties
);
1061 else if(FunctionRepair
)
1063 if(IsProductCode(PackageName
))
1064 WINE_FIXME("Product code treatment not implemented yet\n");
1066 ReturnCode
= MsiReinstallProductW(PackageName
, RepairMode
);
1068 else if(FunctionAdvertise
)
1070 LPWSTR Transforms
= build_transforms( property_list
);
1071 ReturnCode
= MsiAdvertiseProductW(PackageName
, (LPWSTR
) AdvertiseMode
, Transforms
, Language
);
1073 else if(FunctionPatch
)
1075 ReturnCode
= MsiApplyPatchW(PatchFileName
, PackageName
, InstallType
, Properties
);
1077 else if(FunctionDllRegisterServer
)
1079 ReturnCode
= DoDllRegisterServer(DllName
);
1081 else if(FunctionDllUnregisterServer
)
1083 ReturnCode
= DoDllUnregisterServer(DllName
);
1085 else if (FunctionRegServer
)
1087 ReturnCode
= DoRegServer();
1089 else if (FunctionUnregServer
)
1091 ReturnCode
= DoUnregServer();
1093 else if (FunctionServer
)
1095 ReturnCode
= DoService();
1097 else if (FunctionUnknown
)
1099 WINE_FIXME( "Unknown function, ignoring\n" );