ddraw/tests: Use compare_uint() in compare_float() instead of abs().
[wine.git] / programs / msiexec / msiexec.c
blob71ecc0e590ab7468878dda4ff08a21c5342ee9b2
1 /*
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
24 #include <windows.h>
25 #include <commctrl.h>
26 #include <msi.h>
27 #include <winsvc.h>
28 #include <objbase.h>
29 #include <stdio.h>
31 #include "wine/debug.h"
32 #include "wine/heap.h"
34 #include "initguid.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);
44 struct string_list
46 struct string_list *next;
47 WCHAR str[1];
50 static void ShowUsage(int ExitCode)
52 WCHAR msiexec_version[40];
53 WCHAR filename[MAX_PATH];
54 LPWSTR msi_res;
55 LPWSTR msiexec_help;
56 HMODULE hmsi = GetModuleHandleA("msi.dll");
57 DWORD len;
58 DWORD res;
60 /* MsiGetFileVersion need the full path */
61 *filename = 0;
62 res = GetModuleFileNameW(hmsi, filename, ARRAY_SIZE(filename));
63 if (!res)
64 WINE_ERR("GetModuleFileName failed: %ld\n", GetLastError());
66 len = ARRAY_SIZE(msiexec_version);
67 *msiexec_version = 0;
68 res = MsiGetFileVersionW(filename, msiexec_version, &len, NULL, NULL);
69 if (res)
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) {
79 *msi_res = 0;
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)
92 GUID ProductCode;
94 if(lstrlenW(str) != 38)
95 return FALSE;
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]));
105 if(!entry)
107 WINE_ERR("Out of memory!\n");
108 ExitProcess(1);
110 lstrcpyW(entry->str, str);
111 entry->next = NULL;
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.
117 while( *list )
118 list = &(*list)->next;
119 *list = entry;
122 static LPWSTR build_properties(struct string_list *property_list)
124 struct string_list *list;
125 LPWSTR ret, p, value;
126 DWORD len;
127 BOOL needs_quote;
129 if(!property_list)
130 return NULL;
132 /* count the space we need */
133 len = 1;
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 */
140 p = ret;
141 for(list = property_list; list; list = list->next)
143 value = wcschr(list->str,'=');
144 if(!value)
145 continue;
146 len = value - list->str;
147 *p++ = ' ';
148 memcpy(p, list->str, len * sizeof(WCHAR));
149 p += len;
150 *p++ = '=';
152 /* check if the value contains spaces and maybe quote it */
153 value++;
154 needs_quote = wcschr(value,' ') ? 1 : 0;
155 if(needs_quote)
156 *p++ = '"';
157 len = lstrlenW(value);
158 memcpy(p, value, len * sizeof(WCHAR));
159 p += len;
160 if(needs_quote)
161 *p++ = '"';
163 *p = 0;
165 WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret) );
167 return ret;
170 static LPWSTR build_transforms(struct string_list *transform_list)
172 struct string_list *list;
173 LPWSTR ret, p;
174 DWORD len;
176 /* count the space we need */
177 len = 1;
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 */
184 p = ret;
185 for(list = transform_list; list; list = list->next)
187 len = lstrlenW(list->str);
188 lstrcpynW(p, list->str, len );
189 p += len;
190 if(list->next)
191 *p++ = ';';
193 *p = 0;
195 return ret;
198 static DWORD msi_atou(LPCWSTR str)
200 DWORD ret = 0;
201 while(*str >= '0' && *str <= '9')
203 ret *= 10;
204 ret += (*str - '0');
205 str++;
207 return ret;
210 /* str1 is the same as str2, ignoring case */
211 static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2)
213 DWORD len, ret;
214 LPWSTR strW;
216 len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
217 if( !len )
218 return FALSE;
219 if( lstrlenW(str1) != (len-1) )
220 return FALSE;
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] != '-')
232 return FALSE;
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)
241 DWORD len, ret;
242 LPWSTR strW;
244 len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
245 if( !len )
246 return FALSE;
247 if( lstrlenW(str1) < (len-1) )
248 return FALSE;
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] != '-')
260 return FALSE;
262 /* skip over the hyphen or slash */
263 return msi_strprefix(str1 + 1, str2);
266 static VOID *LoadProc(LPCWSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
268 VOID* (*proc)(void);
270 *DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
271 if(!*DllHandle)
273 fprintf(stderr, "Unable to load dll %s\n", wine_dbgstr_w(DllName));
274 ExitProcess(1);
276 proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
277 if(!proc)
279 fprintf(stderr, "Dll %s does not implement function %s\n",
280 wine_dbgstr_w(DllName), ProcName);
281 FreeLibrary(*DllHandle);
282 ExitProcess(1);
285 return proc;
288 static DWORD DoDllRegisterServer(LPCWSTR DllName)
290 HRESULT hr;
291 DLLREGISTERSERVER pfDllRegisterServer = NULL;
292 HMODULE DllHandle = NULL;
294 pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);
296 hr = pfDllRegisterServer();
297 if(FAILED(hr))
299 fprintf(stderr, "Failed to register dll %s\n", wine_dbgstr_w(DllName));
300 return 1;
302 printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName));
303 if(DllHandle)
304 FreeLibrary(DllHandle);
305 return 0;
308 static DWORD DoDllUnregisterServer(LPCWSTR DllName)
310 HRESULT hr;
311 DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
312 HMODULE DllHandle = NULL;
314 pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);
316 hr = pfDllUnregisterServer();
317 if(FAILED(hr))
319 fprintf(stderr, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName));
320 return 1;
322 printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName));
323 if(DllHandle)
324 FreeLibrary(DllHandle);
325 return 0;
328 static DWORD DoRegServer(void)
330 SC_HANDLE scm, service;
331 WCHAR path[MAX_PATH+12];
332 DWORD len, ret = 0;
334 if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CREATE_SERVICE)))
336 fprintf(stderr, "Failed to open the service control manager.\n");
337 return 1;
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");
350 ret = 1;
352 CloseServiceHandle(scm);
353 return ret;
356 static DWORD DoUnregServer(void)
358 SC_HANDLE scm, service;
359 DWORD ret = 0;
361 if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CONNECT)))
363 fprintf(stderr, "Failed to open service control manager\n");
364 return 1;
366 if ((service = OpenServiceW(scm, L"MSIServer", DELETE)))
368 if (!DeleteService(service))
370 fprintf(stderr, "Failed to delete MSI service\n");
371 ret = 1;
373 CloseServiceHandle(service);
375 else if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
377 fprintf(stderr, "Failed to open MSI service\n");
378 ret = 1;
380 CloseServiceHandle(scm);
381 return ret;
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;
391 heap_free(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;
398 DWORD64 thread64;
399 WCHAR buffer[24];
400 HANDLE thread;
401 HANDLE pipe;
402 DWORD size;
404 TRACE("%s\n", debugstr_w(arg));
406 if (!(client_pid = wcstol(arg, NULL, 10)))
408 ERR("Invalid parameter %s\n", debugstr_w(arg));
409 return 1;
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 */
428 CoUninitialize();
429 return 0;
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());
442 CoUninitialize();
443 return GetLastError();
446 ERR("Failed to read from custom action server pipe: %lu\n", GetLastError());
447 CoUninitialize();
448 return GetLastError();
452 * state machine to break up the command line properly
455 enum chomp_state
457 CS_WHITESPACE,
458 CS_TOKEN,
459 CS_QUOTE
462 static int chomp( const WCHAR *in, WCHAR *out )
464 enum chomp_state state = CS_TOKEN;
465 const WCHAR *p;
466 int count = 1;
467 BOOL ignore;
469 for (p = in; *p; p++)
471 ignore = TRUE;
472 switch (state)
474 case CS_WHITESPACE:
475 switch (*p)
477 case ' ':
478 break;
479 case '"':
480 state = CS_QUOTE;
481 count++;
482 break;
483 default:
484 count++;
485 ignore = FALSE;
486 state = CS_TOKEN;
488 break;
490 case CS_TOKEN:
491 switch (*p)
493 case '"':
494 state = CS_QUOTE;
495 break;
496 case ' ':
497 state = CS_WHITESPACE;
498 if (out) *out++ = 0;
499 break;
500 default:
501 if (p > in && p[-1] == '"')
503 if (out) *out++ = 0;
504 count++;
506 ignore = FALSE;
508 break;
510 case CS_QUOTE:
511 switch (*p)
513 case '"':
514 state = CS_TOKEN;
515 break;
516 default:
517 ignore = FALSE;
519 break;
521 if (!ignore && out) *out++ = *p;
523 if (out) *out = 0;
524 return count;
527 static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
529 WCHAR **argv, *p;
530 int i, count;
532 *pargc = 0;
533 *pargv = NULL;
535 count = chomp( cmdline, NULL );
536 if (!(p = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(cmdline) + count + 1) * sizeof(WCHAR) )))
537 return;
539 count = chomp( cmdline, p );
540 if (!(argv = HeapAlloc( GetProcessHeap(), 0, (count + 1) * sizeof(WCHAR *) )))
542 HeapFree( GetProcessHeap(), 0, p );
543 return;
545 for (i = 0; i < count; i++)
547 argv[i] = p;
548 p += lstrlenW( p ) + 1;
550 argv[i] = NULL;
552 *pargc = count;
553 *pargv = argv;
556 static BOOL process_args_from_reg( const WCHAR *ident, int *pargc, WCHAR ***pargv )
558 LONG r;
559 HKEY hkey;
560 DWORD sz = 0, type = 0;
561 WCHAR *buf;
562 BOOL ret = FALSE;
564 r = RegOpenKeyW(HKEY_LOCAL_MACHINE,
565 L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\RunOnceEntries", &hkey);
566 if(r != ERROR_SUCCESS)
567 return FALSE;
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) )))
574 RegCloseKey( hkey );
575 return FALSE;
577 memcpy( buf, *pargv[0], len * sizeof(WCHAR) );
578 buf[len++] = ' ';
579 r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)(buf + len), &sz);
580 if( r == ERROR_SUCCESS )
582 process_args(buf, pargc, pargv);
583 ret = TRUE;
585 HeapFree(GetProcessHeap(), 0, buf);
587 RegCloseKey(hkey);
588 return ret;
591 static WCHAR *get_path_with_extension(const WCHAR *package_name)
593 static const WCHAR ext[] = L".msi";
594 unsigned int p;
595 WCHAR *path;
597 if (!(path = heap_alloc(lstrlenW(package_name) * sizeof(WCHAR) + sizeof(ext))))
599 WINE_ERR("No memory.\n");
600 return NULL;
603 lstrcpyW(path, package_name);
604 p = lstrlenW(path);
605 while (p && path[p] != '.' && path[p] != L'\\' && path[p] != '/')
606 --p;
607 if (path[p] == '.')
609 heap_free(path);
610 return NULL;
612 lstrcatW(path, ext);
613 return path;
616 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
618 int i;
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;
639 LANGID Language = 0;
641 DWORD LogMode = 0;
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;
651 DWORD ReturnCode;
652 int argc;
653 LPWSTR *argvW = NULL;
654 WCHAR *path;
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 ))
670 return 1;
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)
695 argvWi += argLen;
696 else
698 i++;
699 if(i >= argc)
700 ShowUsage(1);
701 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
702 argvWi = 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;
711 i++;
712 if(i >= argc)
713 ShowUsage(1);
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"))
721 int j;
722 int len = lstrlenW(argvW[i]);
723 FunctionRepair = TRUE;
724 for(j = 2; j < len; j++)
726 switch(argvW[i][j])
728 case 'P':
729 case 'p':
730 RepairMode |= REINSTALLMODE_FILEMISSING;
731 break;
732 case 'O':
733 case 'o':
734 RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
735 break;
736 case 'E':
737 case 'e':
738 RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
739 break;
740 case 'D':
741 case 'd':
742 RepairMode |= REINSTALLMODE_FILEEXACT;
743 break;
744 case 'C':
745 case 'c':
746 RepairMode |= REINSTALLMODE_FILEVERIFY;
747 break;
748 case 'A':
749 case 'a':
750 RepairMode |= REINSTALLMODE_FILEREPLACE;
751 break;
752 case 'U':
753 case 'u':
754 RepairMode |= REINSTALLMODE_USERDATA;
755 break;
756 case 'M':
757 case 'm':
758 RepairMode |= REINSTALLMODE_MACHINEDATA;
759 break;
760 case 'S':
761 case 's':
762 RepairMode |= REINSTALLMODE_SHORTCUT;
763 break;
764 case 'V':
765 case 'v':
766 RepairMode |= REINSTALLMODE_PACKAGE;
767 break;
768 default:
769 fprintf(stderr, "Unknown option \"%c\" in Repair mode\n", argvW[i][j]);
770 break;
773 if(len == 2)
775 RepairMode = REINSTALLMODE_FILEMISSING |
776 REINSTALLMODE_FILEEQUALVERSION |
777 REINSTALLMODE_FILEVERIFY |
778 REINSTALLMODE_MACHINEDATA |
779 REINSTALLMODE_SHORTCUT;
781 i++;
782 if(i >= argc)
783 ShowUsage(1);
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])
793 i++;
794 if (i >= argc)
795 ShowUsage(1);
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"))
803 int j;
804 int len = lstrlenW(argvW[i]);
805 FunctionAdvertise = TRUE;
806 for(j = 2; j < len; j++)
808 switch(argvW[i][j])
810 case 'U':
811 case 'u':
812 AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
813 break;
814 case 'M':
815 case 'm':
816 AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
817 break;
818 default:
819 fprintf(stderr, "Unknown option \"%c\" in Advertise mode\n", argvW[i][j]);
820 break;
823 i++;
824 if(i >= argc)
825 ShowUsage(1);
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;
833 i++;
834 if(i >= argc)
835 ShowUsage(1);
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;
843 i++;
844 if(i >= argc)
845 ShowUsage(1);
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"))
851 i++;
852 if(i >= argc)
853 ShowUsage(1);
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"))
859 i++;
860 if(i >= argc)
861 ShowUsage(1);
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"))
867 int j;
868 int len = lstrlenW(argvW[i]);
869 for(j = 2; j < len; j++)
871 switch(argvW[i][j])
873 case 'I':
874 case 'i':
875 LogMode |= INSTALLLOGMODE_INFO;
876 break;
877 case 'W':
878 case 'w':
879 LogMode |= INSTALLLOGMODE_WARNING;
880 break;
881 case 'E':
882 case 'e':
883 LogMode |= INSTALLLOGMODE_ERROR;
884 break;
885 case 'A':
886 case 'a':
887 LogMode |= INSTALLLOGMODE_ACTIONSTART;
888 break;
889 case 'R':
890 case 'r':
891 LogMode |= INSTALLLOGMODE_ACTIONDATA;
892 break;
893 case 'U':
894 case 'u':
895 LogMode |= INSTALLLOGMODE_USER;
896 break;
897 case 'C':
898 case 'c':
899 LogMode |= INSTALLLOGMODE_COMMONDATA;
900 break;
901 case 'M':
902 case 'm':
903 LogMode |= INSTALLLOGMODE_FATALEXIT;
904 break;
905 case 'O':
906 case 'o':
907 LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
908 break;
909 case 'P':
910 case 'p':
911 LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
912 break;
913 case 'V':
914 case 'v':
915 LogMode |= INSTALLLOGMODE_VERBOSE;
916 break;
917 case '*':
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;
933 break;
934 case '+':
935 LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
936 break;
937 case '!':
938 LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
939 break;
940 default:
941 break;
944 i++;
945 if(i >= argc)
946 ShowUsage(1);
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);
953 ExitProcess(1);
956 else if(msi_option_equal(argvW[i], "p") || msi_option_equal(argvW[i], "update"))
958 FunctionPatch = TRUE;
959 i++;
960 if(i >= argc)
961 ShowUsage(1);
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;
990 while (*ptr)
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;
1001 ptr++;
1004 else
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;
1018 i++;
1019 if(i >= argc)
1020 ShowUsage(1);
1021 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
1022 DllName = argvW[i];
1024 else if(msi_option_equal(argvW[i], "z"))
1026 FunctionDllUnregisterServer = TRUE;
1027 i++;
1028 if(i >= argc)
1029 ShowUsage(1);
1030 WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
1031 DllName = argvW[i];
1033 else if(msi_option_equal(argvW[i], "help") || msi_option_equal(argvW[i], "?"))
1035 ShowUsage(0);
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;
1051 else
1052 StringListAppend(&property_list, argvW[i]);
1055 /* start the GUI */
1056 MsiSetInternalUI(InstallUILevel, NULL);
1058 Properties = build_properties( property_list );
1060 if(FunctionInstallAdmin && FunctionPatch)
1061 FunctionInstall = FALSE;
1063 ReturnCode = 1;
1064 if(FunctionInstall)
1066 if(IsProductCode(PackageName))
1067 ReturnCode = MsiConfigureProductExW(PackageName, 0, INSTALLSTATE_DEFAULT, Properties);
1068 else
1070 if ((ReturnCode = MsiInstallProductW(PackageName, Properties)) == ERROR_FILE_NOT_FOUND
1071 && (path = get_path_with_extension(PackageName)))
1073 ReturnCode = MsiInstallProductW(path, Properties);
1074 heap_free(path);
1078 else if(FunctionRepair)
1080 if(IsProductCode(PackageName))
1081 WINE_FIXME("Product code treatment not implemented yet\n");
1082 else
1084 if ((ReturnCode = MsiReinstallProductW(PackageName, RepairMode)) == ERROR_FILE_NOT_FOUND
1085 && (path = get_path_with_extension(PackageName)))
1087 ReturnCode = MsiReinstallProductW(path, RepairMode);
1088 heap_free(path);
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" );
1125 else
1126 ShowUsage(1);
1128 return ReturnCode;