C# console application timer issue - c#

I have a console applciation which is invoked by a Windows Service. This console application creates instances of System.Timers.Timer based on certain App.config file entries (I have created a custom App.config section and the number of timer instances will be same as that of the elements in this section). The console application is expected not to close - if it closes for some reason, the windows service will invoke it again.
To make the console application live for ever, I have an infinite loop written as the last statement of the console application. while (1 == 1) { }.
The issue is, I see that the console application terminates every 5 minutes. I don't understand why is this happening.
If there are any better approaches, please suggest.
Code
static void Main(string[] args)
{
Boolean _isNotRunning;
using (Mutex _mutex = new Mutex(true, _mutexID, out _isNotRunning))
{
if (_isNotRunning)
{
new ProcessScheduler().InitializeTimers();
while (1 == 1) { }
}
else
{
return;
}
}
public class ProcessScheduler
{
public void InitializeTimers()
{
XYZConfigSection.XYZAppSection section = (XYZConfigSection.XYZAppSection)ConfigurationManager.GetSection("XYZApp");
if (section != null)
{
XYZComponentTimer XYZComponentTimer = null;
for (int intCount = 0; intCount < section.XYZComponents.Count; intCount++)
{
XYZComponentTimer = new XYZComponentTimer();
XYZComponentTimer.ComponentId = section.XYZComponents[intCount].ComponentId;
XYZComponentTimer.Interval = int.Parse(section.XYZComponents[intCount].Interval);
XYZComponentTimer.Elapsed += new ElapsedEventHandler(XYZComponentTimer_Elapsed);
XYZComponentTimer.Enabled = true;
}
}
}
}
public class XYZComponentTimer:Timer
{
public string ComponentId { get; set; }
}
Update:
As mentioned in the code, the timer interval for each instance is set based on the config file values for corresponding element. Right now, there are two sections in the config file: one has an interval of 15 seconds, and another one 10 seconds.

Let me guess: The timer interval is 5min and the timer crashes causing the process to exit.
Why don't you log any crashes or attach a debugger?

Handle the AppDomain.UnhandledException event and log the exception.

Mutex was getting instantiated every 5 minutes for some weird reason, and was setting _isNotRunning to true. That was the issue.

Related

UWP crash in ntdll.dll

I've got a UWP (C#) app that's running in production on a remote machine (under windows 10) but it periodically crashes.
My client says, somewhat arbitrarily, every 9 hours or so.
I have several .wer files from the previous crashes but did not have a minidump, the paths referenced in the event viewer entry for the crash are blank other than the WER files.
See edits below for how a minidump was obtained and findings.
The exception is an access violation (0xc0000005) at exception offset 0x0004df23 in ntdll.dll
I have the full source for the application and can run it in debug for long periods without the crash.
If I use DLL Export Viewer and load the exact version of ntdll.dll (copied from the remote machine) then I can see that at relative address 0x0004dc60 is EtwNotificationRegister and at 0x0004e260 is LdrGetDllPath.
Does this mean that my crash is occurring within a line of code in EtwNotificationRegister (which in turn is invoked by something within our code; however very difficult to trace without stack/minidump)
I am not sure if the layout of a dll is such that the address I have can be placed like that?
Edit 2 as per #Raymond: No. There are almost certainly other non-exported functions between EtwNotificationRegister and LdrGetDllPath. On build 17763.475, offset 4df23 is RtlpWaitOnCriticalSection, so you are probably using an uninitialized critical section or an already-deleted critical section.
Is there any way I can extract more detail about this crash? I have remote access to the computer running the app but the crash does not appear to be triggered by a particular event (e.g. we can't hit a button and cause the crash)
Using a minidump now
I am running the program in both local debug as well.
I have a remote debugger to the remote process but can't seem to break or inspect threads, not sure why. Just redeployed with symbols and the debugger attaches no problem but it just skips all breakpoints :(
Our own (rather naive) local log file, originally intended for only local debugging is written with a StreamWriter.WriteLine and immediately followed with a StreamWriter.Flush (wrapped in a try catch since that's not thread safe) just ends at a normal event on the remote machine - there is nothing following this normal event.
We catch App_UnhandledException and write to this log so I'd have expected a stack here.
In Unexplained crashes related to ntdll.dll it is suggested that a crash from ntdll.dll is a canary in a coalmine Unexplained crashes related to ntdll.dll
Edit 1: I have configured an auto crash dump as per https://www.meziantou.net/2018/06/04/tip-automatically-create-a-crash-dump-file-on-error so if I can get it to crash again maybe I'll get a dump file next time?
Here is the detail from the WER
Version=1
EventType=MoAppCrash
EventTime=132017523132123596
ReportType=2
Consent=1
UploadTime=132017523137590717
ReportStatus=268435456
ReportIdentifier=8d467f04-4bdd-4f9e-bf26-b42d143ece1a
IntegratorReportIdentifier=b60f9ca0-4126-4262-a886-98d3844892d3
Wow64Host=34404
NsAppName=praid:App
OriginalFilename=XXXXXX.YYYYYY.exe
AppSessionGuid=00001514-0001-0004-9fe2-6df11905d501
TargetAppId=U:XXXXXX.YYYYYY_1.0.201.0_x64__b0abmt6f49vqj!App
TargetAppVer=1.0.201.0_x64_!2018//01//24:08:17:16!1194d!XXXXXX.YYYYYY.exe
BootId=4294967295
TargetAsId=1298
UserImpactVector=271582000
IsFatal=1
EtwNonCollectReason=4
Response.BucketId=2ee79f27e2e81a541d6200d746866340
Response.BucketTable=5
Response.LegacyBucketId=2117255699418735424
Response.type=4
Sig[0].Name=Package Full Name
Sig[0].Value=XXXXXX.YYYYYY_1.0.201.0_x64__b0abmt6f49vqj
Sig[1].Name=Application Name
Sig[1].Value=praid:App
Sig[2].Name=Application Version
Sig[2].Value=1.0.0.0
Sig[3].Name=Application Timestamp
Sig[3].Value=5a68410c
Sig[4].Name=Fault Module Name
Sig[4].Value=ntdll.dll
Sig[5].Name=Fault Module Version
Sig[5].Value=10.0.17763.475
Sig[6].Name=Fault Module Timestamp
Sig[6].Value=3230aa04
Sig[7].Name=Exception Code
Sig[7].Value=c0000005
Sig[8].Name=Exception Offset
Sig[8].Value=000000000004df23
DynamicSig[1].Name=OS Version
DynamicSig[1].Value=10.0.17763.2.0.0.256.48
DynamicSig[2].Name=Locale ID
DynamicSig[2].Value=5129
DynamicSig[22].Name=Additional Information 1
DynamicSig[22].Value=95b1
DynamicSig[23].Name=Additional Information 2
DynamicSig[23].Value=95b15a88b673e33a5f48839974790b1c
DynamicSig[24].Name=Additional Information 3
DynamicSig[24].Value=283d
DynamicSig[25].Name=Additional Information 4
DynamicSig[25].Value=283dea7b6b6112710c1e3f76ed84d993
Edit 3: screenshot of minidump from a crash last night. In the event log, the WER crash looks the same so this appears to be the same issue. I will see if I can load symbols etc.
Edit 4: Attempting to debug managed. Threads view shows a thread as the exception point but no call stack info.
Edit 5: Debugging native from the minidump. Looks like we have a winner.
#Raymond was correct, it was RtlpWaitOnCriticalSection invoked from BluetoothLEAdvertismentWatcher::AdvertismentReceivedCallbackWorker
Native call stack as text:
Not Flagged > 8748 0 Worker Thread Win64
Thread Windows.Devices.Bluetooth.dll!(void)
ntdll.dll!RtlpWaitOnCriticalSection()
ntdll.dll!RtlpEnterCriticalSectionContended()
ntdll.dll!RtlEnterCriticalSection()
Windows.Devices.Bluetooth.dll!(void)()
Windows.Devices.Bluetooth.dll!wil::ResultFromException<(void)
()
Windows.Devices.Bluetooth.dll!Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher::AdvertisementReceivedCallbackWorker(void)
Windows.Devices.Bluetooth.dll!Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisementWatcher::AdvertisementReceivedThreadpoolWorkCallbackStatic(struct
_TP_CALLBACK_INSTANCE *,void *,struct _TP_WORK *)
ntdll.dll!TppWorkpExecuteCallback()
ntdll.dll!TppWorkerThread()
kernel32.dll!BaseThreadInitThunk()
ntdll.dll!RtlUserThreadStart()
Edit 6: okay so now, what do I do? How can I resolve this problem? My understanding of the stack is it looks like an exception was thrown inside the callback? Is that correct?
So I could put a managed try/catch in the BLE advertisment callback handler and that should (catch - for further debugging) fix it?
Edit 7: code...
Here is the code we use to instantiate the wrapper and subscribe to events.
The BluetoothLEAdvertisementWatcherWrapper is a delgating class (e.g. it just wraps the underlying BluetoothLEAdvertisementWatcher so it can implement an interface; it simply passes all events through and exposes properties. We do this so that we can have a different version that creates virtual events for testing)
bluetoothAdvertisementWatcher = new BluetoothLEAdvertisementWatcherWrapper();
bluetoothAdvertisementWatcher.SignalStrengthFilter.SamplingInterval = TimeSpan.Zero;
bluetoothAdvertisementWatcher.ScanningMode = BluetoothLEScanningMode.Active;
bluetoothAdvertisementWatcher.Received += Watcher_Received;
bluetoothAdvertisementWatcher.Stopped += Watcher_Stopped;
bluetoothAdvertisementWatcher.Start();
Here is the code for the wrapper; just to show it's not doing anything complex:
public class BluetoothLEAdvertisementWatcherWrapper : IBluetoothAdvertismentWatcher, IDisposable
{
private BluetoothLEAdvertisementWatcher bluetoothWatcher;
public BluetoothLEAdvertisementWatcherWrapper()
{
bluetoothWatcher = new BluetoothLEAdvertisementWatcher();
}
public BluetoothSignalStrengthFilter SignalStrengthFilter => bluetoothWatcher.SignalStrengthFilter;
public BluetoothLEScanningMode ScanningMode
{
get
{
return bluetoothWatcher.ScanningMode;
}
set
{
bluetoothWatcher.ScanningMode = value;
}
}
public event TypedEventHandler<BluetoothLEAdvertisementWatcher, BluetoothLEAdvertisementReceivedEventArgs> Received
{
add
{
bluetoothWatcher.Received += value;
}
remove
{
bluetoothWatcher.Received -= value;
}
}
public event TypedEventHandler<BluetoothLEAdvertisementWatcher, BluetoothLEAdvertisementWatcherStoppedEventArgs> Stopped
{
add
{
bluetoothWatcher.Stopped += value;
}
remove
{
bluetoothWatcher.Stopped -= value;
}
}
public BluetoothLEAdvertisementWatcherStatus Status => bluetoothWatcher.Status;
public Action<IPacketFrame, short> YieldAdvertisingPacket { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
public void Start()
{
bluetoothWatcher.Start();
}
public void Stop()
{
bluetoothWatcher.Stop();
}
public void Dispose()
{
if (bluetoothWatcher != null)
{
if (bluetoothWatcher.Status == BluetoothLEAdvertisementWatcherStatus.Started)
{
bluetoothWatcher.Stop();
}
bluetoothWatcher = null;
}
}
}
And here is the code for the Watcher_Received event handler:
private void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
try
{
//we won't queue packets until registered
if (!ApplicationContext.Current.Details.ReceiverId.HasValue)
return;
IPacketFrame frame;
PacketFrameParseResult result = ParseFrame(args, out frame);
if (result == PacketFrameParseResult.Success)
{
ApplicationContext.Current.Details.BluetoothPacketCount++;
}
short rssi = args.RawSignalStrengthInDBm;
string message = FormatPacketForDisplay(args, args.AdvertisementType, rssi, frame, result);
if (BluetoothPacketReceived != null)
{
BluetoothPacketReceived.Invoke(this, new BluetoothPacketReceivedEventArgs(message, result, frame, rssi));
}
}
catch (Exception ex)
{
if (ex.InnerException is Exceptions.PacketFrameParseException && (ex.InnerException as Exceptions.PacketFrameParseException).Result == PacketFrameParseResult.InvalidData)
{
// noop
}
else
{
Logger.Log(LogLevel.Warning, "BLE listener caught bluetooth packet error: {0}", ex);
if (BluetoothPacketError != null)
{
BluetoothPacketError.Invoke(this, new BluetoothPacketErrorEventArgs(ex));
}
}
}
}
You can see here that the entire managed callback is wrapped in a try catch and doesn't rethrow, so I'm not sure if there's anything further I can do to prevent the native exception from bringing the application down.
Current thinking, based on this: RtlpEnterCriticalSectionContended is it a parallel event handler, the native side is raising the handler, and it raises for a new event in the same thread while the previous handler is still executing from a previous event?
Then this is a race condition on the critical section that causes the crash?
Edit 8: To test this theory, I replaced the contents of received with a read + push to a concurrent queue, allowing the managed code to exit the event handler as quickly as possible.
Then added a seperate thread reading from the concurrent queue to perform my application side processing.
Initially, I thought this had resolved the issue as the application actively (listening) ran for approximately 15 hours, however it crashed again this morning with the same symptoms.
Edit 8: Following suggestions in the comments, we tried to ensure that we didn't dispose/GC the watcher after a stop prior to the receive completing.
We did this by using a TaskCompletionSource to function as a promise, subscribing to the Stopped event so we could await on the completion source task which would only have a result set when the Stopped event had fired.
We also used a lock (Monitor.Enter) in both StopAsync and Received to ensure that both could not be running in parallel.
This appeared to reduce the speed at which the system could process events which would make sense if the BLE packets were arriving in parallel.
Updated code as follows:
if ((DateTime.Now - this.LastStartedTimestamp).TotalSeconds > 60)
{
if (this.LastStopReason != BluetoothWatcherStopReason.DeviceCharacteristicWorker)
{
Logger.Log(LogLevel.Debug, "Stopping bluetooth watcher...");
// restart watcher every 10 mins
await this.StopAsync(BluetoothWatcherStopReason.AutomaticRestart);
//start again if automatic restart
Logger.Log(LogLevel.Debug, "Starting bluetooth watcher...");
this.Start(this.testMode);
Logger.Log(LogLevel.Debug, "Started bluetooth watcher");
this.LastStartedTimestamp = DateTime.Now;
}
}
private void Watcher_Stopped(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementWatcherStoppedEventArgs args)
{
string error = args.Error.ToString();
Logger.Log(LogLevel.Warning, string.Format("BLE listening stopped because {0}...", error));
LastError = args.Error;
if (BluetoothWatcherStopped != null)
{
BluetoothWatcherStopped.Invoke(sender, args);
}
}
public class ReceivedBluetoothAdvertismentPacketItem
{
public DateTime Timestamp { get; set; }
public BluetoothLEAdvertisementType Type { get; set; }
public byte[] Buffer { get; set; }
public short Rssi { get; set; }
}
ConcurrentQueue<ReceivedBluetoothAdvertismentPacketItem> BluetoothPacketsReceivedQueue = new ConcurrentQueue<ReceivedBluetoothAdvertismentPacketItem>();
private void Watcher_Received(BluetoothLEAdvertisementWatcher sender, BluetoothLEAdvertisementReceivedEventArgs args)
{
bool lockWasTaken = false;
try
{
//this prevents stop until we're exiting Received
Monitor.Enter(BluetoothWatcherEventSynchronisation, ref lockWasTaken);
if (!lockWasTaken)
{
return;
}
//we won't queue packets until registered
if (!ApplicationContext.Current.ReceiverDetails.ReceiverId.HasValue)
return;
BluetoothLEAdvertisementType type = args.AdvertisementType;
byte[] buffer = GetManufacturerData(args.Advertisement);
short rssi = args.RawSignalStrengthInDBm;
BluetoothPacketsReceivedQueue.Enqueue(new ReceivedBluetoothAdvertismentPacketItem
{
Timestamp = DateTime.UtcNow,
Type = type,
Rssi = rssi,
Buffer = buffer
});
ApplicationContext.Current.ReceiverDetails.UnprocessedQueueLength = BluetoothPacketsReceivedQueue.Count;
}
catch (Exception ex)
{
Logger.Log(LogLevel.Warning, "BLE listener caught bluetooth packet error: {0}", ex);
if (BluetoothPacketError != null)
{
BluetoothPacketError.Invoke(this, new BluetoothPacketErrorEventArgs(ex));
}
}
finally
{
if (lockWasTaken)
{
Monitor.Exit(BluetoothWatcherEventSynchronisation);
}
}
}
public BluetoothWatcherStopReason LastStopReason { get; private set; } = BluetoothWatcherStopReason.Unknown;
private object BluetoothWatcherEventSynchronisation = new object();
public Task<BluetoothWatcherStopReason> StopAsync(BluetoothWatcherStopReason reason)
{
var promise = new TaskCompletionSource<BluetoothWatcherStopReason>();
if (bluetoothAdvertisementWatcher != null)
{
LastStopReason = reason;
UpdateBluetoothStatusInReceiverModel(BluetoothLEAdvertisementWatcherStatus.Stopped); //actually stopping but we lie
bool lockWasTaken = false;
try
{
Monitor.Enter(BluetoothWatcherEventSynchronisation, ref lockWasTaken);
{
bluetoothAdvertisementWatcher.Received -= Watcher_Received;
bluetoothAdvertisementWatcher.Stopped += (sender, args) =>
{
// clean up
if (bluetoothAdvertisementWatcher != null)
{
bluetoothAdvertisementWatcher.Stopped -= Watcher_Stopped;
bluetoothAdvertisementWatcher = null;
}
//notify continuation
promise.SetResult(reason);
};
bluetoothAdvertisementWatcher.Stop();
}
}
finally
{
if (lockWasTaken)
{
Monitor.Exit(BluetoothWatcherEventSynchronisation);
}
}
}
base.Stop();
return promise.Task;
}
Following these changes, the same crash is still occuring in the Windows.Devices.Bluetooth native assembly (as per above)
Edit 9: I've removed the automatic periodic start/stop and now the app has been stable for > 36 hours without a crash. So something inside this flow is causing the crashes. We originally added that to work around an issue with the advertisment watcher just stopping after a while, so we'd like to keep it if we can fix it.
The if statement if ((DateTime.Now - this.LastStartedTimestamp).TotalSeconds > 60) (and block) is currently commented.
I have opened a bug for windows universal here: https://wpdev.uservoice.com/forums/110705-universal-windows-platform/suggestions/37623343-bluetoothleadvertismentwatcher-advertismentreceiv

C# Pause one console application when other console application starts

I have one console application for testing purposes like following:
static void Main(string[] args)
{
do
{
for (int i = 0; i < 10000000; i++)
{
Console.WriteLine("Doing some endless loop");
Console.WriteLine(i.ToString());
}
} while (true);
}
As you can see the code is very basic, and I've set it up to endless loop in order to test what I would like to achieve.
The other console application is called "Updater" and I would like to to pause the "EndlessLoop" console application once the "Updater" application is started.
Does anyone knows if this is doable in c# .NET?
public static bool IsAppRunning()
{
foreach (Process process in Process.GetProcesses())
{
if (process.ProcessName.Contains("Updater"))
{
return true;
}
}
return false;
}
If you call this in while loop it tells you if Updater is running or not.
Not easy to communicate between 2 application
One proposition: When your console Updater starts, you create a file in folder C:\Temps\token.txt. Then, if your console EndlessLoop detects a file names token.txt in C:\Temps, you pause EndlessLoop

C# Timeout with Solidworks VBA Macro

I have a few functions in a Solidworks Addin which call on a VBA macro (Via the runMacro2 method) a co-worker has been working on for the last few weeks. In his code he calls a Solidworks function which, under certain, unknown conditions, hangs for a long period of time. How long seems to depend upon the size and quantity of bodies in the part. Considering at least one of the functions we want to run this from i automatic, this just wont do.
I have tried using the Thread.Join(int) method (shown below) but it doesnt work. I also tried modifying the code from this answer Close a MessageBox after several seconds with the same results. Is there anything I can do either in C# or VBA to handle a timeout for this without re-writing his entire macro?
public void runBB()
{
Stopwatch testStop = new Stopwatch();
Thread workerThread = new Thread(bbRun);
testStop.Start();
workerThread.Start();
if (!workerThread.Join(50))
{
workerThread.Abort();
testStop.Stop();
MessageBox.Show("Unable to generate Bounding Box after " + testStop.ElapsedMilliseconds/1000 + " seconds. Please enter data manually.", "Solidworks Derped Error.");
}
return;
}//Still uses Macro (2-5-16)
public static void bbRun()
{
iSwApp.RunMacro2(macroPath + "BOUNDING_BOX.swp", "test11", "main", 0, out runMacroError);
return;
}
I was getting this same exact issue with SOLIDWORKS hanging on an open of a file. Almost all reference on SO was that you should never do this, but in this scenario, you either have to close it or wait forever. In C# I created a callWithTimeout method:
private void callWithTimeout(Action action, int timeoutMilliseconds, String errorText) {
Thread threadToKill = null;
Action wrappedAction = () =>
{
threadToKill = Thread.CurrentThread;
action();
};
IAsyncResult result = wrappedAction.BeginInvoke(null, null);
if (result.AsyncWaitHandle.WaitOne(timeoutMilliseconds)) {
wrappedAction.EndInvoke(result);
} else {
threadToKill.Abort();
throw new TimeoutException(errorText);
}
}
Then the code that was hanging put in a block as such:
bool timedOut = false;
try {
callWithTimeout(delegate() {
// code that hangs here
}, 60000, "Operation timed out. SOLIDWORKS could not open the file. This file will be processed later.");
} catch (TimeoutException){
timedOut = true;
} finally {
if(timedOut) {
Process[] prs = Process.GetProcesses();
foreach (Process p in prs) {
if (p?.ProcessName.Equals("SLDWORKS") ?? false)
p?.Kill();
}
}
}

Start an offline ClickOnce Application and wait for Exit

I have deployed a ClickOnce Windows Forms application (App A)
Another application (App B) starts App A with a filename as parameter.
I do this with this Code
var basePath = Environment.GetFolderPath(Environment.SpecialFolder.Programs);
var location = String.Format(#"{0}\{1}\{2}\{3}",
basePath, "MyCompany", "MyProduct", "MyApp.appref-ms");
var fileName = #"c:\temp\somefile.ext";
var uri = new Uri(fileName).ToString();
Process.Start(location, uri);
App A grabs the file name from AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData[0] and show the content.
This works like a charm. However, now I want App B to wait for App A to exit.
But a call to Process.WaitForExit() returns instantly.
Is there a way to open a ClickOnce App and wait for it to exit? I can, if necessary, change the way the app is opend but the requirement is that I need to run the app as a ClickOnce app (I know that somewhere in my user profile AppData\Local\Apps\2.0\ folder the exe exists and can be started directly but If I do that ApplicationDeployment.IsNetworkDeployed is false and ApplicationDeployment.CurrentDeployment is null. In that I loose the ClickOnce Update Capabilities).
my suggestion would be to use Mutex in App A, and let App B check and wait for it. This is the cleanest way from my point of view.
App A does this when starts:
private static Mutex mutex;
public static void Main()
{
// if you want your app to be limited to a single instance
// across ALL SESSIONS (multiple users & terminal services), then use the following line instead:
// string mutexName = string.Format("Global\\{0}", ProgramInfo.AssemblyGuid);
var mutexName = string.Format("Local\\{0}", SOME_SHARED_GUID);
mutex = new Mutex(true, mutexName, out singleInstance);
if (singleInstance == false)
{
// that means your app has more than one instance running
// you need to decide what to do here.
}
// rest of initialization code
Application.Run();
// release the mutex so App B can continue
mutex.ReleaseMutex();
}
and App B just waits for the mutex to be released:
Process.Start(location, uri);
Thread.Sleep(5000); // give it 5 seconds or so to check for updates and start
var mutexName = string.Format("Local\\{0}", SOME_SHARED_GUID);
mutex = new Mutex(false, mutexName);
mutex.WaitOne();
The problem is that starting the appref-ms process does not actually start the application it starts the deployment manifest, which then launches the application itself, so the process you are starting exits straight away.
You can add a check to see when you application has started if you know the name (which I assume you do) like this:
string myAppName = "YourAppName";
DateTime startTime = DateTime.Now;
int newProcessId = 0;
List<int> runningProcessIds = new List<int>();
//find all the running processes and record their Ids
foreach (void proc_loopVariable in Process.GetProcessesByName(myAppName)) {
proc = proc_loopVariable;
runningProcessIds.Add(proc.Id);
}
//start the new process
Process.Start(location);
//wait for the new application to be started
while (!(Process.GetProcessesByName(myAppName).Count != runningProcessIds.Count)) {
//timeout if we have not seen the application start
if ((DateTime.Now - startTime).TotalSeconds > 30)
break;
}
//loop through all the running processes again to find the id of the one that has just started
foreach (void proc_loopVariable in Process.GetProcessesByName(myAppName)) {
proc = proc_loopVariable;
if (!runningProcessIds.Contains(proc.Id)) {
newProcessId = proc.Id;
break;
}
}
//wait for the application to finish
Process.GetProcessById(newProcessId).WaitForExit();
Debug.WriteLine("Finished");

Killing Java Process from C# Console App

I posted about this a little while ago, but I resolved the other issue and ran into one more. I am about to deploy this program to 28 hosting machines so I want to make sure this is working before I do so.
I wrote a little c# NET application that is basically a wrapper for a Java application, when my app starts, the Java app starts, when my app closes, it closes, and so on.
Everything works properly except that when I close my application, the Java application continues to run. When I create the process, I store the Process var in a variable outside of the methods, and then use that when my application goes to shutdown. For whatever reason though it is not terminating the Java application.
class Program
{
private static Process minecraftProcess;
public static void LaunchMinecraft(String file, String memoryValue)
{
String memParams = "-Xmx" + memoryValue + "M" + " -Xms" + memoryValue + "M ";
String args = memParams + "-jar " + file + " nogui";
ProcessStartInfo processInfo = new ProcessStartInfo("java.exe", args);
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
try
{
//using (Process minecraftProcess = Process.Start(processInfo))
using (minecraftProcess = Process.Start(processInfo))
{
minecraftProcess.WaitForExit();
}
}
catch
{
// Log Error
}
}
static void Main(string[] args)
{
Arguments CommandLine = new Arguments(args);
// Hook ProcessExit Event
AppDomain.CurrentDomain.ProcessExit += new EventHandler(Current_ProcessExit);
if (CommandLine["file"] != null && CommandLine["memory"] != null)
{
// Launch the Application (Command Line Parameters)
LaunchMinecraft(CommandLine["file"], CommandLine["memory"]);
}
else
{
// Launch the Application (Default Parameters)
LaunchMinecraft("minecraft_server.jar", "1024");
}
}
static void Current_ProcessExit(object sender, EventArgs e)
{
System.Threading.Thread.Sleep(10000);
// If we have an active Minecraft Service, Shut it down
if (minecraftProcess != null)
{
minecraftProcess.Kill();
}
}
}
You can't Sleep in a ProcessExit handler.
The documentation states:
The total execution time of all
ProcessExit event handlers is limited,
just as the total execution time of
all finalizers is limited at process
shutdown. The default is two seconds.
An unmanaged host can change this
execution time by calling the
ICLRPolicyManager::SetTimeout method
with the OPR_ProcessExit enumeration
value.
Nevermind, I just realized the minecraftProcess variable is static.
Don't know if you did not solve this issue by yourself but:
You should be aware that there are Start methods for instances (returning bool) and static (returning a object).
You should not use using with something other than using-local variables!
Just this should work fine:
minecraftProcess = Process.Start(processInfo)
minecraftProcess.WaitForExit();

Categories