I have code running as a Windows service which would like to determine the resolution of the directly attached monitors.
(Yes, I appreciate that the user may be interacting with the machine via RDP, VDI etc., so this doesn't always make sense - but this is just an optimisation, so in the common case where there is a single directly-attached desktop, it would still be useful.)
I'd like to use ::GetSystemMetrics(SM_CXVIRTUALSCREEN) but it returns 0, as I'm running in session 0. Similarly EnumDisplayDevices looks like it will only work for the present interactive session.
We'd like to be able to do this even when no user is logged in.
Is there a way to explore hardware screen resolutions even as a service?
The documentation for EnumDisplayDevices() clearly says:
The EnumDisplayDevices function lets you obtain information about the display devices in the current session.
Since a service runs in session 0, and there are no displays in session 0, there is no monitor information for it to enumerate.
Try using EnumDisplayMonitors() instead.
Related
I have written a piece of software that allows you to control computers remotely and one of its functions is to turn on all the computers that it controls via Magic Packets.
Due to the restrictions of Network Drives and the need for passwords on accounts, I have run into an issue.
When the computers boot, I am stuck at the login screen on each computer and must walk around manually and type in the passwords.
Is there anyway that I can send the passwords to the individual computers or have the computers log themselves in?
You probably want to install your program as a Service. You'll set its startup to automatic, which means it'll start up and run automatically when the computer is started up, even without a user being logged in.
Note that it's also possible to set a Service's startup to Boot. You probably want to avoid this though. Boot startup is primarily for device drivers, not normal services. It runs much earlier in the boot process. You don't need (or probably want) that -- you just want it to run roughly when the system would be ready for somebody to log in if they chose.
I'd also note that a service is (normally) written slightly differently from a normal program. It has a Windows-style event loop, but responds to a different set of "messages" that start the service, stop the service, pause the service, and so on. Most of it isn't terribly difficult but it is somewhat different from a normal program.
I have implemented a class that gets local printers and, depending on the application option, uses one of the local printer available.
Firstly, the class enumerates the printers (EnumPrinters - PRINTER_ENUM_LOCAL) and saves the corresponding PRINTER_INFO_2. Then, it gets printer capabilities (DeviceCapabilites) and DEVMODE. Each class instance will access one printer.
When application selects the printing option, it selects which printer will be used and its corresponding instance of the class. Then, this instance creates the device context:
m_hdc = CreateDC (m_pi2->pDriverName, m_pi2->pPrinterName, NULL, m_pdm));
where
HDC m_hdc;
PRINTER_INFO_2 * m_pi2;
DEVMODE * m_pdm;
and process all printing data accordingly.
The problem is that sometimes, the CreateDC return NULL and GetLastError() return ERROR_FILE_NOT_FOUND(2).
I mean 'sometimes' because in other machines, with same printer, same processor, same Windows XP SP3 image and same test data, the CreateDC processes correctly. In addition, the reinstalling the system and application the problem disappears sometimes no.
I am looking forward to hearing any suggestion that helps me to find out the issue.
Thank you in advance.
It sounds like a problem loading a file required by the printer driver. You could use Process Monitor (a free SysInternals tool downloadable from microsoft.com) to get a bunch of information about what's going on at the time of the error. I'd do a capture and look at failed file and registry accesses. The fact that it fails intermittently on one particular machine seems consistent with a messed up driver configuration.
Another thing you could try is to create an information context rather than a device context. You can't print with an IC, but you can query information about the device, which may be a way to get additional information.
I run Windows 7.
I run windows service that runs a program with GUI.
I cannot see the GUI of my program because it was started from another session by system or even my user.
Is there a way for me to see my program?
Switch desktop to system user?
Use SetThreadDesktop() to change the thread's context in your service to the user's desktop.
SetThreadDesktop() takes a handle to the desktop as it's first parameter; to get that handle, use EnumDesktops().
EnumDesktops() takes a handle to the window station as it's first parameter; to get that handle, use EnumWindowStations()
To understand what's going on with Window Stations and Desktops, try reading this overview from from MSDN.
Be cautious with this technique. Higher-privileged processes (i.e., services) interacting with the user's desktop are the basis for shatter attacks. You need to write a separate application that runs in the user's context and communicates with your service via pipes or similar.
I maintain a variety of managed userlabs on a university campus. These machines all currently run Windows XP and we have a windows service that is used to "lock" a machine by blocking any keyboard or mouse input. The locking happens during our scripted OS installation so that users aren't able to accidentally halt or break the process. It is also used to prevent users from logging into machines until they are checked out at the front desk of a given lab. Ctrl+Alt+Del is blocked via a keyboard filter driver and the rest of the keys and mouse are currently blocked using the BlockInput() function from user32.dll.
In XP, the service runs as Local System and the checkbox for "Allow service to interact with desktop" must be enabled from the BlockInput() call to succeed. Under Vista, this no longer works I'm guessing because of the Session 0 isolation changes. The call succeeds, but input is not actually blocked.
The keyboard filter driver still works just fine and we can use that to block the whole keyboard instead of just Ctrl+Alt+Del. But I'm at a loss as to how we're going to block the mouse now. I'm not even entirely sure the Session 0 isolation is to blame.
Can anyone recommend a fix or a workaround to allow us to block mouse input from a service in Vista and beyond? I've looked for alternative win32 API's without luck. Assuming Session 0 isolation is to blame, is there a legitimate way to call the function from Session 1 or would that sort of defeat the purpose of the isolation? Will I have to rely on an elevated companion exe that runs on user login and communicates with the service?
From a service, you can use WTSEnumerateSessions to get all logged on user sessions, WTSQueryUserToken to get the logged on user's token, and then use CreateProcessAsUser with that token to get code running on the user's desktop. This has the disadvantage that the code will run as the logged on user. Since all input is disabled, this is probably safe enough and is at least as safe as the existing XP solution.]
EDIT: BlockInput requires a high mandatory integrity level. You could try adding this group to the token, but then you have code with higher privileges running on the desktop and potentially open to attacks.
An alternative might be to use your service just to disable all HID class devices on the machine.
I have an application that can list the opened windows of the current session. It uses the EnumWindows method from the user32.dll.
I would like to run this code from a windows service, but as the service is not attached to a user session, it returns nothing obviously.
So the question is, how can I enumerate the open windows of another user session (e.g. with a specific logon user)?
Similarly to EnumWindows, I also would like to get the foreground window of the user session as well (like GetForegroundWindow works for the current user).
As far as I'm aware, you can't access the windows of one session from another. It's also worth noting that there's not really any such thing as "the current session" - there may be multiple users logged on through terminal services, or XP's fast user switching.
One approach to this would be to add a program to each user's profile with no UI that just communicates with your service. You'd still have to cope with the fact that there could be multiple active sessions, though.
According to this document you can create a process in an other user's logon session using CreateProcessAsUser, and could enumerate the windows there. You will still need some IPC mechanism to communicate with the service.
The accepted answer is not correct.
So the question is, how can I enumerate the open windows of another user session?
You can enumerate the open windows of any session if you're running as a service running as the local System account.
To do this first enumerate the sessions with WTSEnumerateSessions. Then enumerate the window stations inside each session with EnumWindowStations. Then enumerate the desktops for each Window Station with EnumDesktops. Finally you an enumerate the Windows in those Desktops with EnumWindows.
(e.g. with a specific logon user)
There can be many concurrent logged on users via Terminal services or fast user switching.
Similarly to EnumWindows, I also would like to get the foreground window of the user session as well (like GetForegroundWindow works for the current user).
This can be done by launching an app with a found user token in the Session, Window Station, and Desktop. From there you can call any Win32 API like GetForegroundWindow and report the info back to your parent process.
You can learn more about how Sessions, Window Stations, and Desktops work here.