I have a Windows Service that runs in my computer and is initialized when the computer turns on (it has a few dependencies on other services, but is initialized right after them).
Then, I have a different application, that depends on that service for running (the application contains an UI).
I would like to start this application whenever the service is started. Therefore, I use the following code in the "OnStart()" event of the service:
var process = new Process();
process.StartInfo = new ProcessStartInfo
{
CreateNoWindow = false,
WindowStyle = ProcessWindowStyle.Maximized
};
try
{
process = Process.Start(applicationPath);
return (true);
}
catch (Exception e)
{
Logger.Log("ApplicationHelper::OpenApplication Error: " + e.Message);
Logger.Log(applicationPath);
return (false);
}
What happens is that the process of the application starts running, but the UI is not shown. I can only see that the process is running in the task manager. How can I launch the application from the Windows Service?
I have already enabled the option to interact with desktop, but it did not help.
I am using .Net 4 and Windows 7.
Thanks!
In fact, when I try to open the service, a windows message pops up saying that it is a different user than the current logged one trying to send a message. But I can't configure the starting user of the service to be the current one.
It is possible for a service to launch an application in the context of the logged-in user, but it is very difficult. In this case, since the service starts as soon as the system has booted, there probably isn't a user logged in yet anyway!
Instead, just add the application to the Run key in the registry, so that it will start when the user logs in. Since it is dependent on the service, you should probably get it to check whether the service has started yet and wait if necessary.
Related
I am trying to deploy an ASP.NET application to IIS using Powershell (run by Ansible).
I want my application to be able to query the performance counters so I am adding it to the Performance Monitor Users using this Powershell script:
appPoolName=$args[0]
$group = [ADSI]"WinNT://$Env:ComputerName/Performance Monitor Users,group"
$ntAccount = New-Object System.Security.Principal.NTAccount("IIS AppPool\$appPoolName")
$strSID = $ntAccount.Translate([System.Security.Principal.SecurityIdentifier])
$user = [ADSI]"WinNT://$strSID"
$group.Add($user.Path)
It actually comes from another SO question: Add IIS AppPool\ASP.NET v4.0 to local windows group.
After the deployment, it can happen that the user is added to the group but the application still can't access the performance counters.
The script is run just before starting the App Pool and the application.
I have tried the following things, without success:
Restart the application
Restart the App Pool
Set AnonymousAuthentication to 'Application pool identity'
Deploy again
I have modified my deployment scripts in the following ways, without success:
Remove the user from the group before adding it
Restart the App Pool after adding the user. IIS actually complains ("The service cannot accept messages at this time").
Add the user to the group after starting the application
Add the user to the group at the beginning of the deploy (before stopping the application and pool)
The only way I have to solve my problem is to restart the machine. I would like to know if there is a better one!
If it is possible, I think making the App Pool user log off and back on would solve my problem. I haven't found how to do that (restarting or recycling the App Pool doesn't work).
The answer was a simple iisreset (in command line or Powershell).
I am trying to build a windows service that automates the running of an 3rd party application, let's call it BlackBox.exe. BlackBox.exe runs fine when run from the current user's session but fails when executed from the service. BlackBox.exe has a UI but it can be automated by passing it command line parameters. In the automated mode it shows some UI, reads input files, writes output files and then exits without any user interaction. We only have binaries for BlackBox.exe.
The service is configured to use the same user as BlackBox.exe was installed and registered under.
I've run a comparison of a the events captured by Process Monitor interactive session vs service session. There's no registry or file operations that are failing for the service session and succeeding for the service session. Notably, the registry keys for the license activation are read successfully in both cases.
When run from the service BlackBox.exe is visible in the Task Manager with 0% cpu and will remain there until killed.
I guess the lack of access to the desktop is causing BlackBox.exe to fail - maybe there's a way to fool BlackBox.exe to think it has access to the desktop without having a user logged in?
How can I run BlackBox.exe from a service?
UPDATE:
If I:
start the "Interactive Services Detection" service,
click "View Message" from the Interactive Services Detection dialog,
watch BlackBox.exe gui do its processing until I see the Interactive Serivces Detection message "the application is no longer requesting your attention"
click "Return Now"
...it works fine. So it might be related to Session 0 isolation
Can you essentially run a popup blocker against this? The assumptions here are that that CloseMainWindow() will have the same effect as "View Message", and that you allow the service to interact with the desktop. Sample code below; adapt to your particular scenario and run as a separate thread from your service.
public class Program
{
public static void Main(string[] args)
{
while (true)
{
DestroyAllBobs();
Thread.Sleep(100);
}
}
private static void DestroyAllBobs()
{
Process[] bobs = Process.GetProcessesByName("robert");
foreach (Process bob in bobs)
{
if ("Microsoft Visual C++ Runtime Library".Equals(bob.MainWindowTitle))
{
bob.WaitForInputIdle();
if ("Microsoft Visual C++ Runtime Library".Equals(bob.MainWindowTitle)
&& "robert".Equals(bob.ProcessName))
{
try
{
bob.CloseMainWindow();
}
catch (Exception)
{
}
}
}
}
}
}
I have a C# program that does this:
Directory.Exists(#"\\PcName\SomeDir");
and prints whether that path is accessible (exists) or not.
This is the problem: I run this app via the Task Scheduler right after log-in (auto-log-in user), using the "On Login" trigger, and it returns false, although that path IS accessible! (I manage to open that path using the explorer.exe few seconds before my app starts). It is marked to:
Run with highest privileges
If I run it manually it runs OK, even when I right click the task and select "Run" via the Task Scheduler!
If I deselect "Run with highest privileges", there is no problem, but it must be ran with highest privileges (accesses registry and whole lot other stuff)
It runs under same user if I run it manually or automatically by the task scheduler - I made sure using Process Explorer
It happens on certain machines (Win8x64, admin-privileges-user with no password, auto-log-in, workgroup machines, not domain), but not on anothers (same: Win8x64, admin-privileges-user with no password, auto-log-in, workgroup machines, not domain).
Even if I insert Thread.Sleep(TimeSpan.FromMinutes(1)); or enter 1-min delay in the task (in the Task Scheduler) it still says this path does not exist
Problem solved. I had to "impersonate", although I’m not really sure why: if I use the scheduler without restarting, it accesses the remote share – exactly the same settings, one to one. Only after restart it fails to access the share (and a moment later, again – same settings, it is able to access).
The only difference in running it immediately after restart is that the app-process’s parent is services.exe and not explorer.exe as usual. My guess is that it has to log in immediately after the restart, so it must use services.exe (explorer.exe is not supposed to exist at that stage, if I'm not mistaken).
Below is the solution in C#, roughly, to whom it may concern:
// LogonUser is a "P/Invoked" API:
// http://www.pinvoke.net/default.aspx/advapi32/LogonUser.html
// this solution works only with the LOGON32_LOGON_NEW_CREDENTIALS as the 4th parameter:
using (var h = LogonUser(username, domain, password,
LogonType.LOGON32_LOGON_NEW_CREDENTIALS,
LogonProvider.LOGON32_PROVIDER_DEFAULT))
{
using (var winImperson8Ctx = WindowsIdentity.Impersonate(h.DangerousGetHandle())) {
return Directory.Exists(path); // now works fine...
}
}
From the Microsoft website (see msdn.microsoft.com/en-us/library/ms683502(VS.85).aspx) it clearly says "Services cannot directly interact with a user as of Windows Vista".
So I decided to test this by using "psexec -s cmd.exe". As far as I know, "psexec" creates a service in order to open a command prompt. Needless to say it worked. I then decided to use "EnumWinSta GUI" in combination with psexec to switch to the winlogon desktop. To my surprise, I could even start "cmd.exe" on this desktop. Does this mean a new process created from a service can be interactive?
Or is it because psexec does some kind of black magic? If so how does it do it?
I am trying to display a full screen window from a service into the winlogon desktop object in Vista as well as XP.
Code running within a service cannot directly interact with an interactive session.
However, code running as a service with sufficient privileges can create a new process running within a specific user's desktop - getting the interactive session's user's token and calling CreateProcessAsUser, for example.
You can use WTSGetActiveConsoleSessionId to get the active console session, the session of the user who is actually on the machine. WTSQueryUserToken can then be used to get the token.
Your service can also use session change notifications in its handler function to see when users logon/logoff, unlock their session, and so on.
I have a Windows executable that is launched from within a service by calling CreateProcessWithLogonW() with a set of specfied user details.
This works fine and the process starts as expected. However, when this process tries to launch other processes itself, currently just using CreateProcess() these start then die straight away - they are executables that require desktop access.
After reading up on Microsoft's article on CreateProcess() - http://msdn.microsoft.com/en-us/library/ms682425(VS.85).aspx
I think can see why this is happening and it makes sense to an extent. CreateProcess() knows the calling process is impersonating a user so it uses it's parent process, which in this case is the Local System account. But of course anything run in the local system account doesn't have the access we need, so the launched process dies.
Oddly when I was previously using LogonUser() and CreateProcessAsUser() to launch the initial executable within the service, it worked fine. But I had to change this to CreateProcessWithLogonW() due to problems with not having the correct privileges.
Does anybody know of a solution to this? I've seen talk about this elsewhere on the web but not with any definite solution. It seems like I possibly need the token of the user i'm logging on with in CreateProcessWithLogonW() with so I can use it to launch the other processes later? But I have no way of getting hold of this token, can this be retreived for the current user in any way?
Any help would be greatly appreciated, thanks :)
We solved the problem using some code that I found long-ago. The "copyright" section of one of the source modules contains the following:
/////////////////////////////////////////////////////////////
// CreateProcessAsUser.cpp
//
// Written by Valery Pryamikov (1999)
//
// Command line utility that executes a command under specified user identity
// by temporarily installing itself as a service.
//
// Based on Keith Brown's AsLocalSystem utility (http://www.develop.com/kbrown)
// Uses some code from Mike Nelson's dcomperm sample utility
// and from tlist sample (Microsoft Source Code Samples)
//
// Use:
// CreateProcessAsUser.exe [-i[nteractive]]|[-s[ystem]]|
// [-u"UserName" -d"DomainName" -p"Password"]|[-a"AppID"] command
// Command must begin with the process (path to the exe file) to launch
// -i process will be launched under credentials of the
// "Interactive User" (retrieved from winlogon\shell process)
// -a process will be launched under credentials of the user
// specified in "RunAs" parameter of AppID.
// -s process will be launched as local system
// -u -d -p process will be launched on the result token of the
// LogonUser(userName,domainName,password,LOGON32_LOGON_BATCH...)
//
// either (-s) or (-i) or (-a) or (-u -d -p) parameters must supplied
//
// Examples:
// CreateProcessAsUser -s cmd.exe
// CreateProcessAsUser -a"{731A63AF-2990-11D1-B12E-00C04FC2F56F}" winfile.exe
//
/////////////////////////////////////////////////////////////
Perhaps this information will yield hits within your Google searches - I attempted a few quick attempts but came up empty-handed.
We decomposed the internals into a set of API that yielded the results we needed.
Do you own the code launched using CreateProcessWithLogonW (and which in turn calls CreateProcess)? If you do not then you might need to perform IAT (or API) hooking on it (i.e. at run-time), as to substitute any calls to CreateProcess with an appropriate procedure that also uses CreateProcessWithLogonW or CreateProcessWithTokenW. See APIHijack, Detours.
After this is done, the child process may require access to HKCU. If you are not already doing this, you should load the profile of each impersonated user, once per user, before calling CreateProcessWithLogonW.
By default, CreateProcessWithLogonW
does not load the specified user
profile into the HKEY_USERS registry
key. This means that access to
information in the HKEY_CURRENT_USER
registry key may not produce results
that are consistent with a normal
interactive logon. It is your
responsibility to load the user
registry hive into HKEY_USERS before
calling CreateProcessWithLogonW, by
using LOGON_WITH_PROFILE, or by
calling the LoadUserProfile function.
Isn't there an option for services to allow them to interact with the desktop? If setting that option for your service is a possibility, that would probably be the simplest solution.
I'm assuming that this process is a service; that isn't specified in the question, but seems logical given that it is running as Local System account.
Where you're getting stuck isn't in CreateProcess, It's in CreateService. If you want your service to be able to interact with the desktop, you have to specify SERVICE_INTERACTIVE_PROCESS as one of the flags to the argument dwServiceType. This setting is inherited by child processes of the service.
You can also modify an existing service's setting by using the Services tool, select Properties for the service, click on the "Log On" tab, and select the check box "Allow service to interact with desktop".