Starting GUI application from Windows Service - Approaches

5.1k Views Asked by At

Note: Despite appearing as yet another question of the same matter, it's not, but choosing the right title seems hard so more appropriate title change is welcome.

I'm trying to solve an issue of starting up GUI application (Windows Forms, .NET) from a service application on Windows 7 Embedded (POS ready), and have come across several solutions, but none of them worked, with one exception I discovered along the way: Using a batch file as a helper file.

Background: There are several posts like this describing why this has been removed and protected, and I understand session 0 isolation, but before any debate dives into why this shouldn't be done, I'm just trying to get my head around it, as I don't like the current solution that our company is using.

I'll be using words "actual app" which represents two GUI applications that I've tried to run. One is Windows Forms .NET exe, and second Adobe AIR build exe app.

I've successfully set up a test service and have done tests with two accounts:

  • Running a service under Local System account (with and without "Interact with desktop" option...)
  • Running a service under Administrator account -> This approach does not allow use of CreateProcessAsUser since its already in user session

Under Local System account, it works as expected, and I've successfully executed CreateProcessAsUser" command as shown here by murrayu and used his helper class implementation, but with no success. While actual app starts up in GUI under user account, it either doesnt start (AIR), or it has issues and erros, mostly with Access Denied exceptions present (.NET app).

Under Administrator account, the solution using CreateProcessAsUser does not work as mentioned, but using approach B (code at the end of post), which works fine for one app starting another app, did not work. There was no GUI present, as expected in a way.

Third try, solution included helper application that service would call, which then starts up the actual app. Testing this using Local System account and CreateProcessAsUser, then calling approach B in the helper app, resulted in the same behaviour as in first test, where service invoked actual app.

Oddly enough, opening up Notepad.exe, cmd.exe, calc.exe using CreateProcessAsUser works just fine.

Since command line worked, I went down that rabbit hole: The fourth try, solution included a batch file, that included a single command:

START D:\TESTAPP\DotNetApp.lnk

Note that it points to the shortcut, as calling exe directly does not work.

To my surprise, this approach worked! The actual app started as if started regularly by executing the file manually.

Now what bugs me the most is - Is there something I haven't found or try yet? Did I miss something important? The .NET app was ran using Administrator account with full rights when it reported Access denied exceptions, and works totaly fine even under User account (restricted) if executed manually form folder, so there's really nothing special about it that would seem like a cause of the errors when ran from service.

Most importantly, or most wondering question though: What does the command line / batch do that makes it work? Is it possible to program it's behaviour / solution in a helper app, or better yet, directly in the service itself?

Also, when wanting to have a service handle GUI application, and where relying on Startup/Autorun is not desirable, what would be the better solution?

Appendix B:

public static void ElevatedExecute(string processName, string command, bool useAdminElevationRights = false)
        {
            Process process = new Process();
            process.StartInfo = new ProcessStartInfo(processName, command);
            if (useAdminElevationRights)
            {
                SecureString ssPwd = new SecureString();
                process.StartInfo.UserName = "Administrator";
                string tmpPass = ADMIN_PASSWORD;
                for (int x = 0; x < tmpPass.Length; x++)
                    ssPwd.AppendChar(tmpPass[x]);
                process.StartInfo.Password = ssPwd;
            }
            process.StartInfo.UseShellExecute = false;
            process.Start();
        }

Any insights would be highly appreciated.

2

There are 2 best solutions below

0
On BEST ANSWER

After some trial and error coding for a day now, I've managed to debug and figure out what was causing errors in solution with using pInvoke to CreateProcessAsUser() method in advapi32.dll module.

As stated in the question itself, theres a topic on how to invoke GUI process from session 0 to current's user session using CreateProcessAsUser() method.

I've mistakenly ignored the working directory path to it, so some relative paths have not been working when GUI process was invoked, resulting in otherwise fully and nicely working implementation of StartProcessAsCurrentUser wrapper for CreateProcessAsUser() pInvoke.

The correct call for an app example is:

StartProcessAsCurrentUser(@"D:\Presentations\GUI.exe", null, @"D:\Presentations", true);

(where Presentations\GUI.exe is an example app. The null parameter is optional arguments parameter, and 3rd parameter is working directory, which I mistakenly always invoked as null.

I figure it might be helpful to leave this here, since there really aren't that many topics about invoking GUI application from service or session 0, or even remotely using ManagementClass instance method InvokeMethod("Create", {0});on a remote host, which works pretty much the same way.

4
On

Is there a compelling reason to have the service start a UI app? I mean, since UI apps are meant for user interaction, how does the service know if a user is actually sitting down at the computer at any given time? What triggers the service to start the app? If the user closes the app, what triggers the app to restart the UI? Why can't the user just start the app directly? What is the service bringing to the table that the app can't do directly?

We use a Windows service as part of our project. It runs in the background 24/7/365. It sits idle in steady-state until a user opens the UI application. During the UI initialization, it establishes a connection to the service which it maintains until the UI is closed. As the user interacts with the UI, commands are sent to the service behind the scenes directing the service what to do.

The point is, is there a reason why you can't have your service operate this way? Instead of the service opening the app, let the user open the app when she's ready to, and then have the app connect to the service via IPC to facilitate any necessary UI-service interaction. You could even provide the feature to allow the app to collapse to the system tray, effectively hiding it until the user needs to access it again. Virus scan software works this way, and we've employed the approach in a version of our tool

HTH