How to keep a user process running until Windows logoff is definite? - windows

I have an application that is running on Windows, in the user's session, that I don't want the user to be able to close by accident.
One way the user can close it is by having unsaved data (e.g. in notepad) and start the process of logging off. Notepad rejects the WM_QUERYSESSION message; Windows prompts the user that there is unsaved data; and the user gets to cancel the logoff.
Other applications (including mine), get the WM_QUERYSESSION, respond with TRUE, then get WM_ENDSESSION, and quit. Importantly, they quit before Windows has even decided to prompt the user about unsaved data in notepad.
If the user cancels the logoff, the end result is that the user's session continues without all the processes that honoured the end session messages.
What I'm trying to find a way of doing is keeping my process running until we're past the point that the user could cancel the logoff. Looking at the microsoft docs on this, I don't see an obvious way of doing that - each process goes through its own WM_QUERYSESSION > WM_ENDSESSION, and the docs are explicit that this happens for each application independently.
I don't want to reply to WM_QUERYSESSION with FALSE, as I don't want to block the logoff myself, just stay running if something else blocks it.
Are there any other ways of finding out when Windows has decided that all the WM_QUERYSESSION messages were replied to with TRUE, and the logoff is inevitable?
(I think this is a Windows question, rather than a question specific to the language I'm using, but in case it matters, it's PureBasic. Any Win32 API approach should be workable though.)

If any app returns FALSE to WM_QUERYENDSESSION, apps that had already returned TRUE will receive WM_ENDSESSION with wParam=FALSE to indicate shutdown has been canceled.
If all apps return TRUE to WM_QUERYENDSESSION, only then will they receive WM_ENDSESSION with wParam=TRUE to indicate shutdown is proceeding.
See Application Shutdown Changes in Windows Vista and Restart Manager: Guidelines for Applications for more details of how WM_QUERYENDSESSION and WM_ENDSESSION work.
Is your app paying attention to the wParam value given by WM_ENDSESSION? Or does it just blindly exit unconditionally when it receives WM_ENDSESSION? Not all apps pay attention like they should be. Make sure yours does.
Update: Per the WM_QUERYENDSESSION documentation, and the Logging Off documentation:
When an application returns TRUE for WM_QUERYENDSESSION, it receives the WM_ENDSESSION message and it is terminated, regardless of how the other applications respond to the WM_QUERYENDSESSION message.
So, there is really nothing your app can do here. Once it responds TRUE to WM_QUERYENDSESSION, that is it, game over. Your app will receive WM_ENDSESSION and be forcibly terminated if it does not exit within a few seconds, regardless of how other apps respond to the shutdown.
So, the only way I can think of to really detect when the session is actually ending is to use a service instead, as a service can receive SERVICE_CONTROL_SHUTDOWN and SERVICE_CONTROL_SESSIONCHANGE notifications.

Related

Intercepting WM_ENDSESSION message

Is it possible to intercept WM_ENDSESSION message to prevent another application from receiving it?
I'd like to command that application to perform an additional action before Windows reboots or shuts down, and it's not possible to configure the application that way.
The application is screen recorder software, and it just throws away the video when Windows shuts down. I need to prevent that and save the video.
Is it possible to intercept WM_ENDSESSION message to prevent another application from receiving it?
Technically yes, using a message hook from SetWindowsHookEx(). Depending on the hook used, you can sometimes modify (not discard) messages to look like another messages, such as WM_NULL. However, in the case of WM_ENDSESSION, it is just a notification, not a request, so Windows is still going to continue shutting down no matter what applications do with that message.
I'd like to command that application to perform an additional action before Windows reboots or shuts down, and it's not possible to configure the application that way.
The application is screen recorder software, and it just throws away the video when Windows shuts down. I need to prevent that and save the video.
So, you don't actually want to avoid WM_ENDSESSION, you just want to delay the other app from processing it until after your action is performed first.
The best option would be to simply contact the recorder author and request a feature be added to save the video on system shutdown.
Beyond that, MSDN says the following:
Application Shutdown Changes in Windows Vista
By default, applications without any visible top-level windows will be given 5 seconds to handle WM_ENDSESSION before being terminated.
If your application may need more than 5 seconds to complete its shutdown processing in response to WM_ENDSESSION, it should call ShutdownBlockReasonCreate() in its WM_QUERYENDSESSION handler, and promptly respond TRUE to WM_QUERYENDSESSION so as not to block shutdown. It should then perform all shutdown processing in its WM_ENDSESSION handler.
This way, Windows will treat your application as if it had visible top-level windows and will give it 30 seconds to handle WM_ENDSESSION.
So, you could try using a message hook to intercept WM_QUERYENDSESSION and have it call ShutdownBlockReasonCreate() and return immediately, then intercept WM_ENDSESSION to invoke the video saving action and call ShutdownBlockReasonDestroy() when it is finished. Assuming, of course, that the recorder is throwing the video away in reply to WM_ENDSESSION and not WM_QUERYENDSESSION.
See MSDN for more info about how these two messages are handled by Windows:
Shutdown Changes for Windows Vista

Should I process WM_ENDSESSION, WM_QUERYENDSESSION, both or neither?

If a system is trying to shut down, an app can block this shutdown by overriding OnQueryEndSession() and returning FALSE. Surely that means WM_ENDSESSION is the only definitive message to respond to regarding shutdown.
On the other hand, the top answer to this question quotes no less than Raymond Chen as saying that responding to WM_ENDSESSION is essentially pointless. So this is confusing.
Is there some kind of "best practice" principles to apply in deciding which of these messages (if any) one should respond to for doing what kinds of application shutdown work?
In particular, if neither message is handled, will a shutdown process cause an application to be closed as if the user had closed the application manually (e.g. click on red X close button)?
This article from Microsoft gives a very comprehensive discussion of end-of-session best practice both pre- and post-Vista. The article makes it quite clear that one should assume that if one receives a WM_QUERYENDSESSION then shutdown will occur at some point.
As soon as all applications have responded to the WM_ENDSESSION message, or been forced to terminate within 5 seconds of receiving the WM_ENDSESSION message, Windows may shut down at any time. This may limit what can be done in response to WM_ENDSESSION.
If an application requires more time to clean itself up:
If your application may need more than 5 seconds to complete its shutdown processing in response to WM_ENDSESSION, it should call ShutdownBlockReasonCreate() in its WM_QUERYENDSESSION handler, and promptly respond TRUE to WM_QUERYENDSESSION so as not to block shutdown. It should then perform all shutdown processing in its WM_ENDSESSION handler.
Windows will apparently not send any additional messages to your application to allow it to exit "gracefully" (e.g. WM_CLOSE). Rather, it will simply call TerminateProcess. If you want a graceful close, you have to build it yourself within the above constraints.
You do need to close down your application in WM_ENDSESSION, at least if you want to support the Restart Manager API. I think both MSDN and Raymond are wrong here. (Maybe it changed recently, or they overlooked the Restart Manager?)
The Restart Manager API is used by installers to close and restart exes which have files locked that they need to replace. To support being restarted by it, you call RegisterApplicationRestart and then need to have a window which handles WM_QUERYENDSESSION and WM_ENDSESSION.
If you don't shut down your app in the WM_ENDSESSION handler than it will simply keep running and block the Restart Manager, and in turn the installers trying to use it.
I found this out the hard way. MSDN explicitly says you don't need to call PostQuitMessage, but if I don't do that then my process keep running.
I suspect the documentation didn't realise the Restart Manager is different, and less forceful, compared to what happens when the entire OS shuts down.
(Edit: I should add, this was with a simple ATL COM EXE server, but as far as I can tell there was nothing about that complicating things, and Windows simply wasn't triggering a WM_QUIT to the message loop unless I did it myself.)

How can I autostart a UI-application under windows that a non-admin user cannot close?

I have developed a C# Windows Forms application that runs in the background as a systray icon and does some stuff when it's clicked. It is supposed to launch when Windows starts and run continously, and a normal user without administrator rights shall not be allowed to shut it down.
What is the best way to do this? I initially intended to run it on the LocalSystem account through Task Scheduler, but then I learned (the hard way) about Session 0 isolation (i.e. the application will run but its UI elements do not show). When I run it with the logged in user, even if it runs elevated, the user can still shut it down through task manager (without requiring elevation).
Is there any way to give a process from LocalSystem access to the UI? I have a winlogon and a csrss process from LocalSystem running in session 1, so I guess it can be done, I just don't know how. Or is there maybe an easier way to disallow users to shut down a process of their own through the task manager? The only other option I can think of is to create an extra Windows Service that keeps polling if the app is running, and immediately launches it again if someone kills it - but that seems incredibly clumsy (also, I want it to stay dead when it crashed by itself, to avoid a single bug causing infinite loops of process creation).
Deponds on why they can't shut it down.
The natural way to go would to have created a service, started by a high priv account, and then had the desktop app just show what it was doing.
If there's something that they should see, but don't becasue they aren't running the service monitor app. (and acknowledge message back to the service), send them an email, send their boss an email, send yourself one and then go shout at them.....
Be a lot easier than trying to get the lid back on this tin of worms.
A nice way to make sure the desktop app is ruuning, would be simply to schedule it to run every X, but drop out immediately if it already is or the somethingwenthorriblywrong flkag is set.
Not worth writing a service to check if it's still there, as they could kill that as well, unless you want to make that a service they can't kill. :(
You are trying to be too draconian with this. Add some sort of auditing so you can see it dies or was shutdown, monitor that and deal with any adverse reports. It's a heck of a lot easier, and gives manage something to do...
You can run an administrative process in the user's logon session. One method would be to for a master process (a system service) to duplicate its own token, use SetTokenInformation to change the session associated with the token, and then call CreateProcessAsUser. The lpStartupInfo parameter can be used to associate the process with a particular window station and desktop. It may be necessary to explicitly change the permissions on the window station and desktop first.
HOWEVER, this is a bad idea from a security standpoint, because GUI applications are vulnerable to malicious messages sent from other processes on the same desktop ("shatter attacks").
It would be safer to run the process in the user's own context but apply an ACL to it. This can be done using the lpProcessAttributes parameter to CreateProcess or CreateProcessAsUser, or with the SetSecurityInfo function. I've never tried this, but it should in theory prevent the user from using task manager to close the process.
If you are creating the process from the user's context, then the user will be the owner, so a sufficiently knowledgeable person could change the permissions back in order to terminate the process. If you need to block this hole, using CreateProcessAsUser from a privileged master process (the token can be duplicated from one of the existing processes in the user's session) should (again, in theory) mean that the user is not the process owner.
Another potential issue: if you listen for messages indicating that the user is logging out (in order to exit) such a message could be faked. If you don't then you need to find some other way of exiting when the user logs out. You may need to experiment here, I'm not sure how the system will react.

C# catch kill command

how can I catch windows taskkill command in order to prevent it?
You cannot prevent your process being killed. The only way to keep a program alive is to have a second Watchdog application that is constantly pooling that process to ensure it's running.
So, you have two Applications; AppA (Main) and AppB (Watchdog).
AppA checks if AppB is running. If
not, AppA starts AppB.
AppB checks if AppA is running. If
not, AppB starts AppA.
But this is usually a design for a malicious process, so I hope you're not doing anything untoward.
You can see the following events in the message queue when End process is clicked:
WM_CLOSE // UI app
CTRL_CLOSE_EVENT //
Console app
But if your application does not close in a timely manner, then the kill command will be sent, which cannot be captured.
I'm pretty sure it is not possible, otherwise a single application could stop windows from ever shutting down. Even when the process is hung, killing it in task manager eventually works. If it was possible, malicous software, etc would probably have a hey-day using it - not only could they take control of various parts of your PC, they'd make sure you could never get it back again.

How to abort shutdown in Windows (XP|Vista) programatically?

I want to be able to 1. detect (and, if needed 2. abort) OS shutdown from my application, preferably by using the Windows API.
I know that it is possible to abort shutdown manually using the command shutdown -a In the worst case, I could ShellExecute this, but I was wondering if there was a better way to prevent the shutdown programatically.
Maybe it would be enough to be notified programatically that the OS is about to shut down - how to do this?
From MSDN:
The WM_QUERYENDSESSION message is sent when the user chooses to end the session or when an application calls one of the system shutdown functions. If any application returns zero, the session is not ended. The system stops sending WM_QUERYENDSESSION messages as soon as one application returns zero.
So, my application's WindowProc now processes the WM_QUERYENDSESSION message and returns 0.
Didn't expect it to be this simple; as a bonus, it also works on Windows 2000.
In regards to 'simply' returning 0 to block a shutdown, it isn't as simple as that if you want to do it in the proper way. Especially on Vista. For example please also read http://msdn.microsoft.com/en-us/library/ms700677(VS.85).aspx

Resources