I'm writing a service/user-mode application that may send the system into sleep after a certain customizable period of inactivity. The user-mode process may require to display a user message/warning before performing a power operation. This works great if a user is logged in, but if the workstation is locked, a user sees a display like this:
So I'm curious if it is possible to display my own window (overlay) above such a logon screen?
The only supported API for this, since Vista, is the credential provider. The documentation can be found over on MSDN.
I found this post that might be exactly what I need. I'll post later when I try it out...
Related
I've been looking all over the internet for an answer to this, and it just doesn't seem to be directly answered, so I thought I would ask.
Case scenario: I want to take a screenshot of what is currently on the computer screen. If it's the Windows Logon screen, I want it to be that. If it's the active user's desktop, I want to to be that. If the user elevates their application, and the UAC prompt shows up, I want it to be that.
As per lots of reading and trial and error, my current setup is as follows:
Program runs as a windows service
Gets the active user's token
Runs CreateProcessAsUser with the user's token to generate another instance of itself
Takes a screenshot and transmits it back via pipes.
Right now this is working great for a logged on user, except that screenshot is black when a UAC prompt is enabled.
Also, this method obviously won't work for getting the logon screen.
Fundamentally I am wondering how exactly does TeamViewer go about achieving this sort of thing? It is able to switch between the logon screen and a user's session flawlessly, whilst also capturing UAC prompts. I am immensely curious as to how it achieves this.
Thanks everyone!
As per the advice of Davison, I have figured out how to do this, and it involves multiple steps.
Firstly, one must use CreateProcessAsUser to create a process inside the console session (obtained from WTSGetActiveConsoleSessionId). Something to note is that this process must have administrative privileges, which simply getting a handle to the user's token will not do. The way around this evidently, is to get a handle to a process running with administrative privileges, get this processes' token, duplicate it, and use that with CreateProcessAsUser. The process I used for this was Winlogon.
After this, the rest is quite simple; use OpenInputDesktop to get a handle to the desktop the user is currently seeing (it will be Default for actual desktop, and Winlogon for the UAC Prompt and login screen). After this, use SetThreadDesktop to set your processes' thread to the appropriate desktop, and capture the screen. Assuming that your Process has the privileges to create a handle to the Winlogon desktop, you will be able to capture the login screen/uac prompts and the regular user desktop.
Again, thanks to Davison, who pointed me in the right direction.
I'm developing a custom credential provider and need to know at runtime if the scenario is a login or an unlock of the session. For this, I check the CREDENTIAL_PROVIDER_USAGE_SCENARIO returned by the SetUsageScenario of the ICredentialProvider interface.
On Windows 10, independently if I'm at login or when the session is locked, I always get CPUS_LOGON as usage scenario, while on previous version of Windows, CPUS_UNLOCK_WORKSTATION was returned when the session was locked and CPUS_LOGON at the login.
So it seems that changes appeared since Windows 10 that are not reported on MSDN.
Is there any other way to detect if the usage scenario is a session locked?
I am currently investigating the same issue and may have a work around until Microsoft can update the documentation.
Although I still receive a CPUS_LOGON, we are still inside the same session as the locked user. By using the function WTSQuerySessionInformationW, you can verify that there is currently a user logged in to the current session. From there, you can proceed as if you were in a CPUS_UNLOCK_WORKSTATION usage scenario.
UPDATE (1/18/2016): It seems Microsoft has finally updated their documentation on this issue. See the excerpt below from the CREDENTIAL_PROVIDER_USAGE_SCENARIO documentation:
Starting in Windows 10, the CPUS_LOGON and CPUS_UNLOCK_WORKSTATION
user scenarios have been combined. This enables the system to support
multiple users logging into a machine without creating and switching
sessions unnecessarily. Any user on the machine can log into it once
it has been locked without needing to back out of a current session
and create a new one. Because of this, CPUS_LOGON can be used both for
logging onto a system or when a workstation is unlocked. However,
CPUS_LOGON cannot be used in all cases. Because of policy restrictions
imposed by various systems, sometimes it is necessary for the user
scenario to be CPUS_UNLOCK_WORKSTATION. Your credential provider
should be robust enough to create the appropriate credential structure
based on the scenario given to it. Windows will request the
appropriate user scenario based on the situation. Some of the factors
that impact whether or not a CPUS_UNLOCK_WORKSTATION scenario must be
used include the following. Note that this is just a subset of
possibilities.
The operating system of the device.
Whether this is a console or remote session.
Group policies such as hiding entry points for fast user switching, or interactive logon that does not display the user's last name.
Credential providers that need to enumerate the
currently user logged into the system as the default tile can keep
track of the current user or leverage APIs such as
WTSQuerySessionInformation to obtain that information
If you turn fast user switching off, you will get the CPUS_UNLOCK_WORKSTATION messages upon locking. Otherwise you will only receive CPUS_LOGON. If you manually lock the PC using the windows API call from code to lock with fast user switching turned on, it will lock sending CPUS_UNLOCK_WORKSTATION and then immediately log off sending CPUS_LOGON.I Hope this helps, i don't have the reputation score to post an answer of my own so i edited this comment.
You might try SENS (System Event Notification Service). This is a Microsoft provided notification service.
https://msdn.microsoft.com/en-us/library/windows/desktop/cc185680(v=vs.85).aspx
It has different events for logon/logoff and screen lock/unlock notifications. It uses a COM+ interface. I am not familiar with the requirements of credential providers so I don't know if the service will run within the context you require or if the timing of the event arrival will meet your needs but it is something you might investigate.
Of all the answers, Justin's one is the more informative one, but nobody provides a workaround to properly restore the Windows7 behavior. Scott's answer mentions turning off Fast User Switching, but that turns off a feature that is available in Windows7, making it not a proper workaround. After carefully reading all the information available and several attempts, I found the following policies that allows only the previous logged user to unlock the machine, hence forcing the LogonUI framework to issue CPUS_UNLOCK_WORKSTATION scenario, but still allowing fast user switch:
Windows Registry Editor Version 5.00
; Computer Configuration -> Windows Settings -> Security Settings ->
; Local Policies -> Security Options "Interactive logon: Do not display last user name"
; Set to "Enabled": asks to unlock the machine only to currently logged user
; https://learn.microsoft.com/en-us/windows/security/threat-protection/security-policy-settings/interactive-logon-do-not-display-last-user-name
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
"dontdisplaylastusername"=dword:00000001
; Computer Configuration -> Administrative Templates -> Windows Components ->
; Windows Logon Options -> "Sign-in last interactive user automatically after a system-initiated restart"
; Set to "Enabled": Prevents last signed user to log in and lock automatically
; after a restart
; https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/component-updates/winlogon-automatic-restart-sign-on--arso-
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
"DisableAutomaticRestartSignOn"=dword:00000001
; Similar in bevahior to "dontdisplaylastusername" but also disables Fast User
; Switching, which was available in Windows7
; https://learn.microsoft.com/en-us/windows/client-management/mdm/policy-csp-windowslogon#windowslogon-hidefastuserswitching
;[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
;"HideFastUserSwitching"=dword:00000001
I am writing a service application that will run with local system credentials. I will need to know from my service if the Windows logon screen is displayed at any particular time. Is there any way to do this?
PS. The screens that can be brought up by locking the workstation:
Or by trying to switch the user:
Or after a Ctrl+Alt+Del:
PS. I need this to run on Windows XP and up.
EDIT: The only viable solution that I came up with so far is to see if LogonUI.exe process is running. The issue with this approach is how to distinguish between the actual system logon process and any other process that has that image name?
As described in the comments you are trying to detect whether or not a process in an interactive desktop session should show a message box. There being no point doing so if the interactive session is not active.
In which case I believe that your proposed solution is the wrong one. Instead you should register for session change notifications by calling WTSRegisterSessionNotification. When you do this you'll get sent WM_WTSSESSION_CHANGE messages that allow you to keep track of the current state.
Note that you do this in your desktop app rather than the service. The service still sends its messages to the desktop app. But the desktop app now knows whether or not it is worth showing them.
Update
Remy suggests a better way in the comments:
And if a separate app is being used, there is no reason to detect session changes at all, that app can simply check if its currently assigned workstation/desktop is the currently interactive workstation/desktop instead, comparing GetThreadDesktop() to OpenInputDesktop(), for instance.
All such screens are presented on a separate desktop. You may try to enumerate the user's desktops and compare it with the current (I am not sure the service in session 0 - Vista and up - can do that; if not, spawn a helper process in the user session). This however may give a false positive if an UAC desktop is up. Another corner case is a userless situation (right after boot before any user looged on).
There are several states in the windows.
Logged-Off State
When Winlogon is in the logged-off state, users are prompted to identify themselves and provide authentication information. If a user provides correct user account information and no restrictions prevent it, the user is logged on and a shell program (such as Windows Explorer) is executed in the application desktop. Winlogon changes to the logged-on state.
Logged-On State
When Winlogon is in the logged-on state, users can interact with the shell, activate additional applications, and do their work. From the logged-on state, users can either stop all work and log off, or lock their workstations (leaving all work in place). If the user decides to log off, Winlogon will terminate all processes associated with that logon session and the workstation will be available for another user. If, instead, the user decides to lock the workstation, Winlogon changes to the workstation-locked state.
Workstation-Locked State
When Winlogon is in the workstation-locked state, a secure desktop is displayed until the user unlocks the workstation by providing the same identification and authentication information as the user who originally logged on, or until an administrator forces a logoff. If the workstation is unlocked, the application desktop is displayed, and work can resume.
reference: https://msdn.microsoft.com/ko-kr/library/windows/desktop/aa380547(v=vs.85).aspx
p.s. registering a secure attention sequence (SAS, CTRL+ALT+Delete) is included in Workstation-Locked state
Similarly, there are several desktop types on windows.
Winlogon desktop
Application desktop(=Default desktop)
Screensaver desktop
Secure desktop
I recommend you read this:
https://msdn.microsoft.com/ko-kr/library/windows/desktop/aa375994(v=vs.85).aspx
I don't know my answers are what you want... but I hope it helps in some ways.
I'm have written a C++ application that is running as a Windows service to limit the volume of a Windows 7 computer. The user can specify different rules for different days and times and the service will smoothly change the volume. To implement this I use the IAudioEndpointVolume interface. In general it works like intended...
However, there exists a strange behavior I cannot explain yet. When switching users the volume suddenly drops but it does not generate a notification as one would expect. What is also strange is that the sliders in the SndVol.exe show the correct value for the volume.
Because of the missing notification my program cannot react to this change and as a result it cannot perform its intend anymore.
I have discovered that the volume will switch back to its correct volume again if I move the volume slider a bit. Of course this generates a notification that will be handled by my service. My service will then force the correct volume.
I don't understand why the volume changes without being visible in SndVol.exe and without creating a notification. Switching back to the first user account does not solve the problem. Even after logging out the volume is still at the wrong level.
As far as I have seen the documentation about the IAudioEndpointVolume interface does not mention anything about different user session.
Any ideas on what might cause this problem or what I could try to fix it?
Your service runs in session 0, the isolated session that prevents malware from exercising shatter attacks. The user's desktop runs in another session, there can be multiple. The WASAPI documentation is silent about exactly how an audio session gets mapped across Windows sessions. You have a very strong hint that it doesn't from the way it behaves.
There are ominous words in the section that talks about grouping parameters. A construct that primarily exists to allow Sndvol to identify processes that share the same volume control. It quotes Explorer as an example, a process that can be started more than once but still has a single volume control. A process that doesn't want to share uses session identifier GUID_NULL to select the default session in IAudioSessionControl::SetGroupingParam() or simply omits the call altogether since that's the default.
And the behavior of Sndvol, it only displays volume controls for the processes that run in the desktop session. You can't see the processes in another desktop session. Giving a very strong hint that audio session GUID_NULL is specific to the session in which it got created.
So quite unlikely that you can find a workaround for it as long as you do this with a service.
Instead, consider running your program as a normal windowless process that runs in the user's desktop session. Getting started by the Run registry key or a Startup folder shortcut or the task scheduler.
Well, after some time I am now quite confident that the volume change is caused by the 3rd party driver we are using. This driver has it's own volume control mechanism. I do not experience the change anymore after just starting the drivers' control GUI. Even after a restart the problem seems to be fixed. However, after some time it gets broken again for a reason I cannot figure out. But this only happened because some security settings prevented the drivers' control GUI to start when logged in as non admin. I fixed this now and expect everything to work now.
Furthermore it looks like that all user sessions share the same volume control. That means if I change the volume with SndVol in one user session the same change happens in the other user session. My service receives notifications for all these changes. So it looks like that I did not receive a notification when switching between users because the change was caused by the driver's control GUI starting when logging in as administrator. But this change happened in the driver, a lower layer, so that Windows is not aware of the change.
The driver we are using is the kX Audio Driver.
This is a coding question. Please read it before you flag it as belonging on ServerFault as the last one I wrote got thrown over there in less than 5 minutes.
I'm testing my win32/c++ application on XP which has the latest service packs. It contains two administrative user accounts both without passwords. I log in as User1 and start my app. The app runs, its main window appears and all is well with the world. I then log User1 off without first closing my app. Yes, I used "log off" not "switch user"
I then log in as User2 and my application is still running. I see it on the User 2 desktop, and I can even interact with it. It appears to be functioning normally. And task manager shows it running as User1.
Any ideas which might be going on here? Other applications (like notepad) don't exhibit this issue, yet mine does. Seems to me I'm doing something wrong in my code, but it really is a rather standard win32/c++ app. Perhaps I'm not processing some shutdown message properly? I'm sorry I can't give more specifics right now. I'm really hoping for some clue to spark further research.
Check windows task manager's for 2 things:
"Session ID" column
"User Name" column
If either of these columns do not show up then select them from View -> Select columns.
Check which username and session your application that is staying open with is on. Then go and start notepad.exe and compare to the session ID and User Name that it is started with.
When you do a logoff it will close the applications running under your Session ID and username.
I'm guessing that your application is running in it's own session ID and/or username.
When you login with the other user it checks to see if it can re-use a session that is already started for the new username. So that is why you will see it running again when you login with the second user.
Are you sure your application isn't running as a service? A service with "Interact with Desktop" could look like this.
UPDATE:
It must be somehow related to a service. A normal application, running in a session will be forced to close by Windows before the logoff is complete. Even if you don't handle the end session messages, Windows will tell the user about the nonresponding process and/or just kill it.
Do you need to be listening for a shutdown or logoff events?
Check out this answer for a similar question.
That answer refers to listening for WM_QUERYENDSESSION.
See WM_QUERYENDSESSION Message