Continue the application even if a thirdparty application fails - c#

I have a thirdparty exe file name Projtest.exe and using a c# console application written by myself for running the Projtest.exe through commandline.
i was able to succeed in writing the application,but my issue is that due to some unknown reasons Projtest.exe stops working and throws an error window.My application is stucking at this point.What i want is to continue my application if Projtest.exe throws an error window.How can i do that.Part of my code is given below.
try
{
var pro = new Process
{
StartInfo = {
Arguments = string.Format("Projtest.exe {0} {1} ", arg1, arg2)
}
};
pro.Start();
pro.WaitForExit();
var exit = pro.ExitCode;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}

What you can to do is regularly check if 'pro' is responding say every 10 seconds.
You can do this simply with a timer or you could create a thread to do it.
Also it is worth checking for exit here as well so you don't block your application.
Here is an example with a timer.
static Timer appCheck = new Timer();
static Process pro;
static void Main(string[] args)
{
appCheck.Interval = (10000); // set timer interval in ms
appCheck.AutoReset = true;
appCheck.Elapsed += new ElapsedEventHandler(appCheck_Tick);
try
{
pro = new Process
{
StartInfo =
{
Arguments = string.Format(#"Projtest.exe"),
FileName = string.Format(#"Projtest.exe")
}
};
pro.Start(); // starts your program
appCheck.Start(); // starts the timer to keep a watch on your program
while (true) // this just keeps your console window open. if you use waitForExit your application will be blocked and the timer won't fire.
{
}
//pro.WaitForExit();
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
static void appCheck_Tick(object sender, EventArgs e)
{
if (!pro.HasExited)
{
if (pro.Responding == false)
{
appCheck.Stop(); // stop the timer so you don't keep getting messages
Console.WriteLine("Not responding");
}
}
else
{
appCheck.Stop(); // stop the timer so you don't keep getting messages
Console.WriteLine("exited");
}
}

Related

Kill a process that opened a file, after the file is closed by the user [WPF application]

I am trying to efficiently open-close-reopen a power bi file (.pbix) from a WPF application button click. My method starts by creating a process that opens the pbix file then kills the process when the file is closed and then when the button is clicked again creates a new process to re-open the file.
Kindly find below the code I use to execute the steps above.
namespace TestApp
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public int CheckFileIsOpen(string filenamepath)
{
try
{
using FileStream fs = new FileStream(filenamepath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
return 0;
}
catch (Exception)
{
WindowEffect = new BlurEffect();
Mouse.OverrideCursor = null;
bool? Result = new CustomMessageBox($"File: {filenamepath.Split(#"\").Last()} in use!\nClose it and try again.", "File used by another process", MessageType.Error, MessageButtons.Ok).ShowDialog(); //this is a MessageBox object
if (Result.Value)
{
WindowEffect = null;
return 1;
}
else
{
WindowEffect = null;
return 2;
}
}
}
private void OpenOnlineLocally(bool open_local)
{
Process p = new Process();
string copy_name = "File_Copy.pbix";
string path = AppDomain.CurrentDomain.BaseDirectory; //the directory the .exe file runs.
try
{
Mouse.OverrideCursor = Cursors.Wait;
if (open_local == true)
{
int IsPBIFileOpen = CheckFileIsOpen($#"{path}{copy_name}");
if (new[] { 1, 2 }.Contains(IsPBIFileOpen))
{
return;
}
//Open the file using the system process
p.StartInfo = new ProcessStartInfo($"{path}{copy_name}")
{
UseShellExecute = true
};
p.Start();
}
else
{
OpenUrl("https://app.powerbi.com/...");
}
}
finally
{
if (p.HasExited) { p.Kill(); } //kill the process if the user closed the .pbix file
}
}
public ICommand ExportPowerBICommand //binded to a button click command in xaml
{
get { return new DelegateCommand<object>(FuncExportPowerBI); }
}
public void FuncExportPowerBI(object parameter)
{
Mouse.OverrideCursor = Cursors.Wait;
try
{
OpenOnlineLocally(true);
}
finally
{
Mouse.OverrideCursor = null;
}
}
}
}
The above code generates this error in the finally statement:
System.InvalidOperationException: 'No process is associated with this object.'
Some notes after experimentation:
The process should be killed when the user closes the .pbix file (i.e. clicks the X icon on top right corner of the desktop app). If the process is not killed and the user re-clicks the button to re-open the file then I get an error that the file is already opened and used by another process.
I prefer to avoid a solution that uses process.WaitForExit(), for two reasons. First, the application freezes while the file is used by the user. Second, it takes a couple of seconds for the desktop to realize that the process has exited so it can kill() it (not time efficient).
Since you're running .NET 5, there's an asynchronous method Process.WaitForExitAsync(). Async operation will not block the UI.
I've made the changes to two methods
private async Task OpenOnlineLocally(bool open_local)
{
Process p = new Process();
string copy_name = "File_Copy.pbix";
string dir = AppDomain.CurrentDomain.BaseDirectory; //the directory the .exe file runs.
string path = Path.Combine(dir, copy_name);
try
{
if (open_local == true)
{
int IsPBIFileOpen = CheckFileIsOpen(path);
if (IsPBIFileOpen != 0)
{
return;
}
//Open the file using the system process
p.StartInfo = new ProcessStartInfo(path)
{
UseShellExecute = true
};
p.Start();
await p.WaitForExitAsync();
}
else
{
OpenUrl("https://app.powerbi.com/...");
}
}
finally
{
if (!p.HasExited) { p.Kill(); } //kill the process if the user closed the .pbix file
}
}
public async void FuncExportPowerBI(object parameter)
{
Mouse.OverrideCursor = Cursors.Wait;
try
{
await OpenOnlineLocally(true);
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message); // handle possible exception here
}
Mouse.OverrideCursor = null;
}

C# - Timer ticks shorter than specified in the interval causing duplicate jobs to run in parallel

I have a windows service that ticks over every 4 minutes. When the timer ticks if runs a DataImporter, the DataImporter has a number of "Jobs" that it can run on each tick, for example, there is a ProcessData job and a RetreiveData job:
RetreiveData job will reach out to 3rd party API and store data in DB for processing.
ProcessData job will take data from DB and process it into our usable DB etc.
As soon as the DataImporter is run it checks a DB table called ScheduledJob - this has a number of scheduling functionality such as FrequencyInterval, ActiveStart/Stop times, StartedLastRun time. The ScheduledJob table has flag called "InProgress", this flag will stop the DataImport picking up that job when it's already running.
There is a continuous issue where a job is picked up twice, a few seconds apart from each other and then both run simultaneously which cause a number of DB constraints when trying to insert identical records. I am not really sure how it can pick two jobs up at the same time, the tick is 4 minutes apart, so in theory it shouldn't be able to even look at the potential jobs to run, how can it run them both a few seconds apart?
Both the RetrieveData and ProcessData jobs need to be able to run in parallel so I can't pause the Timer whilst I execute the job.
Service:
public partial class DataImport : ServiceBase
{
private int _eventId = 0;
readonly Timer _serviceTimer = new Timer(240000);
public DataImport()
{
InitializeComponent();
ImportServiceEventLog.Source = ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource(); ;
}
protected override void OnStart(string[] args)
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " started", EventLogEntryType.Information, _eventId++);
_serviceTimer.AutoReset = true;
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " timer interval = " + _serviceTimer.Interval / 1000 + " seconds", EventLogEntryType.Information, _eventId++);
_serviceTimer.Elapsed += new ElapsedEventHandler(OnTimer);
_serviceTimer.Start();
}
protected override void OnStop()
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " stopped", EventLogEntryType.Information, _eventId++);
}
public void OnTimer(object sender, ElapsedEventArgs args)
{
try
{
Run();
}
catch (System.Exception ex)
{
ImportServiceEventLog.WriteEntry(ServiceSource.DATA_IMPORT_SERVICE.ToString() + Global.ReleaseModeSource() + " error: " + ex.ToString(), EventLogEntryType.Information, _eventId++);
}
}
public void Run()
{
using (var dataImportController = new DataImportController())
{
dataImportController.Run();
}
}
}
DataImportController:
public class DataImportController
{
public void Run()
{
// Gets all the jobs from the ScheduledJob table in the DB
var jobs = GetJobsToRun();
//Get all Processes (from DB)
foreach (var job in jobs)
{
//Check the time it was last run - do this for each process
if (RunJob(job))
{
_messaging.EventMessage("Process " + job.Name + " finished : " + DateTime.Now, ServiceSource.DATA_IMPORT_SERVICE);
}
}
}
public bool RunJob(ScheduledJob job)
{
// Checks if the job is ready to run, i.e. is the InProgress flag set to false and the interval long enough since the StartedLastRun DateTime
if (!job.IsReadyToRun())
{
return false;
}
// Set job to in progress
job.InProgress = true;
job.StartedLastRun = DateTime.Now;
_scheduledJobRepository.Update(job);
_scheduledJobRepository.SaveChanges();
try
{
switch (job.Name.ToUpper())
{
case "RetreiveData":
// RUN JOB
break;
case "ProcessData":
// RUN JOB
break;
}
job.InProgress = false;
job.EndedLastRun = DateTime.Now;
_scheduledJobRepository.Update(job);
_scheduledJobRepository.SaveChanges();
}
catch (Exception exception)
{
_messaging.ReportError("Error occured whilst checking we are ready to run " + exception.Message, exception, null, 0, ServiceSource.DATA_IMPORT_SERVICE);
}
return true;
}
}
EDIT:
Include Program.cs
static void Main()
{
if (!Environment.UserInteractive)
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new DataImport()
};
ServiceBase.Run(ServicesToRun);
}
}
If overlapping is a concern, ditch the timer and make an async loop, leveraging Task.Delay:
async Task SomeFunc(CancellationToken token)
{
while(!token.IsCancellationRequested)
{
DoWork();
await Task.Delay(timeInterval, token);
}
}
try to stop the timer inside the OnTimer function then re-start timer after it has finished executing your task.
You subsribe to timer event in OnStart, and didn't unsubscribe in OnStop.
Move _serviceTimer.Elapsed += new ElapsedEventHandler(OnTimer); and initialization of AutoReset to constructor. Stop timer in OnStop. That should fix your issue. I believe your service is started (restarted) more than once.

Prevent running multiple instances of a mono app

I know how to prevent running multiple instances of a given app on Windows:
Prevent multiple instances of a given app in .NET?
This code does not work under Linux using mono-develop though. It compiles and runs but it does not work. How can I prevent it under Linux using mono?
This is what I have tried but the code deos not work under linux only on windows.
static void Main()
{
Task.Factory.StartNew(() =>
{
try
{
var p = new NamedPipeServerStream("SomeGuid", PipeDirection.In, 1);
Console.WriteLine("Waiting for connection");
p.WaitForConnection();
}
catch
{
Console.WriteLine("Error another insance already running");
Environment.Exit(1); // terminate application
}
});
Thread.Sleep(1000);
Console.WriteLine("Doing work");
// Do work....
Thread.Sleep(10000);
}
I came up with this answer. Call this method passing it a unique ID
public static void PreventMultipleInstance(string applicationId)
{
// Under Windows this is:
// C:\Users\SomeUser\AppData\Local\Temp\
// Linux this is:
// /tmp/
var temporaryDirectory = Path.GetTempPath();
// Application ID (Make sure this guid is different accross your different applications!
var applicationGuid = applicationId + ".process-lock";
// file that will serve as our lock
var fileFulePath = Path.Combine(temporaryDirectory, applicationGuid);
try
{
// Prevents other processes from reading from or writing to this file
var _InstanceLock = new FileStream(fileFulePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
_InstanceLock.Lock(0, 0);
MonoApp.Logger.LogToDisk(LogType.Notification, "04ZH-EQP0", "Aquired Lock", fileFulePath);
// todo investigate why we need a reference to file stream. Without this GC releases the lock!
System.Timers.Timer t = new System.Timers.Timer()
{
Interval = 500000,
Enabled = true,
};
t.Elapsed += (a, b) =>
{
try
{
_InstanceLock.Lock(0, 0);
}
catch
{
MonoApp.Logger.Log(LogType.Error, "AOI7-QMCT", "Unable to lock file");
}
};
t.Start();
}
catch
{
// Terminate application because another instance with this ID is running
Environment.Exit(102534);
}
}

How to check if a thread is running in C #?

I created a thread in C # 4.0 and would like to know how do I check if it is running?
You can use Thread.IsAlive to check to see if a Thread is running.
That being said, if you're using C# 4, it's rarely a good idea to make "threads" manually. You should consider using the TPL and the Task/Task<T> class, as this provides a much cleaner model to attach work to run after the task completes, pull data out of the operation, etc.
I use Mutex to verify this. Sometimes just verify is Thread is alive with Thread.IsAlive is not safe if you are running on Background.
Try this:
private void btnDoSomething()
{
try
{
string nameThread = "testThreadDoSomething";
var newThread = new Thread(delegate() { this.DoSomething(nameThread); });
newThread.IsBackground = true;
newThread.Name = nameThread;
newThread.Start();
//Prevent optimization from setting the field before calling Start
Thread.MemoryBarrier();
}
catch (Exception ex)
{
}
}
public void DoSomething(string threadName)
{
bool ownsMutex;
using (Mutex mutex = new Mutex(true, threadName, out ownsMutex))
{
if (ownsMutex)
{
Thread.Sleep(300000); // 300 seconds
if (Monitor.TryEnter(this, 300))
{
try
{
// Your Source
}
catch (Exception e)
{
string mensagem = "Error : " + e.ToString();
}
finally
{
Monitor.Exit(this);
}
}
//mutex.ReleaseMutex();
}
}
}

Delete file using C# Thread

I was reading this article (Can't delete a file using threads) about my problem but things are getting difficult to me.
My problem is really simple, I just want to delete this old file, if I start the method "dlMoveNovaVersao" normally the file is deleted but if I put this on a thread (like bellow) I got "You are not allow". Someone knows what's the problem? (I wanna use thread).
private void verificaVersaoSupervisor_Tick(object sender, EventArgs e)
{
Thread threadConexao = new Thread(threadVerificaConexao);
threadConexao.Start();
}
public void threadVerificaConexao()
{
try
{
Dns.GetHostEntry("www.google.com.br");
if (verificaVersao())
{
try
{
verificaKillSupervisor();
dlMoveNovaVersao();
Application.Exit();
}
catch (Exception)
{ }
}
else
{
Application.Exit();
}
}
catch (Exception)
{ }
}
public void dlMoveNovaVersao()
{
WebClient webClient = new WebClient();
webClient.DownloadFile("Anywebsite", #"c:\temp\supervisor.exe);
try
{
File.Delete(#"c:\Test\supervisor.exe); //This file is always there!
}
catch (Exception err)
{
MessageBox.Show(err.Message);
}
Just discribe the purpose, My program (Supervisor Starter) check on website if I have an old version of "Supervisor" running (using XML), If it's true my "Supervisor Starter" verify if there is a process called "Supervisor" running and kill it after that "Supervisor Starter" download the new version and run it. (The program is small and the update don't take more then 4 seconds).
The problem start when my "Supervisor Starter" try delete the old version of my program. If I use thread I receive "I haven't permission to access the file", if I use the same method on Form class the file is deleted.
I suspect that you're running the thread while the file is in use. When the thread runs, it runs in parallel with the current thread. Have you ensured that that file is closed?.
Otherwise I think that the thread maybe being created with a credentials that are not yours. But I'm pretty sure this is not the case.
See if this is different for each case
catch (Exception err)
{
MessageBox.Show("User {0}. Message {1}",
System.Security.Principal.WindowsIdentity.GetCurrent().Name,
err.Message);
}
This is my functions for deleting files in threads if the files are in used
private static void Delete(System.IO.FileInfo file)
{
if (file.Exists)
{
int Attempt = 0;
bool ShouldStop = false;
while (!ShouldStop)
{
if (CanDelete(file))
{
file.Delete();
ShouldStop = true;
}
else if (Attempt >= 3)
{
ShouldStop = true;
}
else
{
// wait one sec
System.Threading.Thread.Sleep(1000);
}
Attempt++;
}
}
}
private static bool CanDelete(System.IO.FileInfo file)
{
try
{
//Just opening the file as open/create
using (FileStream fs = new FileStream(file.FullName, FileMode.OpenOrCreate))
{
//If required we can check for read/write by using fs.CanRead or fs.CanWrite
}
return false;
}
catch (IOException ex)
{
//check if message is for a File IO
string __message = ex.Message.ToString();
if (__message.Contains("The process cannot access the file"))
return true;
else
throw;
}
}

Categories