Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Как отменить завершение работы Windows?


Автор: vovams 20.5.2009, 22:11
Мне нужно выключить компьютер так, чтобы пользователь не смог отменить выключение, но и чтобы не потерять несохранённые данные.
Решил сделать так: выключить компьютер API функцией ExitWindowsEx(EWX_SHUTDOWN) (т.е. не завершать приложения, которые ответили отказом на WM_QUERYENDSESSION), если компьютер не выключится через 30 секунд, сгибернировать его (спящий режим с сохранением RAM на HDD).

Теперь проблема. Если какое-то приложение имеет несохранённые данные, оно выдаёт запрос на сохранение. Если ничего не нажимать, то появится диалоговое окно снятия процесса (в Windows XP,  по крайней мере). Если пользователь нажмёт "Отмена" в первом запросе, или "Отмена" или "Снять процесс" во втором запросе, то всё сработает. Но вот если пользователь просто проигнорирует предложение снять процесс, свернёт его и продолжит работать, то моя программа повиснет при попытки сгибернировать компьютер.

Вот код функций выключения и гибернации (скачал из интернета):
Код

function LinkAPI(const module, functionname: string): Pointer; forward;
function SetSuspendState(Hibernate, ForceCritical,DisableWakeEvent: Boolean): Boolean;forward;
function MyExitWindows(RebootParam: Longword): Boolean;forward;

function MyExitWindows;
var
  TTokenHd: THandle;  
  TTokenPvg: TTokenPrivileges;  
  cbtpPrevious: DWORD;  
  rTTokenPvg: TTokenPrivileges;  
  pcbtpPreviousRequired: DWORD;  
  tpResult: Boolean;  
const  
  SE_SHUTDOWN_NAME = 'SeShutdownPrivilege';  
begin  
  if Win32Platform = VER_PLATFORM_WIN32_NT then  
  begin  
    tpResult := OpenProcessToken(GetCurrentProcess(),  
      TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY,  
      TTokenHd);  
    if tpResult then  
    begin  
      tpResult := LookupPrivilegeValue(nil,  
                                       SE_SHUTDOWN_NAME,  
                                       TTokenPvg.Privileges[0].Luid);  
      TTokenPvg.PrivilegeCount := 1;  
      TTokenPvg.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED;  
      cbtpPrevious := SizeOf(rTTokenPvg);  
      pcbtpPreviousRequired := 0;  
      if tpResult then  
        Windows.AdjustTokenPrivileges(TTokenHd,  
                                      False,  
                                      TTokenPvg,  
                                      cbtpPrevious, 
                                      rTTokenPvg, 
                                      pcbtpPreviousRequired); 
    end; 
  end; 
  Result := ExitWindowsEx(RebootParam, 0);
end;

{ 
  The SetSuspendState function suspends the system by shutting power down. 
  Depending on the Hibernate parameter, 
  the system either enters a suspend (sleep) state or hibernation. 

  Syntax: 
}
 {
  function SetSuspendState(
    Hibernate: Boolean;
    ForceCritical: Boolean;
    DisableWakeEvent: Boolean);
  }

 {  Parameters: 

   Hibernate: If this parameter is TRUE, the system hibernates. 
              If the parameter is FALSE, the system is suspended. 
   ForceCritical: If this parameter is TRUE, the system suspends operation immediately; 
                  if it is FALSE, the system broadcasts a PBT_APMQUERYSUSPEND event to 
                  each application to request permission to suspend operation. 
   DisableWakeEvent: If this parameter is TRUE, the system disables all wake events. 
                     If the parameter is FALSE, any system wake events remain enabled. 


  Windows NT/2000/XP: Included in Windows 2000 and later. 
  Windows 95/98/Me: Included in Windows 98 and later. 
}

 {var
   _SetSuspendState: function (Hibernate, ForceCritical, DisableWakeEvent: BOOL): BOOL
   stdcall = nil;                                                                     }



 function SetSuspendState(Hibernate, ForceCritical,
   DisableWakeEvent: Boolean): Boolean;
 var
   _SetSuspendState: function (Hibernate, ForceCritical, DisableWakeEvent: BOOL): BOOL
   stdcall ;

 begin
   _SetSuspendState:=nil;
   if not Assigned(_SetSuspendState) then
     @_SetSuspendState := LinkAPI('POWRPROF.dll', 'SetSuspendState');
   if Assigned(_SetSuspendState) then
     Result := _SetSuspendState(Hibernate, ForceCritical,
       DisableWakeEvent)
   else
     Result := False;
 end;

 function LinkAPI(const module, functionname: string): Pointer;
 var
   hLib: HMODULE;
 begin
   hLib := GetModulehandle(PChar(module));
   if hLib = 0 then
     hLib := LoadLibrary(PChar(module));
   if hLib <> 0 then
     Result := getProcAddress(hLib, PChar(functionname))
   else
     Result := nil;
 end;

// Я вызываю так
MyExitWindows(EWX_SHUTDOWN);
// Потом
SetSuspendState(true,true,false);

Я так понимаю, что Windows не даёт инициировать новое завершение работы, пока активно старое. Если перед тем, как гибернировать, отменить выключение, то, наверное, должно сработать.

Подскажите, как сделать?

Автор: Keeper89 20.5.2009, 22:44
vovams, данные нужно не потерять в сторонних приложениях?
P.S. Может проще дать пользователям время на сохранение, минуту например, а потом принудительно выключать компьютер?

Автор: bems 21.5.2009, 16:28
попробуй отправить всем WM_QUERYENDSESSION и проанализировать результат. Если кто-то отказался  - в хибернейт

Автор: vovams 21.5.2009, 21:42
Цитата(Keeper89 @  20.5.2009,  22:44 Найти цитируемый пост)
vovams, данные нужно не потерять в сторонних приложениях?
P.S. Может проще дать пользователям время на сохранение, минуту например, а потом принудительно выключать компьютер?

Ну, да, нужно.
Нет, так не подойдёт: можно не успеть или не заметить предупреждение. Последний раз, когда программа выдавала предупреждение за минуту, а затем выключала принудительно, одна программа как-раз сохраняла данные, когда компьютер выключился, и все данные были потеряны.

Цитата(bems @  21.5.2009,  16:28 Найти цитируемый пост)
попробуй отправить всем WM_QUERYENDSESSION и проанализировать результат. Если кто-то отказался  - в хибернейт 

А если программа, получившая сигнал, выдаёт запрос на сохранение на экран и пользователь его проигнорирует, какой-то таймаут можно поставить? (И вообще, как это сделать?)

Автор: Keeper89 21.5.2009, 23:04
Цитата(vovams @  21.5.2009,  21:42 Найти цитируемый пост)
А если программа, получившая сигнал, выдаёт запрос на сохранение на экран и пользователь его проигнорирует, какой-то таймаут можно поставить? (И вообще, как это сделать?) 

Как только пользователь отреагирует - гибернация. А таймер, думаю, можно поставить после получения положительного результата WM_QUERYENDSESSION.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)