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

C# 如何关闭(而不是终止)已最小化到系统托盘的应用程序?,c#,windows-7,process,system-tray,C#,Windows 7,Process,System Tray,我正在编写一个应用程序,它关闭一个程序,更改其数据文件,然后重新打开它。我注意到,如果我使用process.Kill(),会有一些数据没有写入文件 如果我使用process.CloseMainWindow(),主窗口将关闭,但进程将最小化到起始托盘 是否有某种方式可以向流程发送优美的关闭消息 如果这很重要,我想关闭的就是磨刀石。不幸的是,要优雅地终止一个不合作的应用程序,您所能做的不多 建议的方法是向感兴趣的窗口发送WM_CLOSE消息;这在这里不起作用,因为应用程序按照您的描述选择隐藏自己。然

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

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

是否有某种方式可以向流程发送优美的关闭消息


如果这很重要,我想关闭的就是磨刀石。

不幸的是,要优雅地终止一个不合作的应用程序,您所能做的不多

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

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

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

更新:以上是我自己的理解,但在同一主题上也有不同的观点。它适用于Win32(不是托管代码),但是可以轻松地调整这些想法。

您需要向应用程序的主窗口发送一条消息。大概,这就是
CloseMainWindow
为您提供的摘要。每次都会很好

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

非常自我重要的应用程序通常会这样做。这是错误的,而且非常令人沮丧,甚至有一种正确的方法来实施它,但这些事情都没有阻止过人们

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


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

对于我的应用程序,我试图关闭一个Python进程,并使用“subprocess.Popen”打开它的派生进程。我试过TerminateProcess,但它太邪恶了。:)我最终决定可以使用console命令。我在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);
    }
//标准kill进程调用
无效停止进程(DWORD pid)
{
STARTUPINFO STARTUPINFO;
LPPROCESS\ U INFORMATION processInfo=新流程\ U信息;
//清除内存以防止垃圾
零内存(&startupInfo,sizeof(startupInfo));
//设置结构尺寸(不使用Ex版本)
startupInfo.cb=sizeof(startupInfo);
//告诉应用程序我们正在设置窗口显示
//此结构中的信息
startupInfo.dwFlags=STARTF_USESHOWWINDOW;
//隐藏过程
startupInfo.wShowWindow=SW_HIDE;
//TerminateProcess(itr->second->hProcess,0);//对进程不友好,并且不会终止子进程
std::stringstream-comStream;

comStream提供的
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();
      }
   }
}
您的“优雅杀手”应用程序:

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

您是否在使用Windows?如果是,请添加标记(或更具体的版本标记)。如何手动关闭Grindstone?您可以在代码中模拟此操作吗?每当我看到与结束进程相关的“数据未写入文件”时,我的第一个想法是“您是否
关闭()
文件?”我不知道你的具体情况,但我建议在终止进程之前先确保关闭所有打开的文件。我已放弃此项目,因此无法验证下面的答案是否有效。