Handling Global Exception Xamarin | Droid | iOS - c#

We all know that mobile is compact platform where we have to look lots of things while building an application. It could be anything e.g. Memory Performance Resolutions Architecture Implementation etc. We never know when and what causes app crash a big ISSUE while playing with the app, It could happen anytime
e.g. App Launch, Load Screen, API Call, Binding Data, Loading Images etc.
And trust me sometime its really hard to find where and what cause an issue in app. I saw many post on forums, tech community and groups which is related to the same issue, where peoples usually asking questions as:
App Crashing at launching.
App Crash at Splash Screen loading.
App Crash while Image showing.
App Crashing while binding data from api.
How to identify issue and where it causes?

Purpose: Our purpose here to grab an exception's stack trace data that help us to identify what exactly causes the issue whether in Release Mode or Debug Mode. We will be able to understand the issue and the reason that causes it. We will store this data in a text file that will be store in device storage.
Solution: Alternatively you can make your own insight grabber that will give you you app insight and clue if something went wrong while testing the app. Its will be your, you can tweak like you want. let's dive to try{} and catch{} globally.
Create a Helper Class file that has a method to generate a Text file for exception data.
public static class ExceptionFileWriter
{
#region Property File Path
static string FilePath
{
get
{
string path = string.Empty;
var _fileName = "Fatal.txt";
#if __IOS__
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); // Documents folder C:\ddddd
string libraryPath = Path.Combine(documentsPath, "..", "Library"); // Library folder C:\dddd\...\library
path = Path.Combine(libraryPath, _fileName); //c:\ddddd\...\library\NBCCSeva.db3
#else
#if __ANDROID__
string dir = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.ToString(), "Exception");
if (Directory.Exists(dir))
return Path.Combine(dir, _fileName);
path= Path.Combine(Directory.CreateDirectory(dir).FullName, _fileName);
#endif
#endif
return path;
}
}
#endregion
#region ToLog Exception
public static void ToLogUnhandledException(this Exception exception)
{
try
{
var errorMessage = String.Format("Time: {0}\r\nError: Unhandled Exception\r\n{1}\n\n", DateTime.Now, string.IsNullOrEmpty(exception.StackTrace) ? exception.ToString() : exception.StackTrace);
File.WriteAllText(FilePath, errorMessage);
}
catch (Exception ex)
{
// just suppress any error logging exceptions
}
}
#endregion
}
Time to implement code: Subscribe following events inside your app's Application file or Splash Activity. I'm using Application in this case.
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
[Application]
public class ExceptionHandlingApp : Application
{
#region Constructor
public ExceptionHandlingApp(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
#endregion
#region OnCreate
public override void OnCreate()
{
base.OnCreate();
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
}
#endregion
#region Task Schedular Exception
private static void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs unobservedTaskExceptionEventArgs)
{
var newExc = new Exception("TaskSchedulerOnUnobservedTaskException", unobservedTaskExceptionEventArgs.Exception);
newExc.ToLogUnhandledException();
}
#endregion
#region Current Domain Exception
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
var newExc = new Exception("CurrentDomainOnUnhandledException", unhandledExceptionEventArgs.ExceptionObject as Exception);
newExc.ToLogUnhandledException();
}
#endregion
}
Note: You can find exceptions record file in Device Storage | File Manager > Exception Folder > fatal.txt
Cheers!!

Beside of doing it on your own you can also use Xamarin.Insights as it is free to use for Xamarin users and has got implementations for all platforms.
You receive usage reports, crash reports etc. online without the need for the user to send you a log file manually.
The only thing you need to do to receive crash reports is to initialize Xamarin.Insights on startup of your app:
Insights.HasPendingCrashReport += (sender, isStartupCrash) =>
{
if (isStartupCrash) {
Insights.PurgePendingCrashReports().Wait();
}
};
Insights.Initialize("Your API Key");

Related

WindowsService FileSystemWatcher Crash on InputOnChanged call

I have a Windows Service that I successfully deploys, successfully works when debugging, but crashes when a file is added to the monitored directory.
I thought it was an issue with my impersonator being used between the OnStart and InputOnChanged, but the crash still happens when I run the service under my own domain user.
I have EventLog set to write to it's own application source, but none of my WriteEntrys are called except the one in the OnStart function. I've been trying different tweaks and feel like I need another set of eyes to see something i'm not:
protected override void OnStart(string[] args)
{
//using(Impersonator context = new Impersonator("XXXXX", "XXXXXXXX", "XXXXXXXXXX"))
//{
try
{
this.fileWatcherService = new FileSystemWatcher(baseFilePath, "*.txt")
{
NotifyFilter = NotifyFilters.LastWrite
};
fileWatcherService.Changed += InputOnChanged;
fileWatcherService.EnableRaisingEvents = true;
eventLog.WriteEntry("XXXX-XXXXX-Service Started");
}
catch (Exception ex)
{
eventLog.WriteEntry($"{baseFilePath} was not accessible to monitor because {ex.Message}", EventLogEntryType.Error);
}
}
protected void InputOnChanged(object source, FileSystemEventArgs e)
{
if (e.ChangeType == WatcherChangeTypes.Changed)
{
eventLog.WriteEntry($"Change Detected - File {e.Name}", EventLogEntryType.Information);
try
{
fileWatcherService.EnableRaisingEvents = false;
eventLog.WriteEntry("Starting process for file: " + e.Name);
if (!File.Exists(e.FullPath))
{
eventLog.WriteEntry($"{e.Name} was not accessible", EventLogEntryType.Error);
}
//Copy File to backup copy before formatting
File.Copy(e.FullPath
, Path.Combine(#"\\XXXXXX\XXXXXX\XXXXXXXX\XXXXXXXXXX XXX XXXXXXXXX\XXX\XXXX\XXXX\BackupFiles", GetBackupFileName(e.Name))
, false);
//Save formatted file to directory
List<string> lines = System.IO.File.ReadAllLines(e.FullPath).ToList();
File.WriteAllText(Path.Combine(#"\\XXXXXX\XXXXXX\XXXXXXXX\XXXXXXXXXX XXX XXXXXXXXX\XXX\XXXX\XXXX\FormattedFiles", GetFormattedFileName(e.Name))
, CSVFormatService.FormatLines(lines));
//Remove file from base path to prevent re-processing
File.Delete(e.FullPath);
eventLog.WriteEntry($"Successfully moved {e.FullPath}", EventLogEntryType.Information);
}
catch (Exception ex)
{
eventLog.WriteEntry("XXXX-XXXXX-Service exception: " + ex.Message, EventLogEntryType.Error);
}
finally
{
fileWatcherService.EnableRaisingEvents = true;
}
}
}
Would expect eventLog.WriteEntry("Starting process for file: " + e.Name); to update the Application log at least because that is before any attempt to touch a file, but I don't see that in the log. However, the service runs until I place a test file in the monitored directory, and then crashes with a unhandled exception of file does not exist
When building out these services, make sure you reference a shared project correctly. This issue was caused by adding a reference to a class library to the project, but the .dll was missing when deploying the service. So when the service tried to access the .dll to process data a FileNotFound exception was being thrown. This also make sense as to why the exception was marked as unhandled.

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

How can I log LoggingChannel LogMessages to the StorageFile?

I want to add logging of exceptions to my Windows Store App. Based on an idea from here, I've started off with this code in App.xaml.cs:
sealed partial class App : Application
{
private LoggingChannel channel;
private LoggingSession session;
/// <summary>
/// Initializes the singleton application object. This is the first line of authored code
/// executed, and as such is the logical equivalent of main() or WinMain().
/// </summary>
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
channel = new LoggingChannel("PlatypiChannel");
session = new LoggingSession("PlatypiSession");
session.AddLoggingChannel(channel, LoggingLevel.Error);
UnhandledException += Application_UnhandledException;
}
async private void Application_UnhandledException(object sender, UnhandledExceptionEventArgs ex)
{
ex.Handled = true;
String exceptionCornucopia = String.Format("Message0 == {0}; Message1 == {1}; HResult == {2}; Inner Ex == {3}; StackTrace == {4}", ex.Message, ex.Exception.Message, ex.Exception.HResult, ex.Exception.InnerException, ex.Exception.StackTrace);
channel.LogMessage(exceptionCornucopia, LoggingLevel.Error);
// not seeing how this saves the channel's logged messages...???
StorageFile logFile = await session.SaveToFileAsync(ApplicationData.Current.LocalFolder, "CrashLog");
}
As the comment indicates, it seems to me the last line simply saves a file named "CrashLog" to the LocalFolder. But how do the LogMessages get into that file? There is obviously a key piece missing here.
I know that this question has been open for a long time, but I just want to provide an answer for anyone else finding this.
The secret here is that the LogMessages are all written into the LoggingChannel, which itself has previously been registered with the LoggingSession:
session.AddLoggingChannel(channel, LoggingLevel.Error);
When the session is then saved to a file, it obviously knows about the associated channels and where to search for pending log messages.

Handling AppDomain.CurrentDomain.UnhandledException in windows service

I have window service which acts as a sync software. I want to add unhanded exception logging on my service, so I modified my program.cs like this:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.ControlAppDomain)]
static void Main()
{
// Register Unhandled Exception Handler
AppDomain.CurrentDomain.UnhandledException +=
new UnhandledExceptionEventHandler(UnhandledExceptionHandler);
// Run Service
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service()
};
ServiceBase.Run(ServicesToRun);
}
static void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args)
{
// Get Exception
Exception ex = (Exception)args.ExceptionObject;
// Generate Error
string ErrorMessage = String.Format(
"Error: {0}\r\n" +
"Runtime Terminating: {1}\r\n----- ----- ----- ----- ----- -----\r\n\r\n" +
"{2}\r\n\r\n####################################\r\n",
ex.Message,
args.IsTerminating,
ex.StackTrace.Trim());
// Write Error To File
try
{
using (StreamWriter sw = File.AppendText("UnhandledExceptions.log"))
sw.WriteLine(errorMessage);
}
catch { }
}
}
Then on my Service.cs file, in the OnStart method, I added a throw new Exception("test"); to see if unhanded exceptions are being logged to file as expected.
When I start my service, it stops immediately as expected; however it doesn't seem to be logging the exception to the specified file.
Any idea what I am doing wrong here? Thanks in advance for any help.
Before you ask, my service runs as Local Service and the directory where my service .exe runs from (c:\mysync) already has Local Service added in the security tab with full read/write access.
OnStart is called in Service base class inside try-catch block. If an exception happens on this stage it catches it and just set a status 1 as a result and do not throw it further:
string[] args = (string[]) state;
try
{
this.OnStart(args);
.....
}
catch (Exception ex)
{
this.WriteEventLogEntry(Res.GetString("StartFailed", new object[1]
{
(object) ((object) ex).ToString()
}), EventLogEntryType.Error);
this.status.currentState = 1;
}
As a result you can find a record in EventLogs, but you can't catch it as an unhanded domain exception, as there is no such exception.
using (StreamWriter sw = File.AppendText("UnhandledExceptions.log"))
It is forever a really bad idea to not use full path names for files (like c:\foo\bar.log). Especially in a service, you have very little control over the default directory for your service. Because it is started by the service control manager, not by the user from the command prompt or a desktop shortcut.
So high odds that you are just looking at the wrong file. The real one probably ended up being written to c:\windows\system32 (or syswow64). The operating system directories are normally write protected but that doesn't work for a service, they run with a highly privileged account so can litter the hard drive anywhere.
Always use full path names. Using the EventLog instead is highly recommended.

What is causing this DatabaseFileLockedException when trying to open a db4o database in an ASP.NET MVC app?

I'm building a small web application with ASP.NET MVC 2, using db4o as a datastore.
I have added an HttpModule—as per the example here—to give the application access to the db4o database, and everything is working perfectly on my development machine under the VS2008 ASP.NET Development Server.
However, when I deploy the app to my web host and try to access it, I get a DatabaseFileLockedException at the line where the HttpModule tries to open the database file. But there should be nothing else accessing the file; indeed on first run of the app it will only just have been created when this exception gets thrown.
The web host's servers are running IIS 7 on Windows Server 2008, and the application is running under Full Trust. It is a sub-application, in case that makes any difference.
I can't work out why this error is occurring on the live server, but not locally on my development server. Can anyone help me out or suggest what I should do next?
That's a mistake in the example-code. It assumes that the HttpModule.Init is only called once, which isn't necessarily true. Depending how your application is configured, it can be called multiple times. To fix this, check in the HttpModule-Handler if the instance is already there:
using System;
using System.Configuration;
using System.Web;
using Db4objects.Db4o;
namespace Db4oDoc.WebApp.Infrastructure
{
public class Db4oProvider : IHttpModule
{
private const string DataBaseInstance = "db4o-database-instance";
private const string SessionKey = "db4o-session";
// #example: open database when the application starts
public void Init(HttpApplication context)
{
if (null==HttpContext.Current.Application[DataBaseInstance])
{
HttpContext.Current.Application[DataBaseInstance] = OpenDatabase();
}
RegisterSessionCreation(context);
}
private IEmbeddedObjectContainer OpenDatabase()
{
string relativePath = ConfigurationSettings.AppSettings["DatabaseFileName"];
string filePath = HttpContext.Current.Server.MapPath(relativePath);
return Db4oEmbedded.OpenFile(filePath);
}
// #end example
// #example: close the database when the application shuts down
public void Dispose()
{
IDisposable toDispose = HttpContext.Current.Application[DataBaseInstance] as IDisposable;
if (null != toDispose)
{
toDispose.Dispose();
}
}
// #end example
// #example: provide access to the database
public static IObjectContainer Database
{
get { return (IObjectContainer)HttpContext.Current.Items[SessionKey]; }
}
// #end example
// #example: A object container per request
private void RegisterSessionCreation(HttpApplication httpApplication)
{
httpApplication.BeginRequest += OpenSession;
httpApplication.EndRequest += CloseSession;
}
private void OpenSession(object sender, EventArgs e)
{
IEmbeddedObjectContainer container =
(IEmbeddedObjectContainer)HttpContext.Current.Application[DataBaseInstance];
IObjectContainer session = container.OpenSession();
HttpContext.Current.Items[SessionKey] = session;
}
private void CloseSession(object sender, EventArgs e)
{
if (HttpContext.Current.Items[SessionKey] != null)
{
IObjectContainer session = (IObjectContainer)HttpContext.Current.Items[SessionKey];
session.Dispose();
}
}
// #end example
}
}
As alternative you could use the Application_Start from the Global.apsx, which is called only once for sure.
You have another problem here.
When AppPools restart there can be an overlap when the old AppPool is finishing request and the new AppPool is servicing new requests.
During this time you will have two processes trying to access the same db4o file
To get around this you can use something like the hack below.
Note the use of Db4oFactory.OpenServer instead of Db4oEmbedded.OpenFile. This allows the use of transactions on a more fine grained basis.
public IObjectServer OpenServer()
{
Logger.Debug("Waiting to open db4o server.");
var attempts = 0;
do
{
try
{
return Db4oFactory.OpenServer(fileName, 0);
}
catch (DatabaseFileLockedException ex)
{
attempts++;
if (attempts > 10)
{
throw new Exception("Couldn't open db4o server. Giving up!", ex);
}
Logger.Warn("Couldn't open db4o server. Trying again in 5sec.");
Thread.Sleep(5.Seconds());
}
} while (true);
}
Hope this helps
Sounds like permission issues if it works on dev. Stick a notepad file in the same directory and try to open that with some bare bones file code. I bet you'll have the same issue.

Categories