1
/* Copyright 2010, Oracle and/or its affiliates. All rights reserved.
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; version 2 of the License.
7
This program is distributed in the hope that it will be useful,
8
but WITHOUT ANY WARRANTY; without even the implied warranty of
9
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
GNU General Public License for more details.
12
You should have received a copy of the GNU General Public License
13
along with this program; if not, write to the Free Software
14
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
32
#include <winservice.h>
34
#define ONE_MB 1048576
35
UINT ExecRemoveDataDirectory(wchar_t *dir)
37
/* Strip stray backslash */
38
DWORD len = (DWORD)wcslen(dir);
39
if(len > 0 && dir[len-1]==L'\\')
42
SHFILEOPSTRUCTW fileop;
43
fileop.hwnd= NULL; /* no status display */
44
fileop.wFunc= FO_DELETE; /* delete operation */
45
fileop.pFrom= dir; /* source file name as double null terminated string */
46
fileop.pTo= NULL; /* no destination needed */
47
fileop.fFlags= FOF_NOCONFIRMATION|FOF_SILENT; /* do not prompt the user */
49
fileop.fAnyOperationsAborted= FALSE;
50
fileop.lpszProgressTitle= NULL;
51
fileop.hNameMappings= NULL;
53
return SHFileOperationW(&fileop);
57
extern "C" UINT __stdcall RemoveDataDirectory(MSIHANDLE hInstall)
60
UINT er = ERROR_SUCCESS;
62
hr = WcaInitialize(hInstall, __FUNCTION__);
63
ExitOnFailure(hr, "Failed to initialize");
64
WcaLog(LOGMSG_STANDARD, "Initialized.");
66
wchar_t dir[MAX_PATH];
68
MsiGetPropertyW(hInstall, L"CustomActionData", dir, &len);
70
er= ExecRemoveDataDirectory(dir);
71
WcaLog(LOGMSG_STANDARD, "SHFileOperation returned %d", er);
73
return WcaFinalize(er);
77
Escape command line parameter fpr pass to CreateProcess().
79
We assume out has enough space to include encoded string
80
2*wcslen(in) is enough.
82
It is assumed that called will add double quotation marks before and after
85
static void EscapeCommandLine(const wchar_t *in, wchar_t *out, size_t buflen)
87
const wchar_t special_chars[]=L" \t\n\v\"";
88
bool needs_escaping= false;
91
for(int i=0; i< sizeof(special_chars) -1; i++)
93
if (wcschr(in, special_chars[i]))
95
needs_escaping = true;
102
wcscpy_s(out, buflen, in);
107
for(int i = 0 ; ; i++)
109
size_t n_backslashes = 0;
111
while (in[i] == L'\\')
121
Escape all backslashes, but let the terminating double quotation mark
122
that caller adds be interpreted as a metacharacter.
124
for(size_t j= 0; j < 2*n_backslashes;j++)
133
Escape all backslashes and the following double quotation mark.
135
for(size_t j= 0; j < 2*n_backslashes + 1; j++)
143
/* Backslashes aren't special here. */
144
for (size_t j=0; j < n_backslashes; j++)
153
Check for if directory is empty during install,
154
sets "<PROPERTY>_NOT_EMPTY" otherise
156
extern "C" UINT __stdcall CheckDirectoryEmpty(MSIHANDLE hInstall,
157
const wchar_t *PropertyName)
160
UINT er = ERROR_SUCCESS;
161
hr = WcaInitialize(hInstall, __FUNCTION__);
162
ExitOnFailure(hr, "Failed to initialize");
163
WcaLog(LOGMSG_STANDARD, "Initialized.");
166
wchar_t buf[MAX_PATH];
167
DWORD len = MAX_PATH;
168
MsiGetPropertyW(hInstall, PropertyName, buf, &len);
169
wcscat_s(buf, MAX_PATH, L"*.*");
171
WcaLog(LOGMSG_STANDARD, "Checking files in %S", buf);
172
WIN32_FIND_DATAW data;
175
h= FindFirstFile(buf, &data);
176
if (h != INVALID_HANDLE_VALUE)
181
if (wcscmp(data.cFileName, L".") || wcscmp(data.cFileName, L".."))
186
if (!FindNextFile(h, &data))
193
/* Non-existent directory, we handle it as empty */
198
WcaLog(LOGMSG_STANDARD, "Directory %S is empty or non-existent",
201
WcaLog(LOGMSG_STANDARD, "Directory %S is NOT empty", PropertyName);
203
wcscpy_s(buf, MAX_PATH, PropertyName);
204
wcscat_s(buf, L"NOTEMPTY");
205
WcaSetProperty(buf, empty? L"":L"1");
208
return WcaFinalize(er);
211
extern "C" UINT __stdcall CheckDataDirectoryEmpty(MSIHANDLE hInstall)
213
return CheckDirectoryEmpty(hInstall, L"DATADIR");
216
bool CheckServiceExists(const wchar_t *name)
218
SC_HANDLE manager =0, service=0;
219
manager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT);
225
service = OpenService(manager, name, SC_MANAGER_CONNECT);
227
CloseServiceHandle(service);
228
CloseServiceHandle(manager);
230
return service?true:false;
233
/* User in rollback of create database custom action */
234
bool ExecRemoveService(const wchar_t *name)
236
SC_HANDLE manager =0, service=0;
237
manager = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS);
243
service = OpenService(manager, name, DELETE);
246
ret= DeleteService(service);
252
CloseServiceHandle(manager);
257
Check if port is free by trying to bind to the port
259
bool IsPortFree(short port)
261
WORD wVersionRequested;
264
wVersionRequested = MAKEWORD(2, 2);
266
WSAStartup(wVersionRequested, &wsaData);
268
struct sockaddr_in sin;
270
sock = socket(AF_INET, SOCK_STREAM, 0);
275
sin.sin_port = htons(port);
276
sin.sin_addr.s_addr = 0;
277
sin.sin_addr.s_addr = INADDR_ANY;
278
sin.sin_family = AF_INET;
279
if(bind(sock, (struct sockaddr *)&sin,sizeof(struct sockaddr_in) ) == -1)
290
Helper function used in filename normalization.
291
Removes leading quote and terminates string at the position of the next one
292
(if applicable, does not change string otherwise). Returns modified string
294
wchar_t *strip_quotes(wchar_t *s)
296
if (s && (*s == L'"'))
299
wchar_t *p = wcschr(s, L'"');
308
Checks for consistency of service configuration.
310
It can happen that SERVICENAME or DATADIR
311
MSI properties are in inconsistent state after somebody upgraded database
312
We catch this case during uninstall. In particular, either service is not
313
removed even if SERVICENAME was set (but this name is reused by someone else)
314
or data directory is not removed (if it is used by someone else). To find out
315
whether service name and datadirectory are in use For every service,
316
configuration is read and checked as follows:
318
- look if a service has to do something with mysql
319
- If so, check its name against SERVICENAME. if match, check binary path
320
against INSTALLDIR\bin. If binary path does not match, then service runs
321
under different installation and won't be removed.
322
- Check options file for datadir and look if this is inside this
323
installation's datadir don't remove datadir if this is the case.
325
"Don't remove" in this context means that custom action is removing
326
SERVICENAME property or CLEANUPDATA property, which later on in course of
327
installation mean, that either datadir or service is kept.
330
void CheckServiceConfig(
331
wchar_t *my_servicename, /* SERVICENAME property in this installation*/
332
wchar_t *datadir, /* DATADIR property in this installation*/
333
wchar_t *bindir, /* INSTALLDIR\bin */
334
wchar_t *other_servicename, /* Service to check against */
335
QUERY_SERVICE_CONFIGW * config /* Other service's config */
339
bool same_bindir = false;
340
wchar_t * commandline= config->lpBinaryPathName;
342
wchar_t **argv= CommandLineToArgvW(commandline, &numargs);
343
WcaLog(LOGMSG_VERBOSE, "CommandLine= %S", commandline);
344
if(!argv || !argv[0] || ! wcsstr(argv[0], L"mysqld"))
349
WcaLog(LOGMSG_STANDARD, "MySQL service %S found: CommandLine= %S",
350
other_servicename, commandline);
351
if (wcsstr(argv[0], bindir))
353
WcaLog(LOGMSG_STANDARD, "executable under bin directory");
357
bool is_my_service = (_wcsicmp(my_servicename, other_servicename) == 0);
360
WcaLog(LOGMSG_STANDARD, "service does not match current service");
362
TODO probably the best thing possible would be to add temporary
363
row to MSI ServiceConfig table with remove on uninstall
366
else if (!same_bindir)
368
WcaLog(LOGMSG_STANDARD,
369
"Service name matches, but not the executable path directory, mine is %S",
371
WcaSetProperty(L"SERVICENAME", L"");
374
/* Check if data directory is used */
375
if(!datadir || numargs <= 1 || wcsncmp(argv[1],L"--defaults-file=",16) != 0)
380
wchar_t current_datadir_buf[MAX_PATH]={0};
381
wchar_t normalized_current_datadir[MAX_PATH+1];
382
wchar_t *current_datadir= current_datadir_buf;
383
wchar_t *defaults_file= argv[1]+16;
384
defaults_file= strip_quotes(defaults_file);
386
WcaLog(LOGMSG_STANDARD, "parsed defaults file is %S", defaults_file);
388
if (GetPrivateProfileStringW(L"mysqld", L"datadir", NULL, current_datadir,
389
MAX_PATH, defaults_file) == 0)
391
WcaLog(LOGMSG_STANDARD,
392
"Cannot find datadir in ini file '%S'", defaults_file);
396
WcaLog(LOGMSG_STANDARD, "datadir from defaults-file is %S", current_datadir);
397
strip_quotes(current_datadir);
399
/* Convert to Windows path */
400
if (GetFullPathNameW(current_datadir, MAX_PATH, normalized_current_datadir,
403
/* Add backslash to be compatible with directory formats in MSI */
404
wcsncat(normalized_current_datadir, L"\\", MAX_PATH+1);
405
WcaLog(LOGMSG_STANDARD, "normalized current datadir is '%S'",
406
normalized_current_datadir);
409
if (_wcsicmp(datadir, normalized_current_datadir) == 0 && !same_bindir)
411
WcaLog(LOGMSG_STANDARD,
412
"database directory from current installation, but different mysqld.exe");
413
WcaSetProperty(L"CLEANUPDATA", L"");
417
LocalFree((HLOCAL)argv);
421
Checks if database directory or service are modified by user
422
For example, service may point to different mysqld.exe that it was originally
423
installed, or some different service might use this database directory. This
424
would normally mean user has done an upgrade of the database and in this case
425
uninstall should neither delete service nor database directory.
427
If this function find that service is modified by user (mysqld.exe used by
428
service does not point to the installation bin directory), MSI public variable
429
SERVICENAME is removed, if DATADIR is used by some other service, variables
430
DATADIR and CLEANUPDATA are removed.
432
The effect of variable removal is that service does not get uninstalled and
433
datadir is not touched by uninstallation.
435
Note that this function is running without elevation and does not use anything
436
that would require special privileges.
439
extern "C" UINT CheckDBInUse(MSIHANDLE hInstall)
441
static BYTE buf[256*1024]; /* largest possible buffer for EnumServices */
442
static char config_buffer[8*1024]; /*largest buffer for QueryServiceConfig */
444
UINT er = ERROR_SUCCESS;
445
wchar_t *servicename= NULL;
446
wchar_t *datadir= NULL;
447
wchar_t *bindir=NULL;
449
SC_HANDLE scm = NULL;
450
ULONG bufsize = sizeof(buf);
451
ULONG bufneed = 0x00;
452
ULONG num_services = 0x00;
453
LPENUM_SERVICE_STATUS_PROCESS info = NULL;
455
hr = WcaInitialize(hInstall, __FUNCTION__);
456
ExitOnFailure(hr, "Failed to initialize");
457
WcaLog(LOGMSG_STANDARD, "Initialized.");
459
WcaGetProperty(L"SERVICENAME", &servicename);
460
WcaGetProperty(L"DATADIR", &datadir);
461
WcaGetFormattedString(L"[INSTALLDIR]bin\\", &bindir);
462
WcaLog(LOGMSG_STANDARD,"SERVICENAME=%S, DATADIR=%S, bindir=%S",
463
servicename, datadir, bindir);
465
scm = OpenSCManager(NULL, NULL,
466
SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
469
ExitOnFailure(E_FAIL, "OpenSCManager failed");
472
BOOL ok= EnumServicesStatusExW( scm,
473
SC_ENUM_PROCESS_INFO,
484
WcaLog(LOGMSG_STANDARD, "last error %d", GetLastError());
485
ExitOnFailure(E_FAIL, "EnumServicesStatusExW failed");
487
info = (LPENUM_SERVICE_STATUS_PROCESS)buf;
488
for (ULONG i=0; i < num_services; i++)
490
SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName,
491
SERVICE_QUERY_CONFIG);
494
WcaLog(LOGMSG_VERBOSE, "Checking Service %S", info[i].lpServiceName);
495
QUERY_SERVICE_CONFIGW *config=
496
(QUERY_SERVICE_CONFIGW *)(void *)config_buffer;
498
BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer),
500
CloseServiceHandle(service);
503
CheckServiceConfig(servicename, datadir, bindir, info[i].lpServiceName,
510
CloseServiceHandle(scm);
512
ReleaseStr(servicename);
515
return WcaFinalize(er);
519
Get maximum size of the buffer process can allocate.
520
this is calculated as min(RAM,virtualmemorylimit)
521
For 32bit processes, virtual address memory is 2GB (x86 OS)
524
Fragmentation due to loaded modules, heap and stack
525
limit maximum size of continous memory block further,
526
so that limit for 32 bit process is about 1200 on 32 bit OS
527
or 2000 MB on 64 bit OS(found experimentally).
529
unsigned long long GetMaxBufferSize(unsigned long long totalPhys)
533
if (IsWow64Process(GetCurrentProcess(), &wow64))
534
return min(totalPhys, 2000ULL*ONE_MB);
536
return min(totalPhys, 1200ULL*ONE_MB);
544
Checks SERVICENAME, PORT and BUFFERSIZE parameters
546
extern "C" UINT __stdcall CheckDatabaseProperties (MSIHANDLE hInstall)
548
wchar_t ServiceName[MAX_PATH]={0};
549
wchar_t SkipNetworking[MAX_PATH]={0};
550
wchar_t QuickConfig[MAX_PATH]={0};
551
wchar_t Password[MAX_PATH]={0};
552
wchar_t EscapedPassword[2*MAX_PATH+2];
554
wchar_t BufferPoolSize[16];
556
bool haveInvalidPort=false;
557
const wchar_t *ErrorMsg=0;
559
UINT er= ERROR_SUCCESS;
562
hr = WcaInitialize(hInstall, __FUNCTION__);
563
ExitOnFailure(hr, "Failed to initialize");
564
WcaLog(LOGMSG_STANDARD, "Initialized.");
566
DWORD ServiceNameLen = MAX_PATH;
567
MsiGetPropertyW (hInstall, L"SERVICENAME", ServiceName, &ServiceNameLen);
570
if(ServiceNameLen > 256)
572
ErrorMsg= L"Invalid service name. The maximum length is 256 characters.";
575
for(DWORD i=0; i< ServiceNameLen;i++)
577
if(ServiceName[i] == L'\\' || ServiceName[i] == L'/'
578
|| ServiceName[i]=='\'' || ServiceName[i] ==L'"')
581
L"Invalid service name. Forward slash and back slash are forbidden."
582
L"Single and double quotes are also not permitted.";
586
if(CheckServiceExists(ServiceName))
589
L"A service with the same name already exists. "
590
L"Please use a different name.";
595
DWORD PasswordLen= MAX_PATH;
596
MsiGetPropertyW (hInstall, L"PASSWORD", Password, &PasswordLen);
597
EscapeCommandLine(Password, EscapedPassword,
598
sizeof(EscapedPassword)/sizeof(EscapedPassword[0]));
599
MsiSetPropertyW(hInstall,L"ESCAPEDPASSWORD",EscapedPassword);
601
DWORD SkipNetworkingLen= MAX_PATH;
602
MsiGetPropertyW(hInstall, L"SKIPNETWORKING", SkipNetworking,
604
MsiGetPropertyW(hInstall, L"PORT", Port, &PortLen);
606
if(SkipNetworking[0]==0 && Port[0] != 0)
609
for(DWORD i=PortLen-1; i > 0; i--)
615
if(PortLen > 5 || PortLen <= 3)
616
haveInvalidPort = true;
619
for (DWORD i=0; i< PortLen && Port[i] != 0;i++)
621
if(Port[i] < '0' || Port[i] >'9')
623
haveInvalidPort=true;
631
L"Invalid port number. Please use a number between 1025 and 65535.";
635
short port = (short)_wtoi(Port);
636
if (!IsPortFree(port))
639
L"The TCP Port you selected is already in use. "
640
L"Please choose a different port.";
646
DWORD QuickConfigLen = MAX_PATH;
647
MsiGetPropertyW (hInstall, L"STDCONFIG", QuickConfig, &QuickConfigLen);
648
if(QuickConfig[0] !=0)
650
MEMORYSTATUSEX memstatus;
651
memstatus.dwLength =sizeof(memstatus);
652
wchar_t invalidValueMsg[256];
654
if (!GlobalMemoryStatusEx(&memstatus))
656
WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx",
658
er= ERROR_INSTALL_FAILURE;
661
DWORD BufferPoolSizeLen= 16;
662
MsiGetPropertyW(hInstall, L"BUFFERPOOLSIZE", BufferPoolSize, &BufferPoolSizeLen);
664
for(DWORD i=BufferPoolSizeLen-1; i > 0; i--)
666
if(BufferPoolSize[i]== ' ')
667
BufferPoolSize[i] = 0;
669
unsigned long long availableMemory=
670
GetMaxBufferSize(memstatus.ullTotalPhys)/ONE_MB;
671
swprintf_s(invalidValueMsg,
672
L"Invalid buffer pool size. Please use a number between 1 and %llu",
674
if(BufferPoolSizeLen == 0 || BufferPoolSizeLen > 15)
676
ErrorMsg= invalidValueMsg;
679
for (DWORD i=0; i < BufferPoolSizeLen && BufferPoolSize[BufferPoolSizeLen];
682
if(BufferPoolSize[i]< '0' || BufferPoolSize[i] > '9')
684
ErrorMsg= invalidValueMsg;
688
BufferPoolSize[BufferPoolSizeLen]=0;
689
MsiSetPropertyW(hInstall, L"BUFFERPOOLSIZE", BufferPoolSize);
690
long long sz = _wtoi64(BufferPoolSize);
691
if(sz <= 0 || sz > (long long)availableMemory)
695
swprintf_s(invalidValueMsg,
696
L"Value for buffer pool size is too large."
697
L"Only approximately %llu MB is available for allocation."
698
L"Please use a number between 1 and %llu.",
699
availableMemory, availableMemory);
701
ErrorMsg= invalidValueMsg;
706
MsiSetPropertyW (hInstall, L"WarningText", ErrorMsg);
707
return WcaFinalize(er);
711
Sets Innodb buffer pool size (1/8 of RAM by default), if not already specified
713
Calculates innodb log file size as min(50, innodb buffer pool size/8)
715
extern "C" UINT __stdcall PresetDatabaseProperties(MSIHANDLE hInstall)
717
unsigned long long InnodbBufferPoolSize= 256;
718
unsigned long long InnodbLogFileSize= 50;
719
wchar_t buff[MAX_PATH];
720
UINT er = ERROR_SUCCESS;
722
MEMORYSTATUSEX memstatus;
723
hr = WcaInitialize(hInstall, __FUNCTION__);
724
ExitOnFailure(hr, "Failed to initialize");
725
WcaLog(LOGMSG_STANDARD, "Initialized.");
727
/* Check if bufferpoolsize parameter was given on the command line*/
728
DWORD BufferPoolsizeParamLen = MAX_PATH;
729
MsiGetPropertyW(hInstall, L"BUFFERPOOLSIZE", buff, &BufferPoolsizeParamLen);
731
if (BufferPoolsizeParamLen && buff[0])
733
WcaLog(LOGMSG_STANDARD, "BUFFERPOOLSIZE=%s, len=%u",buff, BufferPoolsizeParamLen);
734
InnodbBufferPoolSize= _wtoi64(buff);
738
memstatus.dwLength = sizeof(memstatus);
739
if (!GlobalMemoryStatusEx(&memstatus))
741
WcaLog(LOGMSG_STANDARD, "Error %u from GlobalMemoryStatusEx",
743
er= ERROR_INSTALL_FAILURE;
746
unsigned long long totalPhys= memstatus.ullTotalPhys;
747
/* Give innodb 12.5% of available physical memory. */
748
InnodbBufferPoolSize= totalPhys/ONE_MB/8;
751
For 32 bit processes, take virtual address space limitation into account.
752
Do not try to use more than 3/4 of virtual address space, even if there
753
is plenty of physical memory.
755
InnodbBufferPoolSize= min(GetMaxBufferSize(totalPhys)/ONE_MB*3/4,
756
InnodbBufferPoolSize);
758
swprintf_s(buff, L"%llu",InnodbBufferPoolSize);
759
MsiSetPropertyW(hInstall, L"BUFFERPOOLSIZE", buff);
761
InnodbLogFileSize = min(50, InnodbBufferPoolSize);
762
swprintf_s(buff, L"%llu",InnodbLogFileSize);
763
MsiSetPropertyW(hInstall, L"LOGFILESIZE", buff);
766
return WcaFinalize(er);
768
/* Remove service and data directory created by CreateDatabase operation */
769
extern "C" UINT __stdcall CreateDatabaseRollback(MSIHANDLE hInstall)
772
UINT er = ERROR_SUCCESS;
776
hr = WcaInitialize(hInstall, __FUNCTION__);
777
ExitOnFailure(hr, "Failed to initialize");
778
WcaLog(LOGMSG_STANDARD, "Initialized.");
779
wchar_t data[2*MAX_PATH];
781
MsiGetPropertyW(hInstall, L"CustomActionData", data, &len);
783
/* Property is encoded as [SERVICENAME]\[DBLOCATION] */
791
dir= wcschr(data, '\\');
801
ExecRemoveService(service);
805
ExecRemoveDataDirectory(dir);
808
return WcaFinalize(er);
813
Enables/disables optional "Launch upgrade wizard" checkbox at the end of
816
#define MAX_VERSION_PROPERTY_SIZE 64
818
extern "C" UINT __stdcall CheckServiceUpgrades(MSIHANDLE hInstall)
821
UINT er = ERROR_SUCCESS;
824
wchar_t installerVersion[MAX_VERSION_PROPERTY_SIZE];
825
char installDir[MAX_PATH];
826
DWORD size =MAX_VERSION_PROPERTY_SIZE;
827
int installerMajorVersion, installerMinorVersion, installerPatchVersion;
828
bool upgradableServiceFound=false;
830
hr = WcaInitialize(hInstall, __FUNCTION__);
831
WcaLog(LOGMSG_STANDARD, "Initialized.");
832
if (MsiGetPropertyW(hInstall, L"ProductVersion", installerVersion, &size)
835
hr = HRESULT_FROM_WIN32(GetLastError());
836
ExitOnFailure(hr, "MsiGetPropertyW failed");
838
if (swscanf(installerVersion,L"%d.%d.%d",
839
&installerMajorVersion, &installerMinorVersion, &installerPatchVersion) !=3)
845
if (MsiGetPropertyA(hInstall,"INSTALLDIR", installDir, &size)
848
hr = HRESULT_FROM_WIN32(GetLastError());
849
ExitOnFailure(hr, "MsiGetPropertyW failed");
853
SC_HANDLE scm = OpenSCManager(NULL, NULL,
854
SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
857
hr = HRESULT_FROM_WIN32(GetLastError());
858
ExitOnFailure(hr,"OpenSCManager failed");
861
static BYTE buf[64*1024];
862
static BYTE config_buffer[8*1024];
864
DWORD bufsize= sizeof(buf);
867
BOOL ok= EnumServicesStatusExW(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
868
SERVICE_STATE_ALL, buf, bufsize, &bufneed, &num_services, NULL, NULL);
871
hr = HRESULT_FROM_WIN32(GetLastError());
872
ExitOnFailure(hr,"EnumServicesStatusEx failed");
874
LPENUM_SERVICE_STATUS_PROCESSW info =
875
(LPENUM_SERVICE_STATUS_PROCESSW)buf;
877
for (ULONG i=0; i < num_services; i++)
879
SC_HANDLE service= OpenServiceW(scm, info[i].lpServiceName,
880
SERVICE_QUERY_CONFIG);
883
QUERY_SERVICE_CONFIGW *config=
884
(QUERY_SERVICE_CONFIGW*)(void *)config_buffer;
886
BOOL ok= QueryServiceConfigW(service, config,sizeof(config_buffer),
888
CloseServiceHandle(service);
891
mysqld_service_properties props;
892
if (get_mysql_service_properties(config->lpBinaryPathName, &props))
895
Only look for services that have mysqld.exe outside of the current
896
installation directory.
898
if(installDir[0] == 0 || strstr(props.mysqld_exe,installDir) == 0)
900
WcaLog(LOGMSG_STANDARD, "found service %S, major=%d, minor=%d",
901
info[i].lpServiceName, props.version_major, props.version_minor);
902
if(props.version_major < installerMajorVersion
903
|| (props.version_major == installerMajorVersion &&
904
props.version_minor <= installerMinorVersion))
906
upgradableServiceFound= true;
913
if(!upgradableServiceFound)
915
/* Disable optional checkbox at the end of installation */
916
MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT", L"");
917
MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOX",L"");
921
MsiSetPropertyW(hInstall, L"UpgradableServiceFound", L"1");
922
MsiSetPropertyW(hInstall, L"WIXUI_EXITDIALOGOPTIONALCHECKBOX",L"1");
926
CloseServiceHandle(scm);
927
return WcaFinalize(er);
931
/* DllMain - Initialize and cleanup WiX custom action utils */
932
extern "C" BOOL WINAPI DllMain(
933
__in HINSTANCE hInst,
940
case DLL_PROCESS_ATTACH:
941
WcaGlobalInitialize(hInst);
944
case DLL_PROCESS_DETACH: