[英]How do I close (not kill) an application which has minimized to the system tray?
我正在编写一个关闭程序,更改其数据文件然后重新打开的应用程序。 我注意到,如果我使用process.Kill()
,则有些数据没有写入文件中。
如果我使用process.CloseMainWindow()
,则主窗口将关闭,但该过程将最小化到启动托盘。
有什么方法可以向进程发送正常关闭消息?
如果重要的话,我要关闭的是Grindstone。
不幸的是,要优雅地终止不合作的应用程序您无能为力。
建议的方法是将WM_CLOSE消息发送到感兴趣的窗口。 这将在这里不起作用,因为该应用选择了按照您的描述隐藏自己。 但是,这是Microsoft认可的唯一方法。
下一步将变得更加繁琐,并将WM_QUIT消息发送到线程。 这有点问题,因为您必须使用某种形式的进程/线程枚举和PInvoke PostThreadMessage来发布WM_QUIT来查找有问题的线程。 但是,MSDN似乎建议您不要这样做(搜索WM_QUIT)。 实际上,它应该可以工作。
如果那还行不通,那么剩下的就是Process.Kill
。
更新:以上是我自己的理解,但是在同一主题上也有一篇Microsoft KB文章 。 它可以与Win32(非托管代码)一起使用,但是可以很容易地修改思想。
此处提供的BackgroundWorker
解决方案中的EventWaitHandle
对象对我来说非常有效,我认为编写代码比使用win API消息更容易。
基本上,您有一个backgroundworker线程通过myEventWaitHandle.WaitOne
方法等待某些命名事件发生。
另一个应用程序仅创建相同的命名事件并调用myEventWaitHandle.Set()
来触发它。 这将导致后台工作程序中的WaitOne()
方法继续运行,因此将触发RunWorkerCompleted
。 此时,您可以安全地关闭应用程序。
您的主要应用程序:
private void evtBgWorker_DoWork(object sender, DoWorkEventArgs e) {
string evtName = "MyExitRequest" + Process.GetCurrentProcess().Id.ToString();
EventWaitHandle evt = new EventWaitHandle(false, EventResetMode.ManualReset, evtName);
evt.WaitOne(); // the worker stops here until the event is triggered
}
private void evtBgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
this.Close();
}
您的“优雅杀手”应用程序:
private void CloseMainApp()
{
Process[] processes = Process.GetProcessesByName("MyProcessName");
Process myprocess= null;
if (processes.Length > 0)
{
myprocess = processes[0];
string evtName = "MyExitRequest" + myprocess.Id; // the same event name
EventWaitHandle evt = new EventWaitHandle(false, EventResetMode.ManualReset, evtName);
evt.Set(); // triggers the event at the main app
if (!myprocess.WaitForExit(10000)) // waits until the process ends gracefuly
{
// if don't...
myprocess.Kill();
}
}
}
对于我的应用程序,我试图关闭Python进程,并使用“ subprocess.Popen”打开其衍生的进程。 我尝试了TerminateProcess,它太邪恶了。 :)我最终确定可以使用控制台命令taskkill 。 我在C ++程序中做到了这一点:
// standard kill process call
void stopProcess(DWORD pid)
{
STARTUPINFO startupInfo;
LPPROCESS_INFORMATION processInfo = new PROCESS_INFORMATION;
// clear the memory to prevent garbage
ZeroMemory(&startupInfo, sizeof(startupInfo));
// set size of structure (not using Ex version)
startupInfo.cb = sizeof(STARTUPINFO);
// tell the application that we are setting the window display
// information within this structure
startupInfo.dwFlags = STARTF_USESHOWWINDOW;
// hide process
startupInfo.wShowWindow = SW_HIDE;
//TerminateProcess(itr->second->hProcess, 0); // not friendly to process, and does not kill child processes
std::stringstream comStream;
comStream << "taskkill /pid ";
comStream << pid;
//comStream << " /t /f"; // to be more like TerminateProcess
_MESSAGE("%s", comStream.str().c_str());
//system(comStream.str().c_str()); // works, but pops up a window momentarilly when called
//LPSTR s = const_cast<char *>(comStream.str().c_str());
LPSTR cString = strdup( comStream.str().c_str() );
if(!CreateProcess(NULL,cString,NULL,NULL,false,NORMAL_PRIORITY_CLASS,NULL,NULL,&startupInfo,processInfo)){
_MESSAGE("Could not launch '%s'",cString);
SAFE_DELETE(processInfo);
}else{
// clean up
CloseHandle(processInfo);
SAFE_DELETE(processInfo);
}
// clean up
free(cString);
}
您会看到我的其他实验被注释掉了。 我最终决定使用此方法,因为它隐藏了可能出现的所有弹出窗口。 我还发现,这允许Python应用程序正确调用atexit。 但是,即使我没有明确结束子流程,它们仍然会使用taskkill方法关闭。 我猜这是由于Python代码的设计方式引起的。
因此,您可以尝试上述方法,等待进程关闭,如果失败,则可以使用TerminateProcess切换到大手枪,如果它不能配合使用。 如果需要,Taskkill还提供了无情杀戮的模式。
您需要向应用程序的主窗口发送WM_CLOSE
消息 。 大概这就是CloseMainWindow
为您抽象的内容。 每次都可以正常工作。
不幸的是,听起来像该应用程序通过将其自身最小化到任务栏的通知区域来处理WM_CLOSE
消息。 在这种情况下,如果您尝试以其他任何方式退出应用程序,包括“文件”->“退出”或单击标题栏中的“ X”按钮,它将执行相同的操作。
非常重要的应用程序通常会这样做。 这是错误的,强烈建议不要这样做,但是甚至有正确的实现方法,但是这些事情都没有阻止过人们。
因此,实际上要关闭程序的唯一方法是,检查所涉及应用程序的文档,并查看如何关闭它,而不是最小化它。 我敢打赌,其中一个首选项对话框中有一个选项可以控制这一情况。 确保已相应设置该选项。
绝对回避任何与向线程WM_QUIT
消息或杀死整个进程有关的建议。 正如您在问题中指出的那样,这不是推荐的方法,并且可能导致许多问题。 您需要找出一种使应用程序很好地关闭自身的方法。 其他所有事情都完全属于“杀手”类别,这正是您要避免的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.