Windows Forms - Repeatedly Running an External Process - c#

I have a small Windows Forms application that needs to be able to run an external application an unlimited number of times without closing that application after each run.
The external application runs as a single instance and is very resource hungry and slow to load. The basic workflow is as follows:
1: Wait for a trigger to load the external app
2: Trigger raised, open the external app with a command line reference
3: Monitor a log file
3: External app processes the command line data and writes to the log file
4: Log file changed, so send next command line to (already open) external app
5: Go to step 3
The problem I have is that I cannot find a way of loading the external application without first closing/killing the process.
applicationProcess.StartInfo.FileName = commandLine;
applicationProcess.Start();
// Watch for change in log file and then...
applicationProcess.StartInfo.FileName = commandLine;
applicationProcess.Start();
and so on, but if I don't
applicationProcess.Kill();
before I re-issue the applicationProcess.Start() method I get a thread exception.
I think what I need to do is to disconnect from the applicationProcess once it has started, but cannot find any mechanism to do this.
Any advice/direction would be much appreciated.
Thanks.

If you intend to launch a new instance of your external application, then just allocate a new Process() object. Create one Process() object each time you launch your external application, rather than trying to re-use the original one.
If you intend to manipulate an existing instance of your external application, one that you already launched, then you don't need to call Start() again, just continue using applicationProcess.

Does it work when you create a new applicationProcess and keep the old ones in a List or something simmilar?

I have found a solution to this problem by creating a batch file launches the application. The workflow is as follows:
1: My application launches the batch file with a command-line argument
2: The batch file runs-re-runs the main application
3: My application kills and disposes of the process.
Step 3 simply closes the process that is running the batch file, not the main application process - which I need to stay open.
The batch file couldn't be simpler:
#echo off
"C:\Program Files (x86)\Microsoft Office\Office14\Excel.exe" %1
I have run this on a loop for over one hour and have found no problems.

Related

why Control M OS Job that executes an exe gets stuck mid execution?

I have a c# exe that reads some log lines from a remote unix server using sed. when I run this exe several times on my windows server or even my dev pc, it executes fine. However, when I try to run it as a cyclic OS job in Control M eventually (sometimes at the first execution, seems to happen at random) it gets stuck but doesn't throw an exception or anything.
The command of the job runs a .bat file, and the cyclic is configured to 0 minutes with pause time being controlled dynamically by the exe with a thread.sleep.
after searching the web and seeking recommendation from other areas of my workplace that use control M, I have so far tried changing my agent to use local user with ctmwincfg, I also tried with changing the agent service to the same user (had to reverse this one as the agent stopped working properly), I also changed from directly executing my exe on the job to using a .bat file.
one of my hostgroup agents is windows server 2016 and 3 more are windows server 2012
I wasn't able of finding a solution within control M itself, but rather in the code of the .exe. I used a task, it allows the program to terminate the stuck method after a timeout, which in turn allows the control M job to finish normally.
var task = Task.Run(() => MyStuckMethod(arg));
if (task.Wait(TimeSpan.FromSeconds(30)))
return task.Result;
else
throw new Exception("Timed out");
Alternatively, there are a few workarounds within control M which involve sending alerts or creating shouts and then automating the kill of the job, but this is not useful for my case.
Example in BMC communities

What to pass command line argument in running exe. having multiple instance running with same exe name

I am making a window service which will trigger at specific time to start/run an .exe. There are many instance running depend on trigger event
Example
**
1. Trigger A fire at morning and start/run controller.EXE
2. Trigger B fire at Afternoon and start/run controller.EXE
**
EDIT
I have used below
Process A = new Process();
A.StartInfo.FileName = #"Controller.exe";
A.StartInfo.Arguments = strXML+" "+strEndDate;
A.Start();
Process B = new Process();
B.StartInfo.FileName = #"Controller.exe";
B.StartInfo.Arguments = strXML+" "+strEndDate;
B.Start();
Now A and B both are running. I want to pass command line argument to process A while it is running
How can I achieve that. Is that possible with multiple exe having same name(controller.EXE)?
Answer to this question:
Is that possible with multiple exe having same name(controller.EXE)
Yes, sure. Look at Windows Task Manager in Process tab and you will see a lot of processes running with the same name. For instance, every tab in Chrome browser is running in its own process
.
About this
What to pass command line argument in running exe
You can pass any parameters you need or nothing. It depends on your application logic.

Opening file with Process.Start starts process but does not open window

I am trying to open a document with Process.Start in an ASP.NET web application. In this case, I am opening a Word file (.docx), but will be opening any type of file in the future.
My understanding of Process.Start is that the application is determined from the extension of the file path passed as a parameter, and opened.
The code I am using is very simple:
Process.Start("C:\\test.docx");
The file exists, and I am not getting any exceptions when the code is run. However, Word is not opening.
I have monitored my running processes through task manager whilst the code is running, and have noticed that a WINWORD.EXE process is starting with the User Name of the application pool being used (DefaultAppPool) in this case.
Why would the process be starting, but no Word window opening?
Edit: In case there is a better solution, here is my situation:
I am allowing users to upload documents, which are saved in an Oracle database as a BLOB. The user is then able to view their saved documents, and open them. What is the best way to extract a byte array from a BLOB, and open it using the correct application?

How to update your exe from remote host - c# 4.0 - wpf application

I want to update my exe from remote server. So when the button clicked on my wpf application it will download the remote and also a remote txt file and replace the current ones in the same folder that exe running. So it will overwrite the current txt and and exe file while my exe is running. How can i achive this ?
Remote host is url like www.mydomain.com/MyAPP.exe
wpf application , c# 4.0
The way that we resolved this issue was to create a shell exe that as the one that was installed and deployed initially to the client machines.
The "real" executable program is stored in a subdirectory of this initial app. When the shell app is launched, after it has downloaded and installed any updates for the real app, it launches the real app's executable in a separate AppDomain.
Here is the core of the "real" app launching from within the shell app:
System.AppDomainSetup oSetup = new System.AppDomainSetup();
string sApplicationFile = null;
// Use this to ensure that if the application is running when the user performs the update, that we don't run into file locking issues.
oSetup.ShadowCopyFiles = "true";
oSetup.ApplicationName = sAppName;
// Generate the name of the DLL we are going to launch
sApplicationFile = System.IO.Path.Combine(sApplicationDirectory, sAppName + ".exe");
oSetup.ApplicationBase = sApplicationDirectory;
oSetup.ConfigurationFile = sApplicationFile + ".config";
oSetup.LoaderOptimization = LoaderOptimization.MultiDomain;
// Launch the application
System.AppDomain oAppDomain = AppDomain.CreateDomain(sAppName, AppDomain.CurrentDomain.Evidence, oSetup);
oAppDomain.SetData("App", sAppName);
oAppDomain.SetData("User", sUserName);
oAppDomain.SetData("Pwd", sUserPassword);
oAppDomain.ExecuteAssembly(sApplicationFile);
// When the launched application closes, close this application as well
Application.Exit();
Note that in our version, the shell app collects the user name and password from the user in order to access the update web site correctly. This data is then passed to the "real" app through the SetData method on the AppDomain.
The solution depends on your particular case. But there's no straight solution, because you can't update assemblies while they are loaded into memory and being used. I can propose 2 solutions: using shadow copying and using some sort of helper executable. I've used both of them.
Shadow copying.
The obvious way is to make your main executable to be shadow copied, replace it while your app is running and then restart the app. But you can't make your default app domain to be shadow copied, only secondary app domains can be. But you still can move all your code into another assembly (say, MainAppLib.dll) and rewrite your main app executable (MainApp.exe) so that it contains only "loader code". This loader code has to create another app domain, set it to be shadow copied and then run your program logic in the secondary app domain. Beware not to have any direct references from your main app domain into MainAppLib.dll because then this assembly will be loaded into your main app domain which is not shadow copied and the assembly file will get locked. In most cases you can go with AppDomain.ExecuteAssembly() methods.
Helper executable
The idea is to use some sort of update finisher. Your main app remains unchanged, you only add a little amount of code into it, so that your app will download update, put it into temporary folder, and then your main app starts update finisher (in separate process) and exits. Update finisher waits till your app closes and then copies new files from temporary folder into your app folder replacing all files. Update finisher can't replace it's own executable but it can be done by main application before it starts the update finisher. After copying files update finisher runs your application.
p.s. Personally I prefer the former solution because it involves some sort of voodoo magic using app domains, reflection, assemblies e.t.c. And it can be evolved into using plugins if you need (e.g. via MEF framework). But the latter is easier to understand especially if you have never worked with app domains and manual assemblies loading, it's quite straightforward.
You could probably use ClickOnce (based on your comment above that you would be prepared to have another assembly get the exe....as the other poster mentioned you can't replace a running assembly). You can configure it to check at various times (e.g. on startup) for new versions and it automatically downloads them. Its a very robust solution and you can do a lot with the deployment assemblies.

Canceling a programatically started SQL Server Express Install

During my application setup, the user has the option of installing a new SQL Server Express 2005 instance in the local machine if they don't want to use an already existing SQL Server 2005 in their network for whatever reason.
As the SQL Server installation is optional, I don't have it as a prerequisite of my installer. Instead, I just bundle the sqlexpr32.exe setup in my install media and programatically launch it with the appropriate command line arguments in order to perform an automatic install. (Note: I'm using the /qb command line flag so the install is not silent, it shows the user interface, but does not ask for any user input). And in case anyone wants to know, I'm following this Microsoft article on how to launch the SQL Server Express setup.
This is what I'm doing in my custom install action:
// All this runs on a background thread so the user
// can cancel my app's setup at any time
// Launch the installer
Process setupProcess = new Process();
setupProcess.StartInfo.FileName = "sqlexpr32.exe";
setupProcess.StartInfo.Arguments = " a bunch of command line args here";
setupProcess.StartInfo.UseShellExecute = false; // to avoid a shell window
setupProcess.Start();
// At this point the SQL Server installer is running
// Monitor the process on 2-second intervals:
while (!setupProcess.WaitForExit(2000))
{
if(WasCancelled) // flag that is set when the user cancels my app's setup
{
// This following line is my problem. Sending CloseMainWindow does not
// seem to work. The SQL Server installer just keeps running.
setupProcess.CloseMainWindow();
setupProcess.WaitForExit();
break;
}
}
// After this point I build a results report for the user.
// My app's installer does not yet quit even if it was canceled.
So my question is: How could I 'signal' the SQL Server installer process to cancel and exit?
This line does not seem to do anything:
setupProcess.CloseMainWindow();
This also does not work:
setupProcess.Close(); // This closes my handle. Not the target process.
And I obviously wouldn't want to just kill the process as I could be leaving the user's machine in a not-so-desirable state, in the best case with a lot of garbage files or worse, with a corrupt install.
Any ideas? Sending keys or simulating user clicks? Or hopefully something less hacky?
EDIT:
I think I have found out why CloseMainWindow does not work:
The process I start (sqlexpr32.exe) is not really the one that shows the UI for the SQLServer Installer, but a self-extracting exe that in turn launches the sql server real setup.exe as a child process. So this issue gets even harder. =(
What if you wait until the installer finishes, and after that - if the user has cancelled the main process - you uninstall it immediately?
I know it's a more time consuming process but it's clear and easy.

Categories

Resources