mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
a93ac8d95f
MDEV-16447 incorporate Innodb slow shutdown into mysql_upgrade_service.exe
628 lines
16 KiB
C++
628 lines
16 KiB
C++
|
|
// upgradeDlg.cpp : implementation file
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "upgrade.h"
|
|
#include "upgradeDlg.h"
|
|
#include "windows.h"
|
|
#include "winsvc.h"
|
|
#include <msi.h>
|
|
#pragma comment(lib, "msi")
|
|
#pragma comment(lib, "version")
|
|
#include <map>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <winservice.h>
|
|
|
|
using namespace std;
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
#define PRODUCT_NAME "MariaDB"
|
|
|
|
// CUpgradeDlg dialog
|
|
|
|
CUpgradeDlg::CUpgradeDlg(CWnd* pParent /*=NULL*/)
|
|
: CDialog(CUpgradeDlg::IDD, pParent)
|
|
{
|
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
|
}
|
|
|
|
void CUpgradeDlg::DoDataExchange(CDataExchange* pDX)
|
|
{
|
|
CDialog::DoDataExchange(pDX);
|
|
DDX_Control(pDX, IDC_LIST1, m_Services);
|
|
DDX_Control(pDX, IDC_PROGRESS1, m_Progress);
|
|
DDX_Control(pDX, IDOK, m_Ok);
|
|
DDX_Control(pDX, IDCANCEL, m_Cancel);
|
|
DDX_Control(pDX, IDC_EDIT1, m_IniFilePath);
|
|
DDX_Control(pDX, IDC_EDIT2, m_DataDir);
|
|
DDX_Control(pDX, IDC_EDIT3, m_Version);
|
|
DDX_Control(pDX, IDC_EDIT7, m_IniFileLabel);
|
|
DDX_Control(pDX, IDC_EDIT8, m_DataDirLabel);
|
|
DDX_Control(pDX, IDC_EDIT9, m_VersionLabel);
|
|
DDX_Control(pDX, IDC_BUTTON1, m_SelectAll);
|
|
DDX_Control(pDX, IDC_BUTTON2, m_ClearAll);
|
|
}
|
|
|
|
BEGIN_MESSAGE_MAP(CUpgradeDlg, CDialog)
|
|
ON_WM_PAINT()
|
|
ON_WM_QUERYDRAGICON()
|
|
ON_LBN_SELCHANGE(IDC_LIST1, &CUpgradeDlg::OnLbnSelchangeList1)
|
|
ON_CONTROL(CLBN_CHKCHANGE, IDC_LIST1, OnChkChange)
|
|
ON_BN_CLICKED(IDOK, &CUpgradeDlg::OnBnClickedOk)
|
|
ON_BN_CLICKED(IDCANCEL, &CUpgradeDlg::OnBnClickedCancel)
|
|
ON_BN_CLICKED(IDC_BUTTON1,&CUpgradeDlg::OnBnSelectAll)
|
|
ON_BN_CLICKED(IDC_BUTTON2,&CUpgradeDlg::OnBnClearAll)
|
|
END_MESSAGE_MAP()
|
|
|
|
|
|
struct ServiceProperties
|
|
{
|
|
string servicename;
|
|
string myini;
|
|
string datadir;
|
|
string version;
|
|
};
|
|
|
|
vector<ServiceProperties> services;
|
|
|
|
/*
|
|
Get version from an executable.
|
|
Returned version is either major.minor.patch or
|
|
<unknown> , of executable does not have any version
|
|
info embedded (like MySQL 5.1 for example)
|
|
*/
|
|
void GetExeVersion(const string& filename, int *major, int *minor, int *patch)
|
|
{
|
|
DWORD handle;
|
|
*major= *minor= *patch= 0;
|
|
|
|
DWORD size = GetFileVersionInfoSize(filename.c_str(), &handle);
|
|
BYTE* versionInfo = new BYTE[size];
|
|
if (!GetFileVersionInfo(filename.c_str(), handle, size, versionInfo))
|
|
{
|
|
delete[] versionInfo;
|
|
return;
|
|
}
|
|
// we have version information
|
|
UINT len = 0;
|
|
VS_FIXEDFILEINFO* vsfi = NULL;
|
|
VerQueryValue(versionInfo, "\\", (void**)&vsfi, &len);
|
|
|
|
*major= (int)HIWORD(vsfi->dwFileVersionMS);
|
|
*minor= (int)LOWORD(vsfi->dwFileVersionMS);
|
|
*patch= (int)HIWORD(vsfi->dwFileVersionLS);
|
|
delete[] versionInfo;
|
|
}
|
|
|
|
|
|
void GetMyVersion(int *major, int *minor, int *patch)
|
|
{
|
|
char path[MAX_PATH];
|
|
*major= *minor= *patch =0;
|
|
if (GetModuleFileName(NULL, path, MAX_PATH))
|
|
{
|
|
GetExeVersion(path, major, minor, patch);
|
|
}
|
|
}
|
|
// CUpgradeDlg message handlers
|
|
|
|
/* Handle selection changes in services list */
|
|
void CUpgradeDlg::SelectService(int index)
|
|
{
|
|
m_IniFilePath.SetWindowText(services[index].myini.c_str());
|
|
m_DataDir.SetWindowText(services[index].datadir.c_str());
|
|
m_Version.SetWindowText(services[index].version.c_str());
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Iterate over services, lookup for mysqld.exe ones.
|
|
Compare mysqld.exe version with current version, and display
|
|
service if corresponding mysqld.exe has lower version.
|
|
|
|
The version check is not strict, i.e we allow to "upgrade"
|
|
for the same major.minor combination. This can be useful for
|
|
"upgrading" from 32 to 64 bit, or for MySQL=>Maria conversion.
|
|
*/
|
|
void CUpgradeDlg::PopulateServicesList()
|
|
{
|
|
|
|
SC_HANDLE scm = OpenSCManager(NULL, NULL,
|
|
SC_MANAGER_ENUMERATE_SERVICE | SC_MANAGER_CONNECT);
|
|
if (scm == NULL)
|
|
{
|
|
ErrorExit("OpenSCManager failed");
|
|
}
|
|
|
|
static BYTE buf[64*1024];
|
|
static BYTE configBuffer[8*1024];
|
|
|
|
DWORD bufsize= sizeof(buf);
|
|
DWORD bufneed;
|
|
DWORD num_services;
|
|
BOOL ok= EnumServicesStatusEx(scm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
|
|
SERVICE_STATE_ALL, buf, bufsize, &bufneed, &num_services, NULL, NULL);
|
|
if(!ok)
|
|
ErrorExit("EnumServicesStatusEx failed");
|
|
|
|
|
|
LPENUM_SERVICE_STATUS_PROCESS info =
|
|
(LPENUM_SERVICE_STATUS_PROCESS)buf;
|
|
int index=-1;
|
|
for (ULONG i=0; i < num_services; i++)
|
|
{
|
|
SC_HANDLE service= OpenService(scm, info[i].lpServiceName,
|
|
SERVICE_QUERY_CONFIG);
|
|
if (!service)
|
|
continue;
|
|
QUERY_SERVICE_CONFIGW *config=
|
|
(QUERY_SERVICE_CONFIGW*)(void *)configBuffer;
|
|
DWORD needed;
|
|
BOOL ok= QueryServiceConfigW(service, config,sizeof(configBuffer), &needed);
|
|
CloseServiceHandle(service);
|
|
if (ok)
|
|
{
|
|
mysqld_service_properties service_props;
|
|
|
|
if (get_mysql_service_properties(config->lpBinaryPathName,
|
|
&service_props))
|
|
continue;
|
|
|
|
/* Check if service uses mysqld in installation directory */
|
|
if (_strnicmp(service_props.mysqld_exe, m_InstallDir.c_str(),
|
|
m_InstallDir.size()) == 0)
|
|
continue;
|
|
|
|
if(m_MajorVersion > service_props.version_major ||
|
|
(m_MajorVersion == service_props.version_major && m_MinorVersion >=
|
|
service_props.version_minor))
|
|
{
|
|
ServiceProperties props;
|
|
props.myini= service_props.inifile;
|
|
props.datadir= service_props.datadir;
|
|
props.servicename = info[i].lpServiceName;
|
|
if (service_props.version_major)
|
|
{
|
|
char ver[64];
|
|
sprintf(ver, "%d.%d.%d", service_props.version_major,
|
|
service_props.version_minor, service_props.version_patch);
|
|
props.version= ver;
|
|
}
|
|
else
|
|
props.version= "<unknown>";
|
|
|
|
index = m_Services.AddString(info[i].lpServiceName);
|
|
services.resize(index+1);
|
|
services[index] = props;
|
|
}
|
|
}
|
|
if (index != -1)
|
|
{
|
|
m_Services.SetCurSel(0);
|
|
SelectService(m_Services.GetCurSel());
|
|
}
|
|
}
|
|
if (services.size())
|
|
{
|
|
SelectService(0);
|
|
}
|
|
else
|
|
{
|
|
char message[128];
|
|
sprintf(message,
|
|
"There is no service that can be upgraded to " PRODUCT_NAME " %d.%d.%d",
|
|
m_MajorVersion, m_MinorVersion, m_PatchVersion);
|
|
MessageBox(message, PRODUCT_NAME " Upgrade Wizard", MB_ICONINFORMATION);
|
|
exit(0);
|
|
}
|
|
if(scm)
|
|
CloseServiceHandle(scm);
|
|
}
|
|
|
|
BOOL CUpgradeDlg::OnInitDialog()
|
|
{
|
|
CDialog::OnInitDialog();
|
|
m_UpgradeRunning= FALSE;
|
|
// Set the icon for this dialog. The framework does this automatically
|
|
// when the application's main window is not a dialog
|
|
SetIcon(m_hIcon, TRUE); // Set big icon
|
|
SetIcon(m_hIcon, FALSE); // Set small icon
|
|
m_Ok.SetWindowText("Upgrade");
|
|
m_DataDirLabel.SetWindowText("Data directory:");
|
|
m_IniFileLabel.SetWindowText("Configuration file:");
|
|
m_VersionLabel.SetWindowText("Version:");
|
|
|
|
char myFilename[MAX_PATH];
|
|
GetModuleFileName(NULL, myFilename, MAX_PATH);
|
|
char *p= strrchr(myFilename,'\\');
|
|
if(p)
|
|
p[1]=0;
|
|
m_InstallDir= myFilename;
|
|
|
|
GetMyVersion(&m_MajorVersion, &m_MinorVersion, &m_PatchVersion);
|
|
char windowTitle[64];
|
|
|
|
sprintf(windowTitle, PRODUCT_NAME " %d.%d.%d Upgrade Wizard",
|
|
m_MajorVersion, m_MinorVersion, m_PatchVersion);
|
|
SetWindowText(windowTitle);
|
|
|
|
m_JobObject= CreateJobObject(NULL, NULL);
|
|
|
|
/*
|
|
Make all processes associated with the job terminate when the
|
|
last handle to the job is closed or job is teminated.
|
|
*/
|
|
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0};
|
|
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
|
SetInformationJobObject(m_JobObject, JobObjectExtendedLimitInformation,
|
|
&jeli, sizeof(jeli));
|
|
|
|
|
|
m_Progress.ShowWindow(SW_HIDE);
|
|
m_Ok.EnableWindow(FALSE);
|
|
PopulateServicesList();
|
|
return TRUE; // return TRUE unless you set the focus to a control
|
|
}
|
|
|
|
// If you add a minimize button to your dialog, you will need the code below
|
|
// to draw the icon. For MFC applications using the document/view model,
|
|
// this is automatically done for you by the framework.
|
|
|
|
void CUpgradeDlg::OnPaint()
|
|
{
|
|
if (IsIconic())
|
|
{
|
|
CPaintDC dc(this); // device context for painting
|
|
|
|
SendMessage(WM_ICONERASEBKGND,
|
|
reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
|
|
|
|
// Center icon in client rectangle
|
|
int cxIcon = GetSystemMetrics(SM_CXICON);
|
|
int cyIcon = GetSystemMetrics(SM_CYICON);
|
|
CRect rect;
|
|
GetClientRect(&rect);
|
|
int x = (rect.Width() - cxIcon + 1) / 2;
|
|
int y = (rect.Height() - cyIcon + 1) / 2;
|
|
|
|
// Draw the icon
|
|
dc.DrawIcon(x, y, m_hIcon);
|
|
}
|
|
else
|
|
{
|
|
CDialog::OnPaint();
|
|
}
|
|
}
|
|
|
|
// The system calls this function to obtain the cursor to display while the user
|
|
// drags the minimized window.
|
|
HCURSOR CUpgradeDlg::OnQueryDragIcon()
|
|
{
|
|
return static_cast<HCURSOR>(m_hIcon);
|
|
}
|
|
|
|
|
|
void CUpgradeDlg::OnLbnSelchangeList1()
|
|
{
|
|
SelectService(m_Services.GetCurSel());
|
|
}
|
|
|
|
void CUpgradeDlg::OnChkChange()
|
|
{
|
|
if(m_Services.GetCheck( m_Services.GetCurSel()))
|
|
{
|
|
GetDlgItem(IDOK)->EnableWindow();
|
|
}
|
|
else
|
|
{
|
|
for(int i=0; i< m_Services.GetCount(); i++)
|
|
{
|
|
if(m_Services.GetCheck(i))
|
|
return;
|
|
}
|
|
// all items unchecked, disable OK button
|
|
GetDlgItem(IDOK)->EnableWindow(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CUpgradeDlg::ErrorExit(LPCSTR str)
|
|
{
|
|
MessageBox(str, "Fatal Error", MB_ICONERROR);
|
|
exit(1);
|
|
}
|
|
|
|
|
|
const int MAX_MESSAGES=512;
|
|
|
|
/* Main thread of the child process */
|
|
static HANDLE hChildThread;
|
|
|
|
void CUpgradeDlg::UpgradeOneService(const string& servicename)
|
|
{
|
|
static string allMessages[MAX_MESSAGES];
|
|
static char npname[MAX_PATH];
|
|
static char pipeReadBuf[1];
|
|
SECURITY_ATTRIBUTES saAttr;
|
|
STARTUPINFO si={0};
|
|
PROCESS_INFORMATION pi;
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saAttr.bInheritHandle = TRUE;
|
|
saAttr.lpSecurityDescriptor = NULL;
|
|
|
|
HANDLE hPipeRead, hPipeWrite;
|
|
if(!CreatePipe(&hPipeRead, &hPipeWrite, &saAttr, 1))
|
|
ErrorExit("CreateNamedPipe failed");
|
|
|
|
/* Make sure read end of the pipe is not inherited */
|
|
if (!SetHandleInformation(hPipeRead, HANDLE_FLAG_INHERIT, 0) )
|
|
ErrorExit("Stdout SetHandleInformation");
|
|
|
|
string commandline("mysql_upgrade_service.exe --service=");
|
|
commandline += servicename;
|
|
si.cb = sizeof(si);
|
|
si.hStdInput= GetStdHandle(STD_INPUT_HANDLE);
|
|
si.hStdOutput= hPipeWrite;
|
|
si.hStdError= hPipeWrite;
|
|
si.wShowWindow= SW_HIDE;
|
|
si.dwFlags= STARTF_USESTDHANDLES |STARTF_USESHOWWINDOW;
|
|
|
|
|
|
/*
|
|
We will try to assign child process to a job, to be able to
|
|
terminate the process and all of its children. It might fail,
|
|
in case current process is already part of the job which does
|
|
not allows breakaways.
|
|
*/
|
|
if (CreateProcess(NULL, (LPSTR)commandline.c_str(), NULL, NULL, TRUE,
|
|
CREATE_BREAKAWAY_FROM_JOB|CREATE_SUSPENDED, NULL, NULL, &si, &pi))
|
|
{
|
|
if(!AssignProcessToJobObject(m_JobObject, pi.hProcess))
|
|
{
|
|
char errmsg[128];
|
|
sprintf(errmsg, "AssignProcessToJobObject failed, error %d",
|
|
GetLastError());
|
|
ErrorExit(errmsg);
|
|
}
|
|
ResumeThread(pi.hThread);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
Creating a process with CREATE_BREAKAWAY_FROM_JOB, reset this flag
|
|
and retry.
|
|
*/
|
|
if (!CreateProcess(NULL, (LPSTR)commandline.c_str(), NULL, NULL, TRUE,
|
|
0, NULL, NULL, &si, &pi))
|
|
{
|
|
string errmsg("Create Process ");
|
|
errmsg+= commandline;
|
|
errmsg+= " failed";
|
|
ErrorExit(errmsg.c_str());
|
|
}
|
|
}
|
|
|
|
hChildThread = pi.hThread;
|
|
DWORD nbytes;
|
|
int lines=0;
|
|
CloseHandle(hPipeWrite);
|
|
|
|
string output_line;
|
|
while(ReadFile(hPipeRead, pipeReadBuf, 1, &nbytes, NULL))
|
|
{
|
|
if(pipeReadBuf[0] == '\n')
|
|
{
|
|
allMessages[lines%MAX_MESSAGES] = output_line;
|
|
m_DataDir.SetWindowText(allMessages[lines%MAX_MESSAGES].c_str());
|
|
lines++;
|
|
|
|
int curPhase, numPhases;
|
|
|
|
// Parse output line to update progress indicator
|
|
if (strncmp(output_line.c_str(),"Phase ",6) == 0 &&
|
|
sscanf(output_line.c_str() +6 ,"%d/%d",&curPhase,&numPhases) == 2
|
|
&& numPhases > 0 )
|
|
{
|
|
int stepsTotal= m_ProgressTotal*numPhases;
|
|
int stepsCurrent= m_ProgressCurrent*numPhases+ curPhase;
|
|
int percentDone= stepsCurrent*100/stepsTotal;
|
|
m_Progress.SetPos(percentDone);
|
|
m_Progress.SetPos(stepsCurrent * 100 / stepsTotal);
|
|
}
|
|
output_line.clear();
|
|
}
|
|
else
|
|
{
|
|
if(pipeReadBuf[0] != '\r')
|
|
output_line.push_back(pipeReadBuf[0]);
|
|
}
|
|
}
|
|
CloseHandle(hPipeWrite);
|
|
|
|
if(WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0)
|
|
ErrorExit("WaitForSingleObject failed");
|
|
DWORD exitcode;
|
|
if (!GetExitCodeProcess(pi.hProcess, &exitcode))
|
|
ErrorExit("GetExitCodeProcess failed");
|
|
|
|
if (exitcode != 0)
|
|
{
|
|
string errmsg= "mysql_upgrade_service returned error for service ";
|
|
errmsg += servicename;
|
|
errmsg += ":\r\n";
|
|
errmsg+= output_line;
|
|
ErrorExit(errmsg.c_str());
|
|
}
|
|
CloseHandle(pi.hProcess);
|
|
hChildThread= 0;
|
|
CloseHandle(pi.hThread);
|
|
}
|
|
|
|
|
|
void CUpgradeDlg::UpgradeServices()
|
|
{
|
|
|
|
/*
|
|
Disable some dialog items during upgrade (OK button,
|
|
services list)
|
|
*/
|
|
m_Ok.EnableWindow(FALSE);
|
|
m_Services.EnableWindow(FALSE);
|
|
m_SelectAll.EnableWindow(FALSE);
|
|
m_ClearAll.EnableWindow(FALSE);
|
|
|
|
/*
|
|
Temporarily repurpose IniFileLabel/IniFilePath and
|
|
DatDirLabel/DataDir controls to show progress messages.
|
|
*/
|
|
m_VersionLabel.ShowWindow(FALSE);
|
|
m_Version.ShowWindow(FALSE);
|
|
m_Progress.ShowWindow(TRUE);
|
|
m_IniFileLabel.SetWindowText("Converting service:");
|
|
m_IniFilePath.SetWindowText("");
|
|
m_DataDirLabel.SetWindowText("Progress message:");
|
|
m_DataDir.SetWindowText("");
|
|
|
|
|
|
m_ProgressTotal=0;
|
|
for(int i=0; i< m_Services.GetCount(); i++)
|
|
{
|
|
if(m_Services.GetCheck(i))
|
|
m_ProgressTotal++;
|
|
}
|
|
m_ProgressCurrent=0;
|
|
for(int i=0; i< m_Services.GetCount(); i++)
|
|
{
|
|
if(m_Services.GetCheck(i))
|
|
{
|
|
m_IniFilePath.SetWindowText(services[i].servicename.c_str());
|
|
m_Services.SelectString(0, services[i].servicename.c_str());
|
|
UpgradeOneService(services[i].servicename);
|
|
m_ProgressCurrent++;
|
|
}
|
|
}
|
|
|
|
MessageBox("Service(s) successfully upgraded", "Success",
|
|
MB_ICONINFORMATION);
|
|
|
|
/* Rebuild services list */
|
|
vector<ServiceProperties> new_instances;
|
|
for(int i=0; i< m_Services.GetCount(); i++)
|
|
{
|
|
if(!m_Services.GetCheck(i))
|
|
new_instances.push_back(services[i]);
|
|
}
|
|
|
|
services= new_instances;
|
|
m_Services.ResetContent();
|
|
for(size_t i=0; i< services.size();i++)
|
|
m_Services.AddString(services[i].servicename.c_str());
|
|
if(services.size())
|
|
{
|
|
m_Services.SelectString(0,services[0].servicename.c_str());
|
|
SelectService(0);
|
|
}
|
|
else
|
|
{
|
|
/* Nothing to do, there are no upgradable services */
|
|
exit(0);
|
|
}
|
|
|
|
/*
|
|
Restore controls that were temporarily repurposed for
|
|
progress info to their normal state
|
|
*/
|
|
m_IniFileLabel.SetWindowText("Configuration file:");
|
|
m_DataDirLabel.SetWindowText("Data Directory:");
|
|
m_VersionLabel.ShowWindow(TRUE);
|
|
m_Version.ShowWindow(TRUE);
|
|
m_Progress.SetPos(0);
|
|
m_Progress.ShowWindow(FALSE);
|
|
|
|
/* Re-enable controls */
|
|
m_Ok.EnableWindow(TRUE);
|
|
m_Services.EnableWindow(TRUE);
|
|
m_SelectAll.EnableWindow(TRUE);
|
|
m_ClearAll.EnableWindow(TRUE);
|
|
|
|
m_UpgradeRunning= FALSE;
|
|
}
|
|
|
|
|
|
/* Thread procedure for upgrade services operation */
|
|
static UINT UpgradeServicesThread(void *param)
|
|
{
|
|
CUpgradeDlg *dlg= (CUpgradeDlg *)param;
|
|
dlg->UpgradeServices();
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Do upgrade for all services currently selected
|
|
in the list. Since it is a potentially lengthy operation that
|
|
might block it has to be done in a background thread.
|
|
*/
|
|
void CUpgradeDlg::OnBnClickedOk()
|
|
{
|
|
if(m_UpgradeRunning)
|
|
return;
|
|
m_UpgradeRunning= TRUE;
|
|
AfxBeginThread(UpgradeServicesThread, this);
|
|
}
|
|
|
|
|
|
/*
|
|
Cancel button clicked.
|
|
If upgrade is running, suspend mysql_upgrade_service,
|
|
and ask user whether he really wants to stop.Terminate
|
|
upgrade wizard and all subprocesses if users wants it.
|
|
|
|
If upgrade is not running, terminate the Wizard
|
|
*/
|
|
void CUpgradeDlg::OnBnClickedCancel()
|
|
{
|
|
if(m_UpgradeRunning)
|
|
{
|
|
bool suspended = (SuspendThread(hChildThread) != (DWORD)-1);
|
|
int ret = MessageBox(
|
|
"Upgrade is in progress. Are you sure you want to terminate?",
|
|
0, MB_YESNO|MB_DEFBUTTON2|MB_ICONQUESTION);
|
|
if(ret != IDYES)
|
|
{
|
|
if(suspended)
|
|
ResumeThread(hChildThread);
|
|
return;
|
|
}
|
|
}
|
|
TerminateJobObject(m_JobObject, 1);
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
Select all services from the list
|
|
*/
|
|
void CUpgradeDlg::OnBnSelectAll()
|
|
{
|
|
for(int i=0; i < m_Services.GetCount(); i++)
|
|
m_Services.SetCheck(i, 1);
|
|
m_Ok.EnableWindow(TRUE);
|
|
}
|
|
|
|
/*
|
|
Clear all services in the list
|
|
*/
|
|
void CUpgradeDlg::OnBnClearAll()
|
|
{
|
|
for(int i=0; i < m_Services.GetCount(); i++)
|
|
m_Services.SetCheck(i, 0);
|
|
m_Ok.EnableWindow(FALSE);
|
|
}
|