如何关闭(而不是终止)已最小化到系统托盘的应用程序?

发布于 2024-11-17 15:10:19 字数 210 浏览 5 评论 0原文

我正在编写一个应用程序,它可以关闭程序,更改其数据文件,然后重新打开它。我注意到,如果我使用 process.Kill() ,则会有一些数据未写入文件。

如果我使用 process.CloseMainWindow(),主窗口将关闭,但进程会最小化到开始托盘。

有什么方法可以向进程发送优雅的关闭消息吗?

如果它很重要的话,我想关闭的就是Grindstone。

I'm writing an app that closes a program, changes its data file then reopens it. I've noticed that if I use process.Kill() there is some data not written to the file.

If I use process.CloseMainWindow(), the main window closes but the process minimises to the start tray.

Is there some way I can send a graceful close message to the process?

If it's important, it's Grindstone that I'm trying to close.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

情愿 2024-11-24 15:10:19

不幸的是,您无法采取任何措施来优雅地终止不合作的应用程序。

建议的方法是将 WM_CLOSE 消息发送到感兴趣的窗口;这在这里不起作用,因为应用程序选择按照您的描述隐藏自身。然而,这是微软认可的唯一方法。

下一步是更严厉一些,将 WM_QUIT 消息发送到线程。这有点问题,因为您必须使用某种形式的进程/线程枚举和 PInvoke PostThreadMessage 发布 WM_QUIT。但是,MSDN 似乎建议你不应该这样做(搜索 WM_QUIT)。作为一个实际问题,它应该有效。

如果这不起作用,那么您就只剩下 Process.Kill 了。

更新:以上是我自己的理解,但还有一篇Microsoft KB文章 关于同一主题。它适用于 Win32(不是托管代码),但可以毫不费力地调整这些想法。

Unfortunately, there's not much you can do to gracefully terminate an application that is not cooperating.

The suggested approach is to send the WM_CLOSE message to the window(s) of interest; this won't work here since the app chooses to hide itself as you describe. However, this is the only approach that Microsoft endorses.

The next step is to be a bit more heavy-handed and send the WM_QUIT message to a thread. This is a bit problematic because you have to find the thread in question using some form of process/thread enumeration and PInvoke PostThreadMessage to post WM_QUIT. However, MSDN seems to suggest that you should not do this (search for WM_QUIT). As a practical matter, it should work though.

If that doesn't work as well then Process.Kill is all you 're left with.

Update: The above is my own understanding, but there's also a Microsoft KB article on this same subject. It works with Win32 (not managed code), but the ideas can be adapted without much trouble.

丢了幸福的猪 2024-11-24 15:10:19

BackgroundWorker 解决方案中的 EventWaitHandle 对象提供here 对我来说效果很好,而且我认为编码比使用 win API 消息更容易。

基本上,您有一个后台工作线程等待使用 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();
      }
   }
}

The EventWaitHandle object in a BackgroundWorker solution provided here worked pretty good for me, and I think it's easier to code than using win API messages.

Basically you got a backgroundworker thread waiting for certain named event to happen with the myEventWaitHandle.WaitOne method.

Another application just creates the same named event and call myEventWaitHandle.Set() to trigger it. This cause the WaitOne() method in the background-worker to continue and therefore the RunWorkerCompleted to be triggered. At that point you can safely close your application.

Your main application:

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(); 
} 

Your "graceful killer" application:

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();
      }
   }
}
夜吻♂芭芘 2024-11-24 15:10:19

您需要发送WM_CLOSE消息到应用程序的主窗口。据推测,这就是 CloseMainWindow 为您抽象的内容。这样每次都会工作得很好。

不幸的是,听起来相关应用程序通过将自身最小化到任务栏的通知区域来处理 WM_CLOSE 消息。在这种情况下,如果您尝试以其他方式退出应用程序,包括“文件”->“退出”,它也会执行相同的操作。退出或单击标题栏中的“X”按钮。

极其自视甚高的应用程序经常会这样做。这是错误的,并且强烈不鼓励,甚至有一种正确的实现方式,但这些事情以前都没有阻止过人们。

因此,真正关闭程序的唯一方法是检查相关应用程序的文档并了解如何关闭它,而不是最小化它。我敢打赌,首选项对话框之一中有一个选项可以控制这一点。确保您已相应地设置该选项。

绝对避免任何涉及向线程发送 WM_QUIT 的建议消息,或终止整个进程。正如您在问题中指出的那样,这不是推荐的方法,并且可能会导致许多问题。您需要找到一种方法让应用程序很好地自行关闭。其他一切都完全属于“杀戮”的范畴,这正是你希望避免的。

You need to send a WM_CLOSE message to the application's main window. Presumably, this is what CloseMainWindow abstracts for you. That will work fine every time.

Unfortunately, it sounds like the app in question handles the WM_CLOSE message by minimizing itself to the taskbar's notification area. In that case, it would do the same thing if you tried to exit the app any other way, including File -> Exit or clicking the "X" button in the title bar.

Applications that are extremely self-important often do this. It's wrong, and strongly discouraged, and there's even a right way of implementing it, but none of those things have ever stopped people before.

Therefore, the only way to actually get the program to close is going to be to check the documentation for the application in question and see how to close it, rather than minimize it. I'm betting there's an option in one of the preferences dialogs that controls this. Make sure you've set that option accordingly.

Definitely shy away from any suggestions involving sending the thread a WM_QUIT message, or killing the entire process. This is not the recommended approach, and can cause a number of problems, as you point out in the question. You need to figure out a way of getting the app to close itself nicely. Everything else falls squarely into the category of "killing", exactly what you wish to avoid.

半透明的墙 2024-11-24 15:10:19

对于我的应用程序,我试图关闭一个 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 也有持续杀戮的模式。

For my application I was trying to shutdown a Python process and its spawned processes opened using "subprocess.Popen". I tried TerminateProcess and it is too evil. :) I finally determined that I can use the console command taskkill. I did this in a C++ program:

    // 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);
    }

You will see my other experiments commented out. I finally settled on this method because it hides any popup window that might appear. I also found that this allows the Python app to call atexit properly. However, even without my explicit ending of the subprocesses they shutdown anyway using the taskkill method. I am guessing this is due to the way the Python code is designed.

So, you can try the above method, wait for the process to close, and if it fails then you can switch to the big guns using TerminateProcess if it just will not cooperate. Taskkill also has modes for killing unrelentingly if needed too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文