| /* |
| * 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; |
| DWORD size; |
| |
| size = sizeof *entry + lstrlenW(str) * sizeof (WCHAR); |
| entry = HeapAlloc(GetProcessHeap(), 0, size); |
| 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; |
| } |
| |
| static LPWSTR msi_strdup(LPCWSTR str) |
| { |
| DWORD len = lstrlenW(str)+1; |
| LPWSTR ret = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*len); |
| lstrcpyW(ret, 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) |
| { |
| SC_HANDLE scm, service; |
| CHAR path[MAX_PATH+12]; |
| DWORD ret = 0; |
| |
| scm = OpenSCManagerA(NULL, SERVICES_ACTIVE_DATABASEA, SC_MANAGER_CREATE_SERVICE); |
| if (!scm) |
| { |
| fprintf(stderr, "Failed to open the service control manager.\n"); |
| return 1; |
| } |
| |
| GetSystemDirectoryA(path, MAX_PATH); |
| lstrcatA(path, "\\msiexec.exe /V"); |
| |
| service = CreateServiceA(scm, "MSIServer", "MSIServer", GENERIC_ALL, |
| SERVICE_WIN32_SHARE_PROCESS, SERVICE_DEMAND_START, |
| SERVICE_ERROR_NORMAL, path, NULL, NULL, |
| NULL, NULL, NULL); |
| |
| if (service) CloseServiceHandle(service); |
| else if (GetLastError() != ERROR_SERVICE_EXISTS) |
| { |
| fprintf(stderr, "Failed to create MSI service\n"); |
| ret = 1; |
| } |
| CloseServiceHandle(scm); |
| return ret; |
| } |
| |
| static INT DoEmbedding( LPWSTR key ) |
| { |
| printf("Remote custom actions are not supported yet\n"); |
| return 1; |
| } |
| |
| /* |
| * state machine to break up the command line properly |
| */ |
| |
| enum chomp_state |
| { |
| cs_whitespace, |
| cs_token, |
| cs_quote |
| }; |
| |
| static int chomp( WCHAR *str ) |
| { |
| enum chomp_state state = cs_token; |
| WCHAR *p, *out; |
| int count = 1, ignore; |
| |
| for( p = str, out = str; *p; p++ ) |
| { |
| ignore = 1; |
| switch( state ) |
| { |
| case cs_whitespace: |
| switch( *p ) |
| { |
| case ' ': |
| break; |
| case '"': |
| state = cs_quote; |
| count++; |
| break; |
| default: |
| count++; |
| ignore = 0; |
| state = cs_token; |
| } |
| break; |
| |
| case cs_token: |
| switch( *p ) |
| { |
| case '"': |
| state = cs_quote; |
| break; |
| case ' ': |
| state = cs_whitespace; |
| *out++ = 0; |
| break; |
| default: |
| ignore = 0; |
| } |
| break; |
| |
| case cs_quote: |
| switch( *p ) |
| { |
| case '"': |
| state = cs_token; |
| break; |
| default: |
| ignore = 0; |
| } |
| break; |
| } |
| if( !ignore ) |
| *out++ = *p; |
| } |
| |
| *out = 0; |
| |
| return count; |
| } |
| |
| static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv ) |
| { |
| WCHAR **argv, *p = msi_strdup(cmdline); |
| int i, n; |
| |
| n = chomp( p ); |
| argv = HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR*)*(n+1)); |
| for( i=0; i<n; i++ ) |
| { |
| argv[i] = p; |
| p += lstrlenW(p) + 1; |
| } |
| argv[i] = NULL; |
| |
| *pargc = n; |
| *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); |
| } |
| 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")) |
| { |
| 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, "b")) |
| { |
| InstallUILevel = INSTALLUILEVEL_BASIC; |
| } |
| 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_strequal(argvW[i]+2, "b+")) |
| { |
| InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG; |
| } |
| else if(msi_strequal(argvW[i]+2, "b-")) |
| { |
| InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY; |
| } |
| else if(msi_strequal(argvW[i]+2, "b+!")) |
| { |
| InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_ENDDIALOG; |
| WINE_FIXME("Unknown modifier: !\n"); |
| } |
| else if(msi_strequal(argvW[i]+2, "b!")) |
| { |
| InstallUILevel = INSTALLUILEVEL_BASIC; |
| WINE_FIXME("Unknown modifier: !\n"); |
| } |
| else |
| { |
| fprintf(stderr, "Unknown option \"%s\" for UI level\n", |
| wine_dbgstr_w(argvW[i]+2)); |
| } |
| } |
| 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) |
| { |
| WINE_FIXME( "/unregserver not implemented yet, ignoring\n" ); |
| } |
| else if (FunctionServer) |
| { |
| ReturnCode = DoService(); |
| } |
| else if (FunctionUnknown) |
| { |
| WINE_FIXME( "Unknown function, ignoring\n" ); |
| } |
| else |
| ShowUsage(1); |
| |
| return ReturnCode; |
| } |