2014-08-27 79 views
5

我想阻止我的應用程序被關閉由Windows。 該應用程序在Windows 8上運行並使用XE6編寫。 我試過下面的代碼,但它似乎完全被忽略。爲了測試它,我只需通過任務管理器發送「結束任務」給它。 我需要的是一種讓應用程序完成當應用程序被用戶關閉時,由Windows關機的任務管理器完成的方式。 正常關閉不是問題,這是由FormCloseQuery事件處理的。但另外兩種方法我無法工作。直到Windows XP,通過捕獲wm_endsession和wm_queryendsession,從vista開始,您需要使用ShutDownBlockReasonCreate,它會返回true,但似乎無法工作。德爾福防止應用程序關機

procedure WMQueryEndSession(var Msg : TWMQueryEndSession); message WM_QUERYENDSESSION; 
procedure WMEndSession(var Msg: TWMEndSession); message WM_ENDSESSION; 

function ShutdownBlockReasonCreate(hWnd: HWND; Reason: LPCWSTR): Bool; stdcall; external user32; 
function ShutdownBlockReasonDestroy(hWnd: HWND): Bool; stdcall; external user32; 


procedure TForm1.WMEndSession(var Msg: TWMEndSession); 
begin 
    inherited; 

    Msg.Result := lresult(False); 
    ShutdownBlockReasonCreate(Handle, 'please wait while muting...'); 
    Sleep(45000); // do your work here 
    ShutdownBlockReasonDestroy(Handle); 
end; 

procedure TForm1.WMQueryEndSession(var Msg: TWMQueryEndSession); 
begin 
    inherited; 
    Msg.Result := lresult(False); 
    ShutdownBlockReasonCreate(Handle, 'please wait while muting...'); 
    Sleep(45000); // do your work here 
    ShutdownBlockReasonDestroy(Handle); 
end; 

更新

更改消息結果爲true,併除去睡眠改變不了什麼。

procedure TForm1.WMEndSession(var Msg: TWMEndSession); 
begin 
    inherited; 
    Msg.Result := lresult(True); 
    ShutdownBlockReasonDestroy(Application.MainForm.Handle); 
    ShutdownBlockReasonCreate(Application.MainForm.Handle, 'please wait while muting...'); 
end; 

procedure TForm1.WMQueryEndSession(var Msg: TWMQueryEndSession); 
begin 
    inherited; 
    Msg.Result := lresult(True); 
    ShutdownBlockReasonDestroy(Application.MainForm.Handle); 
    ShutdownBlockReasonCreate(Application.MainForm.Handle, 'please wait while muting...'); 
end; 
+1

請參閱[如何暫停窗口關閉](http://stackoverflow.com/a/18347424/576719)。 – 2014-08-27 20:31:14

+1

你不能說功能是「惡作劇」。檢查'ShutDownBlockReasonCreate'的返回值,如果返回false,則使用'GetLastError'來找出失敗的原因。如果你不打算檢查返回值來找出原因,你就不能說「API不工作」。 – 2014-08-27 20:31:41

+0

該函數返回true,如果我從一個按鈕調用它,我不能檢查WMQueryEndSession中的結果,因爲應用程序關閉之前,我可以檢查它的值。 – GuidoG 2014-08-27 20:45:11

回答

8

按照documentation阻止你需要響應返回給FALSE關機WM_QUERYENDSESSION

更重要的是,你不能在這個消息處理程序中工作。這項工作必須發生在別處。如果您沒有及時回覆此消息,系統將不會等待您。

  • 在開始工作之前致電ShutdownBlockReasonCreate
  • 雖然工作返回FALSEWM_QUERYENDSESSION。處理此消息時請勿工作。立即返回。
  • 工作完成後致電ShutdownBlockReasonDestroy

WM_QUERYENDSESSION處理程序可以是這樣的:

procedure TMainForm.WMQueryEndSession(var Msg: TWMQueryEndSession); 
begin 
    if Working then 
    Msg.Result := 0 
    else 
    inherited; 
end; 

然後執行工作需要調用ShutdownBlockReasonCreate在工作開始之前,ShutdownBlockReasonDestroy當工作結束,並確保代碼的Working以上使用的財產在工作時評估爲True

如果你的工作阻塞了主線程,那麼你有麻煩了。主線程必須有響應,否則系統不會等待你。把工作放在一個線程中往往是前進的方向。如果你的主窗口不可見,那麼你沒有機會阻止關機。詳情在這裏解釋:http://msdn.microsoft.com/en-us/library/ms700677.aspx

如果您發送到WM_ENDSESSION那麼它爲時已晚。系統正在降臨,可能會發生什麼。

要測試它,我只需通過任務管理器發送「結束任務」給它。

這與關閉阻塞無關。測試關閉阻止的方式是註銷。如果用戶堅持要殺死你的進程,那麼你可以做的事情很少。塞爾特克的回答詳細介紹了這一點。

最後,忽略API調用的返回值也是非常糟糕的形式。不要這樣做。

+0

此外「如果應用程序超時響應WM_QUERYENDSESSION或WM_ENDSESSION,Windows將終止它。」所以你的「睡眠(45000); //在這裏做你的工作」會導致這個超時。 http://msdn.microsoft.com/en-us/library/ms700677(v=vs.85).aspx – 2014-08-27 20:30:56

+0

我嘗試返回TRUE,但它沒有區別。也沒有睡眠命令,它仍然不起作用。重新運行TRUE並忽略ShutDownBlockReasonDestroy不起作用。我已經嘗試了幾乎所有我能想到的組合,嘗試使用谷歌發現的不同代碼,似乎沒有任何工作。你有一個工作的例子 – GuidoG 2014-08-27 20:31:07

+0

我沒有示例代碼,但我已經成功地使用了這些API。顯然他們不是一個騙局,我不明白爲什麼你認爲他們是。你是否添加了不起作用的API? – 2014-08-27 20:35:03

6

你的代碼似乎完全被忽略,因爲你沒有測試它。你是通過任務管理器向其發送「結束任務」,您發佈的代碼僅在系統關閉時纔有效。

Windows 8的不同之處似乎是任務管理器的行爲方式。在Windows 8之前,來自任務管理器的結束任務將首先嚐試優雅地關閉應用程序(發送WM_CLOSE),您正在使用OnCloseQuery處理此操作。但是,當應用程序拒絕關閉時,任務管理器將強制結束該進程。這,你不能處理。如果您從任務管理器中選擇「結束進程」,則相同。

Windows 8任務管理器不提供強制關閉應用程序的附加對話框,但當應用程序拒絕關閉時立即繼續這樣做。