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
29 #include "wine/debug.h"
30 #include "wine/unicode.h"
32 WINE_DEFAULT_DEBUG_CHANNEL(msiexec
);
34 typedef HRESULT (WINAPI
*DLLREGISTERSERVER
)(void);
35 typedef HRESULT (WINAPI
*DLLUNREGISTERSERVER
)(void);
37 DWORD
DoService(void);
41 struct string_list
*next
;
45 static const char UsageStr
[] =
47 " Install a product:\n"
48 " msiexec {package|productcode} [property]\n"
49 " msiexec /i {package|productcode} [property]\n"
50 " msiexec /package {package|productcode} [property]\n"
51 " msiexec /a package [property]\n"
52 " Repair an installation:\n"
53 " msiexec /f[p|o|e|d|c|a|u|m|s|v] {package|productcode}\n"
54 " Uninstall a product:\n"
55 " msiexec /x {package|productcode} [property]\n"
56 " Advertise a product:\n"
57 " msiexec /j[u|m] package [/t transform] [/g languageid]\n"
58 " msiexec {u|m} package [/t transform] [/g languageid]\n"
60 " msiexec /p patchpackage [property]\n"
61 " msiexec /p patchpackage /a package [property]\n"
62 " Modifiers for above operations:\n"
63 " msiexec /l[*][i|w|e|a|r|u|c|m|o|p|v|][+|!] logfile\n"
64 " msiexec /q{|n|b|r|f|n+|b+|b-}\n"
65 " Register a module:\n"
66 " msiexec /y module\n"
67 " Unregister a module:\n"
68 " msiexec /z module\n"
69 " Display usage and copyright:\n"
71 "NOTE: Product code on commandline unimplemented as of yet\n"
73 "Copyright 2004 Vincent Béron\n";
75 static const WCHAR ActionAdmin
[] = {
76 'A','C','T','I','O','N','=','A','D','M','I','N',0 };
77 static const WCHAR RemoveAll
[] = {
78 'R','E','M','O','V','E','=','A','L','L',0 };
80 static const WCHAR InstallRunOnce
[] = {
81 'S','o','f','t','w','a','r','e','\\',
82 'M','i','c','r','o','s','o','f','t','\\',
83 'W','i','n','d','o','w','s','\\',
84 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
85 'I','n','s','t','a','l','l','e','r','\\',
86 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
88 static void ShowUsage(int ExitCode
)
91 ExitProcess(ExitCode
);
94 static BOOL
IsProductCode(LPWSTR str
)
98 if(lstrlenW(str
) != 38)
100 return ( (CLSIDFromString(str
, &ProductCode
) == NOERROR
) );
104 static VOID
StringListAppend(struct string_list
**list
, LPCWSTR str
)
106 struct string_list
*entry
;
109 size
= sizeof *entry
+ lstrlenW(str
) * sizeof (WCHAR
);
110 entry
= HeapAlloc(GetProcessHeap(), 0, size
);
113 WINE_ERR("Out of memory!\n");
116 lstrcpyW(entry
->str
, str
);
120 * Ignoring o(n^2) time complexity to add n strings for simplicity,
121 * add the string to the end of the list to preserve the order.
124 list
= &(*list
)->next
;
128 static LPWSTR
build_properties(struct string_list
*property_list
)
130 struct string_list
*list
;
131 LPWSTR ret
, p
, value
;
138 /* count the space we need */
140 for(list
= property_list
; list
; list
= list
->next
)
141 len
+= lstrlenW(list
->str
) + 3;
143 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
145 /* add a space before each string, and quote the value */
147 for(list
= property_list
; list
; list
= list
->next
)
149 value
= strchrW(list
->str
,'=');
152 len
= value
- list
->str
;
154 memcpy(p
, list
->str
, len
* sizeof(WCHAR
));
158 /* check if the value contains spaces and maybe quote it */
160 needs_quote
= strchrW(value
,' ') ? 1 : 0;
163 len
= lstrlenW(value
);
164 memcpy(p
, value
, len
* sizeof(WCHAR
));
171 WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret
) );
176 static LPWSTR
build_transforms(struct string_list
*transform_list
)
178 struct string_list
*list
;
182 /* count the space we need */
184 for(list
= transform_list
; list
; list
= list
->next
)
185 len
+= lstrlenW(list
->str
) + 1;
187 ret
= HeapAlloc( GetProcessHeap(), 0, len
*sizeof(WCHAR
) );
189 /* add all the transforms with a semicolon between each one */
191 for(list
= transform_list
; list
; list
= list
->next
)
193 len
= lstrlenW(list
->str
);
194 lstrcpynW(p
, list
->str
, len
);
204 static DWORD
msi_atou(LPCWSTR str
)
207 while(*str
>= '0' && *str
<= '9')
216 static LPWSTR
msi_strdup(LPCWSTR str
)
218 DWORD len
= lstrlenW(str
)+1;
219 LPWSTR ret
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
224 /* str1 is the same as str2, ignoring case */
225 static BOOL
msi_strequal(LPCWSTR str1
, LPCSTR str2
)
230 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
233 if( lstrlenW(str1
) != (len
-1) )
235 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
236 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
237 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
, strW
, len
);
238 HeapFree(GetProcessHeap(), 0, strW
);
239 return (ret
== CSTR_EQUAL
);
242 /* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
243 static BOOL
msi_option_equal(LPCWSTR str1
, LPCSTR str2
)
245 if (str1
[0] != '/' && str1
[0] != '-')
248 /* skip over the hyphen or slash */
249 return msi_strequal(str1
+ 1, str2
);
252 /* str2 is at the beginning of str1, ignoring case */
253 static BOOL
msi_strprefix(LPCWSTR str1
, LPCSTR str2
)
258 len
= MultiByteToWideChar( CP_ACP
, 0, str2
, -1, NULL
, 0);
261 if( lstrlenW(str1
) < (len
-1) )
263 strW
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*len
);
264 MultiByteToWideChar( CP_ACP
, 0, str2
, -1, strW
, len
);
265 ret
= CompareStringW(GetThreadLocale(), NORM_IGNORECASE
, str1
, len
-1, strW
, len
-1);
266 HeapFree(GetProcessHeap(), 0, strW
);
267 return (ret
== CSTR_EQUAL
);
270 /* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
271 static BOOL
msi_option_prefix(LPCWSTR str1
, LPCSTR str2
)
273 if (str1
[0] != '/' && str1
[0] != '-')
276 /* skip over the hyphen or slash */
277 return msi_strprefix(str1
+ 1, str2
);
280 static VOID
*LoadProc(LPCWSTR DllName
, LPCSTR ProcName
, HMODULE
* DllHandle
)
284 *DllHandle
= LoadLibraryExW(DllName
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
287 fprintf(stderr
, "Unable to load dll %s\n", wine_dbgstr_w(DllName
));
290 proc
= (VOID
*) GetProcAddress(*DllHandle
, ProcName
);
293 fprintf(stderr
, "Dll %s does not implement function %s\n",
294 wine_dbgstr_w(DllName
), ProcName
);
295 FreeLibrary(*DllHandle
);
302 static DWORD
DoDllRegisterServer(LPCWSTR DllName
)
305 DLLREGISTERSERVER pfDllRegisterServer
= NULL
;
306 HMODULE DllHandle
= NULL
;
308 pfDllRegisterServer
= LoadProc(DllName
, "DllRegisterServer", &DllHandle
);
310 hr
= pfDllRegisterServer();
313 fprintf(stderr
, "Failed to register dll %s\n", wine_dbgstr_w(DllName
));
316 printf("Successfully registered dll %s\n", wine_dbgstr_w(DllName
));
318 FreeLibrary(DllHandle
);
322 static DWORD
DoDllUnregisterServer(LPCWSTR DllName
)
325 DLLUNREGISTERSERVER pfDllUnregisterServer
= NULL
;
326 HMODULE DllHandle
= NULL
;
328 pfDllUnregisterServer
= LoadProc(DllName
, "DllUnregisterServer", &DllHandle
);
330 hr
= pfDllUnregisterServer();
333 fprintf(stderr
, "Failed to unregister dll %s\n", wine_dbgstr_w(DllName
));
336 printf("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName
));
338 FreeLibrary(DllHandle
);
342 static DWORD
DoRegServer(void)
344 SC_HANDLE scm
, service
;
345 CHAR path
[MAX_PATH
+12];
348 scm
= OpenSCManagerA(NULL
, SERVICES_ACTIVE_DATABASEA
, SC_MANAGER_CREATE_SERVICE
);
351 fprintf(stderr
, "Failed to open the service control manager.\n");
355 GetSystemDirectoryA(path
, MAX_PATH
);
356 lstrcatA(path
, "\\msiexec.exe /V");
358 service
= CreateServiceA(scm
, "MSIServer", "MSIServer", GENERIC_ALL
,
359 SERVICE_WIN32_SHARE_PROCESS
, SERVICE_DEMAND_START
,
360 SERVICE_ERROR_NORMAL
, path
, NULL
, NULL
,
363 if (service
) CloseServiceHandle(service
);
364 else if (GetLastError() != ERROR_SERVICE_EXISTS
)
366 fprintf(stderr
, "Failed to create MSI service\n");
369 CloseServiceHandle(scm
);
373 static INT
DoEmbedding( LPWSTR key
)
375 printf("Remote custom actions are not supported yet\n");
380 * state machine to break up the command line properly
390 static int chomp( WCHAR
*str
)
392 enum chomp_state state
= cs_token
;
394 int count
= 1, ignore
;
396 for( p
= str
, out
= str
; *p
; p
++ )
424 state
= cs_whitespace
;
452 static void process_args( WCHAR
*cmdline
, int *pargc
, WCHAR
***pargv
)
454 WCHAR
**argv
, *p
= msi_strdup(cmdline
);
458 argv
= HeapAlloc(GetProcessHeap(), 0, sizeof (WCHAR
*)*(n
+1));
462 p
+= lstrlenW(p
) + 1;
470 static BOOL
process_args_from_reg( LPWSTR ident
, int *pargc
, WCHAR
***pargv
)
473 HKEY hkey
= 0, hkeyArgs
= 0;
474 DWORD sz
= 0, type
= 0;
478 r
= RegOpenKeyW(HKEY_LOCAL_MACHINE
, InstallRunOnce
, &hkey
);
479 if(r
!= ERROR_SUCCESS
)
481 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, 0, &sz
);
482 if(r
== ERROR_SUCCESS
&& type
== REG_SZ
)
484 buf
= HeapAlloc(GetProcessHeap(), 0, sz
);
485 r
= RegQueryValueExW(hkey
, ident
, 0, &type
, (LPBYTE
)buf
, &sz
);
486 if( r
== ERROR_SUCCESS
)
488 process_args(buf
, pargc
, pargv
);
491 HeapFree(GetProcessHeap(), 0, buf
);
493 RegCloseKey(hkeyArgs
);
497 int WINAPI
WinMain(HINSTANCE hInstance
, HINSTANCE hPrevInstance
, LPSTR lpCmdLine
, int nCmdShow
)
500 BOOL FunctionInstall
= FALSE
;
501 BOOL FunctionInstallAdmin
= FALSE
;
502 BOOL FunctionRepair
= FALSE
;
503 BOOL FunctionAdvertise
= FALSE
;
504 BOOL FunctionPatch
= FALSE
;
505 BOOL FunctionDllRegisterServer
= FALSE
;
506 BOOL FunctionDllUnregisterServer
= FALSE
;
507 BOOL FunctionRegServer
= FALSE
;
508 BOOL FunctionUnregServer
= FALSE
;
509 BOOL FunctionServer
= FALSE
;
510 BOOL FunctionUnknown
= FALSE
;
512 LPWSTR PackageName
= NULL
;
513 LPWSTR Properties
= NULL
;
514 struct string_list
*property_list
= NULL
;
516 DWORD RepairMode
= 0;
518 DWORD_PTR AdvertiseMode
= 0;
519 struct string_list
*transform_list
= NULL
;
523 LPWSTR LogFileName
= NULL
;
524 DWORD LogAttributes
= 0;
526 LPWSTR PatchFileName
= NULL
;
527 INSTALLTYPE InstallType
= INSTALLTYPE_DEFAULT
;
529 INSTALLUILEVEL InstallUILevel
= INSTALLUILEVEL_FULL
;
531 LPWSTR DllName
= NULL
;
534 LPWSTR
*argvW
= NULL
;
536 /* parse the command line */
537 process_args( GetCommandLineW(), &argc
, &argvW
);
540 * If the args begin with /@ IDENT then we need to load the real
541 * command line out of the RunOnceEntries key in the registry.
542 * We do that before starting to process the real commandline,
543 * then overwrite the commandline again.
545 if(argc
>1 && msi_option_equal(argvW
[1], "@"))
547 if(!process_args_from_reg( argvW
[2], &argc
, &argvW
))
551 if (argc
== 3 && msi_option_equal(argvW
[1], "Embedding"))
552 return DoEmbedding( argvW
[2] );
554 for(i
= 1; i
< argc
; i
++)
556 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
558 if (msi_option_equal(argvW
[i
], "regserver"))
560 FunctionRegServer
= TRUE
;
562 else if (msi_option_equal(argvW
[i
], "unregserver") || msi_option_equal(argvW
[i
], "unregister"))
564 FunctionUnregServer
= TRUE
;
566 else if(msi_option_prefix(argvW
[i
], "i") || msi_option_prefix(argvW
[i
], "package"))
568 LPWSTR argvWi
= argvW
[i
];
569 int argLen
= (msi_option_prefix(argvW
[i
], "i") ? 2 : 8);
570 FunctionInstall
= TRUE
;
571 if(lstrlenW(argvW
[i
]) > argLen
)
578 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
581 PackageName
= argvWi
;
583 else if(msi_option_equal(argvW
[i
], "a"))
585 FunctionInstall
= TRUE
;
586 FunctionInstallAdmin
= TRUE
;
587 InstallType
= INSTALLTYPE_NETWORK_IMAGE
;
591 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
592 PackageName
= argvW
[i
];
593 StringListAppend(&property_list
, ActionAdmin
);
595 else if(msi_option_prefix(argvW
[i
], "f"))
598 int len
= lstrlenW(argvW
[i
]);
599 FunctionRepair
= TRUE
;
600 for(j
= 2; j
< len
; j
++)
606 RepairMode
|= REINSTALLMODE_FILEMISSING
;
610 RepairMode
|= REINSTALLMODE_FILEOLDERVERSION
;
614 RepairMode
|= REINSTALLMODE_FILEEQUALVERSION
;
618 RepairMode
|= REINSTALLMODE_FILEEXACT
;
622 RepairMode
|= REINSTALLMODE_FILEVERIFY
;
626 RepairMode
|= REINSTALLMODE_FILEREPLACE
;
630 RepairMode
|= REINSTALLMODE_USERDATA
;
634 RepairMode
|= REINSTALLMODE_MACHINEDATA
;
638 RepairMode
|= REINSTALLMODE_SHORTCUT
;
642 RepairMode
|= REINSTALLMODE_PACKAGE
;
645 fprintf(stderr
, "Unknown option \"%c\" in Repair mode\n", argvW
[i
][j
]);
651 RepairMode
= REINSTALLMODE_FILEMISSING
|
652 REINSTALLMODE_FILEEQUALVERSION
|
653 REINSTALLMODE_FILEVERIFY
|
654 REINSTALLMODE_MACHINEDATA
|
655 REINSTALLMODE_SHORTCUT
;
660 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
661 PackageName
= argvW
[i
];
663 else if(msi_option_prefix(argvW
[i
], "x"))
665 FunctionInstall
= TRUE
;
666 PackageName
= argvW
[i
]+2;
672 PackageName
= argvW
[i
];
674 WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName
));
675 StringListAppend(&property_list
, RemoveAll
);
677 else if(msi_option_prefix(argvW
[i
], "j"))
680 int len
= lstrlenW(argvW
[i
]);
681 FunctionAdvertise
= TRUE
;
682 for(j
= 2; j
< len
; j
++)
688 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
692 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
695 fprintf(stderr
, "Unknown option \"%c\" in Advertise mode\n", argvW
[i
][j
]);
702 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
703 PackageName
= argvW
[i
];
705 else if(msi_strequal(argvW
[i
], "u"))
707 FunctionAdvertise
= TRUE
;
708 AdvertiseMode
= ADVERTISEFLAGS_USERASSIGN
;
712 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
713 PackageName
= argvW
[i
];
715 else if(msi_strequal(argvW
[i
], "m"))
717 FunctionAdvertise
= TRUE
;
718 AdvertiseMode
= ADVERTISEFLAGS_MACHINEASSIGN
;
722 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
723 PackageName
= argvW
[i
];
725 else if(msi_option_equal(argvW
[i
], "t"))
730 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
731 StringListAppend(&transform_list
, argvW
[i
]);
733 else if(msi_option_equal(argvW
[i
], "g"))
738 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
739 Language
= msi_atou(argvW
[i
]);
741 else if(msi_option_prefix(argvW
[i
], "l"))
744 int len
= lstrlenW(argvW
[i
]);
745 for(j
= 2; j
< len
; j
++)
751 LogMode
|= INSTALLLOGMODE_INFO
;
755 LogMode
|= INSTALLLOGMODE_WARNING
;
759 LogMode
|= INSTALLLOGMODE_ERROR
;
763 LogMode
|= INSTALLLOGMODE_ACTIONSTART
;
767 LogMode
|= INSTALLLOGMODE_ACTIONDATA
;
771 LogMode
|= INSTALLLOGMODE_USER
;
775 LogMode
|= INSTALLLOGMODE_COMMONDATA
;
779 LogMode
|= INSTALLLOGMODE_FATALEXIT
;
783 LogMode
|= INSTALLLOGMODE_OUTOFDISKSPACE
;
787 LogMode
|= INSTALLLOGMODE_PROPERTYDUMP
;
791 LogMode
|= INSTALLLOGMODE_VERBOSE
;
794 LogMode
= INSTALLLOGMODE_FATALEXIT
|
795 INSTALLLOGMODE_ERROR
|
796 INSTALLLOGMODE_WARNING
|
797 INSTALLLOGMODE_USER
|
798 INSTALLLOGMODE_INFO
|
799 INSTALLLOGMODE_RESOLVESOURCE
|
800 INSTALLLOGMODE_OUTOFDISKSPACE
|
801 INSTALLLOGMODE_ACTIONSTART
|
802 INSTALLLOGMODE_ACTIONDATA
|
803 INSTALLLOGMODE_COMMONDATA
|
804 INSTALLLOGMODE_PROPERTYDUMP
|
805 INSTALLLOGMODE_PROGRESS
|
806 INSTALLLOGMODE_INITIALIZE
|
807 INSTALLLOGMODE_TERMINATE
|
808 INSTALLLOGMODE_SHOWDIALOG
;
811 LogAttributes
|= INSTALLLOGATTRIBUTES_APPEND
;
814 LogAttributes
|= INSTALLLOGATTRIBUTES_FLUSHEACHLINE
;
823 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
824 LogFileName
= argvW
[i
];
825 if(MsiEnableLogW(LogMode
, LogFileName
, LogAttributes
) != ERROR_SUCCESS
)
827 fprintf(stderr
, "Logging in %s (0x%08x, %u) failed\n",
828 wine_dbgstr_w(LogFileName
), LogMode
, LogAttributes
);
832 else if(msi_option_equal(argvW
[i
], "p"))
834 FunctionPatch
= TRUE
;
838 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
839 PatchFileName
= argvW
[i
];
841 else if(msi_option_prefix(argvW
[i
], "q"))
843 if(lstrlenW(argvW
[i
]) == 2 || msi_strequal(argvW
[i
]+2, "n") ||
844 msi_strequal(argvW
[i
] + 2, "uiet"))
846 InstallUILevel
= INSTALLUILEVEL_NONE
;
848 else if(msi_strequal(argvW
[i
]+2, "b"))
850 InstallUILevel
= INSTALLUILEVEL_BASIC
;
852 else if(msi_strequal(argvW
[i
]+2, "r"))
854 InstallUILevel
= INSTALLUILEVEL_REDUCED
;
856 else if(msi_strequal(argvW
[i
]+2, "f"))
858 InstallUILevel
= INSTALLUILEVEL_FULL
|INSTALLUILEVEL_ENDDIALOG
;
860 else if(msi_strequal(argvW
[i
]+2, "n+"))
862 InstallUILevel
= INSTALLUILEVEL_NONE
|INSTALLUILEVEL_ENDDIALOG
;
864 else if(msi_strequal(argvW
[i
]+2, "b+"))
866 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
868 else if(msi_strequal(argvW
[i
]+2, "b-"))
870 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_PROGRESSONLY
;
872 else if(msi_strequal(argvW
[i
]+2, "b+!"))
874 InstallUILevel
= INSTALLUILEVEL_BASIC
|INSTALLUILEVEL_ENDDIALOG
;
875 WINE_FIXME("Unknown modifier: !\n");
879 fprintf(stderr
, "Unknown option \"%s\" for UI level\n",
880 wine_dbgstr_w(argvW
[i
]+2));
883 else if(msi_option_equal(argvW
[i
], "y"))
885 FunctionDllRegisterServer
= TRUE
;
889 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
892 else if(msi_option_equal(argvW
[i
], "z"))
894 FunctionDllUnregisterServer
= TRUE
;
898 WINE_TRACE("argvW[%d] = %s\n", i
, wine_dbgstr_w(argvW
[i
]));
901 else if(msi_option_equal(argvW
[i
], "h") || msi_option_equal(argvW
[i
], "?"))
905 else if(msi_option_equal(argvW
[i
], "m"))
907 FunctionUnknown
= TRUE
;
908 WINE_FIXME("Unknown parameter /m\n");
910 else if(msi_option_equal(argvW
[i
], "D"))
912 FunctionUnknown
= TRUE
;
913 WINE_FIXME("Unknown parameter /D\n");
915 else if (msi_option_equal(argvW
[i
], "V"))
917 FunctionServer
= TRUE
;
920 StringListAppend(&property_list
, argvW
[i
]);
924 MsiSetInternalUI(InstallUILevel
, NULL
);
926 Properties
= build_properties( property_list
);
928 if(FunctionInstallAdmin
&& FunctionPatch
)
929 FunctionInstall
= FALSE
;
934 if(IsProductCode(PackageName
))
935 ReturnCode
= MsiConfigureProductExW(PackageName
, 0, INSTALLSTATE_DEFAULT
, Properties
);
937 ReturnCode
= MsiInstallProductW(PackageName
, Properties
);
939 else if(FunctionRepair
)
941 if(IsProductCode(PackageName
))
942 WINE_FIXME("Product code treatment not implemented yet\n");
944 ReturnCode
= MsiReinstallProductW(PackageName
, RepairMode
);
946 else if(FunctionAdvertise
)
948 LPWSTR Transforms
= build_transforms( property_list
);
949 ReturnCode
= MsiAdvertiseProductW(PackageName
, (LPWSTR
) AdvertiseMode
, Transforms
, Language
);
951 else if(FunctionPatch
)
953 ReturnCode
= MsiApplyPatchW(PatchFileName
, PackageName
, InstallType
, Properties
);
955 else if(FunctionDllRegisterServer
)
957 ReturnCode
= DoDllRegisterServer(DllName
);
959 else if(FunctionDllUnregisterServer
)
961 ReturnCode
= DoDllUnregisterServer(DllName
);
963 else if (FunctionRegServer
)
965 ReturnCode
= DoRegServer();
967 else if (FunctionUnregServer
)
969 WINE_FIXME( "/unregserver not implemented yet, ignoring\n" );
971 else if (FunctionServer
)
973 ReturnCode
= DoService();
975 else if (FunctionUnknown
)
977 WINE_FIXME( "Unknown function, ignoring\n" );