How to change name of console app as displayed in task manager? - c#

I am programmatically launching console applications. They all appear as conhost.exe in Task Manager.
How can I set a process name such that it appears as such in the task manager?
Example of how I'm setting up my process:
public static Process CreateMultiCoreExeProcess(int coreIndex, int coresToUse, string executableFilepath)
{
//* Create your Process
Process process = new Process
{
StartInfo =
{
FileName = executableFilepath,
Arguments = coreIndex + " " + coresToUse,
UseShellExecute = false,
CreateNoWindow = true,
RedirectStandardOutput = true,
RedirectStandardError = true
},
EnableRaisingEvents = true
};
return process;
}
...as you can see there isn't an attribute that sets the process name (that I know of). I would like to be able to set the name of the process at the time that I create it.
DUPLICATE QUESTION REMARK: this is not a duplicate as attempted. The solution presented as a duplicate is for a STATIC name for the process, being set within project properties. I am running my process DYNAMICALLY (at run time) and would like to set the process name at that time.
UPDATE: I am convinced that this isn't possible. At least not in a realistic (i.e. permanent) way. I'm fairly certain we are "stuck" with the name as provided in the assembly information and it can't be programmatically changed in the Task Manager. This is unfortunate for my application, which is to spawn 10+ identical process executables. Sometimes I want to kill just one of them, but I can't determine which-is-which in the task manager because they all have the same name.

Use the line:
Console.Title = "My Console Application";
to set the console name
And to set the name/description in task manager, it's been answered before quite a few times. Check this link How can I set the Task Manager description for my program?

You can use P/Invoke to change the process name after creating it. (As you cannot specify it when you create it. The default name is the name specified in the program.)
[DllImport("user32.dll")]
static extern int SetWindowText(IntPtr hWnd, string text);
...
SetWindowText(process.MainWindowHandle, "Process Name/Title");
Edit: Looking around on the net, it appears you may need to wait 100ms or so before setting the name (either through Thread.Sleep or process.WaitForInputIdle in the case of a GUI application).

Related

Start and manage external process from a WPF app

I have a WPF application and I need to spin up a separate MFC application and then communicate with it. I was going to use Process.Start, but I'm wondering if there is a better way to do this these days. I can research things myself, but I need to know where to start. Thanks.
Edits:
I found this suggesting there isn't. Is this true?
Alternatives to System.Diagnostics.Process.Start()
Thanks.
For your immediate question, there is nothing new in the recent versions of .NET that gives a better or more up-to-date way to start a local executable. Process.Start is (and has been) the way to go.
The simplest, and most convenient, is to select one of the five static methods on Process. Passing strings or a populated StartInfo instance. You would use the latter if you needed more control over how the process got raised. Or of interest in your case, if you wanted to pipe the program's stdio as a stream into your own application. Here's a sample of populating a Start Info instance from one of my utilities...
ProcessStartInfo start = new ProcessStartInfo(BaseIoConstantsProvider.CommandProcessor)
{
Arguments = BaseIoConstantsProvider.KeepAlive,
UseShellExecute = false,
CreateNoWindow = BaseIoConstantsProvider.NoDosWindow,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true,
WindowStyle = ProcessWindowStyle.Hidden,
};
For the second part of your question, the static method will not do if you need to interact with the process once it is started. From the same utility...
Process p = new Process { StartInfo = start, EnableRaisingEvents = true };
p.ErrorDataReceived += PErrorDataReceived;
p.Exited += PExited;
p.Start();
p.StandardInput.AutoFlush = true;
p.StandardInput.WriteLine(cmdLine);
p.BeginOutputReadLine();
This example shows a two events being hooked along with reading the stdio from the process. It's great for that purpose, but overkill if you just want to start another executable.
So the main determinate in selecting a start method is the question: does my app need to interact with the process once it's started?
And finally, sometimes you may want to invoke a canonical verb, or even create a verb of your own to start a given process. These appear in the context menu when you right click an item and give you lots of additional flexibility for starting a process. There's an excellent article here http://msdn.microsoft.com/en-us/library/windows/desktop/cc144101(v=vs.85).aspx#canonical on how to implement a verb.

Running a Windows Scheduled Task from ASP.net

I have a Windows scheduled task that runs a database import process every hour, but I'd like users to be able to kick it off out-of-schedule by hitting a button in an ASP.net dashboard (running in IIS6 on Windows Server 2003).
The following works perfectly in code-behind ...
var proc = new Process
{
StartInfo =
{
UseShellExecute = false,
FileName = #"C:\Windows\System32\schtasks.exe",
Arguments = "/run /tn Loader",
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
proc.WaitForExit();
... but only if the application pool identity is set to Local System (not ideal!). If I leave it as Network Service, the task does not start. So it is presumably a permissions issue.
Is there a way ASP.net can kick off a scheduled task on the server without running the application as Local System? If not, what good alternatives are there?
Update: if nobody on SO knows, I guess it is not possible so I will go with my idea of having my web application write requests to a database table (doubling as an audit log) and creating a second task to poll that table and kick off the main task.
Update your schedule task to trigger off a specific event. Then have your website log that event when the button is clicked - thus starting your scheduled task.
Ex:
In my installer I create an event log source for my program, since creating the source requires administrative privileges (you can also use the command line to create the source)
if (EventLog.SourceExists("MyApp"))
{
EventLog.CreateEventSource("MyApp", "Application");
}
Then in my application, I create an event log entry when the button is clicked.
private void btnOpenOtherApp_Click (object sender, EventArgs e)
{
try
{
var log = new EventLog
{
Source = "MyApp"
};
log.WriteEntry("Start MyOtherApp", EventLogEntryType.Information, 1337);
}
catch (Exception ex)
{
...
}
}
And the task scheduler set to open MyOtherApp when the event is logged.
You need an administrator user in windows. This code will help you to call the task:
var securePass = new System.Security.SecureString();
foreach (char c in "my_password")
{
pass.AppendChar(c);
}
var proc = new Process
{
StartInfo =
{
UseShellExecute = false,
FileName = #"C:\Windows\System32\schtasks.exe",
Arguments = "/run /tn Loader",
UserName = "myAdminUser", //NEW
Password = securePass, //NEW
RedirectStandardError = true,
RedirectStandardOutput = true,
CreateNoWindow = true
}
};
proc.Start();
proc.WaitForExit();
If your application needs to run as SYSTEM Account you can use this arguments:
If the /RU username and /RP Password parameters match the currently
logged-in user, the task will run interactively (visible in the
foreground).
For the system account, /RU username can be written as "", "NT
AUTHORITY\SYSTEM" or "SYSTEM", a Password is not required. The system
account has full access to the local machine but has no permissions on
any other machines (or mapped drives) across the Network.
Source: http://ss64.com/nt/schtasks.html
You can utilize the cache of ASP.NET to run schedule tasks. I personally used this and it worked like charm. Ofcourse it has some limitation (Accuracy) but you can adjust/change.
https://blog.stackoverflow.com/2008/07/easy-background-tasks-in-aspnet/
Regards
If using a 3rd party scheduler service such as Quartz.NET is not an option, and changing permissions is not allowed either (not a good idea in any case) another possibility is to to write a helper Windows service as a bridge between ASP.NET application and Windows scheduler. You would store list of tasks in a DB and a sequence of events would be something like this:
When you need to run a task, from ASP.NET GUI you set flag in the DB for that task
Helper service runs on schedule to check the DB at given intervals - sees the flag, starts the task, reset the flag.
Oh and there're libraries out there (e.g. http://taskscheduler.codeplex.com/ and others) that wrap Window Task Scheduler, so you don't have to execute them directly, but rather in a nice managed way.

InvalidOperationException if the process is started as a default associated program for a file type

Situation:
I spawn processes for various file types (pictures, Word documents, etc) by relying on the default associated handler program. This means that I only specify the particular file name as the StartInfo.FileName, and no actual executable precedes that file name. At the same time I specify StartInfo.UseShellExecute = true. This way the associated software with that file type will start.
My goal is to get notified when that process exits.
Process process = new Process();
try
{
process.StartInfo.UseShellExecute = true;
process.StartInfo.FileName = pFullPath;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardError = true;
bool notReused = process.Start();
}
catch (Win32Exception ex)
Variations:
Process's static Start method does return a Process, but I get back null/empty object for my scenario. So that's why I instantiate a Process class explicitly, set parameters for its StartInfo property, and then call non-static Start() in the end, so I have a hold of that Process object instance.
If I say StartInfo.UseShellExecute = false, I get an exception, 'cause I didn't specify executable name. It seems that that's the way to invoke associated program with a file type.
The non-static Process.Start has a boolean return value, and it indicates that the system reused a Process. I don't want that, but I don't know how to avoid it.
I tried to set Redirect* booleans of StartInfo, but that throws the InvalidOperationException again, and that conforms to the documentation.
If I had a "good" Process object, I could hook up the Exited handler and set EnableRaisingEvents to true. I'm amazed and sad how such a PITA is this.
This is completely impossible.
Launching a file is not guaranteed to create a process at all.
Raymond Chen has far more detail.

Kill process tree programmatically in C#

I am starting Internet Explorer programmatically with code that looks like this:
ProcessStartInfo startInfo = new ProcessStartInfo("iexplore.exe");
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "http://www.google.com";
Process ieProcess = Process.Start(startInfo);
This generates 2 processes visible in the Windows Task Manager. Then, I attempt to kill the process with:
ieProcess.Kill();
This results in one of the processes in Task Manager being shut down, and the other remains. I tried checking for any properties that would have children processes, but found none. How can I kill the other process also? More generally, how do you kill all the processes associated with a process that you start with Process.Start?
This worked very nicely for me:
/// <summary>
/// Kill a process, and all of its children, grandchildren, etc.
/// </summary>
/// <param name="pid">Process ID.</param>
private static void KillProcessAndChildren(int pid)
{
// Cannot close 'system idle process'.
if (pid == 0)
{
return;
}
ManagementObjectSearcher searcher = new ManagementObjectSearcher
("Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection moc = searcher.Get();
foreach (ManagementObject mo in moc)
{
KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
}
try
{
Process proc = Process.GetProcessById(pid);
proc.Kill();
}
catch (ArgumentException)
{
// Process already exited.
}
}
Update 2016-04-26
Tested on Visual Studio 2015 Update 2 on Win7 x64. Still works as well now as it did 3 years ago.
Update 2017-11-14
Added check for system idle process if (pid == 0)
Update 2018-03-02
Need to add a reference to the System.Management namespace, see comment from #MinimalTech below. If you have ReSharper installed, it will offer to do this for you automatically.
Update 2018-10-10
The most common use case for this is killing any child processes that our own C# process has started.
In this case, a better solution is to use Win32 calls within C# to make any spawned process a child process. This means that when the parent process exits, any child processes are automatically closed by Windows, which eliminates the need for the code above. Please let me know if you want me to post the code.
If anyone needs a dotnet core solution,
Dotnet core 3.0
process.Kill(true);
See official documentation
Dotnet core 2.0
For .Net 2.0 dotnet cli came up with an implementation based on taskill as mentioned above and recursive pgrep/kill for unix based systems. Full implementation can be found on github. Sadly, the class is internal so you'll have to copy it into your code base.
List Child processes (has to be done recursively):
$"pgrep -P {parentId}"
Kill on process:
$"kill -TERM {processId}"
I'm not a fan of any of the solutions presented here.
Here's what I came up with:
private static void EndProcessTree(string imageName)
{
Process.Start(new ProcessStartInfo
{
FileName = "taskkill",
Arguments = $"/im {imageName} /f /t",
CreateNoWindow = true,
UseShellExecute = false
}).WaitForExit();
}
How to use:
EndProcessTree("chrome.exe");
Process Class (System.Diagnostics)
ProcessStartInfo Class (System.Diagnostics)
Taskkill
You should call Process.CloseMainWindow() which will send a message to the main window of the process. Think of it as having the user click the "X" close button or File | Exit menu item.
It is safer to send a message to Internet Explorer to close itself down, than go and kill all its processes. Those processes could be doing anything and you need to let IE do its thing and finish before just killing it in the middle of doing something that may be important for future runs. This goes true for any program you kill.
If anyone is interested, I took one of the answers from the other page and modified it slightly. It is a self contained class now with static methods. It does not have proper error handling or logging. Modify to use for your own needs. Providing your root Process to KillProcessTree will do it.
class ProcessUtilities
{
public static void KillProcessTree(Process root)
{
if (root != null)
{
var list = new List<Process>();
GetProcessAndChildren(Process.GetProcesses(), root, list, 1);
foreach (Process p in list)
{
try
{
p.Kill();
}
catch (Exception ex)
{
//Log error?
}
}
}
}
private static int GetParentProcessId(Process p)
{
int parentId = 0;
try
{
ManagementObject mo = new ManagementObject("win32_process.handle='" + p.Id + "'");
mo.Get();
parentId = Convert.ToInt32(mo["ParentProcessId"]);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
parentId = 0;
}
return parentId;
}
private static void GetProcessAndChildren(Process[] plist, Process parent, List<Process> output, int indent)
{
foreach (Process p in plist)
{
if (GetParentProcessId(p) == parent.Id)
{
GetProcessAndChildren(plist, p, output, indent + 1);
}
}
output.Add(parent);
}
}
Another solution is to use the taskill command. I use the next code in my applications:
public static void Kill()
{
try
{
ProcessStartInfo processStartInfo = new ProcessStartInfo("taskkill", "/F /T /IM your_parent_process_to_kill.exe")
{
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
UseShellExecute = false,
WorkingDirectory = System.AppDomain.CurrentDomain.BaseDirectory,
RedirectStandardOutput = true,
RedirectStandardError = true
};
Process.Start(processStartInfo);
}
catch { }
}
Are you using IE8 or IE9? That would absolutely start more than one process due to its new multi-process architecture. Anyway, have a look at this other answer for getting a process tree and killing it.
Another approach that can be very useful is using the Windows API for Job Objects. A process can be assigned to a job object. The child processes of such a process are automatically assigned to the same job object.
All processes assigned to a job object can be killed at once e.g. with TerminateJobObject which:
Terminates all processes currently associated with the job.
The C# example in this answer (based on this answer) uses the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag instead, which:
Causes all processes associated with the job to terminate when the last handle to the job is closed.
With .NET Core 3.0 there is a method just for that, namely new overload of the already existing Process.Kill() method. IOW, doing process.Kill(true) on the variable process of type Process kills the process with all its descendants. This is cross-platform, naturally.
As per documentation
The Kill method executes asynchronously. After calling the Kill method, call the WaitForExit method to wait for the process to exit, or check the HasExited property to determine if the process has exited.
ProcessStartInfo startInfo = new ProcessStartInfo("iexplore.exe");
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "http://www.google.com";
Process ieProcess = Process.Start(startInfo);
ieProcess.Kill();
ieProcess.WaitForExit();
How to properly close Internet Explorer when launched from PowerShell?
Several of those commented in the above thread that this is caused by a bug in Win7 (as it does not seem to occur for users that are using other versions of windows). Many pages on the internet, including microsoft's page claim user error, and tell you to simply use the available quit method on the IE object which is SUPPOSED to close all child processes as well (and reportedly does in Win8/XP etc)
I must admit, for my part, it WAS user error. I am in win7 and the reason the quit method was not working for me was because of an error in coding. Namely I was creating the IE object at declaration, and then creating another (attached to the same object) later on in the code... I had almost finished hacking the parent-child killing routine to work for me when I realized the issue.
Because of how IE functions, the processID you spawned as the parent could be attached to other windows/subprocesses that you did NOT create. Use quit, and keep in mind that depending on user settings (like empty cache on exit) it could take a few minutes for the processes to finish their tasks and close.

How do I find out if a process is already running using c#?

I have C# winforms application that needs to start an external exe from time to time, but I do not wish to start another process if one is already running, but rather switch to it.
So how in C# would I so this in the example below?
using System.Diagnostics;
...
Process foo = new Process();
foo.StartInfo.FileName = #"C:\bar\foo.exe";
foo.StartInfo.Arguments = "Username Password";
bool isRunning = //TODO: Check to see if process foo.exe is already running
if (isRunning)
{
//TODO: Switch to foo.exe process
}
else
{
foo.Start();
}
This should do it for ya.
Check Processes
//Namespaces we need to use
using System.Diagnostics;
public bool IsProcessOpen(string name)
{
//here we're going to get a list of all running processes on
//the computer
foreach (Process clsProcess in Process.GetProcesses()) {
//now we're going to see if any of the running processes
//match the currently running processes. Be sure to not
//add the .exe to the name you provide, i.e: NOTEPAD,
//not NOTEPAD.EXE or false is always returned even if
//notepad is running.
//Remember, if you have the process running more than once,
//say IE open 4 times the loop thr way it is now will close all 4,
//if you want it to just close the first one it finds
//then add a return; after the Kill
if (clsProcess.ProcessName.Contains(name))
{
//if the process is found to be running then we
//return a true
return true;
}
}
//otherwise we return a false
return false;
}
You can use LINQ as well,
var processExists = Process.GetProcesses().Any(p => p.ProcessName.Contains("<your process name>"));
I have used the AppActivate function in VB runtime to activate an existing process.
You will have to import Microsoft.VisualBasic dll into the C# project.
using System;
using System.Diagnostics;
using Microsoft.VisualBasic;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Process[] proc = Process.GetProcessesByName("notepad");
Interaction.AppActivate(proc[0].MainWindowTitle);
}
}
}
You can simply enumerate processes using Process.GetProcesses method.
I found out that Mutex is not working like in the Console application. So using WMI to query processes that can be seen using Task Manager window will solved your problem.
Use something like this:
static bool isStillRunning() {
string processName = Process.GetCurrentProcess().MainModule.ModuleName;
ManagementObjectSearcher mos = new ManagementObjectSearcher();
mos.Query.QueryString = #"SELECT * FROM Win32_Process WHERE Name = '" + processName + #"'";
if (mos.Get().Count > 1)
{
return true;
}
else
return false;
}
NOTE: Add assembly reference "System.Management" to enable the type intellisense.
I think the complete answer to your problem requires understanding of what happens when your application determines that an instance of foo.exe is already running i.e what does '//TODO: Switch to foo.exe process' actually mean?
In a past project I needed to prevent multiple execution of a process, so I added a some code in the init section of that process which creates a named mutex. This mutext was created and acquired before continuing the rest of the process. If the process can create the mutex and acquire it, then it is the first one running. If another process already controls the mutex, then the one which fails is not the first so it exits immediately.
I was just trying to prevent a second instance from running, due to dependencies on specific hardware interfaces. Depending on what you need with that "switch to" line, you might need a more specific solution such as a process id or handle.
Also, I had source code access to the process I was trying to start. If you can not modify the code, adding the mutex is obviously not an option.
Two concerns to keep in mind:
Your example involved placing a
password on a command line. That
cleartext representation of a secret
could be a security vulnerability.
When enumerating processes, ask
yourself which processes you really
want to enumerate. All users, or
just the current user? What if the
current user is logged in twice (two
desktops)?
Mnebuerquo wrote:
Also, I had source code access to the
process I was trying to start. If you
can not modify the code, adding the
mutex is obviously not an option.
I don't have source code access to the process I want to run.
I have ended up using the proccess MainWindowHandle to switch to the process once I have found it is alread running:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetForegroundWindow(IntPtr hWnd);

Categories

Resources