I'm trying to get the exit code of my wpf application called in a Powershell script.
My main in WPF :
[STAThread]
public static int Main(string[] args)
{
if (args != null && args.Length > 0)
{
NB_ERRORS = AutomaticTests.Program.Main(args);
return NB_ERRORS;
//Application.Current.Shutdown(NB_ERRORS);
}
else
{
App app = new App();
app.Run(new MainWindow());
//Application.Current.Shutdown(NB_ERRORS);
return NB_ERRORS;
}
}
And in powershell I call it like this :
& $PathToExeTests 0 $LogTestsStagging
$nbFailed = $LASTEXITCODE
But it always contains 0.
I've tried to manually set the Environment.ExitCode, to shutdown the application with the code, to override OnExit like this :
protected override void OnExit(ExitEventArgs e)
{
e.ApplicationExitCode = AutomaticTests.GUI.Program.NB_ERRORS;
base.OnExit(e);
}
But I always have 0 in the LastExitCode.
By default, when you start a GUI app, PowerShell (just like cmd.exe) will not wait for the app to exit; it'll just tell Windows to start loading the app, and then continue running the script.
There are a couple of ways to wait for a GUI app to exit.
Option 1: Start the app using Start-Process, and then pass the resulting Process object to Wait-Process. This can be easily written as a pipeline:
Start-Process $PathToExeTests -ArgumentList #(0, $LogTestsStaging) | Wait-Process
Option 2: If you do something with the app's standard output (assign it into a variable, or pipe it into another command), then PowerShell will automatically wait for the process to exit.
& $PathToExeTests 0 $LogTestsStaging | Out-Null
Option 1 is probably going to be a lot more readable if someone else is ever going to maintain your code, but occasionally you'll see option 2 as well.
Related
In a WPF C# app, users can launch the "explorer.exe" process from a given menu.
This is achieved as usual, with
Process.Start("explorer.exe");
However, I need to restrict the explorer quantity of simultaneous processes to one instance, instead of as many instances as the user starts by clicking on a button.
So the usual way is to count how many instance of the given process, "explorer.exe" are actually running and if there is more than one, then block the Process.Start().
The issue is that I'm stucked in the counting function. here is what I wrote:
static bool CountProcess(string name) {
return false; // by defualt it returns false.
int counter = 0;
while(true) {
counter = Process.GetProcessesByName(name).length; // get the quantity of processes for a given name.
if(counter > 1) {
return true;
break;
}
}
}
Then I invoke the function as this:
if(countProcess("explorer")) {
// Do nothing.
} else {
Process p = Process.Start("explorer.exe");
}
However after build and execute, the app gets stucked when opening the given process. Indeed, Visual Studio does not give any debug feedback.
How can this function be refactored to be 1) operational, 2) efficient.
Why there is while loop in CountProcess method? It should be simple if.
if(Process.GetProcessByName("explorer").Length == 0)
{
Process.Start("explorer.exe");
}
=== UPDATE ===
Ok, I'm starting to realize what is your problem.
If this wasn't explorer.exe - this code should work:
private static Process proc { get; set; }
private void button1_Click(object sender, EventArgs e)
{
if (proc == null || proc.HasExited)
{
proc = Process.Start("explorer.exe");
}
}
It checks whether Process was ever created (if first time - allow processing, if not - deny starting a new one) If he clicks for the second time, the process is not null but it SHOULD BE as proc.HasExited == false (if you didn't close it)
But if you run this code - probably starting new explorer window will be possible because this newly created process is being closed immediately. And this is because:
The reason that WaitForSingleObject returns immediately is that Explorer is a single-instance program (well, limited-instance)
You can try modifying the registry as proposed here :
Open explorer window and wait for it to close
But if this to be client application to be installed on others computer, I wouldn't advise changing programmatically someone registry.
=== UPDATE 2 ====
This solution below works - but with some restrictions (You must add com reference: "Microsoft Internet Controls") It allows to open one explorer window - and then checks whether window with the same "start folder path" as the base is already opened (watch out for slash and backslash difference in two different places of the code)
using SHDocVw;
public bool ExistOpenedWindow()
{
ShellWindows _shellWindows = new SHDocVw.ShellWindows();
string processType;
foreach (InternetExplorer ie in _shellWindows)
{
//this parses the name of the process
processType = Path.GetFileNameWithoutExtension(ie.FullName).ToLower();
//this could also be used for IE windows with processType of "iexplore"
if (processType.Equals("explorer") && ie.LocationURL.Contains("C:/"))
{
return true;
}
}
return false;
}
private void button1_Click(object sender, EventArgs e)
{
if (proc == null || !ExistOpenedWindow())
{
proc = Process.Start("explorer.exe", #"C:\");
}
}
So if you choose your base path (which will be sent as argument to explorer.exe") to be C:/, after clicking button once again, it will check whether there is ANY explorer window containing such path (opened by you or not)
Compare here: Start explorer.exe without creating a window C#
And here: Is there a way to close a particular instance of explorer with C#?
=== UPDATE 3 ====
After some thoughts - i've managed to come to working solution:
public bool ExistOpenedWindow()
{
var currentlyOpenedWindows = GetAllOpenedExplorerWindow();
return currentlyOpenedWindows.Any(t => t.HWND == ActiveOpenedWindowHwnd);
}
public List<InternetExplorer> GetAllOpenedExplorerWindow()
{
List<InternetExplorer> windows = new List<InternetExplorer>();
ShellWindows _shellWindows = new SHDocVw.ShellWindows();
string processType;
foreach (InternetExplorer ie in _shellWindows)
{
//this parses the name of the process
processType = Path.GetFileNameWithoutExtension(ie.FullName).ToLower();
//this could also be used for IE windows with processType of "iexplore"
if (processType.Equals("explorer"))
{
windows.Add(ie);
}
}
return windows;
}
public static int ActiveOpenedWindowHwnd;
private void button1_Click(object sender, EventArgs e)
{
var currentlyOpenedWindows = GetAllOpenedExplorerWindow();
if (ActiveOpenedWindowHwnd == 0 || !ExistOpenedWindow())
{
Process.Start("explorer.exe");
ShellWindows windows;
while ((windows = new SHDocVw.ShellWindows()).Count <= currentlyOpenedWindows.Count)
{
Thread.Sleep(50);
}
var currentlyOpenedWindowsNew = GetAllOpenedExplorerWindow();
var openedWindow = currentlyOpenedWindowsNew.Except(currentlyOpenedWindows).Single();
ActiveOpenedWindowHwnd = openedWindow.HWND;
}
}
I'm writing a simple watchdog application that will start and stop another application I'm writing based on whether the third application is running or not.
in other words, if application A is running then start application B. When application A stops, stop application B.
the problems is that my watchdog keeps stopping application B and immediately restarts it.
here is what I have:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Diagnostics;
using System.Windows.Forms;
using WindowScrape.Types;
namespace ConnectAndWait
{
class CheckForApplication
{
public static System.Windows.Forms.Timer _timer = new System.Windows.Forms.Timer();
public static bool goodtogo;
public static void checking()
{
_timer.Interval = 3000;
_timer.Tick += _timer_Tick;
_timer.Start();
}
public static void _timer_Tick(object sender, EventArgs e)
{
Process[] myprocess = Process.GetProcessesByName("ApplicationA");
Process[] proc = Process.GetProcessesByName("notepad");
if (myprocess.Length == 0)
{
goodtogo = false;
}
else
{
var win = Process.GetProcessesByName("ApplicationA");
var mainform = NativeMethods.FindWindow("TMainForm_ihm", null);
var children = NativeMethods.FindWindowEx(mainform, IntPtr.Zero, "TRzPageControl", null);
var final = NativeMethods.FindWindowEx(children, IntPtr.Zero, "TRzTabSheet", "Operation" );
if (final.ToString() != "0")
{
goodtogo = true;
}
else
{
goodtogo = false;
}
}
if (goodtogo == true)
{
if (proc.Length == 0)
{
Process.Start("notepad.exe");
MessageBox.Show("notepad started");
}
}
else if (goodtogo == false)
{
if (proc.Length != 0)
{
proc[0].Kill();
MessageBox.Show("process killed"); // <-- This never gets fired
// as long as application A keeps running. At first I thought I was stopping it
// with this code so I put the messageBox in to test that theory.
}
}
}
}
}
I am starting and stopping notepad for now just for testing.
can anyone see what I am doing wrong?
First: what is causing notepad to stop and then restart again even though the other application is still running?
Second: if there is anything else I should be doing differently please point that out as well.
As always, thank you so much for any help you can provide.
EDIT:
I didn't mention it before because of my lack of knowledge I didn't think it would be relevant.
The entire scope of the project is that I am writing an integration between two existing applications. If the one application is not running then there is no need for my application to use resources. So my thought was that a watchdog would take up fewer resources than the application itself.
My integration application uses multiple threads and gets and sets a lot of information between the other two applications.
The end user will start and stop application A whenever needed.
Application B is my integration application.
Application C - the one previously not mentioned - runs as a service and interacts with a database.
The watchdog application in question is simply to start and stop my integration application whenever Application A stops or starts.
There is simply no need for such a 'watchdog'. Use Job objects and bind the processes in a job. Read Destroying all child processes (and grandchildren) when the parent exits. See Working example of CreateJobObject/SetInformationJobObject pinvoke in .net? for C# examples.
For process start, use WMI Win32_ProcessStartTrace, see .NET Process Monitor.
In my MainWindow I have a button that can be used to open a Process (native OpenProcess call) and perform some checks on it's memory, but the method called on Click is asynchronous:
<Button Content="Attach" Click="OnClickAttach"/>
private async void OnClickAttach(Object sender, RoutedEventArgs e)
{
AttachmentResult result = await m_ViewModel.Attach();
switch (result)
// Different MessageBox depending on the result.
}
Now, let's see the ViewModel portion of code...
// MemoryProcess class is just a wrapper for Process' handle and memory regions.
private MemoryProcess m_MemoryProcess;
public async Task<AttachmentResult> Attach()
{
AttachmentResult result = AttachmentResult.Success;
MemoryProcess memoryProcess = NativeMethods.OpenProcess(m_SelectedBrowserInstance.Process);
if (memoryProcess == null)
result = AttachmentResult.FailProcessNotOpened;
else
{
Boolean check1 = false;
Boolean check2 = false;
foreach (MemoryRegion region in memoryProcess)
{
// I perform checks on Process' memory regions and I eventually change the value of check1 or check2...
await Task.Delay(1);
}
if (!check1 && !check2)
{
NativeMethods.CloseHandle(memoryProcess.Handle);
result = AttachmentResult.FailProcessNotValid;
}
else
{
// I keep the Process opened for further use. I save it to a private variable.
m_MemoryProcess = memoryProcess;
m_MemoryProcess.Check1 = check1;
m_MemoryProcess.Check2 = check2;
}
}
return result;
}
Now... here comes the problem. When the user closes the application, if a Process is opened, I must properly close its handle. So in my MainWindow I have the following code:
protected override void OnClosing(CancelEventArgs e)
{
m_ViewModel.Detach();
base.OnClosing(e);
}
And in my ViewModel I have the following code:
public void Detach()
{
if (m_MemoryProcess != null)
{
if (m_MemoryProcess.Check1)
// Do something...
if (m_MemoryProcess.Check2)
// Do something...
NativeMethods.CloseHandle(m_MemoryProcess.Handle);
m_MemoryProcess = null;
}
}
The Attach() method can take very long time, more than 2 minutes sometimes. I need to find a solution for the following issues:
If the user closes the application while Attach() method is running and before memoryProcess is saved to the private variable, the Process handle will not be closed.
If I save the MemoryProcess instance to the private variable just at the beginning of the Attach() method, there is a risk for the user to get a NullReferenceException if he closes the application while the Attach() method is processing its foreach loop.
I absolutely don't want to make the user wait for Attach() method to complete before letting him close the application. That's horrible.
How can I do this?
IMO, if you do not explicitly and specifically target to create separate detached/independent processes like, for example, through:
using PInvoke.CreateProcess
using
(new System.Management.ManagementClass("Win32_ProcessStartup"))
.Properties["CreateFlags"].Value = 8;
or maintaining child process alive upon app closing by launching them through separate shell scripts or other processes remaining to run after app closing;
creating a new thread in another independent process using CreateRemoteThread
etc.
or finding already run independently processes, you don't need to and probably should not "close" or dispose spawned by app processes. Windows (operting system) will close any unclosed spawned by app processes.
Also, I believe that it is impossible to execute any code in an application once it has started exiting or being closed.
PS (off-topic comment):
I do not even see that you close (really one should kill) or dispose your processes in your code...
I have some code that creates a Process instance and later starts it. There's some logic that need to check if the Process has been started. HasExited can be used to check if a started process has been exited, but I can not find a similar function for HasStarted. At first glance StartTime looked like a good option, but this function will throw if the process has exited. Also, the documentation says that StartTime only has meaning for started processes.
What is the "correct" approach for determining if a process has started (has been started, but might have quit)?
While the methods suggested by others will work, it is not the most efficient way to handle such things. If you keep a loop checking whether the Process has exited or not, you will waste a lot of system resources.
Your concern should be to just know when the process is exiting, and not sit looping for it to check whether it has exited. So, the correct way is to handle Events.
The code below explains how to do that using Events.
// Declare your process object with WithEvents, so that events can be handled.
private Process withEventsField_MyProcess;
Process MyProcess {
get { return withEventsField_MyProcess; }
set {
if (withEventsField_MyProcess != null) {
withEventsField_MyProcess.Exited -= MyProcess_Exited;
}
withEventsField_MyProcess = value;
if (withEventsField_MyProcess != null) {
withEventsField_MyProcess.Exited += MyProcess_Exited;
}
}
}
bool MyProcessIsRunning;
private void Button1_Click(System.Object sender, System.EventArgs e)
{
// start the process. this is an example.
MyProcess = Process.Start("Notepad.exe");
// enable raising events for the process.
MyProcess.EnableRaisingEvents = true;
// set the flag to know whether my process is running
MyProcessIsRunning = true;
}
private void MyProcess_Exited(object sender, System.EventArgs e)
{
// the process has just exited. what do you want to do?
MyProcessIsRunning = false;
MessageBox.Show("The process has exited!");
}
EDIT:
Knowing whether the process has started or not should be easy since are starting the process somewhere in the code. So you can set a flag there and set it to false when the process is exiting. I updated the code above to show how such a flag can be set easily.
Search your process in Process.GetProcesses();, the list returned by this method give all processes currently running on the machine.
You can use the Process.GetProcesses method (in the System.Diagnostics
namespace) to get a list of processes currently running on the PC.
Process.GetProcessesByName() can also be used to just get a list of
instances of a particular program.
// Get all instances of Notepad running on the local computer.
Process [] localByName = Process.GetProcessesByName("YourProcess");
You could check that there is atleast one thread in the process. This would indicate that the process is started and running.
Edit:
You could also check the process Id. It will throw an exception if the process hasn't started.
Edit 2:
Actually Threads will also throw an exception if the Id is not set:
bool ProcessIsRunning(Process p)
{
bool isRunning;
try {
isRunning = !p.HasExited && p.Threads.Count > 0;
}
catch(SystemException sEx)
{
isRunning = false;
}
catch(PlatformNotSupportedException pnsEx)
{
throw;
}
return isRunning;
}
This is a ClickOnce application. According to the documentation, "If your application was originally supplied command-line options when it first executed, Restart will launch the application again with the same options.". But I don't know if this is supposed to work or not with ClickOnce applications. If so, what am I doing wrong?
Here is my code:
public Form1()
{
InitializeComponent();
textBox1.Text = string.Join(Environment.NewLine, GetCommandLineFile());
}
private static string[] GetCommandLineFile()
{
if (AppDomain.CurrentDomain != null &&
AppDomain.CurrentDomain.SetupInformation != null &&
AppDomain.CurrentDomain.SetupInformation.ActivationArguments != null &&
AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData != null &&
AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData.Any())
{
return AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData;
}
else return new string[] { };
}
private void button1_Click(object sender, EventArgs e)
{
Application.Restart();
}
I associated my application with the .abc extension and when I double click my .abc file, the application will launch with the file name as the only argument, but then when I restart (by pressing my button1), GetCommandLineFile() will return an empty array.
I believe Application.Restart was designed for standard command line arguments instead of how ClickOnce applications handle it.
Looking at Microsoft's code for Application.Restart, they explicitly check if the application is a ClickOnce application and then restart it without any arguments being passed. Any other application, gets Environment.GetCommandLineArgs() parsed and sent to a new process.
I think a better solution, instead of writing arguments to a file, is to simply start a new process as such :
"path\Application Name.appref-ms" arg1,arg2,arg3
That way, when your application starts up, GetCommandLineFile() should grab the arguments again.