Handling interrupt signals + logoff/restart/shutdown

Handling interrupt signals + logoff/restart/shutdown

I have a console application that is running a calculation and I would like to be able to do certain work like saving unsaved data and properly stopping the calculation if the Windows system is going to log off, shut down or restart.

I would like to be able to do something similar to handling the WM_QUERYENDSESSION message in MFC applications - this message is sent by the system to indicate shutdown or logoff.

What I have in my application now is handling interrupt signals, which looks like this:

interface
  function processBreakSignal ( signum ) ! SIG$INT - CTRL+CSIGNAL
    !dec$ attributes C :: processBreakSignal
    integer(4) :: processBreakSignal
    integer(2) :: signum
  end function
  function processTerminationSignal ( signum ) ! SIG$TERM - Termination request
    !dec$ attributes C :: processTerminationSignal
    integer(4) :: processTerminationSignal
    integer(2) :: signum
  end function
end interface

integer(4) :: status4
       
status4 = SIGNALQQ ( SIG$INT, processBreakSignal )
status4 = SIGNALQQ ( SIG$TERM, processTerminationSignal )
status4 = SIGNALQQ ( SIG$ABORT, processTerminationSignal )

integer(4) function processBreakSignal ( signum )
  !dec$ attributes C :: processBreakSignal
  integer(2) :: signum
  ...
  processBreakSignal = signum
  return
end

processTerminationSignal is coded in the same way like processBreakSignal.

If my application is running and I press Ctrl+C, everything is OK.

If I press Ctrl+Break, the system (Windows 7 Professional 64-bit) immediately closes my console application's window. I thought Ctrl+C and Ctrl+Break fire the same interrupt type SIG$INT, don't they?

Finally, is any of the interrupt types, e.g. SIG$TERM or SIG$ABORT, supposed to handle system restart/shutdown or logoff? Right now, just like with Ctrl+Break, it does not seems so - logoff or shutdown close the console window immediately.

What can I do to handle system shutdown/restart/logoff?

10 posts / 0 new
Last post
For more complete information about compiler optimizations, see our Optimization Notice.

SetConsoleCtrlHandler might help.  I've never played with it though.

As it happens, I was looking at SetConsoleCtrlHandler last week because the Fortran run-time library establishes such a handler in a console application. It is exactly intended for this purpose. If you call the API to register your handler, it will get called before the RTL's.

Steve

I replaced SIGNALQQ with SetConsoleCtrlHandler and my application is now handling Ctrl+C and even Ctrl+Break correctly. However, closing the window, logoff and shutdown/restart are still not handled correctly - if I close the console window or if I logoff/shutdown, the console window closes immediately and the code I have in the handler routine is not executed, so my application does not have time to do anything before the system is shut down or restarted.

Is there anything else I can do to prevent the system from closing my console application's window immediately?

This is the code I use to handle signals now (code is simplified and does not show operations I would like to do before logoff/shutdown/restart):

program ConsoleCtrlHandlerTest

use kernel32, only : SetConsoleCtrlHandler, BOOL, TRUE

integer(BOOL), external :: ConsoleCtrlHandlerRoutine
!dec$ attributes stdcall :: ConsoleCtrlHandlerRoutine
integer(BOOL) :: handlerSetResult

handlerSetResult = SetConsoleCtrlHandler( loc(ConsoleCtrlHandlerRoutine), TRUE )

... (calculation is running here)

end

integer(BOOL) function ConsoleCtrlHandlerRoutine ( code )
!dec$ attributes stdcall :: ConsoleCtrlHandlerRoutine

use kernel32, only : BOOL, DWORD, TRUE, FALSE

integer(dword) :: code

select case (code)
  case (0)
    write(*,'(a)') ' Stopping due to Ctrl+C. '
    ...
    ConsoleCtrlHandlerRoutine = TRUE
  case (1)
    write(*,'(a)') ' Stopping due to Ctrl+Break. '
    ...
    ConsoleCtrlHandlerRoutine = TRUE
  case (2)
    write(*,'(a)') ' Stopping due to closing the application window. '
    ...
    ConsoleCtrlHandlerRoutine = TRUE
  case (5)
    write(*,'(a)') ' Stopping due to system logoff. '
    ...
    ConsoleCtrlHandlerRoutine = FALSE
  case (6)
    write(*,'(a)') ' Stopping due to system shutdown/restart. '
    ...
    ConsoleCtrlHandlerRoutine = FALSE
  case default
    write(*,'(a)') ' Stopping due to unknown event. '
    ConsoleCtrlHandlerRoutine = FALSE
end select

end function ConsoleCtrlHandlerRoutine

 

Yes, it does. But you don't see the write because the window closes. Change the writes in your test to write to a file instead and you'll see. I tried it.

Steve

My Windows GUI (ie, not a Quickwin console) app needs to run 24x7 and own its machine, and this is done in part by 1) subclassing the desktop which removes the Windows taskbar and start button (and also gets rid of Metro in Win8), and 2) setting low-level keyboard hooks to disable the various system keystroke interrupts.  The only way to exit the app is via its explicit menu option, ensuring a graceful shutdown with data saving and the like for a later graceful restart.  I don't know if this is useful for a console app, but if your app also needs to own its machine, a design which restricts user interactions can be more effective than trapping them after the fact.

If you want to go to that extreme - just make your application the shell!

I do not want to make my application that extreme; I was naive to think that dealing with logoff and shutdown cannot be so complicated.

Anyway, I still must be doing something wrong, because my application's window is immediately closed on logoff, but only on my computer (Windows 7 Professional 64-bit). I changed the code to simulate that a calculation is running and it needs certain time to do cleanup. The application writes to log files that the calculation simulation is running and that the cleanup operation is in progress.

If I log off on my computer, I do not see any file cleanup.log that should be written in the subroutine Cleanup that is called from the handler. If I try the same on another computer with Windows 7 Professional 64-bit, the file cleanup.log contains 5 entries, i.e. the application and the handler were running 5 seconds from the logoff signal and the application was then terminated. It works even differently on Windows XP Professional 64-bit - the cleanup (approximately 10 seconds) is completely finished and the application is then terminated.

As I focus on Windows 7/8, I need to find a way how to get more time than 5 seconds from logoff to do cleanup. Put another way, I need to explain Windows to wait for my application to do cleanup before logoff or shutdown are completed.

program ConsoleCtrlHandler

  use ifcore
  use kernel32, only : SetConsoleCtrlHandler, BOOL, TRUE
  implicit none
  integer(BOOL), external :: ConsoleCtrlHandlerRoutine
  !dec$ attributes stdcall :: ConsoleCtrlHandlerRoutine
  integer(BOOL) :: handlerSetResult
  logical(4) :: pressed, emptyBuffer
  character(1) :: key
  integer(4) :: unit, iteration
  
  write(*,'(a)') ' Starting Console Ctrl Handler test, press any key to stop. '
  write(*,'(a)') ' '
  
  handlerSetResult = SetConsoleCtrlHandler( loc(ConsoleCtrlHandlerRoutine), TRUE )
  
  unit = 101
  OPEN ( UNIT=unit, FILE='simulation.log' )
  
  iteration = 0
  write(*,'(a,i0)') ' Simulating calculation, iteration: ', iteration
  write(unit,'(a,i0)') ' Simulating calculation, iteration: ', iteration
  
  pressed = .false.
  do while ( .NOT.pressed )
    pressed = PEEKCHARQQ ( )
    call SLEEPQQ ( 1000 )
    iteration = iteration + 1
    write(*,'(a,i0)') ' Simulating calculation, iteration: ', iteration
    write(unit,'(a,i0)') ' Simulating calculation, iteration: ', iteration
  enddo
  
  emptyBuffer = .false.
  do while ( .NOT.emptyBuffer )
    key = GETCHARQQ()
    emptyBuffer = .NOT.PEEKCHARQQ()
  enddo
  
  CLOSE ( unit )
  
  write(*,'(a)') ' '
  write(*,'(a)') ' Console Ctrl Handler test finished. '
  write(*,'(a)') ' '
  
end program ConsoleCtrlHandler

integer(BOOL) function ConsoleCtrlHandlerRoutine ( code )
!dec$ attributes stdcall :: ConsoleCtrlHandlerRoutine
  use kernel32, only : BOOL, DWORD, TRUE, FALSE
  integer(dword) :: code
  
  call Cleanup ( code )
  
  select case (code)
  case (0)
    ConsoleCtrlHandlerRoutine = TRUE
  case (1)
    ConsoleCtrlHandlerRoutine = TRUE
  case (2)
    ConsoleCtrlHandlerRoutine = TRUE
  case (5)
    ConsoleCtrlHandlerRoutine = FALSE
  case (6)
    ConsoleCtrlHandlerRoutine = FALSE
  case default
    ConsoleCtrlHandlerRoutine = FALSE
  end select
  
end function ConsoleCtrlHandlerRoutine

subroutine Cleanup ( code )
  use ifcore
  integer(4) :: code, unit, step, maxSteps
  
  unit = 100
  
  OPEN ( UNIT=unit, FILE='cleanup.log' )
  
  select case (code)
  case (0)
    write(unit,'(a)') ' Cleanup initialized due to Ctrl+C '
  case (1)
    write(unit,'(a)') ' Cleanup initialized due to Ctrl+Break '
  case (2)
    write(unit,'(a)') ' Cleanup initialized due to window closing '
  case (5)
    write(unit,'(a)') ' Cleanup initialized due to system logoff '
  case (6)
    write(unit,'(a)') ' Cleanup initialized due to system shutdown/restart '
  case default
    write(unit,'(a)') ' Cleanup initialized due to unknown event '
  end select
  
  write(*,'(a)') ' Cleanup operation: started '
  write(unit,'(a)') ' Cleanup operation: started '
  
  cleaned = .false.
  maxSteps = 10
  step = 1
  
  do while ( step.le.maxSteps )
    
    write(*,'(a,i0,a,i0)') ' Cleanup operation: step ', step, ' out of ', maxSteps
    write(unit,'(a,i0,a,i0)') ' Cleanup operation: step ', step, ' out of ', maxSteps
    
    call SLEEPQQ ( 1000 )
    step = step+1
    
  enddo
  
  write(*,'(a)') ' Cleanup operation: finished '
  write(unit,'(a)') ' Cleanup operation: finished '
  
  CLOSE ( unit )
  
  return
end

 

You might find this topic interesting:

Application Shutdown Changes in Windows Vista

 

http://msdn.microsoft.com/en-us/library/ms700677(v=vs.85).aspx

 

You might find this topic interesting:

Application Shutdown Changes in Windows Vista

 

http://msdn.microsoft.com/en-us/library/ms700677(v=vs.85).aspx

 

Login to leave a comment.