[英]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.