Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
wine-mirror
GitHub Repository: wine-mirror/wine
Path: blob/master/programs/msiexec/msiexec.c
4393 views
1
/*
2
* msiexec.exe implementation
3
*
4
* Copyright 2004 Vincent Béron
5
* Copyright 2005 Mike McCormack
6
*
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.
11
*
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.
16
*
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
20
*/
21
22
#define WIN32_LEAN_AND_MEAN
23
24
#include <windows.h>
25
#include <commctrl.h>
26
#include <msi.h>
27
#include <winsvc.h>
28
#include <objbase.h>
29
30
#include "wine/debug.h"
31
#include "msiexec_internal.h"
32
33
#include "initguid.h"
34
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
35
36
WINE_DEFAULT_DEBUG_CHANNEL(msiexec);
37
38
typedef HRESULT (WINAPI *DLLREGISTERSERVER)(void);
39
typedef HRESULT (WINAPI *DLLUNREGISTERSERVER)(void);
40
41
DWORD DoService(void);
42
static BOOL silent;
43
44
struct string_list
45
{
46
struct string_list *next;
47
WCHAR str[1];
48
};
49
50
void report_error(const char* msg, ...)
51
{
52
char buffer[2048];
53
va_list va_args;
54
55
va_start(va_args, msg);
56
vsnprintf(buffer, sizeof(buffer), msg, va_args);
57
va_end(va_args);
58
59
if (silent)
60
MESSAGE("%s", buffer);
61
else
62
MsiMessageBoxA(NULL, buffer, "MsiExec", 0, GetUserDefaultLangID(), 0);
63
}
64
65
static void ShowUsage(int ExitCode)
66
{
67
WCHAR msiexec_version[40];
68
WCHAR filename[MAX_PATH];
69
LPWSTR msi_res;
70
LPWSTR msiexec_help;
71
HMODULE hmsi = GetModuleHandleA("msi.dll");
72
DWORD len;
73
DWORD res;
74
75
/* MsiGetFileVersion need the full path */
76
*filename = 0;
77
res = GetModuleFileNameW(hmsi, filename, ARRAY_SIZE(filename));
78
if (!res)
79
WINE_ERR("GetModuleFileName failed: %ld\n", GetLastError());
80
81
len = ARRAY_SIZE(msiexec_version);
82
*msiexec_version = 0;
83
res = MsiGetFileVersionW(filename, msiexec_version, &len, NULL, NULL);
84
if (res)
85
WINE_ERR("MsiGetFileVersion failed with %ld\n", res);
86
87
/* Return the length of the resource.
88
No typo: The LPWSTR parameter must be a LPWSTR * for this mode */
89
len = LoadStringW(hmsi, 10, (LPWSTR) &msi_res, 0);
90
91
msi_res = malloc((len + 1) * sizeof(WCHAR));
92
msiexec_help = malloc((len + 1) * sizeof(WCHAR) + sizeof(msiexec_version));
93
if (msi_res && msiexec_help) {
94
*msi_res = 0;
95
LoadStringW(hmsi, 10, msi_res, len + 1);
96
97
swprintf(msiexec_help, len + 1 + ARRAY_SIZE(msiexec_version), msi_res, msiexec_version);
98
MsiMessageBoxW(0, msiexec_help, NULL, 0, GetUserDefaultLangID(), 0);
99
}
100
free(msi_res);
101
free(msiexec_help);
102
ExitProcess(ExitCode);
103
}
104
105
static BOOL IsProductCode(LPWSTR str)
106
{
107
GUID ProductCode;
108
109
if(lstrlenW(str) != 38)
110
return FALSE;
111
return ( (CLSIDFromString(str, &ProductCode) == NOERROR) );
112
113
}
114
115
static VOID StringListAppend(struct string_list **list, LPCWSTR str)
116
{
117
struct string_list *entry;
118
119
entry = malloc(FIELD_OFFSET(struct string_list, str[wcslen(str) + 1]));
120
if(!entry)
121
{
122
WINE_ERR("Out of memory!\n");
123
ExitProcess(1);
124
}
125
lstrcpyW(entry->str, str);
126
entry->next = NULL;
127
128
/*
129
* Ignoring o(n^2) time complexity to add n strings for simplicity,
130
* add the string to the end of the list to preserve the order.
131
*/
132
while( *list )
133
list = &(*list)->next;
134
*list = entry;
135
}
136
137
static LPWSTR build_properties(struct string_list *property_list)
138
{
139
struct string_list *list;
140
LPWSTR ret, p, value;
141
DWORD len;
142
BOOL needs_quote;
143
144
if(!property_list)
145
return NULL;
146
147
/* count the space we need */
148
len = 1;
149
for(list = property_list; list; list = list->next)
150
len += lstrlenW(list->str) + 3;
151
152
ret = malloc(len * sizeof(WCHAR));
153
154
/* add a space before each string, and quote the value */
155
p = ret;
156
for(list = property_list; list; list = list->next)
157
{
158
value = wcschr(list->str,'=');
159
if(!value)
160
continue;
161
len = value - list->str;
162
*p++ = ' ';
163
memcpy(p, list->str, len * sizeof(WCHAR));
164
p += len;
165
*p++ = '=';
166
167
/* check if the value contains spaces and maybe quote it */
168
value++;
169
needs_quote = *value != '"' && wcschr(value, ' ');
170
if(needs_quote)
171
*p++ = '"';
172
len = lstrlenW(value);
173
memcpy(p, value, len * sizeof(WCHAR));
174
p += len;
175
if(needs_quote)
176
*p++ = '"';
177
}
178
*p = 0;
179
180
WINE_TRACE("properties -> %s\n", wine_dbgstr_w(ret) );
181
182
return ret;
183
}
184
185
static LPWSTR build_transforms(struct string_list *transform_list)
186
{
187
struct string_list *list;
188
LPWSTR ret, p;
189
DWORD len;
190
191
/* count the space we need */
192
len = 1;
193
for(list = transform_list; list; list = list->next)
194
len += lstrlenW(list->str) + 1;
195
196
ret = malloc(len * sizeof(WCHAR));
197
198
/* add all the transforms with a semicolon between each one */
199
p = ret;
200
for(list = transform_list; list; list = list->next)
201
{
202
len = lstrlenW(list->str);
203
lstrcpynW(p, list->str, len );
204
p += len;
205
if(list->next)
206
*p++ = ';';
207
}
208
*p = 0;
209
210
return ret;
211
}
212
213
static DWORD msi_atou(LPCWSTR str)
214
{
215
DWORD ret = 0;
216
while(*str >= '0' && *str <= '9')
217
{
218
ret *= 10;
219
ret += (*str - '0');
220
str++;
221
}
222
return ret;
223
}
224
225
/* str1 is the same as str2, ignoring case */
226
static BOOL msi_strequal(LPCWSTR str1, LPCSTR str2)
227
{
228
DWORD len, ret;
229
LPWSTR strW;
230
231
len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
232
if( !len )
233
return FALSE;
234
if( lstrlenW(str1) != (len-1) )
235
return FALSE;
236
strW = malloc(sizeof(WCHAR) * len);
237
MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
238
ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len, strW, len);
239
free(strW);
240
return (ret == CSTR_EQUAL);
241
}
242
243
/* prefix is hyphen or dash, and str1 is the same as str2, ignoring case */
244
static BOOL msi_option_equal(LPCWSTR str1, LPCSTR str2)
245
{
246
if (str1[0] != '/' && str1[0] != '-')
247
return FALSE;
248
249
/* skip over the hyphen or slash */
250
return msi_strequal(str1 + 1, str2);
251
}
252
253
/* str2 is at the beginning of str1, ignoring case */
254
static BOOL msi_strprefix(LPCWSTR str1, LPCSTR str2)
255
{
256
DWORD len, ret;
257
LPWSTR strW;
258
259
len = MultiByteToWideChar( CP_ACP, 0, str2, -1, NULL, 0);
260
if( !len )
261
return FALSE;
262
if( lstrlenW(str1) < (len-1) )
263
return FALSE;
264
strW = malloc(sizeof(WCHAR) * len);
265
MultiByteToWideChar( CP_ACP, 0, str2, -1, strW, len);
266
ret = CompareStringW(GetThreadLocale(), NORM_IGNORECASE, str1, len-1, strW, len-1);
267
free(strW);
268
return (ret == CSTR_EQUAL);
269
}
270
271
/* prefix is hyphen or dash, and str2 is at the beginning of str1, ignoring case */
272
static BOOL msi_option_prefix(LPCWSTR str1, LPCSTR str2)
273
{
274
if (str1[0] != '/' && str1[0] != '-')
275
return FALSE;
276
277
/* skip over the hyphen or slash */
278
return msi_strprefix(str1 + 1, str2);
279
}
280
281
static VOID *LoadProc(LPCWSTR DllName, LPCSTR ProcName, HMODULE* DllHandle)
282
{
283
VOID* (*proc)(void);
284
285
*DllHandle = LoadLibraryExW(DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
286
if(!*DllHandle)
287
{
288
report_error("Unable to load dll %s\n", wine_dbgstr_w(DllName));
289
ExitProcess(1);
290
}
291
proc = (VOID *) GetProcAddress(*DllHandle, ProcName);
292
if(!proc)
293
{
294
report_error("Dll %s does not implement function %s\n",
295
wine_dbgstr_w(DllName), ProcName);
296
FreeLibrary(*DllHandle);
297
ExitProcess(1);
298
}
299
300
return proc;
301
}
302
303
static DWORD DoDllRegisterServer(LPCWSTR DllName)
304
{
305
HRESULT hr;
306
DLLREGISTERSERVER pfDllRegisterServer = NULL;
307
HMODULE DllHandle = NULL;
308
309
pfDllRegisterServer = LoadProc(DllName, "DllRegisterServer", &DllHandle);
310
311
hr = pfDllRegisterServer();
312
if(FAILED(hr))
313
{
314
report_error("Failed to register dll %s\n", wine_dbgstr_w(DllName));
315
return 1;
316
}
317
MESSAGE("Successfully registered dll %s\n", wine_dbgstr_w(DllName));
318
if(DllHandle)
319
FreeLibrary(DllHandle);
320
return 0;
321
}
322
323
static DWORD DoDllUnregisterServer(LPCWSTR DllName)
324
{
325
HRESULT hr;
326
DLLUNREGISTERSERVER pfDllUnregisterServer = NULL;
327
HMODULE DllHandle = NULL;
328
329
pfDllUnregisterServer = LoadProc(DllName, "DllUnregisterServer", &DllHandle);
330
331
hr = pfDllUnregisterServer();
332
if(FAILED(hr))
333
{
334
report_error("Failed to unregister dll %s\n", wine_dbgstr_w(DllName));
335
return 1;
336
}
337
MESSAGE("Successfully unregistered dll %s\n", wine_dbgstr_w(DllName));
338
if(DllHandle)
339
FreeLibrary(DllHandle);
340
return 0;
341
}
342
343
static DWORD DoRegServer(void)
344
{
345
SC_HANDLE scm, service;
346
WCHAR path[MAX_PATH+12];
347
DWORD len, ret = 0;
348
349
if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CREATE_SERVICE)))
350
{
351
report_error("Failed to open the service control manager.\n");
352
return 1;
353
}
354
len = GetSystemDirectoryW(path, MAX_PATH);
355
lstrcpyW(path + len, L"\\msiexec /V");
356
if ((service = CreateServiceW(scm, L"MSIServer", L"MSIServer", GENERIC_ALL,
357
SERVICE_WIN32_SHARE_PROCESS, SERVICE_DEMAND_START,
358
SERVICE_ERROR_NORMAL, path, NULL, NULL, NULL, NULL, NULL)))
359
{
360
CloseServiceHandle(service);
361
}
362
else if (GetLastError() != ERROR_SERVICE_EXISTS)
363
{
364
report_error("Failed to create MSI service\n");
365
ret = 1;
366
}
367
CloseServiceHandle(scm);
368
return ret;
369
}
370
371
static DWORD DoUnregServer(void)
372
{
373
SC_HANDLE scm, service;
374
DWORD ret = 0;
375
376
if (!(scm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, SC_MANAGER_CONNECT)))
377
{
378
report_error("Failed to open service control manager\n");
379
return 1;
380
}
381
if ((service = OpenServiceW(scm, L"MSIServer", DELETE)))
382
{
383
if (!DeleteService(service))
384
{
385
report_error("Failed to delete MSI service\n");
386
ret = 1;
387
}
388
CloseServiceHandle(service);
389
}
390
else if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
391
{
392
report_error("Failed to open MSI service\n");
393
ret = 1;
394
}
395
CloseServiceHandle(scm);
396
return ret;
397
}
398
399
extern UINT CDECL __wine_msi_call_dll_function(DWORD client_pid, const GUID *guid);
400
401
static DWORD client_pid;
402
403
static DWORD CALLBACK custom_action_thread(void *arg)
404
{
405
GUID guid = *(GUID *)arg;
406
free(arg);
407
return __wine_msi_call_dll_function(client_pid, &guid);
408
}
409
410
static int custom_action_server(const WCHAR *arg)
411
{
412
GUID guid, *thread_guid;
413
DWORD64 thread64;
414
WCHAR buffer[24];
415
HANDLE thread;
416
HANDLE pipe;
417
DWORD size;
418
419
TRACE("%s\n", debugstr_w(arg));
420
421
if (!(client_pid = wcstol(arg, NULL, 10)))
422
{
423
ERR("Invalid parameter %s\n", debugstr_w(arg));
424
return 1;
425
}
426
427
swprintf(buffer, ARRAY_SIZE(buffer), L"\\\\.\\pipe\\msica_%x_%d", client_pid, sizeof(void *) * 8);
428
pipe = CreateFileW(buffer, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
429
if (pipe == INVALID_HANDLE_VALUE)
430
{
431
ERR("Failed to create custom action server pipe: %lu\n", GetLastError());
432
return GetLastError();
433
}
434
435
/* We need this to unmarshal streams, and some apps expect it to be present. */
436
CoInitializeEx(NULL, COINIT_MULTITHREADED);
437
438
while (ReadFile(pipe, &guid, sizeof(guid), &size, NULL) && size == sizeof(guid))
439
{
440
if (IsEqualGUID(&guid, &GUID_NULL))
441
{
442
/* package closed; time to shut down */
443
CoUninitialize();
444
return 0;
445
}
446
447
thread_guid = malloc(sizeof(GUID));
448
memcpy(thread_guid, &guid, sizeof(GUID));
449
thread = CreateThread(NULL, 0, custom_action_thread, thread_guid, 0, NULL);
450
451
/* give the thread handle to the client to wait on, since we might have
452
* to run a nested action and can't block during this one */
453
thread64 = (DWORD_PTR)thread;
454
if (!WriteFile(pipe, &thread64, sizeof(thread64), &size, NULL) || size != sizeof(thread64))
455
{
456
ERR("Failed to write to custom action server pipe: %lu\n", GetLastError());
457
CoUninitialize();
458
return GetLastError();
459
}
460
}
461
ERR("Failed to read from custom action server pipe: %lu\n", GetLastError());
462
CoUninitialize();
463
return GetLastError();
464
}
465
466
/*
467
* state machine to break up the command line properly
468
*/
469
470
enum chomp_state
471
{
472
CS_WHITESPACE,
473
CS_TOKEN,
474
CS_QUOTE
475
};
476
477
static int chomp( const WCHAR *in, WCHAR *out )
478
{
479
enum chomp_state state = CS_TOKEN;
480
const WCHAR *p;
481
int count = 1;
482
BOOL ignore;
483
484
for (p = in; *p; p++)
485
{
486
ignore = TRUE;
487
switch (state)
488
{
489
case CS_WHITESPACE:
490
switch (*p)
491
{
492
case ' ':
493
break;
494
case '"':
495
state = CS_QUOTE;
496
count++;
497
ignore = FALSE;
498
break;
499
default:
500
count++;
501
ignore = FALSE;
502
state = CS_TOKEN;
503
}
504
break;
505
506
case CS_TOKEN:
507
switch (*p)
508
{
509
case '"':
510
state = CS_QUOTE;
511
ignore = FALSE;
512
break;
513
case ' ':
514
state = CS_WHITESPACE;
515
if (out) *out++ = 0;
516
break;
517
default:
518
if (p > in && p[-1] == '"')
519
{
520
if (out) *out++ = 0;
521
count++;
522
}
523
ignore = FALSE;
524
}
525
break;
526
527
case CS_QUOTE:
528
switch (*p)
529
{
530
case '"':
531
state = CS_TOKEN;
532
ignore = FALSE;
533
break;
534
default:
535
ignore = FALSE;
536
}
537
break;
538
}
539
if (!ignore && out) *out++ = *p;
540
}
541
if (out) *out = 0;
542
return count;
543
}
544
545
static void process_args( WCHAR *cmdline, int *pargc, WCHAR ***pargv )
546
{
547
WCHAR **argv, *p;
548
int i, count;
549
550
*pargc = 0;
551
*pargv = NULL;
552
553
count = chomp( cmdline, NULL );
554
if (!(p = malloc( (wcslen(cmdline) + count + 1) * sizeof(WCHAR) )))
555
return;
556
557
count = chomp( cmdline, p );
558
if (!(argv = malloc( (count + 1) * sizeof(WCHAR *) )))
559
{
560
free( p );
561
return;
562
}
563
for (i = 0; i < count; i++)
564
{
565
argv[i] = p;
566
p += lstrlenW( p ) + 1;
567
}
568
argv[i] = NULL;
569
570
*pargc = count;
571
*pargv = argv;
572
}
573
574
static BOOL process_args_from_reg( const WCHAR *ident, int *pargc, WCHAR ***pargv )
575
{
576
LONG r;
577
HKEY hkey;
578
DWORD sz = 0, type = 0;
579
WCHAR *buf;
580
BOOL ret = FALSE;
581
582
r = RegOpenKeyW(HKEY_LOCAL_MACHINE,
583
L"Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\RunOnceEntries", &hkey);
584
if(r != ERROR_SUCCESS)
585
return FALSE;
586
r = RegQueryValueExW(hkey, ident, 0, &type, 0, &sz);
587
if(r == ERROR_SUCCESS && type == REG_SZ)
588
{
589
int len = lstrlenW( *pargv[0] );
590
if (!(buf = malloc( (len + 1) * sizeof(WCHAR) )))
591
{
592
RegCloseKey( hkey );
593
return FALSE;
594
}
595
memcpy( buf, *pargv[0], len * sizeof(WCHAR) );
596
buf[len++] = ' ';
597
r = RegQueryValueExW(hkey, ident, 0, &type, (LPBYTE)(buf + len), &sz);
598
if( r == ERROR_SUCCESS )
599
{
600
process_args(buf, pargc, pargv);
601
ret = TRUE;
602
}
603
free(buf);
604
}
605
RegCloseKey(hkey);
606
return ret;
607
}
608
609
static WCHAR *get_path_with_extension(const WCHAR *package_name)
610
{
611
static const WCHAR ext[] = L".msi";
612
unsigned int p;
613
WCHAR *path;
614
615
if (!(path = malloc(wcslen(package_name) * sizeof(WCHAR) + sizeof(ext))))
616
{
617
WINE_ERR("No memory.\n");
618
return NULL;
619
}
620
621
lstrcpyW(path, package_name);
622
p = lstrlenW(path);
623
while (p && path[p] != '.' && path[p] != L'\\' && path[p] != '/')
624
--p;
625
if (path[p] == '.')
626
{
627
free(path);
628
return NULL;
629
}
630
lstrcatW(path, ext);
631
return path;
632
}
633
634
static WCHAR *remove_quotes( const WCHAR *filename )
635
{
636
const WCHAR *ptr = filename;
637
int len = wcslen( filename );
638
WCHAR *ret;
639
640
if (!(ret = malloc( (len + 1) * sizeof(WCHAR) ))) return NULL;
641
if (*ptr == '"')
642
{
643
ptr++;
644
len--;
645
}
646
wcscpy( ret, ptr );
647
if (len && ret[len - 1] == '"') ret[len - 1] = 0;
648
return ret;
649
}
650
651
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
652
{
653
int i;
654
BOOL FunctionInstall = FALSE;
655
BOOL FunctionInstallAdmin = FALSE;
656
BOOL FunctionRepair = FALSE;
657
BOOL FunctionAdvertise = FALSE;
658
BOOL FunctionPatch = FALSE;
659
BOOL FunctionDllRegisterServer = FALSE;
660
BOOL FunctionDllUnregisterServer = FALSE;
661
BOOL FunctionRegServer = FALSE;
662
BOOL FunctionUnregServer = FALSE;
663
BOOL FunctionServer = FALSE;
664
BOOL FunctionUnknown = FALSE;
665
666
LPWSTR PackageName = NULL;
667
LPWSTR Properties = NULL;
668
struct string_list *property_list = NULL;
669
670
DWORD RepairMode = 0;
671
672
DWORD_PTR AdvertiseMode = 0;
673
struct string_list *transform_list = NULL;
674
LANGID Language = 0;
675
676
DWORD LogMode = 0;
677
DWORD LogAttributes = 0;
678
679
LPWSTR PatchFileName = NULL;
680
INSTALLTYPE InstallType = INSTALLTYPE_DEFAULT;
681
682
INSTALLUILEVEL InstallUILevel = INSTALLUILEVEL_FULL;
683
684
LPWSTR DllName = NULL;
685
DWORD ReturnCode;
686
int argc;
687
LPWSTR *argvW = NULL;
688
WCHAR *path, *package_unquoted = NULL, *dll_unquoted = NULL, *patch_unquoted = NULL, *log_unquoted;
689
690
InitCommonControls();
691
692
/* parse the command line */
693
process_args( GetCommandLineW(), &argc, &argvW );
694
695
/*
696
* If the args begin with /@ IDENT then we need to load the real
697
* command line out of the RunOnceEntries key in the registry.
698
* We do that before starting to process the real commandline,
699
* then overwrite the commandline again.
700
*/
701
if(argc>1 && msi_option_equal(argvW[1], "@"))
702
{
703
if(!process_args_from_reg( argvW[2], &argc, &argvW ))
704
return 1;
705
}
706
707
if (argc == 3 && msi_option_equal(argvW[1], "Embedding"))
708
return custom_action_server(argvW[2]);
709
710
for(i = 1; i < argc; i++)
711
{
712
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
713
714
if (msi_option_equal(argvW[i], "regserver"))
715
{
716
FunctionRegServer = TRUE;
717
}
718
else if (msi_option_equal(argvW[i], "unregserver") || msi_option_equal(argvW[i], "unregister")
719
|| msi_option_equal(argvW[i], "unreg"))
720
{
721
FunctionUnregServer = TRUE;
722
}
723
else if(msi_option_prefix(argvW[i], "i") || msi_option_prefix(argvW[i], "package"))
724
{
725
LPWSTR argvWi = argvW[i];
726
int argLen = (msi_option_prefix(argvW[i], "i") ? 2 : 8);
727
FunctionInstall = TRUE;
728
if(lstrlenW(argvW[i]) > argLen)
729
argvWi += argLen;
730
else
731
{
732
i++;
733
if(i >= argc)
734
ShowUsage(1);
735
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
736
argvWi = argvW[i];
737
}
738
PackageName = argvWi;
739
}
740
else if(msi_option_equal(argvW[i], "a"))
741
{
742
FunctionInstall = TRUE;
743
FunctionInstallAdmin = TRUE;
744
InstallType = INSTALLTYPE_NETWORK_IMAGE;
745
i++;
746
if(i >= argc)
747
ShowUsage(1);
748
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
749
PackageName = argvW[i];
750
StringListAppend(&property_list, L"ACTION=ADMIN");
751
}
752
else if(msi_option_prefix(argvW[i], "f"))
753
{
754
int j;
755
int len = lstrlenW(argvW[i]);
756
FunctionRepair = TRUE;
757
for(j = 2; j < len; j++)
758
{
759
switch(argvW[i][j])
760
{
761
case 'P':
762
case 'p':
763
RepairMode |= REINSTALLMODE_FILEMISSING;
764
break;
765
case 'O':
766
case 'o':
767
RepairMode |= REINSTALLMODE_FILEOLDERVERSION;
768
break;
769
case 'E':
770
case 'e':
771
RepairMode |= REINSTALLMODE_FILEEQUALVERSION;
772
break;
773
case 'D':
774
case 'd':
775
RepairMode |= REINSTALLMODE_FILEEXACT;
776
break;
777
case 'C':
778
case 'c':
779
RepairMode |= REINSTALLMODE_FILEVERIFY;
780
break;
781
case 'A':
782
case 'a':
783
RepairMode |= REINSTALLMODE_FILEREPLACE;
784
break;
785
case 'U':
786
case 'u':
787
RepairMode |= REINSTALLMODE_USERDATA;
788
break;
789
case 'M':
790
case 'm':
791
RepairMode |= REINSTALLMODE_MACHINEDATA;
792
break;
793
case 'S':
794
case 's':
795
RepairMode |= REINSTALLMODE_SHORTCUT;
796
break;
797
case 'V':
798
case 'v':
799
RepairMode |= REINSTALLMODE_PACKAGE;
800
break;
801
default:
802
report_error("Unknown option \"%c\" in Repair mode\n", argvW[i][j]);
803
break;
804
}
805
}
806
if(len == 2)
807
{
808
RepairMode = REINSTALLMODE_FILEMISSING |
809
REINSTALLMODE_FILEEQUALVERSION |
810
REINSTALLMODE_FILEVERIFY |
811
REINSTALLMODE_MACHINEDATA |
812
REINSTALLMODE_SHORTCUT;
813
}
814
i++;
815
if(i >= argc)
816
ShowUsage(1);
817
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
818
PackageName = argvW[i];
819
}
820
else if(msi_option_prefix(argvW[i], "x") || msi_option_equal(argvW[i], "uninstall"))
821
{
822
FunctionInstall = TRUE;
823
if(msi_option_prefix(argvW[i], "x")) PackageName = argvW[i]+2;
824
if(!PackageName || !PackageName[0])
825
{
826
i++;
827
if (i >= argc)
828
ShowUsage(1);
829
PackageName = argvW[i];
830
}
831
WINE_TRACE("PackageName = %s\n", wine_dbgstr_w(PackageName));
832
StringListAppend(&property_list, L"REMOVE=ALL");
833
}
834
else if(msi_option_prefix(argvW[i], "j"))
835
{
836
int j;
837
int len = lstrlenW(argvW[i]);
838
FunctionAdvertise = TRUE;
839
for(j = 2; j < len; j++)
840
{
841
switch(argvW[i][j])
842
{
843
case 'U':
844
case 'u':
845
AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
846
break;
847
case 'M':
848
case 'm':
849
AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
850
break;
851
default:
852
report_error("Unknown option \"%c\" in Advertise mode\n", argvW[i][j]);
853
break;
854
}
855
}
856
i++;
857
if(i >= argc)
858
ShowUsage(1);
859
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
860
PackageName = argvW[i];
861
}
862
else if(msi_strequal(argvW[i], "u"))
863
{
864
FunctionAdvertise = TRUE;
865
AdvertiseMode = ADVERTISEFLAGS_USERASSIGN;
866
i++;
867
if(i >= argc)
868
ShowUsage(1);
869
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
870
PackageName = argvW[i];
871
}
872
else if(msi_strequal(argvW[i], "m"))
873
{
874
FunctionAdvertise = TRUE;
875
AdvertiseMode = ADVERTISEFLAGS_MACHINEASSIGN;
876
i++;
877
if(i >= argc)
878
ShowUsage(1);
879
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
880
PackageName = argvW[i];
881
}
882
else if(msi_option_equal(argvW[i], "t"))
883
{
884
i++;
885
if(i >= argc)
886
ShowUsage(1);
887
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
888
StringListAppend(&transform_list, argvW[i]);
889
}
890
else if(msi_option_equal(argvW[i], "g"))
891
{
892
i++;
893
if(i >= argc)
894
ShowUsage(1);
895
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
896
Language = msi_atou(argvW[i]);
897
}
898
else if(msi_option_prefix(argvW[i], "l"))
899
{
900
int j;
901
int len = lstrlenW(argvW[i]);
902
for(j = 2; j < len; j++)
903
{
904
switch(argvW[i][j])
905
{
906
case 'I':
907
case 'i':
908
LogMode |= INSTALLLOGMODE_INFO;
909
break;
910
case 'W':
911
case 'w':
912
LogMode |= INSTALLLOGMODE_WARNING;
913
break;
914
case 'E':
915
case 'e':
916
LogMode |= INSTALLLOGMODE_ERROR;
917
break;
918
case 'A':
919
case 'a':
920
LogMode |= INSTALLLOGMODE_ACTIONSTART;
921
break;
922
case 'R':
923
case 'r':
924
LogMode |= INSTALLLOGMODE_ACTIONDATA;
925
break;
926
case 'U':
927
case 'u':
928
LogMode |= INSTALLLOGMODE_USER;
929
break;
930
case 'C':
931
case 'c':
932
LogMode |= INSTALLLOGMODE_COMMONDATA;
933
break;
934
case 'M':
935
case 'm':
936
LogMode |= INSTALLLOGMODE_FATALEXIT;
937
break;
938
case 'O':
939
case 'o':
940
LogMode |= INSTALLLOGMODE_OUTOFDISKSPACE;
941
break;
942
case 'P':
943
case 'p':
944
LogMode |= INSTALLLOGMODE_PROPERTYDUMP;
945
break;
946
case 'V':
947
case 'v':
948
LogMode |= INSTALLLOGMODE_VERBOSE;
949
break;
950
case '*':
951
LogMode = INSTALLLOGMODE_FATALEXIT |
952
INSTALLLOGMODE_ERROR |
953
INSTALLLOGMODE_WARNING |
954
INSTALLLOGMODE_USER |
955
INSTALLLOGMODE_INFO |
956
INSTALLLOGMODE_RESOLVESOURCE |
957
INSTALLLOGMODE_OUTOFDISKSPACE |
958
INSTALLLOGMODE_ACTIONSTART |
959
INSTALLLOGMODE_ACTIONDATA |
960
INSTALLLOGMODE_COMMONDATA |
961
INSTALLLOGMODE_PROPERTYDUMP |
962
INSTALLLOGMODE_PROGRESS |
963
INSTALLLOGMODE_INITIALIZE |
964
INSTALLLOGMODE_TERMINATE |
965
INSTALLLOGMODE_SHOWDIALOG;
966
break;
967
case '+':
968
LogAttributes |= INSTALLLOGATTRIBUTES_APPEND;
969
break;
970
case '!':
971
LogAttributes |= INSTALLLOGATTRIBUTES_FLUSHEACHLINE;
972
break;
973
default:
974
break;
975
}
976
}
977
i++;
978
if(i >= argc)
979
ShowUsage(1);
980
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
981
if (!(log_unquoted = remove_quotes(argvW[i]))) return 1;
982
if (MsiEnableLogW(LogMode, log_unquoted, LogAttributes) != ERROR_SUCCESS)
983
{
984
report_error("Logging in %s (0x%08lx, %lu) failed\n",
985
wine_dbgstr_w(log_unquoted), LogMode, LogAttributes);
986
ExitProcess(1);
987
}
988
}
989
else if(msi_option_equal(argvW[i], "p") || msi_option_equal(argvW[i], "update"))
990
{
991
FunctionPatch = TRUE;
992
i++;
993
if(i >= argc)
994
ShowUsage(1);
995
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
996
PatchFileName = argvW[i];
997
}
998
else if(msi_option_prefix(argvW[i], "q"))
999
{
1000
if(lstrlenW(argvW[i]) == 2 || msi_strequal(argvW[i]+2, "n") ||
1001
msi_strequal(argvW[i] + 2, "uiet"))
1002
{
1003
silent = TRUE;
1004
InstallUILevel = INSTALLUILEVEL_NONE;
1005
}
1006
else if(msi_strequal(argvW[i]+2, "r"))
1007
{
1008
InstallUILevel = INSTALLUILEVEL_REDUCED;
1009
}
1010
else if(msi_strequal(argvW[i]+2, "f"))
1011
{
1012
InstallUILevel = INSTALLUILEVEL_FULL|INSTALLUILEVEL_ENDDIALOG;
1013
}
1014
else if(msi_strequal(argvW[i]+2, "n+"))
1015
{
1016
InstallUILevel = INSTALLUILEVEL_NONE|INSTALLUILEVEL_ENDDIALOG;
1017
}
1018
else if(msi_strprefix(argvW[i]+2, "b"))
1019
{
1020
const WCHAR *ptr = argvW[i] + 3;
1021
1022
InstallUILevel = INSTALLUILEVEL_BASIC;
1023
1024
while (*ptr)
1025
{
1026
if (msi_strprefix(ptr, "+"))
1027
InstallUILevel |= INSTALLUILEVEL_ENDDIALOG;
1028
if (msi_strprefix(ptr, "-"))
1029
InstallUILevel |= INSTALLUILEVEL_PROGRESSONLY;
1030
if (msi_strprefix(ptr, "!"))
1031
{
1032
WINE_FIXME("Unhandled modifier: !\n");
1033
InstallUILevel |= INSTALLUILEVEL_HIDECANCEL;
1034
}
1035
ptr++;
1036
}
1037
}
1038
else
1039
{
1040
report_error("Unknown option \"%s\" for UI level\n",
1041
wine_dbgstr_w(argvW[i]+2));
1042
}
1043
}
1044
else if(msi_option_equal(argvW[i], "passive"))
1045
{
1046
InstallUILevel = INSTALLUILEVEL_BASIC|INSTALLUILEVEL_PROGRESSONLY|INSTALLUILEVEL_HIDECANCEL;
1047
StringListAppend(&property_list, L"REBOOTPROMPT=\"S\"");
1048
}
1049
else if(msi_option_equal(argvW[i], "y"))
1050
{
1051
FunctionDllRegisterServer = TRUE;
1052
i++;
1053
if(i >= argc)
1054
ShowUsage(1);
1055
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
1056
DllName = argvW[i];
1057
}
1058
else if(msi_option_equal(argvW[i], "z"))
1059
{
1060
FunctionDllUnregisterServer = TRUE;
1061
i++;
1062
if(i >= argc)
1063
ShowUsage(1);
1064
WINE_TRACE("argvW[%d] = %s\n", i, wine_dbgstr_w(argvW[i]));
1065
DllName = argvW[i];
1066
}
1067
else if(msi_option_equal(argvW[i], "help") || msi_option_equal(argvW[i], "?"))
1068
{
1069
ShowUsage(0);
1070
}
1071
else if(msi_option_equal(argvW[i], "m"))
1072
{
1073
FunctionUnknown = TRUE;
1074
WINE_FIXME("Unknown parameter /m\n");
1075
}
1076
else if(msi_option_equal(argvW[i], "D"))
1077
{
1078
FunctionUnknown = TRUE;
1079
WINE_FIXME("Unknown parameter /D\n");
1080
}
1081
else if (msi_option_equal(argvW[i], "V"))
1082
{
1083
FunctionServer = TRUE;
1084
}
1085
else
1086
StringListAppend(&property_list, argvW[i]);
1087
}
1088
1089
/* start the GUI */
1090
MsiSetInternalUI(InstallUILevel, NULL);
1091
1092
Properties = build_properties( property_list );
1093
1094
if(FunctionInstallAdmin && FunctionPatch)
1095
FunctionInstall = FALSE;
1096
1097
if (PackageName && !(package_unquoted = remove_quotes(PackageName))) return 1;
1098
if (PatchFileName && !(patch_unquoted = remove_quotes(PatchFileName))) return 1;
1099
if (DllName && !(dll_unquoted = remove_quotes(DllName))) return 1;
1100
1101
ReturnCode = 1;
1102
if(FunctionInstall)
1103
{
1104
if(IsProductCode(package_unquoted))
1105
ReturnCode = MsiConfigureProductExW(package_unquoted, 0, INSTALLSTATE_DEFAULT, Properties);
1106
else
1107
{
1108
if ((ReturnCode = MsiInstallProductW(package_unquoted, Properties)) == ERROR_FILE_NOT_FOUND
1109
&& (path = get_path_with_extension(package_unquoted)))
1110
{
1111
ReturnCode = MsiInstallProductW(path, Properties);
1112
free(path);
1113
}
1114
}
1115
}
1116
else if(FunctionRepair)
1117
{
1118
if(IsProductCode(package_unquoted))
1119
WINE_FIXME("Product code treatment not implemented yet\n");
1120
else
1121
{
1122
if ((ReturnCode = MsiReinstallProductW(package_unquoted, RepairMode)) == ERROR_FILE_NOT_FOUND
1123
&& (path = get_path_with_extension(package_unquoted)))
1124
{
1125
ReturnCode = MsiReinstallProductW(path, RepairMode);
1126
free(path);
1127
}
1128
}
1129
}
1130
else if(FunctionAdvertise)
1131
{
1132
LPWSTR Transforms = build_transforms( property_list );
1133
ReturnCode = MsiAdvertiseProductW(package_unquoted, (LPWSTR) AdvertiseMode, Transforms, Language);
1134
}
1135
else if(FunctionPatch)
1136
{
1137
ReturnCode = MsiApplyPatchW(patch_unquoted, package_unquoted, InstallType, Properties);
1138
}
1139
else if(FunctionDllRegisterServer)
1140
{
1141
ReturnCode = DoDllRegisterServer(dll_unquoted);
1142
}
1143
else if(FunctionDllUnregisterServer)
1144
{
1145
ReturnCode = DoDllUnregisterServer(dll_unquoted);
1146
}
1147
else if (FunctionRegServer)
1148
{
1149
ReturnCode = DoRegServer();
1150
}
1151
else if (FunctionUnregServer)
1152
{
1153
ReturnCode = DoUnregServer();
1154
}
1155
else if (FunctionServer)
1156
{
1157
ReturnCode = DoService();
1158
}
1159
else if (FunctionUnknown)
1160
{
1161
WINE_FIXME( "Unknown function, ignoring\n" );
1162
}
1163
else
1164
ShowUsage(1);
1165
1166
return ReturnCode;
1167
}
1168
1169