Set a process name in C# - c#

I write an application that starts a 3rd party program that downloads a media stream from a tv channel on my program. The program I am using is rtmpdump.exe. They run as independent processes like this:
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = path + rtmpdump;
startInfo.Arguments = rtmpdump_argument;
startInfo.WorkingDirectory = path;
startInfo.WindowStyle = ProcessWindowStyle.Minimized;
Process p = Process.Start(startInfo);
But I would like to name these processes, so if my program crashes or has to be restarted, then I can check the names of the current processes to see if for example there is one called "BBC World - News at 7". Something like that to identify which has already been started and is currently running. Is it possible? I can't find a way to set a friendly name.

Extending what Alexei said - you could create a Dictionary< int, string > to keep track of the process ID / descriptive name that you create. Maybe you should write this out to a file in case your program crashes - but you'd need some special startup handling to deal with processes exiting.
On startup you'd want to read in the file, and check current processes to see if they match with what you have, and remove any processes that no longer exist (wrong process id or exe name). You might want to do that every time you create a new process and write to the file.

You can't change process names, but instead you can:
use Process.Id to identify processes (Id: "...system-generated unique identifier of the process...")
get process command line and see if there anything interesting (may need some PInvoke / WMI for that - How to read command line arguments of another process in C#?).

Here is some code that you could use.
public class ProcessTracker {
public Dictionary<int, string> Processes { get; set; }
public ProcessTracker() {
Processes = new Dictionary<int, string>();
}
public void AddProcess(Process process, string name) {
Processes.Add(process.Id, name);
}
//Check if what processes are still open after crash.
public void UpdateProcesses() {
List<Process> runningProcesses =
Process.GetProcesses().ToList();
Processes = Processes
.Where(pair => runningProcesses
.Any(process => process.Id == pair.Key))
.ToDictionary(pair => pair.Key, pair => pair.Value);
}
//Use this to see if you have to restart a process.
public bool HasProcess(string name) {
return Processes.Any(pair => pair.Value != name);
}
//Write the file on crash.
public void ReadFile(string path) {
if (!(new FileInfo(path).Exists))
return;
using (StreamReader reader = new StreamReader(path)) {
foreach (string line in reader.ReadToEnd()
.Split(new[] {"\n"}, StringSplitOptions.RemoveEmptyEntries)) {
string[] keyPair = line.Split(',');
Processes.Add(int.Parse(keyPair[0]), keyPair[1]);
}
}
}
//Read the file on startup.
public void SaveFile(string path) {
using (StreamWriter writer = new StreamWriter(path, false)) {
foreach (KeyValuePair<int, string> process in Processes) {
writer.WriteLine("{0},{1}",
process.Key, process.Value);
}
}
}
}

Writing the process information to a file (or a memory mapped file) might be the way to go;
However, you could also look into windows messaging. i.e. send a message to each process; and have them reply with their "internal name".
Basically you have everything listen in on that queue; You first send a command that orders the other process to report their internal name, which they then dump into the message queue along with their own process id;
See MSMQ

I had the same question to myself, for my solution created a new class -
MyProcess which inherits from Process and added a property - ProcessId.
In my case I store the ProcessId on a db which is acossiated to other data. Based on the status of the other data I decide to kill the process.
Then I store process on a dictionary which can be passed to other classes if needed where can be access to kill a process.
So when I decide to kill a process, I pull the process from the dictionary by ProcessId and then kill it.
Sample below:
public class Program
{
private static Dictionary<string, MyProcess> _processes;
private static void Main()
{
// can store this to file or db
var processId = Guid.NewGuid().ToString();
var myProcess = new MyProcess
{
StartInfo = { FileName = #"C:\HelloWorld.exe" },
ProcessId = processId
};
_processes = new Dictionary<string, MyProcess> {{processId, myProcess}};
myProcess.Start();
Thread.Sleep(5000);
// read id from file or db or another
var pr = _processes[processId];
pr.Kill();
Console.ReadKey();
}
}
public class MyProcess : Process
{
public string ProcessId { get; set; }
}

Related

How to Get Running Application with C# Windows Service

I've build a Windows service with C# which get all running applications on my computer (Notepad,...). I've tryed this following code but it doesn't work :
Thank you all for your help!
Process[] processes = Process.GetProcesses();
using (TextWriter tw = new StreamWriter(#"C:\Users\Public\Documents\Info.txt"))
{
foreach(Process p in processes)
{
if(!String.IsNullOrEmpty(p.MainWindowTitle))
{
tw.WriteLine(p.MainWindowTitle);
}
}
}
That's why you are checking MainWindowTitle on your code. You have to know MainWindowTitle has value just for those processes which they have GUI like notepad or skype or ...
To identify all the process name it's better to check ProcessName property to get the process name.
So I recommend you to change your code like this:
using (TextWriter tw = new StreamWriter(#"C:\Users\Public\Documents\Info.txt"))
{
foreach (Process p in processes)
{
if (!String.IsNullOrEmpty(p.ProcessName))
{
var processTitle= !string.IsNullOrEmpty(p.MainWindowTitle) ? p.MainWindowTitle: "N/A";
tw.WriteLine(string.Format("Process Name: {0} \t\t Process GUI Title:{1}",p.ProcessName, processTitle));
}
}
}

Passing parameters from one app to the other

Following on from this thread Starting application before target application
I have an application which gets passed a parameter (a filename) and does some registry work before opening Microsoft InfoPath.
I need to open InfoPath with the parameter that was passed to the original application.
Here is how I open InfoPath
System.Diagnostics.Process prc = new System.Diagnostics.Process();
prc.StartInfo.Arguments = ConvertArrayToString(Constants.Arguments);
//prc.StartInfo.Arguments = "hello";
prc.StartInfo.FileName = Constants.PathToInfoPath;
prc.Start();
Note that when I set the Arguments to "hello" InfoPath pops up a message saying cannot find file "hello" however when I set it Constants.Arguments I get an error and Windows asks me if I want to debug or close the applicatiion.
Here is how I set Constants.Arguments in the Main(string[] args)
static void Main(string[] args)
{
Constants.Arguments = args;
//...
}
And here is ConvertArrayToString
private string ConvertArrayToString(string[] arr)
{
string rtn = "";
foreach (string s in arr)
{
rtn += s;
}
return rtn;
}
I suppose the format of the parameter is causing the error, any idea why?
The value of Arguments after being stringed is
c:\users\accountname\Desktop\HSE-000403.xml
Edit:
Thanks to N K's answer.
The issue is in order for my application to open when InfoPath files are opened, I have changed the name of INFOPATH.EXE to INFOPATH0.EXE and my application is called INFOPATH.EXE and is in the InfoPath folder, so when files are opened my application opens.
Now when I do not change the name (eg I leave it as INFOPATH.EXE) it works as expected, however if it is called anything other than that then I get the error.
Unfortunately I need my application to open first.
I tried the below and it's works fine. Let me know what you get with this. (Don't forget to change path to files)
class Program
{
static void Main(string[] args)
{
System.Diagnostics.Process prc = new System.Diagnostics.Process();
prc.StartInfo.Arguments = string.Join("", Constants.Arguments);
prc.StartInfo.FileName = Constants.PathToInfoPath;
prc.Start();
}
}
public class Constants
{
public static string PathToInfoPath = #"C:\Program Files (x86)\Microsoft Office\Office14\INFOPATH.EXE";
public static string[] Arguments = new string[] { #"c:\users\accountname\Desktop\HSE-000403.xml" };
}

Kill Process Excel C#

I have to 2 process excel. For example:
1) example1.xlsx
2) example2.xlsx
How to kill first "example1.xlsx"?
I use this code:
foreach (Process clsProcess in Process.GetProcesses())
if (clsProcess.ProcessName.Equals("EXCEL")) //Process Excel?
clsProcess.Kill();
That kill a both.
I wanna kill just one...
Thank you.
The ProcessMainWindow Title will do it for you, it appends "Microsoft Excel - " to the name of the file:
So essentially (quick code):
private void KillSpecificExcelFileProcess(string excelFileName)
{
var processes = from p in Process.GetProcessesByName("EXCEL")
select p;
foreach (var process in processes)
{
if (process.MainWindowTitle == "Microsoft Excel - " + excelFileName)
process.Kill();
}
}
Use:
KillSpecificExcelFileProcess("example1.xlsx");
Edit: Tested and verified to work.
kd7's post is an awesome answer and works well, just two things to add,
MainWindowTitle format is - "Filename.xlsx - Excel"
If your excel document is not visible then your MainWindowTitle will be ""
using the "" for MainWindowTitle will kill all zombie excel process'.
If your current code is working, this amendment should kill the first process it finds with the name "EXCEL".
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Equals("EXCEL"))
{
clsProcess.Kill();
break;
}
}
If you want to kill a specific process, you're going to have to give a bit more information.
Excel will always be a single process, AFAIK. The same process/windows opens multiple documents inside it. What you want to do is use Excel automation to CLOSE the document you want to. Perhaps this will get you started. http://support.microsoft.com/kb/302084
Hope this helps.
Copy and paste this. Its done!
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("Excel");
foreach (System.Diagnostics.Process p in process)
{
if (!string.IsNullOrEmpty(p.ProcessName))
{
try
{
p.Kill();
}
catch { }
}
}
You need to check file handles, that are opened by process and then kill it.
How to check which file handles process is holding: How do I get the list of open file handles by process in C#?
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Equals("EXCEL") && HasFileHandle(fileName, clsProcess))
{
clsProcess.Kill();
break;
}
}
Try getting the main window title
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.Equals("EXCEL")&& clsProcess.MainWindowTitle =="example")
{
clsProcess.CloseMainWindow();
break;
}
}
just did a quick search on Google, try Process.MainWindowTitle() to get the title of the Excel process, and decide which one is that you want to kill.
I am not sure about this method, but hope this will help:
http://msdn.microsoft.com/en-us/library/system.diagnostics.process.mainwindowtitle.aspx
Use below logic to prevent Zombie Excel processes in Task Manager
List<int> GetAllExcelProcessID()
{
List<int> ProcessID = new List<int>();
if (currentExcelProcessID == -1)
{
List<System.Diagnostics.Process> currentExcelProcessList = System.Diagnostics.Process.GetProcessesByName("EXCEL").ToList();
foreach(var item in currentExcelProcessList)
{
ProcessID.Add(item.Id);
}
}
return ProcessID;
}
int GetApplicationExcelProcessID(List<int> ProcessID1, List<int> ProcessID2)
{
foreach(var processid in ProcessID2)
{
if (!ProcessID1.Contains(processid)) { currentExcelProcessID = processid; }
}
return currentExcelProcessID;
}
void KillExcel()
{
System.Diagnostics.Process process = System.Diagnostics.Process.GetProcessById(currentExcelProcessID);
process.Kill();
}
List<int> ProcessID1 = GetAllExcelProcessID();
excel = new Excel.Application();
List<int> ProcessID2 = GetAllExcelProcessID();
currentExcelProcessID = GetApplicationExcelProcessID(ProcessID1, ProcessID2);
In the namespace section add this using statement.
using System.Diagnostics;
This example instantiated Excel with this:
_Application excel = new _Excel.Application();
This method kills the right Excel task by using the window handle.
public void Kill()
{
Int32 ExcelHwnd = excel.Hwnd;
Process[] localExcel = Process.GetProcessesByName("EXCEL");
foreach (Process Pgm in localExcel)
{
// xlMinimized keeps the screen from flashing when the user interface is made
// visible with the excel.visible needed to set the MainWindowHandle
excel.WindowState = XlWindowState.xlMinimized;
excel.Visible = true;
if ((Pgm.ProcessName == "EXCEL") && (ExcelHwnd == Pgm.MainWindowHandle.ToInt32()))
{
Pgm.Kill();
}
}
}
This worked without fail.

How can I capture, log and display the console output of any arbitrary process with my own c# app?

I want to run on a C# program a specific running file and during it display the output on the screen and also saving it in the file.
I don't want to save the output in the file and later display it on screen.
I want them both to happen together.
I know a way to do it by "tee" but I failed each time I tried doing so.
Can anyone give me an example (that works) by using "tee"?
The main question is, do you have control over the source code of the program whose output you want logged and displayed?
If so, then you have a couple options. Probably the easiest would be to "hijack" the Console's output stream with a compound TextWriter:
public class CompoundWriter:TextWriter
{
public readonly List<TextWriter> Writers = new List<TextWriter>();
public override void WriteLine(string line)
{
if(Writers.Any())
foreach(var writer in Writers)
writer.WriteLine(line);
}
//override other TextWriter methods as necessary
}
...
//When the program starts, get the default Console output stream
var consoleOut = Console.Out;
//Then replace it with a Compound writer set up with a file writer and the normal Console out.
var compoundWriter = new CompoundWriter();
compoundWriter.Writers.Add(consoleOut);
compoundWriter.Writers.Add(new TextWriter("c:\temp\myLogFile.txt");
Console.SetOut(compoundWriter);
//From now on, any calls to Console's Write methods will go to your CompoundWriter,
//which will send them to the console and the file.
You can also use the Trace listeners to handle any output you want to go to both places:
Trace.Listeners.Clear();
Trace.Listeners.Add(new TextWriterTraceListener(Console.Out));
Trace.Listeners.Add(new TextWriterTraceListener(File.Open("C:\temp\myLogFile.txt");
//replace any call to Console.WriteLine() with Trace.WriteLine()
if you do NOT have control over the source code of the console app you want to "tee", and the console app does not require any input in the middle of its execution, then you can use a named pipe to get the output of the app and redirect it.
var appLocation = #"C:\temp\myApp.exe";
var pipeName = "ConsoleNamedPipe";
using(var namedPipe = new NamedPipeServerStream(pipeName, PipeDirection.In))
{
var info = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = String.Format(#"/C {1} >>\\.\pipe\{0}",
pipeName, appLocation),
WindowStyle = ProcessWindowStyle.Hidden
};
ConsoleProcess = Process.Start(info);
pipe.WaitForConnection();
using (var reader = new StreamReader(pipe))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
Console.WriteLine(line);
myFileWriter.WriteLine(line);
}
}
}
This is a very simple example; you can provide more interaction by using a two-way pipe but it will require quite a bit more code on your end.
You can try the following:
C:\you_csharp_program.exe arg1 arg2 arg3 |tee filename
I'm not going to write out specific code examples but I will tell you that you can look at logging frameworks like log4net which has console and file appenders which will do what you want. You cant wrong log statements in your code Log.Debug("some message") setup the log4net config to use any number of appenders you want and have it write the message to all of the sources at once, so for example screen, file, db, and email you all at the same time.
I seem to have missed the last sentence of the question about making it work with Tee so my answer may not be valid.
Elaborating on KeithS's answer, this is a working implementation. It should work for all Write/WriteLine calls without having to override every overload because the current (4.0, and I assume earlier) implementation of TextWriter directs all writes through Write(char).
public class TeeTextWriter : TextWriter
{
readonly TextWriter[] _redirectTo;
public override Encoding Encoding { get { return Encoding.UTF8; } }
public TeeTextWriter(params TextWriter[] redirectTo)
{
_redirectTo = redirectTo ?? new TextWriter[0];
}
public override void Write(char value)
{
foreach (var textWriter in _redirectTo)
{
textWriter.Write(value);
}
}
}
Usage:
var realConsoleStream = Console.Out;
using (var fileOut = new StreamWriter(outFileName, false))
{
Console.SetOut(new TeeTextWriter(fileOut, realConsoleStream));
try
{
Console.WriteLine("Test");
}
finally
{
Console.SetOut(realConsoleStream);
}
}

Re-Parse response from command line

Im encoding a video using a command line app. The app returns a line which says:
% complete : 34%
This is updated as the media encodes. Is there a way using the process class to keep checking the standard output and passing it back to the main execution script? I have a class that starts the process and then writes the standard output to stringbuilder but I want to know how to keep checking it. This is the curent code...
public static Dictionary<string, string> StartProcess(string exePathArg, string argumentsArg, int timeToWaitForProcessToExit)
{
//the dictionary with the
Dictionary<string, string> retDirects = new Dictionary<string, string>();
using (Process p = new Process())
{
p.StartInfo.FileName = exePathArg;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.Arguments = argumentsArg;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
try
{
p.Start();
p.WaitForExit(timeToWaitForProcessToExit);
int exitCode;
try
{
exitCode = p.ExitCode;
StreamReader standardOutput = p.StandardOutput;
StreamReader standardError = p.StandardError;
retDirects.Add("StandardOutput", standardOutput.ReadToEnd());
retDirects.Add("StandardError", standardError.ReadToEnd());
}
catch { }
}
catch { }
finally
{
try
{
p.Kill();
p.CloseMainWindow();
}
catch { }
}
}
return retDirects;
}
You can use the Process.BeginOutputReadLine to initiate the firing of the Process.OutputDataRecieved event. UseShellExecute must be false and Redirect<StreamOfChoice>Output must be true, as in your example code.
There is an example on MSDN which I won't regurgitate here. I've noticed that some programs use the different streams for what I thought were unexpected purposes so it may be appropriate to use the same handler for events from the different streams.
Instead of using "ReadToEnd", use "Read" of a few bytes (even one byte at time) in a loop. Read will block until it reads the number of bytes you specified. Find the correct number of bytes, and you should be able to read the strings from the standard output.

Categories