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

时间:2011-06-26 22:45:04

标签: c# windows-7 process system-tray

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

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

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

如果重要的是,我正试图关闭Grindstone。

4 个答案:

答案 0 :(得分:3)

不幸的是,你没有太多能够优雅地终止不合作的应用程序。

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

下一步是更加严厉,并将WM_QUIT消息发送给线程。这有点问题,因为你必须使用某种形式的进程/线程枚举和PInvoke PostThreadMessage找到有问题的线程来发布WM_QUIT。但是,MSDN似乎suggest你不应该这样做(搜索WM_QUIT)。实际上,它应该可以工作。

如果这不起作用,那么Process.Kill就是你剩下的。

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

答案 1 :(得分:1)

EventWaitHandle解决方案BackgroundWorker提供的myEventWaitHandle.WaitOne对象对我来说非常有用,我认为编码比使用win API消息更容易。

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

你的“优雅杀手”应用程序:

{{1}}

答案 2 :(得分:0)

您需要将WM_CLOSE message发送到应用程序的主窗口。据推测,这就是CloseMainWindow为您提取的内容。这样每次都会很好。

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

非常重要的应用程序通常会这样做。这是错误的,并且强烈劝阻,甚至还有一种正确的方式来实现它,但这些东西之前都没有阻止过。

因此,实际让程序关闭的唯一方法是检查相关应用程序的文档并查看如何关闭它,而不是将其最小化。我打赌在其中一个控制它的首选项对话框中有一个选项。确保您已相应地设置该选项。

绝对回避任何涉及发送线程WM_QUIT message的建议,或者杀死整个过程。这不是推荐的方法,并且可能会导致许多问题,正如您在问题中指出的那样。您需要找到一种方法让应用程序很好地关闭自己。其他一切都完全属于“杀戮”类别,正是你想要避免的。

答案 3 :(得分:0)

对于我的应用程序,我试图关闭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还有无情的杀戮模式。