Running a command line exe from windows service - c#

I have a windows service that is intended to do the following:
Monitor a folder on the server for PDF files
When file arrives, run a third party exe to convert the PDF to Excel. No text output is generated. The third party tool simply uses the input file path and generates an output excel file. No need for a window launch. No need to track sessions.
Windows service then reads the data from the Excel, processes it, and outputs an xml into a folder.
All this works fine in debug mode. However, when I try to run the windows service on my local machine in release mode (using installutil) as a service (as opposed to in visual studio), it does not work. When I attach-to-process, I notice the cursor just hangs on waitforexit and no excel is generated. Since it works in debug but not in release mode, I suspect it's a permissions issue. Any feedback will be appreciated.
Already tried checking "Allow service to interact with desktop". Didn't help.
EDIT: correction - cursor actually hangs on exeProcess.WaitForExit()
ProcessStartInfo sInfo = new ProcessStartInfo();
sInfo.FileName = ConfigurationManager.AppSettings["FileName"];
sInfo.Arguments = GetArguments();
sInfo.UseShellExecute = false;
sInfo.CreateNoWindow = true;
sInfo.ErrorDialog = false;
sInfo.WindowStyle = ProcessWindowStyle.Hidden;
//sInfo.RedirectStandardError = true; //didn't work
//sInfo.RedirectStandardInput = true; //didn't work
//sInfo.RedirectStandardOutput = true; //didn't work
using (Process exeProcess = Process.Start(sInfo))
{
//StreamWriter inputWriter = exeProcess.StandardInput;
//StreamReader outputReader = exeProcess.StandardOutput;
//StreamReader errorReader = exeProcess.StandardError;
exeProcess.WaitForExit();
}

The problem is almost certainly that steps 2 and 3 do not work in the non-interactive session 0. The big difference is not between debug and release builds. But between running on the interactive desktop, and the service running in session 0.
To get out of this, and continue using a service, you need to make sure that all steps can operate in session 0. Step 2 we know nothing about. Step 3 looks like it involves automating Excel. That is officially not supported and known not to work under session 0. You'll need to read the Excel file using something other than Excel. As for step 2, that depends on the third party tool that converts from PDF to Excel.

I would put into the code proper exception handling and logging to e.g. event log to find out if there is anything different than expected.
Next step may be to configure the service with credentials which you know will have access to executable you try to run, to the directories where you export the files generated etc.
If possible / available then try to use assembly which you can reference instead of running another executable from your code. This way you will have it more under control;)
EDIT:
In general it is possible to run command line tool by windows service. There just need to be clear how the tool expects to get the data, if it is from command line and tool is not waiting for user to enter anything. Do you have problem just with this command line utility or in general?
Service:
private FileSystemWatcher watcher;
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
watcher = new FileSystemWatcher();
watcher.Path = #"c:\temp\service";
watcher.Created += watcher_Created;
watcher.EnableRaisingEvents = true;
}
protected override void OnStop()
{
}
private void watcher_Created(object sender, FileSystemEventArgs e)
{
try
{
ProcessStartInfo startInfo = new ProcessStartInfo(pathToExe, e.Name);
EventLog.WriteEntry(e.Name);
Process process = Process.Start(startInfo);
process.EnableRaisingEvents = true;
}
catch (Exception ex)
{
EventLog.WriteEntry(ex.ToString());
}
}
Command line tool - expects to get name of the file as parameter
public class Program
{
public static void Main(string[] args)
{
File.AppendAllText(pathToLog, string.Format("{0} - File got created in watched folder - doing conversion of {1}\n", DateTime.Now, args[0]));
}
}

Related

Delete windows service folder with files throws error

I know this question was already asked, but I couldn't find the solution so far.
What I'm trying to do is uninstall a windows service and delete the folder with the windows service using C#.
Windows service uninstall
public static void Uninstall(string exeFilename)
{
var commandLineOptions = new string[1] { "/LogFile=uninstall.log" };
if (!Directory.Exists(exeFilename)) return;
var fileNames = Directory.GetFiles(exeFilename);
var serviceFile = fileNames.FirstOrDefault(f => f.EndsWith(".exe"));
var serviceFileName = Path.GetFileName(serviceFile);
var serviceName = Path.GetFileNameWithoutExtension(serviceFile);
var serviceExists = ServiceController.GetServices().Any(s => s.ServiceName == serviceName);
if (!serviceExists) return;
var installer =
new AssemblyInstaller($"{exeFilename}\\{serviceFileName}", commandLineOptions)
{
UseNewContext = true
};
installer.Uninstall(null);
installer.Dispose();
}
Folder and files delete
public static void DeleteFolder(string folderPath)
{
if(!Directory.Exists(folderPath)) return;
try
{
foreach (var folder in Directory.GetDirectories(folderPath))
{
DeleteFolder(folder);
}
foreach (var file in Directory.GetFiles(folderPath))
{
var pPath = Path.Combine(folderPath, file);
File.SetAttributes(pPath, FileAttributes.Normal);
File.Delete(file);
}
Directory.Delete(folderPath);
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
Error that I get
Access to the path 'c:\services\x\x.exe' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.File.InternalDelete(String path, Boolean checkHost)
at System.IO.File.Delete(String path)
This error occurs randomly. The .exe file is not readonly, sometimes the files get deleted.
Does anyone know what's wrong?
Stopping a Windows service does not equate to exiting a process. An executable can house multiple Windows services (it's essentially the physical shell for the services).
So what you're running into, it looks like, is that the service likely stopped just fine, and uninstall can proceed and the deletion of files can proceed, but only up until the point where it reaches the executable, which hasn't yet had a chance to exit.
Stopping, exiting, uninstalling are all asynchronous and need time to complete before moving to the next step.
What you have to do is follow this formula
Ensure your code is running with elevated privileges if you can; if you can't you may run into Access Denied. You can also try changing the ownership of the target executable.
Stop or ensure the service is stopped. If there are multiple services, stop all of them.
Wait for stop to actually occur. It's not always immediate.
Call the Uninstall().
Wait some amount of time. Check to see if the process is running. If it is you will call Process.Kill() (see an implementation below).
Finally, you can call the DeleteFolder() for which your implementation looks adequate to me.
Exiting the Process
Write a method that looks something like this. You might want to tweak it.
void Exit(Process p)
{
for (int i = 0; i <= 5; i++)
{
// doesn't block
if (p.HasExited) return;
// doesn't block; pass true to terminate children (if any)
p.Kill(true);
// wait 5 seconds then try again
Thread.Sleep(5000);
}
}
After all that you should be good to go.
Is the service stopped / executable stopped and does the program have Administrator access? If you don't have administrator access, refer to:
How do I force my .NET application to run as administrator?
to run as administrator. When uninstalling a program, administrator permissions are almost always required. If this still fails, you are not the owner of the file and you must change the owner to yourself or Administrators. You can do this programatically here: Getting / setting file owner in C#
I think you have to stop the service first.
try using
sc stop <service name>
to stop the service.
Then uninstall should work.
try using installutil.exe if your uninstall code does not work. It can give your error output also.
The exe cannot be deleted if it is currently executing so make sure exe is not executing when you try to delete it.
You can use setup project, it generates the uninstaller automatically and gives you a range of possibilities with it.
Example of the service installer
You must download the addon in the market place

How to capture the output of a process (from OR Tools solver) and display it through console

I have a C# winform application in which an optimization model is solved by OR-Tools. The optimization solver has the capability of sending the whole optimization process as stdout.This is done by:
Slvr.EnableOutput();
Solver.ResultStatus restatus = Slvr.Solve();
However, the solver does not automatically open up the console.
Currently, what I have done is:
Projects properties --> Application --> Output type --> Console Application
and the console is ready from the beginning till the end of the application run. Hence, that process stdout is automatically displayed.
What I want is to open the console exactly when the above part of code is run and display the stdout from the solver. Then wait for a key from the user to close the console and continue with the main application.
I guess your problem is you are trying to run the solver as part of the Winforms application, inside the GUI process right? But Console output is usually disabled in a Winforms application. You have basically two options:
use one of the options described here in this older SO answer to attach a console window for output to a Winforms application
split the application into two exe files: one command line program which runs the solver, and a Winforms part, just containing the UI. Then run the command line part as a separate process by System.Diagnostics.Process.Start, which allows finegrained control about output redirection. You may need the UI to pass parameters to the command line program, for example, by using a temporary file.
The second option is more work, especially for the communication between the GUI and the command line tool, but can be implemented easier in a way the GUI is not blocked, is more robust against bugs / program crashes in the solver part and performs usually better in case you want to introduce parallelization / run multiple solver processes at once.
Doc Brown has already answered your question, I'm only adding this to provide some code of how we implemented it here-- it's exactly what he suggests. We have a separate testPlugin.exe that get's started here. The communication is via files read and written on the file system. The console output gets captured in the "output handlers"
using System;
using System.Diagnostics;
using System.IO;
...
private void startTest()
{
int result = 2;
setFormStatus("working..."); // My method to inform the user with the form to wait.
getFormData(); // My method to get the data from the form
string errorMessage = null;
System.Diagnostics.Process testPlugInProcess = new System.Diagnostics.Process();
try
{
using (testPlugInProcess)
{
testPlugInProcess.StartInfo.UseShellExecute = false;
testPlugInProcess.StartInfo.FileName = System.IO.Path.Combine(assemblyDirectory, TestPlugInExe); // The name of the exe file
testPlugInProcess.StartInfo.CreateNoWindow = false;
testPlugInProcess.StartInfo.Arguments = getModelTestCommandLineArgs(); // My method to create the command line arguments
testPlugInProcess.StartInfo.RedirectStandardError = true;
testPlugInProcess.StartInfo.RedirectStandardOutput = true;
testPlugInProcess.OutputDataReceived += pluginTestOutputHandler;
testPlugInProcess.ErrorDataReceived += pluginTestOutputHandler;
testPlugInProcess.Start();
testPlugInProcess.BeginErrorReadLine();
testPlugInProcess.BeginOutputReadLine();
testPlugInProcess.WaitForExit();
result = testPlugInProcess.ExitCode;
}
setFormStatus("");
}
catch (Exception ex)
{
errorMessage = ex.Message;
}
testPlugInProcess = null;
}
Both the console and error output get written to the same file here, but you could separate them.
The plug-in handler looks like this:
private static void pluginTestOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
for (int i = 0; i < numberOfTriesForWriting; i++)
{
try
{
using (StreamWriter sw = File.AppendText(lastPlugInTestTraceFilePath)) // The file name where the data is written.
{
sw.WriteLine(outLine.Data);
sw.Flush();
return;
}
}
catch (IOException)
{
System.Threading.Thread.Sleep(msToWaitBetweenTries);
}
}
}
}

How to run Outlook using Process.Start("outlook.exe") and get the control back

My C# program needs to launch Office Outlook and get the current "running outlook application".
In order to do that I've implemented the following simple program (so if you want you can test it simply):
using Outlook = Microsoft.Office.Interop.Outlook;
using System.Runtime.InteropServices;
static void Main(string[] args)
{
Outlook.Application outlookObj = null;
if (Process.GetProcessesByName("OUTLOOK").Count().Equals(0))
{
Process.Start("outlook.exe"); // MY PROGRAM STOPS HERE
}
var process = Process.GetProcessesByName("OUTLOOK").First();
while (!process.HasExited)
{
try
{
outlookObj = (Outlook.Application)Marshal.GetActiveObject("Outlook.Application");
break;
}
catch
{
outlookObj = null;
}
System.Threading.Thread.Sleep(10);
}
string result = (outlookObj== null)? "DOES NOT WORK" : "OK";
Console.WriteLine(result);
Console.ReadLine();
}
My problem is that once Office Outlook starts running then my C# console application does not continue its job. After the Process.Start("outlook.exe"); instruction is executed then I must click on Visual Studio GUI in order to restart the console application and finally read "OK" on my console application.
How can I solve my problem?
Microsoft wrote a example about how to log into a outlook instance. Although this is directly what you asked for in your question, the example contains how to start a new outlook application in the intended way
application = new Outlook.Application();
as a side note: in your example you use the following code:
while (!process.HasExited)
{
try
{
outlookObj = (Outlook.Application)Marshal.GetActiveObject("Outlook.Application");
break;
}
catch
{
outlookObj = null;
}
System.Threading.Thread.Sleep(10);
}
This is bad practice in your main thread as your applying 'busy waiting' by using the thread.sleep. This means you will 1. use CPU power while your application is doing nothing. 2. make your GUI completely unresponsive and if the thread.sleep is called to many times Windows will suggest to shut the process down (the whole screen gets white and eventually you get a popup asking you if you want to wait or just shut it down). There are plenty of ways in the .net framework to prevent both of these issues (for example using a waithandle, background worker or locking)
There is no need to run the a new process using the Process.Start method. Instead, you can add the Outlook reference to your C# project and create a new instance of the Application class. See C# app automates Outlook (CSAutomateOutlook) sample project for more information.
Also you may find the following articles helpful:
How to automate Outlook and Word by using Visual C# .NET to create a pre-populated e-mail message that can be edited
How to use Visual C# to automate a running instance of an Office program
This works:
public static void StartOutlookIfNotRunning()
{
string OutlookFilepath = #"C:\Program Files (x86)\Microsoft Office\Office12\OUTLOOK.EXE";
if (Process.GetProcessesByName("OUTLOOK").Count() > 0) return;
Process process = new Process();
process.StartInfo = new ProcessStartInfo(OutlookFilepath);
process.Start();
}
Use the Process.Start overload that takes a ProcessStartInfo instead so you can set UseShellExecute
var startInfo = new ProcessStartInfo()
{
FileName = "Outlook.exe",
UseShellExecute = true
};
Process.Start(startInfo);
MAYBE the process need some time to start.
Try this:
if (Process.GetProcessesByName("OUTLOOK").Count().Equals(0))
{
Process.Start("outlook.exe"); // MY PROGRAM STOPS HERE
}
while ((Process.GetProcessesByName("OUTLOOK").Count().Equals(0));
var process = Process.GetProcessesByName("OUTLOOK").First();
This should cause starting process and waiting until it is avaible before trying to catch it...

How to check a Window already opened when button is clicked?

I am Working in Visual Studio 2008 Winforms Application project in Windows 7 (32 bit).I am doing the project in C#.
I have placed some buttons in a tab and added actions for that once it is clicked. While clicking the button am just running a .exe file in its action part.
My problem is that, i opened a window by clicking one button(so the .exe file is running), now while am clicking the button again it is opening same window again irrespective of checking that it is open or not. I want to solve this issue,as when a window is opened it must not open again on another click on same button. How to solve this issue. ?
Please help....
Thanks in advance..
You could check if the process is already running, when re-clicking the button:
private void btnStartExecutable_Click(object sender, EventArgs e)
{
Process[] processName = Process.GetProcessesByName("InsertProcessNameHere");
if (pname.Length == 0)
{
MessageBox.Show("Application isn't running yet.");
//Start application here
Process.Start("InsertProcessNameHere");
}
else
{
MessageBox.Show("Application is already running.");
//Don't start application, since it has been started already
}
}
You can try this:
bool processExited = true;
private void button1_Click(object sender, EventArgs e)
{
if (processExited)
{
Process process = new Process();
process.EnableRaisingEvents = true;
process.Exited += MyProcessExited;
process.StartInfo = new ProcessStartInfo();
process.StartInfo.FileName = "notepad.exe";
process.Start();
processExited = false;
}
else
{
MessageBox.Show("Still running");
}
}
void MyProcessExited(object sender, EventArgs e)
{
processExited = true;
}
The right answer here IMHO is that unless the two application shares a common resource or can talk to each other through some channel, there is no safe and efficient way to achieve what you want. Since the process is external, it could already be running before your calling app starts, or even while it's already running. You won't be able to tell if the process has been started from your app or not.
By the time I'm writing this your question does not yet state if you are in liberty to modify the external app you are calling. If you are however, using a Mutex would be a quick and easy way to solve your problem.
In your external app, whenever you want to make the other app aware of whatever condition you want (be it that the process is running or that a specific window is opened), have a Mutex instance created like this:
var mutex = new Threading.Mutex(true, "mutex unique identifier");
And in your calling app, try to create a Mutex instance with the same identifier:
bool alreadyExists;
var mutex = new Threading.Mutex(false, "mutex unique identifier", out alreadyExists);
Here the alreadyExists variable will tell you whether or not the external process is running or not. This is much safer than trying to identify it via its name, as other processes could have the same or a new version could be of a different name. Of course, the mutex identifier must be as unique as possible (like a Guid), otherwise you may encounter the same problem. ;)
Whenever you feel like the mutex must be released (at external app level), release it:
mutex.ReleaseMutex();
Note that if the process ends the mutex will be automatically released by the OS.
If the external app isn't a .NET based app, you can still create a mutex with Win32 API functions.
Thanks for the support.. I got the answer like this..
1) Creating an event'Exit' for the process in function button click
2) Define a function for the exit event where you set a flag
3) Check the flag is set or not everytime while opening the process in the function button click
Event for Exit: 'P' is the name of process:
p.Exited += new EventHandler(p_Exited);
p_Exited will be the function name where we will set the flag.
Thanks all...
If you know the name of the process that gets started or the path the .exe is run from you can use the Process class to check to see if it is currently running.
http://msdn.microsoft.com/en-us/library/system.diagnostics.process(v=vs.110).aspx

programmatically kill a process in vista/windows 7 in C#

I want to kill a process programmatically in vista/windows 7 (I'm not sure if there's significant problems in the implementation of the UAC between the two to make a difference).
Right now, my code looks like:
if(killProcess){
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("MyProcessName");
// Before starting the new process make sure no other MyProcessName is running.
foreach (System.Diagnostics.Process p in process)
{
p.Kill();
}
myProcess = System.Diagnostics.Process.Start(psi);
}
I have to do this because I need to make sure that if the user crashes the program or exits abruptly, this secondary process is restarted when the application is restarted, or if the user wants to change the parameters for this secondary process.
The code works fine in XP, but fails in Windows 7 (and I assume in Vista) with an 'access is denied' message. From what the Almighty Google has told me, I need to run my killing program as administrator to get around this problem, but that's just weak sauce. The other potential answer is to use LinkDemand, but I don't understand the msdn page for LinkDemand as it pertains to processes.
I could move the code into a thread, but that has a whole host of other difficulties inherent to it that I really don't want to discover.
You are correct in that it's because you don't have administrative priveleges. You can solve this by installing a service under the local system user and running a custom command against it as needed.
In your windows form app:
private enum SimpleServiceCustomCommands { KillProcess = 128 };
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "SERVICE_NAME");
scp.Assert();
System.ServiceProcess.ServiceController serviceCon = new System.ServiceProcess.ServiceController("SERVICE_NAME", Environment.MachineName);
serviceCon.ExecuteCommand((int)SimpleServiceCustomCommands.KillProcess);
myProcess = System.Diagnostics.Process.Start(psi);
In your service:
private enum SimpleServiceCustomCommands { KillProcess = 128 };
protected override void OnCustomCommand(int command)
{
switch (command)
{
case (int)SimpleServiceCustomCommands.KillProcess:
if(killProcess)
{
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("MyProcessName");
// Before starting the new process make sure no other MyProcessName is running.
foreach (System.Diagnostics.Process p in process)
{
p.Kill();
}
}
break;
default:
break;
}
}
I'll add the code for Simon Buchan's suggestion. It makes sense and should work as well, assuming your windows form is what launched the process in the first place.
Here's where you create the process. Notice the variable myProc. That's your handle on it:
System.Diagnostics.Process myProc = new System.Diagnostics.Process();
myProc.EnableRaisingEvents=false;
myProc.StartInfo.FileName="PATH_TO_EXE";
myProc.Start();
Later, just kill it with:
myProc.Kill();

Categories

Resources