开发者

How do I close (not kill) an application which has minimized to the system tray?

I'm writing an app that closes a program, changes its data file t开发者_Go百科hen 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.


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.


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


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.


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜