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)
{
}
}
}
}
}
}
Related
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...
}
}
A previous post on this subject has been helpful (see How to export TestExecute/TestComplete results to teamcity?)
Right now, our TestComplete solution creates a text file that MSBuild consumes with a Microsoft.Build.Utilities.Task helper that sends the messages back up to TeamCity. However, I'm wondering if we can do better.
The TeamCity help topic (http://confluence.jetbrains.com/display/TCD65/Build+Script+Interaction+with+TeamCity#BuildScriptInteractionwithTeamCity-ServiceMessages) says, messages "should be printed into standard output stream of the build". What I'm trying to figure out is, can I directly access that stream from the TestComplete script?
I was hoping that it would be something as simple as:
dotNET.System.Console.WriteLine_11("##teamCity[testSuiteStarted name='Foo']");
But that is obviously not giving me the correct output stream. Thoughts? Can it be done?
The issue you are experiencing is that TestComplete does not support writing to the standard output stream, even if you invoke the CLR Console.WriteLine, you are writing to the process named tcHostingProcess.exe in which all CLR objects exist.
In order to work around this, you need a console application running that can accept messages from your TestComplete project. There are a number of ways to do this, but here is a proposed solution using TCP/IP for the necessary IPC.
In TeamCity
In the TeamCity build steps, you want the TestComplete or TestExecute process to launch without holding up the build script and then to launch a custom console application which will receive the messages from TestComplete.
start TestComplete.exe [arg1] [arg2] ...
TCConsoleHost.exe
Console Host Application
The console host program will start a TcpListener, and once a client connects, it will read messages from the resulting NetworkStream object and print them to the console. This program will continue until there is an error in reading from the stream (i.e. TestComplete has exited).
class Program
{
static void Main(string[] args)
{
TcpListener listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 9800);
listener.Start();
TcpClient client = listener.AcceptTcpClient();
NetworkStream stream = client.GetStream();
BinaryReader reader = new BinaryReader(stream);
while (true)
{
try
{
string message = reader.ReadString();
Console.WriteLine(message);
}
catch
{
break;
}
}
}
}
Message Client Class
Likewise, we can create a TcpClient which can connect to our listening process and relay messages. The real trick here is to wrap this in a static class with a static constructor so that once TestComplete loads the CLR bridge, it will automatically connect and be ready to send the messages. This example has a TeamCity service message function SendMessage which automatically formats the message (including escaping single quotes).
public static class TCServiceMessageClient
{
static BinaryWriter writer;
static NetworkStream stream;
static TCServiceMessageClient()
{
TcpClient client = new TcpClient();
client.Connect("127.0.0.1", 9800);
stream = client.GetStream();
writer = new BinaryWriter(stream);
}
public static void SendMessage(string message)
{
writer.Write(string.Format("##teamcity[message text='{0}'", message.Replace("'","|'")));
}
}
In TestComplete
Now, since the client will automatically connect when the CLR bridge is loaded, the only code necessary in TestComplete is:
dotNET["TCServiceMessageClient"]["TCServiceMessageClient"]["SendMessage"]("Hello TeamCity!");
Additional Notes
There are a few caveats with the above code, namely timing, reconnect, etc. A deployed solution should have better error handling of the network conditions. Additionally, it may be more desirable that the TCMessageHost application actually launches TestComplete directly with a System.Process object so that it can more reliably wait for the application to exit.
I'm working with TeamCity and TestComplete as well. I ran into the exact same problem, and you can't write to the console no matter what you try to do, at least not that I've found. I even tried writing a C# app that I used the CLR bridge in TestComplete to talk to it, and still nothing.
What we ended up doing was to create batch file that handles the TestExecute call. We then call the batch file from TeamCity. To call TestExecute use this (with your pjs file and /p specific to your tests):
start /wait TestExecute "{TestCaseID}.pjs" /r /e /p:"{TestCaseID}" /silentmode /forceconversion
Then we check the ERRORLEVEL property returned by TestExecute. A level of 2 means that the test execution failed. In that case, we print the following with an echo statement:
##teamcity[testFailed name='{TestCaseID} - {TestName}' message='TestExecute reported a failure' details='See artifacts']
After TestExecute closes, it will attempt to open the log file. We have our build servers set so that mht files are opened by the calculator since they take a while to open (set it as the default in the Windows Right-Click -> Open With menu). So, after TestExecute completes, it opens the calculator, then our batch file just kills the calculator using taskkill.
We then copy the result file from TestExecute into a specified location (this is a separate executable that we wrote). The we finish the test execution and publish our artifacts to TestExecute:
echo ##teamcity[testFinished name='{TestCaseID} - {TestName}' duration='%milliSecondsElapsed%']
echo ##teamcity[publishArtifacts 'C:\BuildResults\{GroupName}\{TestCaseID}\*.mht']
One thing to note: We were never able to get TestExecute to reliably run a single test at a time, so we actually use a list of our test cases to generate separate pjs and mds files for each of our test cases. Then we use those generated pjs and mds files in TestExecute. That's where the {GroupName}, {TestCaseID}, and {TestName} variables from above come from. Those are specific to our implementation, but you may need different indicators there.
Hope this helps!
If you are using TestExecute this has become "Relatively" simple recently
Execute TestExecute with the following parameters:
/exportlog:"log\Index.html" /SilentMode /DoNotShowLog
For a full rundown of TestExecute parameters check: https://support.smartbear.com/testexecute/docs/running/automating/command-line/command-line.html
Setup your TestComplete project with a build level report:
Please be aware it is case sensitive
On your build configuration, in the general settings tab, export the generated log folder as an artifact.
Once the build has run, you will be able to see the test log embedded within TeamCity:
This is using TestComplete V14 and TeamCity 2019.1
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.
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".
I'm using an AutoIt script to start and automate a GUI application. I need to activate the script each hour.
Will AutoIt scripts (which perform actions on a GUI) work when used as a service? The script will be run as a service (not scheduled task).
You can easily make an autoit script run as a service using service.au3 written by archer of the autoit forums. Unfortunately or fortunately since it is a security measure. A service needs to start independent of the current user session (before login). It cant access send APIs for input manipulation of the current user session from there. It does sound much more like you need a scheduled task and not a service.
As mentioned above, a scheduled task is what you're looking for. To run a script as a service read this:
Q4. How can I run my script as a service?
This is also a question with multiple answers, and none of them are the only way to do it. The first question to ask yourself is whether or not you wish to install the service on other computers besides your own.
A1. If you only wish to install the service on your own computer, The easiest way to do this is to use Pirmasoft RunAsSvc. This program makes services easy to install and easy to remove when necessary.
A2. If you wish to make the service available to anyone running your script, you can use SRVANY.EXE and ServiceControl.au3. You can then use this code to install your script as a service:
#include "ServiceControl.au3"
$servicename = "MyServiceName"
_CreateService("", $servicename, "My AutoIt Script", "C:\Path_to_srvany.exe", "LocalSystem", "", 0x110)
RegWrite("HKLM\SYSTEM\CurrentControlSet\Services\" & $servicename & "\Parameters", "Application", "REG_SZ", #ScriptFullPath)
or use the following code to delete this service:
#include "ServiceControl.au3"
$servicename = "MyServiceName"
_DeleteService("", $servicename)
There is one caveat to setting up AutoIt as a service. If the service is not installed using the above code, it must have the "allow service to interact with the desktop" setting or else automation functions such as Control* or Win* functions will not function. To assure the service does indeed have this setting, use the following code:
RegWrite("HKLM\SYSTEM\CurrentControlSet\Services[ServiceName]", "Type", "REG_DWORD", 0x110)
Taken from the FAQ topic on the AutoIt Forums. www.autoitscript.com/forum/index.php?showtopic=37289)
It sounds like you're want to use a scheduled task instead of a service. Scheduled tasks can execute every hour, while you're logged in, and should also be able to interact with your desktop. Just remember that a task run as a normal user can not interact (send input) to a elevated program if you're using Vista/Windows Server 2008 with User Account Control enabled.