reactos/base/system/msiexec/msiexec.c
winesync 1fa71cf3c5
[WINESYNC] msi: Create the custom action thread inside msiexec.exe.
This patch is effectively a no-op by itself, but makes the next patch
architecturally much simpler. We need the main thread to be non-blocking
to allow nested custom actions to work, so creating the thread inside of
msiexec allows the custom action thread to write the result of the action to
the server pipe while the main thread simply reads from it.

Signed-off-by: Zebediah Figura <z.figura12@gmail.com>
Signed-off-by: Hans Leidekker <hans@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>

wine commit id 7908d6ee399f4ef556e194dfa5cdef746fc091b6 by Zebediah Figura <z.figura12@gmail.com>
2022-03-20 19:27:57 +01:00

1063 lines
26 KiB
C

/*
* msiexec.exe implementation
*
* Copyright 2004 Vincent Béron
* Copyright 2005 Mike McCormack
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <msi.h>
#include <winsvc.h>
#include <objbase.h>
#include <stdio.h>
#include "wine/debug.h"
#include "wine/unicode.h"
WINE_DEFAULT_DEBUG_CHANNEL(msiexec);
typedef HRESULT (WINAPI *DLLREGISTERSERVER)(void);
typedef HRESULT (WINAPI *DLLUNREGISTERSERVER)(void);
DWORD DoService(void);
struct string_list
{
struct string_list *next;
WCHAR str[1];
};
static const WCHAR ActionAdmin[] = {
'A','C','T','I','O','N','=','A','D','M','I','N',0 };
static const WCHAR RemoveAll[] = {
'R','E','M','O','V','E','=','A','L','L',0 };
static const WCHAR InstallRunOnce[] = {
'S','o','f','t','w','a','r','e','\\',
'M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\',
'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'I','n','s','t','a','l','l','e','r','\\',
'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
static void ShowUsage(int ExitCode)
{
WCHAR msiexec_version[40];
WCHAR filename[MAX_PATH];
LPWSTR msi_res;
LPWSTR msiexec_help;
HMODULE hmsi = GetModuleHandleA("msi.dll");
DWORD len;
DWORD res;
/* MsiGetFileVersion need the full path */
*filename = 0;
res = GetModuleFileNameW(hmsi, filename, sizeof(filename) / sizeof(filename[0]));
if (!res)
WINE_ERR("GetModuleFileName failed: %d\n", GetLastError());
len = sizeof(msiexec_version) / sizeof(msiexec_version[0]);
*msiexec_version = 0;
res = MsiGetFileVersionW(filename, msiexec_version, &len, NULL, NULL);
if (res)
WINE_ERR("MsiGetFileVersion failed with %d\n", res);
/* Return the length of the resource.
No typo: The LPWSTR parameter must be a LPWSTR * for this mode */
len = LoadStringW(hmsi, 10, (LPWSTR) &msi_res, 0);
msi_res = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR));
msiexec_help = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR) + sizeof(msiexec_version));
if (msi_res && msiexec_help) {
*msi_res = 0;
LoadStringW(hmsi, 10, msi_res, len + 1);
sprintfW(msiexec_help, msi_res, msiexec_version);
MsiMessageBoxW(0, msiexec_help, NULL, 0, GetUserDefaultLangID(), 0);
}
HeapFree(GetProcessHeap(), 0, msi_res);
HeapFree(GetProcessHeap(), 0, msiexec_help);
ExitProcess(ExitCode);
}
static BOOL IsProductCode(LPWSTR str)
{
GUID ProductCode;
if(lstrlenW(str) != 38)
return FALSE;
return ( (CLSIDFromString(str, &ProductCode) == NOERROR) );
}
static VOID StringListAppend(struct string_list **list, LPCWSTR str)
{
struct string_list *entry;
entry = HeapAlloc(GetProcessHeap(), 0, FIELD_OFFSET(struct string_list, str[lstrlenW(str) + 1]));
if(!entry)
{
WINE_ERR("Out of memory!\n");
ExitProcess(1);
}
lstrcpyW(entry->str, str);
entry->next = NULL;
/*
* Ignoring o(n^2) time complexity to add n strings for simplicity,
* add the string to the end of the list to preserve the order.
*/
while( *list )
list = &(*list)->next;
*list = entry;
}
static LPWSTR build_properties(struct string_list *property_list)
{
struct string_list *list;
LPWSTR ret, p, value;
DWORD len;
BOOL needs_quote;
if(!property_list)
return NULL;
/* count the space we need */
len = 1;
for(list = property_list; list; list = list->next)
len += lstrlenW(list->str) + 3;
ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
/* add a space before each string, and quote the value */
p = ret;
for(list = property_list; list; list = list->next)
{
value = strchrW(list->str,'=');
if(!value)
continue;
len = value - list->str;
*p++ = ' ';
memcpy(p, list->str, len * sizeof(WCHAR));
p += len;
*p++ = '=';
/* check if the value contains spaces and maybe quote it */
value++;
needs_quote = strchrW(value,' ') ? 1 : 0;
if(needs_quote)
*p++ = '"';
len = lstrlenW(value);
memcpy(p, value, len * sizeof(WCHAR));
p += len;
if(needs_quote)
*p++ = '"';
}
*p = 0;
WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret) );
return ret;
}
static LPWSTR build_transforms(struct string_list *transform_list)
{
struct string_list *list;
LPWSTR ret, p;
DWORD len;
/* count the space we need */
len = 1;
for(list = transform_list; list; list = list->next)
len += lstrlenW(list->str) + 1;
ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
/* add all the transforms with a semicolon between each one */
p = ret;
for(list = transform_list; list; list = list->next)
{
len = lstrlenW(list->str);
lstrcpynW(p, list->str, len );
p += len;
if(list->next)
*p++ = ';';
}
*p = 0;
return ret;
}
static DWORD msi_atou(LPCWSTR str)
{
DWORD ret = 0;
while(*str >= '0' && *str <= '9')
{
ret *= 10;
ret += (*str - '0');
str++;
}
return ret;
}
/* str1 is the same as str2, ignoring case */
static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2)
{
DWORD len, ret;
LPWSTR strW;
len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
if( !len )
return FALSE;
if( lstrlenW(str1) != (len-1) )
return FALSE;
strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len, strW, len);
HeapFree(GetProcessHeap(), 0, strW);
return (ret == CSTR_EQUAL);
}
/* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
static BOOL msi_option_equal(LPCWSTR str1, LPCSTR str2)
{
if (str1[0] != '/' && str1[0] != '-')
return FALSE;
/* skip over the hyphen or slash */
return msi_strequal(str1 + 1, str2);
}
/* str2 is at the beginning of str1, ignoring case */
static BOOL msi_strprefix(LPCWSTR str1, LPCSTR str2)
{
DWORD len, ret;
LPWSTR strW;
len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
if( !len )
return FALSE;
if( lstrlenW(str1) < (len-1) )
return FALSE;
strW = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len);
MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len-1, strW, len-1);
HeapFree(GetProcessHeap(), 0, strW);
return (ret == CSTR_EQUAL);
}
/* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
static BOOL msi_option_prefix(LPCWSTR str1, LPCSTR str2)
{
if (str1[0] != '/' && str1[0] != '-')
return FALSE;
/* skip over the hyphen or slash */
return msi_strprefix(str1 + 1, str2);
}
static VOID *LoadProc(LPCWSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
{
VOID* (*proc)(void);
*DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
if(!*DllHandle)
{
fprintf(stderr, "Unable to load dll %s\n", wine_dbgstr_w(DllName));
ExitProcess(1);
}
proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
if(!proc)
{
fprintf(stderr, "Dll %s does not implement function %s\n",
wine_dbgstr_w(DllName), ProcName);
FreeLibrary(*DllHandle);
ExitProcess(1);
}
return proc;
}
static DWORD DoDllRegisterServer(LPCWSTR DllName)
{
HRESULT hr;
DLLREGISTERSERVER pfDllRegisterServer = NULL;
HMODULE DllHandle = NULL;
pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);
hr = pfDllRegisterServer();
if(FAILED(hr))
{
fprintf(stderr, "Failed to register dll %s\n", wine_dbgstr_w(DllName));
return 1;
}
printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName));
if(DllHandle)
FreeLibrary(DllHandle);
return 0;
}
static DWORD DoDllUnregisterServer(LPCWSTR DllName)
{
HRESULT hr;
DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
HMODULE DllHandle = NULL;
pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);
hr = pfDllUnregisterServer();
if(FAILED(hr))
{
fprintf(stderr, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName));
return 1;
}
printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName));
if(DllHandle)
FreeLibrary(DllHandle);
return 0;
}
static DWORD DoRegServer(void)
{
static const WCHAR msiserverW[] = {'M','S','I','S','e','r','v','e','r',0};
static const WCHAR msiexecW[] = {'\\','m','s','i','e','x','e','c',' ','/','V',0};
SC_HANDLE scm, service;
WCHAR path[MAX_PATH+12];
DWORD len, ret = 0;
if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CREATE_SERVICE)))
{
fprintf(stderr, "Failed to open the service control manager.\n");
return 1;
}
len = GetSystemDirectoryW(path, MAX_PATH);
lstrcpyW(path + len, msiexecW);
if ((service = CreateServiceW(scm, msiserverW, msiserverW, GENERIC_ALL,
SERVICE_WIN32_SHARE_PROCESS, SERVICE_DEMAND_START,
SERVICE_ERROR_NORMAL, path, NULL, NULL, NULL, NULL, NULL)))
{
CloseServiceHandle(service);
}
else if (GetLastError() != ERROR_SERVICE_EXISTS)
{
fprintf(stderr, "Failed to create MSI service\n");
ret = 1;
}
CloseServiceHandle(scm);
return ret;
}
static DWORD DoUnregServer(void)
{
static const WCHAR msiserverW[] = {'M','S','I','S','e','r','v','e','r',0};
SC_HANDLE scm, service;
DWORD ret = 0;
if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CONNECT)))
{
fprintf(stderr, "Failed to open service control manager\n");
return 1;
}
if ((service = OpenServiceW(scm, msiserverW, DELETE)))
{
if (!DeleteService(service))
{
fprintf(stderr, "Failed to delete MSI service\n");
ret = 1;
}
CloseServiceHandle(service);
}
else if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
{
fprintf(stderr, "Failed to open MSI service\n");
ret = 1;
}
CloseServiceHandle(scm);
return ret;
}
extern UINT CDECL __wine_msi_call_dll_function(GUID *guid);
static DWORD CALLBACK custom_action_thread(void *arg)
{
GUID *guid = arg;
return __wine_msi_call_dll_function(guid);
}
static int DoEmbedding(LPCWSTR key)
{
HANDLE thread;
GUID guid;
UINT r;
/* We need this to unmarshal streams, and some apps expect it to be present. */
CoInitializeEx(NULL, COINIT_MULTITHREADED);
CLSIDFromString(key, &guid);
thread = CreateThread(NULL, 0, custom_action_thread, &guid, 0, NULL);
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &r);
CloseHandle(thread);
CoUninitialize();
return r;
}
/*
* state machine to break up the command line properly
*/
enum chomp_state
{
CS_WHITESPACE,
CS_TOKEN,
CS_QUOTE
};
static int chomp( const WCHAR *in, WCHAR *out )
{
enum chomp_state state = CS_TOKEN;
const WCHAR *p;
int count = 1;
BOOL ignore;
for (p = in; *p; p++)
{
ignore = TRUE;
switch (state)
{
case CS_WHITESPACE:
switch (*p)
{
case ' ':
break;
case '"':
state = CS_QUOTE;
count++;
break;
default:
count++;
ignore = FALSE;
state = CS_TOKEN;
}
break;
case CS_TOKEN:
switch (*p)
{
case '"':
state = CS_QUOTE;
break;
case ' ':
state = CS_WHITESPACE;
if (out) *out++ = 0;
break;
default:
if (p > in && p[-1] == '"')
{
if (out) *out++ = 0;
count++;
}
ignore = FALSE;
}
break;
case CS_QUOTE:
switch (*p)
{
case '"':
state = CS_TOKEN;
break;
default:
ignore = FALSE;
}
break;
}
if (!ignore && out) *out++ = *p;
}
if (out) *out = 0;
return count;
}
static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
{
WCHAR **argv, *p;
int i, count;
*pargc = 0;
*pargv = NULL;
count = chomp( cmdline, NULL );
if (!(p = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(cmdline) + count + 1) * sizeof(WCHAR) )))
return;
count = chomp( cmdline, p );
if (!(argv = HeapAlloc( GetProcessHeap(), 0, (count + 1) * sizeof(WCHAR *) )))
{
HeapFree( GetProcessHeap(), 0, p );
return;
}
for (i = 0; i < count; i++)
{
argv[i] = p;
p += lstrlenW( p ) + 1;
}
argv[i] = NULL;
*pargc = count;
*pargv = argv;
}
static BOOL process_args_from_reg( const WCHAR *ident, int *pargc, WCHAR ***pargv )
{
LONG r;
HKEY hkey;
DWORD sz = 0, type = 0;
WCHAR *buf;
BOOL ret = FALSE;
r = RegOpenKeyW(HKEY_LOCAL_MACHINE, InstallRunOnce, &hkey);
if(r != ERROR_SUCCESS)
return FALSE;
r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
if(r == ERROR_SUCCESS && type == REG_SZ)
{
int len = lstrlenW( *pargv[0] );
if (!(buf = HeapAlloc( GetProcessHeap(), 0, sz + (len + 1) * sizeof(WCHAR) )))
{
RegCloseKey( hkey );
return FALSE;
}
memcpy( buf, *pargv[0], len * sizeof(WCHAR) );
buf[len++] = ' ';
r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)(buf + len), &sz);
if( r == ERROR_SUCCESS )
{
process_args(buf, pargc, pargv);
ret = TRUE;
}
HeapFree(GetProcessHeap(), 0, buf);
}
RegCloseKey(hkey);
return ret;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
int i;
BOOL FunctionInstall = FALSE;
BOOL FunctionInstallAdmin = FALSE;
BOOL FunctionRepair = FALSE;
BOOL FunctionAdvertise = FALSE;
BOOL FunctionPatch = FALSE;
BOOL FunctionDllRegisterServer = FALSE;
BOOL FunctionDllUnregisterServer = FALSE;
BOOL FunctionRegServer = FALSE;
BOOL FunctionUnregServer = FALSE;
BOOL FunctionServer = FALSE;
BOOL FunctionUnknown = FALSE;
LPWSTR PackageName = NULL;
LPWSTR Properties = NULL;
struct string_list *property_list = NULL;
DWORD RepairMode = 0;
DWORD_PTR AdvertiseMode = 0;
struct string_list *transform_list = NULL;
LANGID Language = 0;
DWORD LogMode = 0;
LPWSTR LogFileName = NULL;
DWORD LogAttributes = 0;
LPWSTR PatchFileName = NULL;
INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
INSTALLUILEVEL InstallUILevel = INSTALLUILEVEL_FULL;
LPWSTR DllName = NULL;
DWORD ReturnCode;
int argc;
LPWSTR *argvW = NULL;
/* parse the command line */
process_args( GetCommandLineW(), &argc, &argvW );
/*
* If the args begin with /@ IDENT then we need to load the real
* command line out of the RunOnceEntries key in the registry.
* We do that before starting to process the real commandline,
* then overwrite the commandline again.
*/
if(argc>1 && msi_option_equal(argvW[1], "@"))
{
if(!process_args_from_reg( argvW[2], &argc, &argvW ))
return 1;
}
if (argc == 3 && msi_option_equal(argvW[1], "Embedding"))
return DoEmbedding( argvW[2] );
for(i = 1; i < argc; i++)
{
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
if (msi_option_equal(argvW[i], "regserver"))
{
FunctionRegServer = TRUE;
}
else if (msi_option_equal(argvW[i], "unregserver") || msi_option_equal(argvW[i], "unregister")
|| msi_option_equal(argvW[i], "unreg"))
{
FunctionUnregServer = TRUE;
}
else if(msi_option_prefix(argvW[i], "i") || msi_option_prefix(argvW[i], "package"))
{
LPWSTR argvWi = argvW[i];
int argLen = (msi_option_prefix(argvW[i], "i") ? 2 : 8);
FunctionInstall = TRUE;
if(lstrlenW(argvW[i]) > argLen)
argvWi += argLen;
else
{
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
argvWi = argvW[i];
}
PackageName = argvWi;
}
else if(msi_option_equal(argvW[i], "a"))
{
FunctionInstall = TRUE;
FunctionInstallAdmin = TRUE;
InstallType = INSTALLTYPE_NETWORK_IMAGE;
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
PackageName = argvW[i];
StringListAppend(&property_list, ActionAdmin);
WINE_FIXME("Administrative installs are not currently supported\n");
}
else if(msi_option_prefix(argvW[i], "f"))
{
int j;
int len = lstrlenW(argvW[i]);
FunctionRepair = TRUE;
for(j = 2; j < len; j++)
{
switch(argvW[i][j])
{
case 'P':
case 'p':
RepairMode |= REINSTALLMODE_FILEMISSING;
break;
case 'O':
case 'o':
RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
break;
case 'E':
case 'e':
RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
break;
case 'D':
case 'd':
RepairMode |= REINSTALLMODE_FILEEXACT;
break;
case 'C':
case 'c':
RepairMode |= REINSTALLMODE_FILEVERIFY;
break;
case 'A':
case 'a':
RepairMode |= REINSTALLMODE_FILEREPLACE;
break;
case 'U':
case 'u':
RepairMode |= REINSTALLMODE_USERDATA;
break;
case 'M':
case 'm':
RepairMode |= REINSTALLMODE_MACHINEDATA;
break;
case 'S':
case 's':
RepairMode |= REINSTALLMODE_SHORTCUT;
break;
case 'V':
case 'v':
RepairMode |= REINSTALLMODE_PACKAGE;
break;
default:
fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argvW[i][j]);
break;
}
}
if(len == 2)
{
RepairMode = REINSTALLMODE_FILEMISSING |
REINSTALLMODE_FILEEQUALVERSION |
REINSTALLMODE_FILEVERIFY |
REINSTALLMODE_MACHINEDATA |
REINSTALLMODE_SHORTCUT;
}
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
PackageName = argvW[i];
}
else if(msi_option_prefix(argvW[i], "x") || msi_option_equal(argvW[i], "uninstall"))
{
FunctionInstall = TRUE;
if(msi_option_prefix(argvW[i], "x")) PackageName = argvW[i]+2;
if(!PackageName || !PackageName[0])
{
i++;
if (i >= argc)
ShowUsage(1);
PackageName = argvW[i];
}
WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName));
StringListAppend(&property_list, RemoveAll);
}
else if(msi_option_prefix(argvW[i], "j"))
{
int j;
int len = lstrlenW(argvW[i]);
FunctionAdvertise = TRUE;
for(j = 2; j < len; j++)
{
switch(argvW[i][j])
{
case 'U':
case 'u':
AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
break;
case 'M':
case 'm':
AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
break;
default:
fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argvW[i][j]);
break;
}
}
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
PackageName = argvW[i];
}
else if(msi_strequal(argvW[i], "u"))
{
FunctionAdvertise = TRUE;
AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
PackageName = argvW[i];
}
else if(msi_strequal(argvW[i], "m"))
{
FunctionAdvertise = TRUE;
AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
PackageName = argvW[i];
}
else if(msi_option_equal(argvW[i], "t"))
{
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
StringListAppend(&transform_list, argvW[i]);
}
else if(msi_option_equal(argvW[i], "g"))
{
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
Language = msi_atou(argvW[i]);
}
else if(msi_option_prefix(argvW[i], "l"))
{
int j;
int len = lstrlenW(argvW[i]);
for(j = 2; j < len; j++)
{
switch(argvW[i][j])
{
case 'I':
case 'i':
LogMode |= INSTALLLOGMODE_INFO;
break;
case 'W':
case 'w':
LogMode |= INSTALLLOGMODE_WARNING;
break;
case 'E':
case 'e':
LogMode |= INSTALLLOGMODE_ERROR;
break;
case 'A':
case 'a':
LogMode |= INSTALLLOGMODE_ACTIONSTART;
break;
case 'R':
case 'r':
LogMode |= INSTALLLOGMODE_ACTIONDATA;
break;
case 'U':
case 'u':
LogMode |= INSTALLLOGMODE_USER;
break;
case 'C':
case 'c':
LogMode |= INSTALLLOGMODE_COMMONDATA;
break;
case 'M':
case 'm':
LogMode |= INSTALLLOGMODE_FATALEXIT;
break;
case 'O':
case 'o':
LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
break;
case 'P':
case 'p':
LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
break;
case 'V':
case 'v':
LogMode |= INSTALLLOGMODE_VERBOSE;
break;
case '*':
LogMode = INSTALLLOGMODE_FATALEXIT |
INSTALLLOGMODE_ERROR |
INSTALLLOGMODE_WARNING |
INSTALLLOGMODE_USER |
INSTALLLOGMODE_INFO |
INSTALLLOGMODE_RESOLVESOURCE |
INSTALLLOGMODE_OUTOFDISKSPACE |
INSTALLLOGMODE_ACTIONSTART |
INSTALLLOGMODE_ACTIONDATA |
INSTALLLOGMODE_COMMONDATA |
INSTALLLOGMODE_PROPERTYDUMP |
INSTALLLOGMODE_PROGRESS |
INSTALLLOGMODE_INITIALIZE |
INSTALLLOGMODE_TERMINATE |
INSTALLLOGMODE_SHOWDIALOG;
break;
case '+':
LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
break;
case '!':
LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
break;
default:
break;
}
}
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
LogFileName = argvW[i];
if(MsiEnableLogW(LogMode, LogFileName, LogAttributes) != ERROR_SUCCESS)
{
fprintf(stderr, "Logging in %s (0x%08x, %u) failed\n",
wine_dbgstr_w(LogFileName), LogMode, LogAttributes);
ExitProcess(1);
}
}
else if(msi_option_equal(argvW[i], "p") || msi_option_equal(argvW[i], "update"))
{
FunctionPatch = TRUE;
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
PatchFileName = argvW[i];
}
else if(msi_option_prefix(argvW[i], "q"))
{
if(lstrlenW(argvW[i]) == 2 || msi_strequal(argvW[i]+2, "n") ||
msi_strequal(argvW[i] + 2, "uiet"))
{
InstallUILevel = INSTALLUILEVEL_NONE;
}
else if(msi_strequal(argvW[i]+2, "r"))
{
InstallUILevel = INSTALLUILEVEL_REDUCED;
}
else if(msi_strequal(argvW[i]+2, "f"))
{
InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
}
else if(msi_strequal(argvW[i]+2, "n+"))
{
InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
}
else if(msi_strprefix(argvW[i]+2, "b"))
{
const WCHAR *ptr = argvW[i] + 3;
InstallUILevel = INSTALLUILEVEL_BASIC;
while (*ptr)
{
if (msi_strprefix(ptr, "+"))
InstallUILevel |= INSTALLUILEVEL_ENDDIALOG;
if (msi_strprefix(ptr, "-"))
InstallUILevel |= INSTALLUILEVEL_PROGRESSONLY;
if (msi_strprefix(ptr, "!"))
{
WINE_FIXME("Unhandled modifier: !\n");
InstallUILevel |= INSTALLUILEVEL_HIDECANCEL;
}
ptr++;
}
}
else
{
fprintf(stderr, "Unknown option \"%s\" for UI level\n",
wine_dbgstr_w(argvW[i]+2));
}
}
else if(msi_option_equal(argvW[i], "passive"))
{
static const WCHAR rebootpromptW[] =
{'R','E','B','O','O','T','P','R','O','M','P','T','=','"','S','"',0};
InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY|INSTALLUILEVEL_HIDECANCEL;
StringListAppend(&property_list, rebootpromptW);
}
else if(msi_option_equal(argvW[i], "y"))
{
FunctionDllRegisterServer = TRUE;
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
DllName = argvW[i];
}
else if(msi_option_equal(argvW[i], "z"))
{
FunctionDllUnregisterServer = TRUE;
i++;
if(i >= argc)
ShowUsage(1);
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
DllName = argvW[i];
}
else if(msi_option_equal(argvW[i], "help") || msi_option_equal(argvW[i], "?"))
{
ShowUsage(0);
}
else if(msi_option_equal(argvW[i], "m"))
{
FunctionUnknown = TRUE;
WINE_FIXME("Unknown parameter /m\n");
}
else if(msi_option_equal(argvW[i], "D"))
{
FunctionUnknown = TRUE;
WINE_FIXME("Unknown parameter /D\n");
}
else if (msi_option_equal(argvW[i], "V"))
{
FunctionServer = TRUE;
}
else
StringListAppend(&property_list, argvW[i]);
}
/* start the GUI */
MsiSetInternalUI(InstallUILevel, NULL);
Properties = build_properties( property_list );
if(FunctionInstallAdmin && FunctionPatch)
FunctionInstall = FALSE;
ReturnCode = 1;
if(FunctionInstall)
{
if(IsProductCode(PackageName))
ReturnCode = MsiConfigureProductExW(PackageName, 0, INSTALLSTATE_DEFAULT, Properties);
else
ReturnCode = MsiInstallProductW(PackageName, Properties);
}
else if(FunctionRepair)
{
if(IsProductCode(PackageName))
WINE_FIXME("Product code treatment not implemented yet\n");
else
ReturnCode = MsiReinstallProductW(PackageName, RepairMode);
}
else if(FunctionAdvertise)
{
LPWSTR Transforms = build_transforms( property_list );
ReturnCode = MsiAdvertiseProductW(PackageName, (LPWSTR) AdvertiseMode, Transforms, Language);
}
else if(FunctionPatch)
{
ReturnCode = MsiApplyPatchW(PatchFileName, PackageName, InstallType, Properties);
}
else if(FunctionDllRegisterServer)
{
ReturnCode = DoDllRegisterServer(DllName);
}
else if(FunctionDllUnregisterServer)
{
ReturnCode = DoDllUnregisterServer(DllName);
}
else if (FunctionRegServer)
{
ReturnCode = DoRegServer();
}
else if (FunctionUnregServer)
{
ReturnCode = DoUnregServer();
}
else if (FunctionServer)
{
ReturnCode = DoService();
}
else if (FunctionUnknown)
{
WINE_FIXME( "Unknown function, ignoring\n" );
}
else
ShowUsage(1);
return ReturnCode;
}