I have a C# Window Service that runs a batch file (.bat) which in turn, executes a java app. The service runs the .bat file (cmd.exe) with no problem. However, when I try to stop the window service, the cmd.exe process does not die. A new cmd process is stacked if I start the service again.
How do I kill the running cmd.exe process?
Code:
private const string BATCH_FILE_PATH_APPKEY = "Service_Batch_File_Path";
private const string BATCH_FILE_DEFAULT = "Service.bat";
private static Process _proc;
private static bool _hasStarted = false;
public AService()
{
InitializeComponent();
_proc = new Process();
}
protected override void OnStart(string[] args)
{
try
{
string appDirectory = System.Windows.Forms.Application.ExecutablePath;
appDirectory = appDirectory.Substring(0, appDirectory.LastIndexOf("\\"));
string workingDirectory = appDirectory;
string batchFilePath = string.Empty;
batchFilePath = workingDirectory + "Service.bat";
// Make sure it exists
_proc.StartInfo = new ProcessStartInfo(batchFilePath);
_proc.StartInfo.CreateNoWindow = true;
_proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
_proc.Start();
_hasStarted = true;
}
catch (System.Exception ex)
{
eventLog1.WriteEntry(ex.ToString() + "\n\nStack Trace:\n" + ex.StackTrace);
OnStop();
}
}
protected override void OnStop()
{
if (_hasStarted)
_proc.CloseMainWindow();
//_proc.Close();
}
TIA,
Alex.
_proc.Kill();
See the details in MSDN: Process.Kill Method
You may find the following link very useful: Process.Close() is not terminating created process,c#
_proc.Kill () will work.... but it will orphan your java app. I have done something similar where a third process was started. You will also need to know which java process to kill. To do this, you can use the ParentProcess Performance Counter.
Here are some details on using the ParentProcess performance counter.
Also, version of Windows do you plan to deploy this on? WindowsServer2008 seems to have a cmd.exe and a conhost.exe. That may pose a problem for you (again one that can probably be resolved by knowing the parent process.
Have you tried _proc.Kill() in your service's closedown processing? This is async and you should then call WaitForExit to give it a decent chance to go away. Log any failures or exceptions in this logic for investigation.
You should also (for cleanliness) reinstate the _proc.Close() after calling WaitForExit(), to ensure Dispose() gets called properly for the Process. I know your service is about to exit, but this is a good habit and means you are less likely to leak if you decide to manage the child Process more dynamically in future.
Related
I have create a application for text to speech using Speechlib SpVoice. it is working fine with windows application.
But when i Create windows service using same code . It give me this error
System.Runtime.InteropServices.COMException (0x8004503A): Exception
from HRESULT: 0x8004503A at SpeechLib.ISpeechVoice.Speak
this is my code
public partial class LEDPlayService : ServiceBase
{
static int MessageID = 0;
static SpeechLib.SpVoice VoiceObj = new SpeechLib.SpVoice();
static System.Timers.Timer myTimer = new System.Timers.Timer();
protected override void OnStart(string[] args)
{
myTimer.Elapsed += new ElapsedEventHandler(OnElapsedTime);
//This statement is used to set interval to 1 minute (= 60,000 milliseconds)
myTimer.Interval = 60* 1000;
// enabling the timer
myTimer.Enabled = true; ;
myTimer.AutoReset = false;
}
private static void OnElapsedTime(object source, ElapsedEventArgs e)
{
((System.Timers.Timer)source).Stop();
myTimer.Enabled = false; ;
bool result =PlayAudio("Hello prithvi");
((System.Timers.Timer)source).Start();
myTimer.Enabled = true;
// TraceService(""+DateTime.Now.TimeOfDay);
}
public static bool PlayAudio(string text)
{
bool res = false;
try
{
VoiceObj.Speak(text, SpeechLib.SpeechVoiceSpeakFlags.SVSFDefault);
res = true;
}
catch(Exception e)
{
TraceService("error in sound........."+e.InnerException+e.Message+" "+e.ToString());
res = false;
}
return res;
}
}
Please help me..
It is a low-level error returned by a SAPI call, SPERR_NOT_FOUND. You are making it excessively difficult to answer the question reliably when you don't post a snippet and the stack trace of the exception. Or how you even observed it, these COM errors are normally translated to .NET exceptions.
The error code doesn't much more than "can't find what's needed to do the job". The call context ought to make it bit clear what might be missing, but we can't see this. Having this code run in a service is a cue of sorts. The user account under which this service runs matters, a lot of config for System.Speech is stored in the registry and a service is going to have a hard time finding config that's stored in HKCU instead of HKLM. Not uncommon for example if you purchased a voice and registered it. And it might well have a hard time finding hardware, like a microphone or speaker.
So first thing to try is to configure the service to run with a specific user account (like yours) instead of the default System account. Next thing to try is to use SysInternals' Process Monitor, you'll see your program search the registry for keys. Compare a good trace, one you got from running it as a desktop program, against the trace you got when running it from the service. And update your question with the required info to get a better answer.
When I try to start my c# service it says:"starting" for a second and it turns back to being "stopped"
What can be the problem? I had the same code before, and it worked but made some changes in the code now and it stopped working. Here is what I added to my code:
App Config:
<add key="cut-copy" value="copy"/>
Normal code:
private void fileSystemWatcher1_Created(object sender, System.IO.FileSystemEventArgs e)
{
String cut_copy = ConfigurationManager.AppSettings[#"cut-copy"];
if (cut_copy == "copy")
{
cut = false;
}
else
{
cut = true;
}
if (WaitForFileAvailable(e.FullPath, TimeSpan.FromSeconds(10)))
{
var file = Path.Combine(source, e.Name);
var copy_file = Path.Combine(target, e.Name);
var destination = Path.Combine(target, Path.ChangeExtension(source, Path.GetExtension(source)));
if (File.Exists(file))// Check to see if the file exists.
{ //If it does delete the file in the target and copy the one from the source to the target.
File.Delete(copy_file);
File.Copy(e.FullPath, Path.Combine(target, e.Name));
}
else// If it doesn't, just copy the file.
{
if (cut == true)
{
if (File.Exists(file))// Check to see if the file exists.
{ //If it does delete the file in the target and copy the one from the source to the target.
File.Delete(copy_file);
File.Move(Path.Combine(e.FullPath, e.Name), target);
}
}
else
{
if (File.Exists(file))// Check to see if the file exists.
{ //If it does delete the file in the target and copy the one from the source to the target.
File.Delete(copy_file);
File.Copy(e.FullPath, Path.Combine(target, e.Name));
}
}
//under this is more code that didn't change
}
EDIT: ONSTART:
protected override void OnStart(string[] args)
{
base.OnStart(args);
this.fileSystemWatcher1 = new System.IO.FileSystemWatcher();
((System.ComponentModel.ISupportInitialize)(this.fileSystemWatcher1)).BeginInit();
fileSystemWatcher1.Path = source;
fileSystemWatcher1.Changed += new FileSystemEventHandler(fileSystemWatcher1_Changed);
fileSystemWatcher1.Created += new FileSystemEventHandler(fileSystemWatcher1_Created);
fileSystemWatcher1.Deleted += new FileSystemEventHandler(fileSystemWatcher1_Deleted);
fileSystemWatcher1.Renamed += new RenamedEventHandler(fileSystemWatcher1_Renamed);
this.fileSystemWatcher1.EnableRaisingEvents = true;
this.fileSystemWatcher1.IncludeSubdirectories = true;
((System.ComponentModel.ISupportInitialize)(this.fileSystemWatcher1)).EndInit();
logger("Service started "+ DateTime.Now);
}
What am I doing wrong?
Usually this behavior indicates that the OnStart method ends without leaving any threads running. I have experienced that if an unhandled exception is thrown starting the service, the service doesn't go into Stopped state, but the service manager shows the "Starting service" window for 30 seconds and then fails saying it can't determine the service's state.
I'm not sure whether the FileSystemWatcher actually spawns a new thread that keeps running. You could (just to try it), also create a System.Timers.Timer that fires every 30 seconds in OnStart and stop that timer in OnStop. If the service keeps running then, you have to spawn a new thread for the FileSystemWatcher.
Usually, in OnStart you'd spawn a separate thread that does the service's work. Be it waiting for TCP connections, a timer that does things on a regular basis or any other kind of thread. If you don't do that, the service just stops as soon as there are no more threads active.
For the code, the only answer that anyone could give you are just "guessing". You better debug it yourself.
The easiest way to hit a break-point in a Windows Service is to put this line of code at the beginning of the OnStart method:
Debugger.Break();
Compile your service in Debug mode, so you can have all the necessary symbols in your executable.
Install your service
Start it from the service list.
You will get a prompt for debugging the "yourservicename.exe" program.
Say Yes-debug, Choose debugger.
Choose the correct Visual Studio version as your debugger.
Now you will be in the Debugger.Break line
Have fun
I am trying to automate multiple parallel instances of Office InfoPath 2010 via a windows service. I understand automating Office from a service is not supported however it is a requirement of my customer.
I can automate other Office applications in a parallel fashion, however InfoPath behaves differently.
What I have found is that there will only ever be one instance of the INFOPATH.EXE process created, no matter how many parallel calls to CreateObject("InfoPath.Application") are made. In contrast to this, multiple instances of WINWORD.EXE can be created via the similar mechanism CreateObject("Word.Application")
To reproduce this issue, a simple console application can be used.
static void Main(string[] args) {
// Create two instances of word in parallel
ThreadPool.QueueUserWorkItem(Word1);
ThreadPool.QueueUserWorkItem(Word2);
System.Threading.Thread.Sleep(5000);
// Attempt to create two instances of infopath in parallel
ThreadPool.QueueUserWorkItem(InfoPath1);
ThreadPool.QueueUserWorkItem(InfoPath2);
}
static void Word1(object context) {
OfficeInterop.WordTest word = new OfficeInterop.WordTest();
word.Test();
}
static void Word2(object context) {
OfficeInterop.WordTest word = new OfficeInterop.WordTest();
word.Test();
}
static void InfoPath1(object context) {
OfficeInterop.InfoPathTest infoPath = new OfficeInterop.InfoPathTest();
infoPath.Test();
}
static void InfoPath2(object context) {
OfficeInterop.InfoPathTest infoPath = new OfficeInterop.InfoPathTest();
infoPath.Test();
}
The InfoPathTest and WordTest classes (VB) are in another project.
Public Class InfoPathTest
Public Sub Test()
Dim ip As Microsoft.Office.Interop.InfoPath.Application
ip = CreateObject("InfoPath.Application")
System.Threading.Thread.Sleep(5000)
ip.Quit(False)
End Sub
End Class
Public Class WordTest
Public Sub Test()
Dim app As Microsoft.Office.Interop.Word.Application
app = CreateObject("Word.Application")
System.Threading.Thread.Sleep(5000)
app.Quit(False)
End Sub
End Class
The interop classes simply create the automation objects, sleep and then quit (although in the case of Word, I have completed more complex tests).
When running the console app, I can see (via Task Manager) two WINWORD.EXE processes created in parallel, and only a single INFOPATH.EXE process created. In fact when the first instance of InfoPathTest calls ip.Quit, the INFOPATH.EXE process terminates. When the second instance of InfoPathTest calls ip.Quit, a DCOM timeout exception is thrown - it appears as though the two instances were sharing the same underlying automation object, and that object no longer exists after the first call to ip.Quit.
At this stage my thoughts were only a single INFOPATH.EXE is supported per user login. I expanded the windows service to start two new processes (a console application called InfoPathTest), each running under a different user account. These new processes would then attempt to automate INFOPATH.EXE
Here's where it gets interesting, this actually works, but only on some machines, and I cannot figure out why that is the case.
And the service code (with help from AsproLock):
public partial class InfoPathService : ServiceBase {
private Thread _mainThread;
private bool isStopping = false;
public InfoPathService() {
InitializeComponent();
}
protected override void OnStart(string[] args) {
if (_mainThread == null || _mainThread.IsAlive == false) {
_mainThread = new Thread(ProcessController);
_mainThread.Start();
}
}
protected override void OnStop() {
isStopping = true;
}
public void ProcessController() {
while (isStopping == false) {
try {
IntPtr hWinSta = GetProcessWindowStation();
WindowStationSecurity ws = new WindowStationSecurity(hWinSta, System.Security.AccessControl.AccessControlSections.Access);
ws.AddAccessRule(new WindowStationAccessRule("user1", WindowStationRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow));
ws.AddAccessRule(new WindowStationAccessRule("user2", WindowStationRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow));
ws.AcceptChanges();
IntPtr hDesk = GetThreadDesktop(GetCurrentThreadId());
DesktopSecurity ds = new DesktopSecurity(hDesk, System.Security.AccessControl.AccessControlSections.Access);
ds.AddAccessRule(new DesktopAccessRule("user1", DesktopRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow));
ds.AddAccessRule(new DesktopAccessRule("user2", DesktopRights.AllAccess, System.Security.AccessControl.AccessControlType.Allow));
ds.AcceptChanges();
ThreadPool.QueueUserWorkItem(Process1);
ThreadPool.QueueUserWorkItem(Process2);
} catch (Exception ex) {
System.Diagnostics.Debug.WriteLine(String.Format("{0}: Process Controller Error {1}", System.Threading.Thread.CurrentThread.ManagedThreadId, ex.Message));
}
Thread.Sleep(15000);
}
}
private static void Process1(object context) {
SecureString pwd2;
Process process2 = new Process();
process2.StartInfo.FileName = #"c:\debug\InfoPathTest.exe";
process2.StartInfo.UseShellExecute = false;
process2.StartInfo.LoadUserProfile = true;
process2.StartInfo.WorkingDirectory = #"C:\debug\";
process2.StartInfo.Domain = "DEV01";
pwd2 = new SecureString(); foreach (char c in "password") { pwd2.AppendChar(c); };
process2.StartInfo.Password = pwd2;
process2.StartInfo.UserName = "user1";
process2.Start();
process2.WaitForExit();
}
private static void Process2(object context) {
SecureString pwd2;
Process process2 = new Process();
process2.StartInfo.FileName = #"c:\debug\InfoPathTest.exe";
process2.StartInfo.UseShellExecute = false;
process2.StartInfo.LoadUserProfile = true;
process2.StartInfo.WorkingDirectory = #"C:\debug\";
process2.StartInfo.Domain = "DEV01";
pwd2 = new SecureString(); foreach (char c in "password") { pwd2.AppendChar(c); };
process2.StartInfo.Password = pwd2;
process2.StartInfo.UserName = "user2";
process2.Start();
process2.WaitForExit();
}
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetProcessWindowStation();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetThreadDesktop(int dwThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern int GetCurrentThreadId();
}
The InfoPathTest.exe process simply calls the InfoPathTest.Test() method detailed above.
In summary, this works, but only on certain machines. When it fails, the second INFOPATH.EXE process is actually created, but immediately quits with an exitcode of 0. There is nothing in the event logs, nor any exceptions in the code.
I've looked at many things to try and differentiate between working / non working machines, but I'm now stuck.
Any pointers appreciated, especially if you have other thoughts on how to automate multiple InfoPath instances in parallel.
I'm guessing you'd get similar behavior if you tried to do the same thing with Outlook, which would mean Microsoft thinks it is a bad idea to run multiple copies.
If that is so, I see two options.
Option one is to make your Infopath automation synchronous, running one instance at a time.
Option two, and I have NO idea if it would even work, would be to see if you can launch virtual machines to accomplish youe InfoPath work.
I hope this can at least spark some new train of though that will lead to success.
I’ve encountered a very similar issue with Outlook. The restriction of allowing only a single instance of the application to be running does not apply per user, but per interactive login session. You may read more about it in Investigating Outlook's Single-Instance Restriction:
Outlook was determining whether or not another instance was already running in the interactive login session. […] During Outlook's initialization, it checks to see if a window named "Microsoft Outlook" with class name "mspim_wnd32" exists, and if so, it assumes that another instance is already running.
There are ways of hacking around it – there is a tool for launching multiple Outlook instances on the Hammer of God site (scroll down) – but they will probably involve intercepting Win32 calls.
As for your code only working on certain machines: That’s probably due to a race condition. If both processes manage to start up fast enough simultaneously, then they won’t detect each other’s window, and assume that they’re the only instance running. However, if the machine is slow, one process would open its window before the other, thereby causing the second process to detect the first process’s window and shut itself down. To reproduce, try introducing a delay of several seconds between launching the first process and the second – this way, only the first process should ever succeed.
I created a utility to lock files lockfile.exe, which accepts two parameters: fileName (file to be locked) and fileShare (share to be applied). I then call this utility from within another application to test error conditions when processing locked files. The lockfile.exe utility locks a file with the specified share and then waits for any character input to release the lock and exit.
In my test application I created a class FileLocker with two methods LockFile and UnlockFile as follows:
public class FileLocker
{
private Process process;
public void LockFile(string fileName)
{
this.process = new Process();
this.process.StartInfo.RedirectStandardInput = true;
this.process.StartInfo.UseShellExecute = false;
this.process.StartInfo.CreateNoWindow = true;
this.process.StartInfo.FileName = "lockfile.exe";
this.process.StartInfo.Arguments =
"\"" + fileName + "\" " + FileShare.Read.ToString();
this.process.Start();
}
public void UnlockFile()
{
this.process.StandardInput.Write('X');
this.process.StandardInput.Flush();
this.process.WaitForExit();
this.process.Close();
}
}
When I call the method LockFile the process immediately exits after starting. It does not wait for the input in UnlockFile. When I do not redirect the standard input, then the process waits for keyboard input.
What do I need to change/fix, so that the process does not immediately exit. I need it to wait for the input provided in UnlockFile and only then should the process exit?
Update:
Updated to show FileLocker class above and to provide sample calls below:
class Program
{
static void Main(string[] args)
{
FileLocker locker = new FileLocker();
locker.LockFile("HelloWorld.txt");
locker.UnlockFile();
}
}
The problem is likely that you are using Console.ReadKey() in your lockfile.exe application. This does not accept input from standard input but uses low-level keyboard hooks to check for key presses which you cannot send from another application.
I tested your program with a LockFile.exe that just contained a simple Console.ReadLine(), and it works just as expected if I change the Write('X') call to a WriteLine("X") (since the call to readline needs a newline before returning). So I'm suspecting your problem is in the lockfile.exe program rather than this one. Ensure you are using Console.ReadLine() to wait for input from standard input in your lockfile.exe application.
I am trying towrite a simple program that has two methods, one that starts a process and one that takes down the same process.
as in:
public Process StartProc(string procname)
{
Process proc = new Process();
proc.StartInfo.FileName = procname;
proc.Start();
return proc;
}
public void StopProc(Process proc)
{
proc.Close();
}
Is it possible to do this like that?
Yes, the method you are after is called Kill, not Close:
public void StopProc(Process proc)
{
proc.Kill();
}
This will forcibly close the process - when possible it is preferable to signal the application to close such as by requesting that the application close the main window:
public void StopProc(Process proc)
{
proc.CloseMainWindow();
}
This allows the application to perform clean-up logic (such as saving files), however may allow the process to continue running if it chooses to ignore the request and will do nothing if the process does not have a main window (for example with a console application).
For more information see the documentation on the Process.CloseMainWindow method.
I think you are looking for Process.Kill().
You don't really need a StopProc() method, you can just write proc.Kill() directly.
However, it is not generally recommended that you terminate processes in such a brutal way. Doing so can leave shared objects in an undefined state. If you can find a way to co-operatively close the process that is to be preferred.
By starting the process, you can get the unique Id of that process and then you can kill it like this:
public static int Start(string processName)
{
var process =
Process.Start(processName);
return
process.Id;
}
public static void Stop(int processId)
{
var process =
Process.GetProcessById(processId);
process.Kill();
}