我如何等待由我的应用程序启动的另一个应用程序启动的应用程序 Qt/C++
How can I wait for an application launched by another application launched by my application Qt/C++
我正在 Qt 5.4 中创建一个 Windows Add/Remove 程序应用程序,我正疯狂地解决一些问题 "puzzle":
我的应用程序 (APP_0) 运行另一个应用程序 (APP_1) 并等待这个 APP_1 直到它终止。
APP_1 是一个卸载程序(即 uninstall.exe),我没有 APP_1 的源代码,只有我的 Qt APP_0.
APP_1,而不是执行卸载作业,它只是将自身复制到文件系统中的某处(我看到的是 Au_.exe,但其他应用程序可以使用不同的名称和位置),运行此副本本身 (APP_2) 并终止。
APP_2 有一个 GUI,运行 APP_2.
的最终用户需要我正在等待的作业(卸载)
在这种情况下,我的应用程序 (APP_0) 实际上立即停止等待 APP_1(因为它启动 APP_1 并等待 APP_1)。但是为了正常工作,显然,我需要知道 APP_2 何时终止...
所以问题是:
有没有办法(使用一些技术(挂钩?))知道 APP_2 是否以及何时终止?
注意:考虑到标准 Windows Add/Remove 程序实用程序成功地完成了这项工作(它似乎在等待 APP_2)。您可以对此进行测试,例如,安装 Adobe Digital Edition。它的卸载程序 (uninstall.exe) 将自身复制到 User_Local_Temp 文件夹中的一个新文件夹中作为 Au_.exe,运行它并终止。但是 OS 实用程序成功等待 Au_.exe,并且只有在它终止后才会刷新已安装程序的列表。
如果采用这种技术(uninstall.exe 将自身复制到某处,并且总是使用相同的名称 (Au_.exe)),那么问题就可以得到解决,显然,非常简单。但我不认为复制的卸载程序的名称总是相同的,而且我不喜欢假设我不确定的东西是真实的。
非常感谢
感谢 IInspectable 的建议(请参阅他的评论...非常感谢他!)我创建了一个函数来解决我的问题!我将在这里分享这个功能,它可能对其他有相同(或类似)问题的人有用。
根据我的需要,函数接收要卸载的项目的索引(来自 QList)作为参数并获取卸载字符串(例如:C:\ProgramFiles\MyApp\uninstall.exe)。
然后使用这个卸载字符串,我将创建一个进程 (CreateProcess) 并将其句柄放入作业对象中,这样我的函数将等待该进程的所有进程 运行。
功能本身很简单,可以改进。
请注意,必须使用 CREATE_BREAKAWAY_FROM_JOB 选项创建流程,否则 AssignProcessToJobObject 将失败并出现 "Access Denied" 错误。
void MainWindow::uniButtonClick(int idx)
{
QMessageBox::StandardButton reply;
QMessageBox::StandardButton err;
reply = QMessageBox::question(this, "Uninstall/Change", "Uninstall " +
ip[idx].displayName +"?\r\n\r\n" + ip[idx].uninstallString,
QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes)
{
//QString s = "C:\windows\notepad.exe"; // Just to test Job assignment and createprocess
QString s = ip[idx].uninstallString; // the real uninstaller string
QString jobName = "MyJobObject";
try
{
PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter
STARTUPINFO StartupInfo; //This is an [in] parameter
PJOBOBJECT_BASIC_PROCESS_ID_LIST pList;
HANDLE hProcess;
BOOL bJobAllEnd;
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field
wchar_t* path;
path = (wchar_t*) malloc (sizeof(wchar_t)*s.length()+1);
s.toWCharArray(path);
path[s.length()]=0; // Null terminate the string
// Create the process with CREATE_BREAKAWAY_FROM_JOB to overcome the AccessDenied issue on AssignProcessToJobObject.
if(CreateProcess(NULL, path, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB|CREATE_SUSPENDED, NULL, NULL,&StartupInfo, &ProcessInfo))
{
pList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST)GlobalAlloc(GMEM_FIXED, 10000);
HANDLE jobObj = CreateJobObject(NULL, (const wchar_t*)jobName.utf16());
if (AssignProcessToJobObject(jobObj, ProcessInfo.hProcess) != 0)
{
ResumeThread(ProcessInfo.hThread); // Process assigned to JobObjext, resume it now
do
{
QueryInformationJobObject(jobObj, JobObjectBasicProcessIdList, pList, 10000, NULL);
bJobAllEnd = TRUE;
for(DWORD i=0; i<pList->NumberOfProcessIdsInList; i++)
{
hProcess = OpenProcess(SYNCHRONIZE, FALSE, pList->ProcessIdList[i]);
if(hProcess != NULL)
{
CloseHandle(hProcess);
bJobAllEnd = FALSE;
}
}
Sleep(500);
} while(!bJobAllEnd);
}
else
qDebug() << "AssignProcess to Job failed: error = " << QString::number(GetLastError());
GlobalFree(pList);
CloseHandle(jobObj);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
}
}
catch(QString error)
{
QMessageBox::critical(this, "File not found!", "The requested uninstaller doesn't exists", QMessageBox::Ok);
}
// refresh list
handleButton();
}
}
我正在 Qt 5.4 中创建一个 Windows Add/Remove 程序应用程序,我正疯狂地解决一些问题 "puzzle":
我的应用程序 (APP_0) 运行另一个应用程序 (APP_1) 并等待这个 APP_1 直到它终止。
APP_1 是一个卸载程序(即 uninstall.exe),我没有 APP_1 的源代码,只有我的 Qt APP_0.
APP_1,而不是执行卸载作业,它只是将自身复制到文件系统中的某处(我看到的是 Au_.exe,但其他应用程序可以使用不同的名称和位置),运行此副本本身 (APP_2) 并终止。
APP_2 有一个 GUI,运行 APP_2.
的最终用户需要我正在等待的作业(卸载)在这种情况下,我的应用程序 (APP_0) 实际上立即停止等待 APP_1(因为它启动 APP_1 并等待 APP_1)。但是为了正常工作,显然,我需要知道 APP_2 何时终止...
所以问题是: 有没有办法(使用一些技术(挂钩?))知道 APP_2 是否以及何时终止?
注意:考虑到标准 Windows Add/Remove 程序实用程序成功地完成了这项工作(它似乎在等待 APP_2)。您可以对此进行测试,例如,安装 Adobe Digital Edition。它的卸载程序 (uninstall.exe) 将自身复制到 User_Local_Temp 文件夹中的一个新文件夹中作为 Au_.exe,运行它并终止。但是 OS 实用程序成功等待 Au_.exe,并且只有在它终止后才会刷新已安装程序的列表。
如果采用这种技术(uninstall.exe 将自身复制到某处,并且总是使用相同的名称 (Au_.exe)),那么问题就可以得到解决,显然,非常简单。但我不认为复制的卸载程序的名称总是相同的,而且我不喜欢假设我不确定的东西是真实的。
非常感谢
感谢 IInspectable 的建议(请参阅他的评论...非常感谢他!)我创建了一个函数来解决我的问题!我将在这里分享这个功能,它可能对其他有相同(或类似)问题的人有用。
根据我的需要,函数接收要卸载的项目的索引(来自 QList)作为参数并获取卸载字符串(例如:C:\ProgramFiles\MyApp\uninstall.exe)。
然后使用这个卸载字符串,我将创建一个进程 (CreateProcess) 并将其句柄放入作业对象中,这样我的函数将等待该进程的所有进程 运行。
功能本身很简单,可以改进。 请注意,必须使用 CREATE_BREAKAWAY_FROM_JOB 选项创建流程,否则 AssignProcessToJobObject 将失败并出现 "Access Denied" 错误。
void MainWindow::uniButtonClick(int idx)
{
QMessageBox::StandardButton reply;
QMessageBox::StandardButton err;
reply = QMessageBox::question(this, "Uninstall/Change", "Uninstall " +
ip[idx].displayName +"?\r\n\r\n" + ip[idx].uninstallString,
QMessageBox::Yes|QMessageBox::No);
if (reply == QMessageBox::Yes)
{
//QString s = "C:\windows\notepad.exe"; // Just to test Job assignment and createprocess
QString s = ip[idx].uninstallString; // the real uninstaller string
QString jobName = "MyJobObject";
try
{
PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter
STARTUPINFO StartupInfo; //This is an [in] parameter
PJOBOBJECT_BASIC_PROCESS_ID_LIST pList;
HANDLE hProcess;
BOOL bJobAllEnd;
ZeroMemory(&StartupInfo, sizeof(StartupInfo));
StartupInfo.cb = sizeof StartupInfo ; //Only compulsory field
wchar_t* path;
path = (wchar_t*) malloc (sizeof(wchar_t)*s.length()+1);
s.toWCharArray(path);
path[s.length()]=0; // Null terminate the string
// Create the process with CREATE_BREAKAWAY_FROM_JOB to overcome the AccessDenied issue on AssignProcessToJobObject.
if(CreateProcess(NULL, path, NULL, NULL, FALSE, CREATE_BREAKAWAY_FROM_JOB|CREATE_SUSPENDED, NULL, NULL,&StartupInfo, &ProcessInfo))
{
pList = (PJOBOBJECT_BASIC_PROCESS_ID_LIST)GlobalAlloc(GMEM_FIXED, 10000);
HANDLE jobObj = CreateJobObject(NULL, (const wchar_t*)jobName.utf16());
if (AssignProcessToJobObject(jobObj, ProcessInfo.hProcess) != 0)
{
ResumeThread(ProcessInfo.hThread); // Process assigned to JobObjext, resume it now
do
{
QueryInformationJobObject(jobObj, JobObjectBasicProcessIdList, pList, 10000, NULL);
bJobAllEnd = TRUE;
for(DWORD i=0; i<pList->NumberOfProcessIdsInList; i++)
{
hProcess = OpenProcess(SYNCHRONIZE, FALSE, pList->ProcessIdList[i]);
if(hProcess != NULL)
{
CloseHandle(hProcess);
bJobAllEnd = FALSE;
}
}
Sleep(500);
} while(!bJobAllEnd);
}
else
qDebug() << "AssignProcess to Job failed: error = " << QString::number(GetLastError());
GlobalFree(pList);
CloseHandle(jobObj);
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
}
}
catch(QString error)
{
QMessageBox::critical(this, "File not found!", "The requested uninstaller doesn't exists", QMessageBox::Ok);
}
// refresh list
handleButton();
}
}