I have been reading a lot about executing a GUI application from a Windows Service. The "Allow service to interact with desktop" check box worked for me when the Service runs as the SYSTEM user (I am using Windows XP).
Now I need the Service to run as a User defined in a domain (from the network). Everything works fine (even if no user is logged into the machine) but the GUIs are not shown (even if the same network user is logged in!).
I know that the GUIs are running, it's just that they are hidden. Why is that? Is there a way to show them if a user is logged on (like when created by the SYSTEM user and allowed interaction with desktop!) ?
if so, would it work if the user logged in is not the same as the one the service is running on?
Edit:
#casperOne: I see your solution, and it is the same that people (even you) have been posting around. In my case though, I am sure I am running on a secure environment and ONLY one user will be logged into a machine at a time. Isn't there anything one can do to simply unhide the GUIs? Why would this work with the user SYSTEM allowing interaction with desktop and not with another user?
Your approach is completely wrong, and will not work when deployed on Vista.
Services should NEVER assume a login session with a desktop to interact with.
Rather, you should have a second application which is run when the user logs in (or some other point in time) which communicates with the service and then displays UI elements when it receives notifications/responses from the service.
See this other question (and answers) for further information:
How to detect if a Window can be Shown?
Short answer: No, you can't do this
Long answer: Noooooo.
Basically, Microsoft are making changes to further prevent this. As casperOne stated, you'll need to separate your UI components away from the service.
And even on XP it didn't work on non domain joined machines (if you have multiple users using Fast User Switching the popups showed up on either the wrong desktop or no desktop at all).
As to why Microsoft changed this, do a quick search for "Shatter Attack" - by isolating service code from the desktop they completely cut off this entire family of security vulnerabilities.
Related
I have written a Windows service that allows me to remotely run and stop applications. These applications are run using CreateProcess, and this works for me because most of them only perform backend processing. Recently, I need to run applications that present GUI to the current log in user. How do I code in C++ to allow my service to locate the currently active desktop and run the GUI on it?
Roger Lipscombe's answer, to use WTSEnumerateSessions to find the right desktop, then CreateProcessAsUser to start the application on that desktop (you pass it the handle of the desktop as part of the STARTUPINFO structure) is correct.
However, I would strongly recommend against doing this. In some environments, such as Terminal Server hosts with many active users, determining which desktop is the 'active' one isn't easy, and may not even be possible.
But most importantly, if an application will suddenly appear on a user's desktop, this may very well occur at a bad time (either because the user simply isn't expecting it, or because you're trying to launch the app when the session isn't quite initialized yet, in the process of shutting down, or whatever).
A more conventional approach would be to put a shortcut to a small client app for your service in the global startup group. This app will then launch along with every user session, and can be used start other apps (if so desired) without any juggling of user credentials, sessions and/or desktops.
Also, this shortcut can be moved/disabled by administrators as desired, which will make deployment of your application much easier, since it doesn't deviate from the standards used by other Windows apps...
The short answer is "You don't", as opening a GUI program running under another user context is a security vulnerability commonly known as a Shatter Attack.
Take a look at this MSDN article: Interactive Services. It gives some options for a service to interact with a user.
In short you have these options:
Display a dialog box in the user's session using the WTSSendMessage function.
Create a separate hidden GUI application and use the CreateProcessAsUser function to run the application within the context of the interactive user. Design the GUI application to communicate with the service through some method of interprocess communication (IPC), for example, named pipes. The service communicates with the GUI application to tell it when to display the GUI. The application communicates the results of the user interaction back to the service so that the service can take the appropriate action. Note that IPC can expose your service interfaces over the network unless you use an appropriate access control list (ACL).
If this service runs on a multiuser system, add the application to the following key so that it is run in each session: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run. If the application uses named pipes for IPC, the server can distinguish between multiple user processes by giving each pipe a unique name based on the session ID.
WTSEnumerateSessions and CreateProcessAsUser.
Several people suggested WTSEnumerateSessions and CreateProcessAsUser. I wonder why no one suggested WTSGetActiveConsoleSessionId, since you said you only want to target one logged in user.
Several people sure are right to suggest CreateProcessAsUser though. If you call plain old CreateProcess the way you said, then the application's GUI will run with your service's privileges instead of the user's privileges.
That problems Session 0 , Interactive Services ,
Windows Service Allow Service To Interact With Desktop
on Windows 7 or Windows Vista
You can read this article
http://www.codeproject.com/KB/vista-security/SubvertingVistaUAC.aspx
I try explained here it's working on Windows 7
On Win2K, XP and Win2K3 the console user is logged on in Session 0, the same session the services live in. If a service is configured as interactive, it'll be able to show the UI on the user's desktop.
However, on Vista, no user can be logged on in Session 0. Showing UI from a service there is a bit trickier. You need to enumerate the active sessions using WTSEnumerateSessions API, find the console session and create the process as that user. Of course, you need also a token or user credentials to be able to do that. You can read more details about this process here.
I think as long as you have only one user logged in, it will automatically display on that user's desktop.
Anyway, be very careful when having a service start an exe.
If the write access to the folder with the exe is not restricted, any user can replace that exe with any other program, which will then be run with sytem rights. Take for example cmd.exe (available on all windows sytems). The next time the service tries to start your exe, you get a command shell with system rights...
If you launch a GUI from your service it will show up on the currently active desktop.
But only if you adjusted the service permissions: You need to allow it to interact with the desktop.
Important Services cannot directly interact with a user as of Windows Vista. Therefore, the techniques mentioned in the section titled Using an Interactive Service should not be used in new code.
This is taken from : http://msdn.microsoft.com/en-us/library/ms683502(VS.85).aspx
Im working on application for Windows Server 2008 R2 (.NET 3.5.1) that would work even after cold reboot, without requring someone to log on any account on the server.
Few words about application itself, it is written in c# application for registering employees work time at the company. Users (employees), have thier cards which are beeing scanned by barcode scanner, each scan means either "work started" or "work stopped", everything is serlialized into xml file which is later on modified and put into .csv but that doesn't matter.
Barcode scanner is working as a keyboard, so all codes are beeing "typed" like from a keyborad, to the PC. I made application read the keys despite the fact that console application is not in focus, or not visible at all.
What i need to do is to make that application work even after cold reboot, it has to be fully automatic.
So far i figured out 2 approaches to do it, one is to create a service which would keep another process alive (if its not working, just turn it on), i didin't have much luck with this one, i have already created service that launches another process for me, but the process is working differently, if i would run it myself, there is no communication with the process so i cannot even tell if its the right one.
Another one is to just put my app into registery /microsoft/windows/current version/run, and enable autologon for user with limited prividges. This actually could work but it is not perfect solution, because after all we do not want to have user logged in on server in company 24/7 right?
I know that most of you are way more experienced in programming than i am, so i would appriciate any solutions how to solve my problem
Lichoniespi
Your options depend on physical security of the system (whether passers-by can do much to it apart from scanning a barcode), but let us assume that it is an easily accessible desktop. In that case, you probably do not want a logged in user.
Use the service approach. You do NOT need a separate process for accessing the keyboard. Create a global hook of type WH_KEYBOARD_LL.
Declare your callback function like this and put it into place with SetWindowsHookEx.
I would use the first approach, create a service, and to comunicate with the running application i would be using a network socket or pipe. For the service be sure that you're using an existing user account (not System) and allow it to interact with the Desktop.
i wanted to know which process in Windows is user specific, i mean it get created for each user login. i tried explorer.exe but when u switch user and log into new account then it shows old login name in my code. basically i need to just log which user logging when in app.
If all you need to know is which user(s) are using your app, can you just check Environment.UserName when you start your app?
I missed the tag indicating you created a Windows Service. That's a very different type of animal than a regular application, and the advice you receive for one is not necessarily transferable to the other.
Specifically, I notice that you've tagged this question windows-7. If you're trying to run this service under Windows 7, you need to understand a few things about how the model for Windows Services was substantially altered starting with Windows Vista. Specifically, they now run in an isolated session and are prohibited from interacting directly with the user.
Also see my answer here for a better explanation.
The fundamental point is that, from the perspective of a Windows Service, there is no such concept as the currently logged-on user. A Windows Service runs in its own isolated session and is not affiliated with any particular user. That's why the code you found to determine the user associated with a particular process is not working as you expect for a Windows Service. A standard user doesn't own the process running the service. (And replacing your service with an application is also not a viable option, given how I understand your requirements. As I explain here, user-mode applications are started when a particular user logs on and will be closed whenever that user logs off.)
Another problem is that more than one user can be logged in simultaneously to a single workstation. Windows is a thoroughly multi-user operating system, so the best that you can hope for is to enumerate all of the currently logged in users. The NetWkstaUserEnum function will get you that list, but note that it includes all types of logons, including interactive users, services, and batch logons. To call this function from C#, you will need to P/Invoke—you can find information about that over on pinvoke.net.
I have a Windows service which I want to periodically execute an external program. I'm currently doing this the usual way
Process program = Process.Start(#"C:\mpewatch\db_parameters\DBParameters.exe");
This doesn't seem to be working. I'm executing this from a separate thread which is started in my service's OnStart handler. Is there any conceptual problem with this? Is it not possible to execute external programs from a service like this?
You can execute external programs from a service, but there are security issues. For example, your service may be running under an account which does not have read access to the folder where the external program resides, even if your interactive account does have that access.
For test purposes, try to configure the service to run under your interactive account. If the program is invoked as expected, then the problem with the original account is that it does not have sufficient privileges to run the program.
Your question didn't indicate the operating system.
On Windows XP, you can configure your Windows service to interact with the desktop by opening the service control panel, double-clicking your service, selecting the Log On tab, configuring the service to run as local system, and checking the checkbox. It's pretty straightforward. You might try testing with something like Notepad.exe just to see if you can get it working.
On Vista (and presumably Windows 7), however, you may be out of luck. I have read that the ability for Windows services to interact with the desktop has been removed in Vista. I forget what the terminology is, but basically services will run in "shell 0," whereas users will occupy "shell 1". User applications will be able to communicate with services and vice versa using technology like WCF, but services will not be able to communicate directly with the desktop. For example, any error boxes that pop up will have to be dealt with by swapping to "shell 0." Again, this is based on something I read a few months ago, and I haven't gone looking at it again. For me, I've structured my Windows service to be configured using WCF via a front-end app.
I'm sorry I don't have a link for you, but if your service will eventually have to migrate to a newer OS (or you are already there), this is something to check on.
Another critical consideration with Windows Services is that there is no GUI. Technically, there is an option to allow the service to interact with a local GUI, but you will not see it. This is due to services running as the Local System user.
Within a service, any modal dialog box (OK, Cancel, etc) is considered an error.
I am writing an application in C# that needs to run as a service but also have user interaction. I understand that services have no UI, etc, so I've divided up my program into a windows form application and a service that can communicate with each other.
The problem I'm having is that I need the service to make sure the windows form application is always running and restart it if it is not. I'm able to detect if it is running, and restart it with the following code on Windows 2000/XP:
System.Diagnostics.Process.Start("ExePath");
but on Vista, it runs the new process as a Local/System process which is invisible to the user. Does someone way around this? Is there some way to detect which user is currently logged on and run the new process as that user? I don't need to account for fast-user switching at this point. Something - anything - basic would suffice.
I would be grateful for any help or tips you have on the subject.
I need to clarify that I am setting the "Allow service to interact with desktop" option when the service is installed. This is what allows it to work on 2000/XP. However, Vista still has the aforementioned problem.
The general idea for this sort of thing is, if the user needs to interact with a service, they should launch a separate application. If you want to help them out, you can configure that separate application to start with windows by placing a shortcut in the start up menu. You can also build crash recovery into your application so it can automatically restart.
You shouldn't really rely on monitoring the forms application, what if no one is logged in? What if multiple people are logged in? It just gets messy doing things this way.
Having the service just sit there and broadcast to listeners is the way to go. When the forms application starts it can notify the service it wants to listen to events.
See the question: How can a Windows Service execute a GUI application?. It addresses the same question from C/C++ (short answer: CreateProcessAsUser), but the answer's still valid (with some P/Invoke) for C#.
In this case, you will have to have a third monitor process which detects if the program fails and restart it in that case.
However, you end up with an unsolvable problem here, as the monitor process will have to be watched to make sure it doesn't get shut down, and so on, and so on, and so on.
You might want to reconsider this approach.
Its a tough situation. As mentioned in a couple places, if you must have a UI then technically you shouldn't be using a service. Afterall, services run without a user even logged on. If nobody is logged in, you cannot have a UI.
Normally, when I need a service needs to communicate with the outside world, there are two things I opt for. I can either place an entry in the event log, or I can drop a message in a queue.
In your case I'd use a queue. When a user logs in, you can auto start an app for them that monitors the queue. If the app is running, when the message is received, they are alerted that way as well. However, if the user closes the app then the same thing occurs... they won't know.
First, a quick answer: Does the 'Allow service to interact with desktop' option (service -> Properties -> LogOn) or specifying an account allow what you're wanting? If so, both of these can be configured on your service installer class.
Like the others, I suspect there is a better approach to this and either one of the following is true:
-The code inside the service could be included in the winforms app (perhaps running in a background thread), and added to windows startup. Both will be running
-The winforms app can just listen to the service when it's on, and doesn't need to be started from the service. Or similarly, the app could be added to startup.
To have your service run the application as a user (which seems to be what you are trying to do) you need to do the following:
System.Security.SecureString ss = new System.Security.SecureString();
foreach (char c in password)
ss.AppendChar(c);
System.Diagnostics.Process proc = Process.Start(path, arguments, username, ss, domain);
Where:
path = full path (including filename) of the executable.
arguments = string of arguments (use an empty string is none)
username = The name of an user account on your server/computer
domain = your network domain (if your using a network account- blank if none)
Also, In order for your service to have permission to launch an application, it must be running as a service also. To do this, you need to add these lines to your service installer class:
serviceProcessInstaller.Account = ServiceAccount.User;
serviceProcessInstaller.Username = "yourdomain\\yourAccountName"; //Or just "AccountName" for local accounts..
serviceProcessInstaller.Password = "yourPassword";
In Windows 2000 and XP, there is an option (checkbox) on the Logon tab of the service properties window to allow the service to interact with the desktop. I believe this is what you are looking for. I just wrote a quick service in VB.NET with a Process.Start("calc.exe") and Windows Calculator opened just fine.
I'm not 100% sure this works the same way in Vista though.
Sounds like you might not need half of it running as a service (unless there's a requirement of higher privileges), as your service would need to cope with when there is no interactive user logged on as well.