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
31 #include "wine/debug.h"
32 #include "wine/heap.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 void ShowUsage(int ExitCode
)
52 WCHAR msiexec_version
[40];
53 WCHAR filename
[MAX_PATH
];
56 HMODULE hmsi
= GetModuleHandleA("msi.dll");
60 /* MsiGetFileVersion need the full path */
62 res
= GetModuleFileNameW(hmsi
, filename
, ARRAY_SIZE(filename
));
64 WINE_ERR("GetModuleFileName failed: %ld\n", GetLastError());
66 len
= ARRAY_SIZE(msiexec_version
);
68 res
= MsiGetFileVersionW(filename
, msiexec_version
, &len
, NULL
, NULL
);
70 WINE_ERR("MsiGetFileVersion failed with %ld\n", res
);
72 /* Return the length of the resource.
73 No typo: The LPWSTR parameter must be a LPWSTR * for this mode */
74 len
= LoadStringW(hmsi
, 10, (LPWSTR
) &msi_res
, 0);
76 msi_res
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
));
77 msiexec_help
= HeapAlloc(GetProcessHeap(), 0, (len
+ 1) * sizeof(WCHAR
) + sizeof(msiexec_version
));
78 if (msi_res
&& msiexec_help
) {
80 LoadStringW(hmsi
, 10, msi_res
, len
+ 1);
82 swprintf(msiexec_help
, len
+ 1 + ARRAY_SIZE(msiexec_version
), msi_res
, msiexec_version
);
83 MsiMessageBoxW(0, msiexec_help
, NULL
, 0, GetUserDefaultLangID(), 0);
85 HeapFree(GetProcessHeap(), 0, msi_res
);
86 HeapFree(GetProcessHeap(), 0, msiexec_help
);
87 ExitProcess(ExitCode
);
90 static BOOL
IsProductCode(LPWSTR str
)
94 if(lstrlenW(str
) != 38)
96 return ( (CLSIDFromString(str
, &ProductCode
) == NOERROR
) );
100 static VOID
StringListAppend(struct string_list
**list
, LPCWSTR str
)
102 struct string_list
*entry
;
104 entry
= HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(struct string_list
, str
[lstrlenW(str
) + 1]));
107 WINE_ERR("Out of memory!\n");
110 lstrcpyW(entry
->str
, str
);
114 * Ignoring o(n^2) time complexity to add n strings for simplicity,
115 * add the string to the end of the list to preserve the order.
118 list
= &(*list
)->next
;
122 static LPWSTR
build_properties(struct string_list
*property_list
)
124 struct string_list
*list
;
125 LPWSTR ret
, p
, value
;
132 /* count the space we need */
134 for(list
= property_list
; list
; list
= list
->next
)
135 len
+= lstrlenW(list
->str
) + 3;
137 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
139 /* add a space before each string, and quote the value */
141 for(list
= property_list
; list
; list
= list
->next
)
143 value
= wcschr(list
->str
,'=');
146 len
= value
- list
->str
;
148 memcpy(p
, list
->str
, len
* sizeof(WCHAR
));
152 /* check if the value contains spaces and maybe quote it */
154 needs_quote
= wcschr(value
,' ') ? 1 : 0;
157 len
= lstrlenW(value
);
158 memcpy(p
, value
, len
* sizeof(WCHAR
));
165 WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret
) );
170 static LPWSTR
build_transforms(struct string_list
*transform_list
)
172 struct string_list
*list
;
176 /* count the space we need */
178 for(list
= transform_list
; list
; list
= list
->next
)
179 len
+= lstrlenW(list
->str
) + 1;
181 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
183 /* add all the transforms with a semicolon between each one */
185 for(list
= transform_list
; list
; list
= list
->next
)
187 len
= lstrlenW(list
->str
);
188 lstrcpynW(p
, list
->str
, len
);
198 static DWORD
msi_atou(LPCWSTR str
)
201 while(*str
>= '0' && *str
<= '9')
210 /* str1 is the same as str2, ignoring case */
211 static BOOL
msi_strequal(LPCWSTR str1
, LPCSTR str2
)
216 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
219 if( lstrlenW(str1
) != (len
-1) )
221 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
222 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
223 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
, strW
, len
);
224 HeapFree(GetProcessHeap(), 0, strW
);
225 return (ret
== CSTR_EQUAL
);
228 /* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
229 static BOOL
msi_option_equal(LPCWSTR str1
, LPCSTR str2
)
231 if (str1
[0] != '/' && str1
[0] != '-')
234 /* skip over the hyphen or slash */
235 return msi_strequal(str1
+ 1, str2
);
238 /* str2 is at the beginning of str1, ignoring case */
239 static BOOL
msi_strprefix(LPCWSTR str1
, LPCSTR str2
)
244 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
247 if( lstrlenW(str1
) < (len
-1) )
249 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
250 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
251 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
-1, strW
, len
-1);
252 HeapFree(GetProcessHeap(), 0, strW
);
253 return (ret
== CSTR_EQUAL
);
256 /* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
257 static BOOL
msi_option_prefix(LPCWSTR str1
, LPCSTR str2
)
259 if (str1
[0] != '/' && str1
[0] != '-')
262 /* skip over the hyphen or slash */
263 return msi_strprefix(str1
+ 1, str2
);
266 static VOID
*LoadProc(LPCWSTR DllName
, LPCSTR ProcName
, HMODULE
* DllHandle
)
270 *DllHandle
= LoadLibraryExW(DllName
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
273 fprintf(stderr
, "Unable to load dll %s\n", wine_dbgstr_w(DllName
));
276 proc
= (VOID
*) GetProcAddress(*DllHandle
, ProcName
);
279 fprintf(stderr
, "Dll %s does not implement function %s\n",
280 wine_dbgstr_w(DllName
), ProcName
);
281 FreeLibrary(*DllHandle
);
288 static DWORD
DoDllRegisterServer(LPCWSTR DllName
)
291 DLLREGISTERSERVER pfDllRegisterServer
= NULL
;
292 HMODULE DllHandle
= NULL
;
294 pfDllRegisterServer
= LoadProc(DllName
, "DllRegisterServer", &DllHandle
);
296 hr
= pfDllRegisterServer();
299 fprintf(stderr
, "Failed to register dll %s\n", wine_dbgstr_w(DllName
));
302 printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName
));
304 FreeLibrary(DllHandle
);
308 static DWORD
DoDllUnregisterServer(LPCWSTR DllName
)
311 DLLUNREGISTERSERVER pfDllUnregisterServer
= NULL
;
312 HMODULE DllHandle
= NULL
;
314 pfDllUnregisterServer
= LoadProc(DllName
, "DllUnregisterServer", &DllHandle
);
316 hr
= pfDllUnregisterServer();
319 fprintf(stderr
, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName
));
322 printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName
));
324 FreeLibrary(DllHandle
);
328 static DWORD
DoRegServer(void)
330 SC_HANDLE scm
, service
;
331 WCHAR path
[MAX_PATH
+12];
334 if (!(scm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, SC_MANAGER_CREATE_SERVICE
)))
336 fprintf(stderr
, "Failed to open the service control manager.\n");
339 len
= GetSystemDirectoryW(path
, MAX_PATH
);
340 lstrcpyW(path
+ len
, L
"\\msiexec /V");
341 if ((service
= CreateServiceW(scm
, L
"MSIServer", L
"MSIServer", GENERIC_ALL
,
342 SERVICE_WIN32_SHARE_PROCESS
, SERVICE_DEMAND_START
,
343 SERVICE_ERROR_NORMAL
, path
, NULL
, NULL
, NULL
, NULL
, NULL
)))
345 CloseServiceHandle(service
);
347 else if (GetLastError() != ERROR_SERVICE_EXISTS
)
349 fprintf(stderr
, "Failed to create MSI service\n");
352 CloseServiceHandle(scm
);
356 static DWORD
DoUnregServer(void)
358 SC_HANDLE scm
, service
;
361 if (!(scm
= OpenSCManagerW(NULL
, SERVICES_ACTIVE_DATABASEW
, SC_MANAGER_CONNECT
)))
363 fprintf(stderr
, "Failed to open service control manager\n");
366 if ((service
= OpenServiceW(scm
, L
"MSIServer", DELETE
)))
368 if (!DeleteService(service
))
370 fprintf(stderr
, "Failed to delete MSI service\n");
373 CloseServiceHandle(service
);
375 else if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST
)
377 fprintf(stderr
, "Failed to open MSI service\n");
380 CloseServiceHandle(scm
);
384 extern UINT CDECL
__wine_msi_call_dll_function(DWORD client_pid
, const GUID
*guid
);
386 static DWORD client_pid
;
388 static DWORD CALLBACK
custom_action_thread(void *arg
)
390 GUID guid
= *(GUID
*)arg
;
392 return __wine_msi_call_dll_function(client_pid
, &guid
);
395 static int custom_action_server(const WCHAR
*arg
)
397 GUID guid
, *thread_guid
;
404 TRACE("%s\n", debugstr_w(arg
));
406 if (!(client_pid
= wcstol(arg
, NULL
, 10)))
408 ERR("Invalid parameter %s\n", debugstr_w(arg
));
412 swprintf(buffer
, ARRAY_SIZE(buffer
), L
"\\\\.\\pipe\\msica_%x_%d", client_pid
, sizeof(void *) * 8);
413 pipe
= CreateFileW(buffer
, GENERIC_READ
| GENERIC_WRITE
, 0, NULL
, OPEN_EXISTING
, 0, NULL
);
414 if (pipe
== INVALID_HANDLE_VALUE
)
416 ERR("Failed to create custom action server pipe: %lu\n", GetLastError());
417 return GetLastError();
420 /* We need this to unmarshal streams, and some apps expect it to be present. */
421 CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
423 while (ReadFile(pipe
, &guid
, sizeof(guid
), &size
, NULL
) && size
== sizeof(guid
))
425 if (IsEqualGUID(&guid
, &GUID_NULL
))
427 /* package closed; time to shut down */
432 thread_guid
= heap_alloc(sizeof(GUID
));
433 memcpy(thread_guid
, &guid
, sizeof(GUID
));
434 thread
= CreateThread(NULL
, 0, custom_action_thread
, thread_guid
, 0, NULL
);
436 /* give the thread handle to the client to wait on, since we might have
437 * to run a nested action and can't block during this one */
438 thread64
= (DWORD_PTR
)thread
;
439 if (!WriteFile(pipe
, &thread64
, sizeof(thread64
), &size
, NULL
) || size
!= sizeof(thread64
))
441 ERR("Failed to write to custom action server pipe: %lu\n", GetLastError());
443 return GetLastError();
446 ERR("Failed to read from custom action server pipe: %lu\n", GetLastError());
448 return GetLastError();
452 * state machine to break up the command line properly
462 static int chomp( const WCHAR
*in
, WCHAR
*out
)
464 enum chomp_state state
= CS_TOKEN
;
469 for (p
= in
; *p
; p
++)
497 state
= CS_WHITESPACE
;
501 if (p
> in
&& p
[-1] == '"')
521 if (!ignore
&& out
) *out
++ = *p
;
527 static void process_args( WCHAR
*cmdline
, int *pargc
, WCHAR
***pargv
)
535 count
= chomp( cmdline
, NULL
);
536 if (!(p
= HeapAlloc( GetProcessHeap(), 0, (lstrlenW(cmdline
) + count
+ 1) * sizeof(WCHAR
) )))
539 count
= chomp( cmdline
, p
);
540 if (!(argv
= HeapAlloc( GetProcessHeap(), 0, (count
+ 1) * sizeof(WCHAR
*) )))
542 HeapFree( GetProcessHeap(), 0, p
);
545 for (i
= 0; i
< count
; i
++)
548 p
+= lstrlenW( p
) + 1;
556 static BOOL
process_args_from_reg( const WCHAR
*ident
, int *pargc
, WCHAR
***pargv
)
560 DWORD sz
= 0, type
= 0;
564 r
= RegOpenKeyW(HKEY_LOCAL_MACHINE
,
565 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\RunOnceEntries", &hkey
);
566 if(r
!= ERROR_SUCCESS
)
568 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, 0, &sz
);
569 if(r
== ERROR_SUCCESS
&& type
== REG_SZ
)
571 int len
= lstrlenW( *pargv
[0] );
572 if (!(buf
= HeapAlloc( GetProcessHeap(), 0, sz
+ (len
+ 1) * sizeof(WCHAR
) )))
577 memcpy( buf
, *pargv
[0], len
* sizeof(WCHAR
) );
579 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, (LPBYTE
)(buf
+ len
), &sz
);
580 if( r
== ERROR_SUCCESS
)
582 process_args(buf
, pargc
, pargv
);
585 HeapFree(GetProcessHeap(), 0, buf
);
591 static WCHAR
*get_path_with_extension(const WCHAR
*package_name
)
593 static const WCHAR ext
[] = L
".msi";
597 if (!(path
= heap_alloc(lstrlenW(package_name
) * sizeof(WCHAR
) + sizeof(ext
))))
599 WINE_ERR("No memory.\n");
603 lstrcpyW(path
, package_name
);
605 while (p
&& path
[p
] != '.' && path
[p
] != L
'\\' && path
[p
] != '/')
616 int WINAPI
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPSTR lpCmdLine
, int nCmdShow
)
619 BOOL FunctionInstall
= FALSE
;
620 BOOL FunctionInstallAdmin
= FALSE
;
621 BOOL FunctionRepair
= FALSE
;
622 BOOL FunctionAdvertise
= FALSE
;
623 BOOL FunctionPatch
= FALSE
;
624 BOOL FunctionDllRegisterServer
= FALSE
;
625 BOOL FunctionDllUnregisterServer
= FALSE
;
626 BOOL FunctionRegServer
= FALSE
;
627 BOOL FunctionUnregServer
= FALSE
;
628 BOOL FunctionServer
= FALSE
;
629 BOOL FunctionUnknown
= FALSE
;
631 LPWSTR PackageName
= NULL
;
632 LPWSTR Properties
= NULL
;
633 struct string_list
*property_list
= NULL
;
635 DWORD RepairMode
= 0;
637 DWORD_PTR AdvertiseMode
= 0;
638 struct string_list
*transform_list
= NULL
;
642 LPWSTR LogFileName
= NULL
;
643 DWORD LogAttributes
= 0;
645 LPWSTR PatchFileName
= NULL
;
646 INSTALLTYPE InstallType
= INSTALLTYPE_DEFAULT
;
648 INSTALLUILEVEL InstallUILevel
= INSTALLUILEVEL_FULL
;
650 LPWSTR DllName
= NULL
;
653 LPWSTR
*argvW
= NULL
;
656 InitCommonControls();
658 /* parse the command line */
659 process_args( GetCommandLineW(), &argc
, &argvW
);
662 * If the args begin with /@ IDENT then we need to load the real
663 * command line out of the RunOnceEntries key in the registry.
664 * We do that before starting to process the real commandline,
665 * then overwrite the commandline again.
667 if(argc
>1 && msi_option_equal(argvW
[1], "@"))
669 if(!process_args_from_reg( argvW
[2], &argc
, &argvW
))
673 if (argc
== 3 && msi_option_equal(argvW
[1], "Embedding"))
674 return custom_action_server(argvW
[2]);
676 for(i
= 1; i
< argc
; i
++)
678 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
680 if (msi_option_equal(argvW
[i
], "regserver"))
682 FunctionRegServer
= TRUE
;
684 else if (msi_option_equal(argvW
[i
], "unregserver") || msi_option_equal(argvW
[i
], "unregister")
685 || msi_option_equal(argvW
[i
], "unreg"))
687 FunctionUnregServer
= TRUE
;
689 else if(msi_option_prefix(argvW
[i
], "i") || msi_option_prefix(argvW
[i
], "package"))
691 LPWSTR argvWi
= argvW
[i
];
692 int argLen
= (msi_option_prefix(argvW
[i
], "i") ? 2 : 8);
693 FunctionInstall
= TRUE
;
694 if(lstrlenW(argvW
[i
]) > argLen
)
701 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
704 PackageName
= argvWi
;
706 else if(msi_option_equal(argvW
[i
], "a"))
708 FunctionInstall
= TRUE
;
709 FunctionInstallAdmin
= TRUE
;
710 InstallType
= INSTALLTYPE_NETWORK_IMAGE
;
714 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
715 PackageName
= argvW
[i
];
716 StringListAppend(&property_list
, L
"ACTION=ADMIN");
717 WINE_FIXME("Administrative installs are not currently supported\n");
719 else if(msi_option_prefix(argvW
[i
], "f"))
722 int len
= lstrlenW(argvW
[i
]);
723 FunctionRepair
= TRUE
;
724 for(j
= 2; j
< len
; j
++)
730 RepairMode
|= REINSTALLMODE_FILEMISSING
;
734 RepairMode
|= REINSTALLMODE_FILEOLDERVERSION
;
738 RepairMode
|= REINSTALLMODE_FILEEQUALVERSION
;
742 RepairMode
|= REINSTALLMODE_FILEEXACT
;
746 RepairMode
|= REINSTALLMODE_FILEVERIFY
;
750 RepairMode
|= REINSTALLMODE_FILEREPLACE
;
754 RepairMode
|= REINSTALLMODE_USERDATA
;
758 RepairMode
|= REINSTALLMODE_MACHINEDATA
;
762 RepairMode
|= REINSTALLMODE_SHORTCUT
;
766 RepairMode
|= REINSTALLMODE_PACKAGE
;
769 fprintf(stderr
, "Unknown option \"%c\" in Repair mode\n", argvW
[i
][j
]);
775 RepairMode
= REINSTALLMODE_FILEMISSING
|
776 REINSTALLMODE_FILEEQUALVERSION
|
777 REINSTALLMODE_FILEVERIFY
|
778 REINSTALLMODE_MACHINEDATA
|
779 REINSTALLMODE_SHORTCUT
;
784 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
785 PackageName
= argvW
[i
];
787 else if(msi_option_prefix(argvW
[i
], "x") || msi_option_equal(argvW
[i
], "uninstall"))
789 FunctionInstall
= TRUE
;
790 if(msi_option_prefix(argvW
[i
], "x")) PackageName
= argvW
[i
]+2;
791 if(!PackageName
|| !PackageName
[0])
796 PackageName
= argvW
[i
];
798 WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName
));
799 StringListAppend(&property_list
, L
"REMOVE=ALL");
801 else if(msi_option_prefix(argvW
[i
], "j"))
804 int len
= lstrlenW(argvW
[i
]);
805 FunctionAdvertise
= TRUE
;
806 for(j
= 2; j
< len
; j
++)
812 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
816 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
819 fprintf(stderr
, "Unknown option \"%c\" in Advertise mode\n", argvW
[i
][j
]);
826 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
827 PackageName
= argvW
[i
];
829 else if(msi_strequal(argvW
[i
], "u"))
831 FunctionAdvertise
= TRUE
;
832 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
836 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
837 PackageName
= argvW
[i
];
839 else if(msi_strequal(argvW
[i
], "m"))
841 FunctionAdvertise
= TRUE
;
842 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
846 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
847 PackageName
= argvW
[i
];
849 else if(msi_option_equal(argvW
[i
], "t"))
854 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
855 StringListAppend(&transform_list
, argvW
[i
]);
857 else if(msi_option_equal(argvW
[i
], "g"))
862 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
863 Language
= msi_atou(argvW
[i
]);
865 else if(msi_option_prefix(argvW
[i
], "l"))
868 int len
= lstrlenW(argvW
[i
]);
869 for(j
= 2; j
< len
; j
++)
875 LogMode
|= INSTALLLOGMODE_INFO
;
879 LogMode
|= INSTALLLOGMODE_WARNING
;
883 LogMode
|= INSTALLLOGMODE_ERROR
;
887 LogMode
|= INSTALLLOGMODE_ACTIONSTART
;
891 LogMode
|= INSTALLLOGMODE_ACTIONDATA
;
895 LogMode
|= INSTALLLOGMODE_USER
;
899 LogMode
|= INSTALLLOGMODE_COMMONDATA
;
903 LogMode
|= INSTALLLOGMODE_FATALEXIT
;
907 LogMode
|= INSTALLLOGMODE_OUTOFDISKSPACE
;
911 LogMode
|= INSTALLLOGMODE_PROPERTYDUMP
;
915 LogMode
|= INSTALLLOGMODE_VERBOSE
;
918 LogMode
= INSTALLLOGMODE_FATALEXIT
|
919 INSTALLLOGMODE_ERROR
|
920 INSTALLLOGMODE_WARNING
|
921 INSTALLLOGMODE_USER
|
922 INSTALLLOGMODE_INFO
|
923 INSTALLLOGMODE_RESOLVESOURCE
|
924 INSTALLLOGMODE_OUTOFDISKSPACE
|
925 INSTALLLOGMODE_ACTIONSTART
|
926 INSTALLLOGMODE_ACTIONDATA
|
927 INSTALLLOGMODE_COMMONDATA
|
928 INSTALLLOGMODE_PROPERTYDUMP
|
929 INSTALLLOGMODE_PROGRESS
|
930 INSTALLLOGMODE_INITIALIZE
|
931 INSTALLLOGMODE_TERMINATE
|
932 INSTALLLOGMODE_SHOWDIALOG
;
935 LogAttributes
|= INSTALLLOGATTRIBUTES_APPEND
;
938 LogAttributes
|= INSTALLLOGATTRIBUTES_FLUSHEACHLINE
;
947 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
948 LogFileName
= argvW
[i
];
949 if(MsiEnableLogW(LogMode
, LogFileName
, LogAttributes
) != ERROR_SUCCESS
)
951 fprintf(stderr
, "Logging in %s (0x%08lx, %lu) failed\n",
952 wine_dbgstr_w(LogFileName
), LogMode
, LogAttributes
);
956 else if(msi_option_equal(argvW
[i
], "p") || msi_option_equal(argvW
[i
], "update"))
958 FunctionPatch
= TRUE
;
962 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
963 PatchFileName
= argvW
[i
];
965 else if(msi_option_prefix(argvW
[i
], "q"))
967 if(lstrlenW(argvW
[i
]) == 2 || msi_strequal(argvW
[i
]+2, "n") ||
968 msi_strequal(argvW
[i
] + 2, "uiet"))
970 InstallUILevel
= INSTALLUILEVEL_NONE
;
972 else if(msi_strequal(argvW
[i
]+2, "r"))
974 InstallUILevel
= INSTALLUILEVEL_REDUCED
;
976 else if(msi_strequal(argvW
[i
]+2, "f"))
978 InstallUILevel
= INSTALLUILEVEL_FULL
|INSTALLUILEVEL_ENDDIALOG
;
980 else if(msi_strequal(argvW
[i
]+2, "n+"))
982 InstallUILevel
= INSTALLUILEVEL_NONE
|INSTALLUILEVEL_ENDDIALOG
;
984 else if(msi_strprefix(argvW
[i
]+2, "b"))
986 const WCHAR
*ptr
= argvW
[i
] + 3;
988 InstallUILevel
= INSTALLUILEVEL_BASIC
;
992 if (msi_strprefix(ptr
, "+"))
993 InstallUILevel
|= INSTALLUILEVEL_ENDDIALOG
;
994 if (msi_strprefix(ptr
, "-"))
995 InstallUILevel
|= INSTALLUILEVEL_PROGRESSONLY
;
996 if (msi_strprefix(ptr
, "!"))
998 WINE_FIXME("Unhandled modifier: !\n");
999 InstallUILevel
|= INSTALLUILEVEL_HIDECANCEL
;
1006 fprintf(stderr
, "Unknown option \"%s\" for UI level\n",
1007 wine_dbgstr_w(argvW
[i
]+2));
1010 else if(msi_option_equal(argvW
[i
], "passive"))
1012 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_PROGRESSONLY
|INSTALLUILEVEL_HIDECANCEL
;
1013 StringListAppend(&property_list
, L
"REBOOTPROMPT=\"S\"");
1015 else if(msi_option_equal(argvW
[i
], "y"))
1017 FunctionDllRegisterServer
= TRUE
;
1021 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
1024 else if(msi_option_equal(argvW
[i
], "z"))
1026 FunctionDllUnregisterServer
= TRUE
;
1030 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
1033 else if(msi_option_equal(argvW
[i
], "help") || msi_option_equal(argvW
[i
], "?"))
1037 else if(msi_option_equal(argvW
[i
], "m"))
1039 FunctionUnknown
= TRUE
;
1040 WINE_FIXME("Unknown parameter /m\n");
1042 else if(msi_option_equal(argvW
[i
], "D"))
1044 FunctionUnknown
= TRUE
;
1045 WINE_FIXME("Unknown parameter /D\n");
1047 else if (msi_option_equal(argvW
[i
], "V"))
1049 FunctionServer
= TRUE
;
1052 StringListAppend(&property_list
, argvW
[i
]);
1056 MsiSetInternalUI(InstallUILevel
, NULL
);
1058 Properties
= build_properties( property_list
);
1060 if(FunctionInstallAdmin
&& FunctionPatch
)
1061 FunctionInstall
= FALSE
;
1066 if(IsProductCode(PackageName
))
1067 ReturnCode
= MsiConfigureProductExW(PackageName
, 0, INSTALLSTATE_DEFAULT
, Properties
);
1070 if ((ReturnCode
= MsiInstallProductW(PackageName
, Properties
)) == ERROR_FILE_NOT_FOUND
1071 && (path
= get_path_with_extension(PackageName
)))
1073 ReturnCode
= MsiInstallProductW(path
, Properties
);
1078 else if(FunctionRepair
)
1080 if(IsProductCode(PackageName
))
1081 WINE_FIXME("Product code treatment not implemented yet\n");
1084 if ((ReturnCode
= MsiReinstallProductW(PackageName
, RepairMode
)) == ERROR_FILE_NOT_FOUND
1085 && (path
= get_path_with_extension(PackageName
)))
1087 ReturnCode
= MsiReinstallProductW(path
, RepairMode
);
1092 else if(FunctionAdvertise
)
1094 LPWSTR Transforms
= build_transforms( property_list
);
1095 ReturnCode
= MsiAdvertiseProductW(PackageName
, (LPWSTR
) AdvertiseMode
, Transforms
, Language
);
1097 else if(FunctionPatch
)
1099 ReturnCode
= MsiApplyPatchW(PatchFileName
, PackageName
, InstallType
, Properties
);
1101 else if(FunctionDllRegisterServer
)
1103 ReturnCode
= DoDllRegisterServer(DllName
);
1105 else if(FunctionDllUnregisterServer
)
1107 ReturnCode
= DoDllUnregisterServer(DllName
);
1109 else if (FunctionRegServer
)
1111 ReturnCode
= DoRegServer();
1113 else if (FunctionUnregServer
)
1115 ReturnCode
= DoUnregServer();
1117 else if (FunctionServer
)
1119 ReturnCode
= DoService();
1121 else if (FunctionUnknown
)
1123 WINE_FIXME( "Unknown function, ignoring\n" );