Update: I have found a likely root cause; When the Maui application on Windows closes (either through clicking the 'x' on the window or programatically (Application.Current.Quit()), the app is kept alive as a background process instead of the PID dying. If I force end the PID, the lock on the file is removed and reopening the app will work again. This is why it works on the first try for a fresh install; there is no background process of the app to lock the file. Why is the Maui App not fully dying on close and staying as a background process??
I have run into an interesting problem in my MAUI .Net 8 application that uses the MetroLog.Maui Package (v2.1.0).
Once I started testing on a different machine installing the app via MSIX, then all of a sudden we started getting an error where when using the MetroLog StreamingFileLogger options to save the compressed logs to a file, where the MetroLog log file is being used by another process. I can't even find this file on my system where it says it's located. This was crashing my app and I added a try/catch to be able to display the error message in a popup in the attached screenshot.
When the app is installed, and not being run through Visual Studio (Release or Debug configuration), and I try to get the Compressed Logs with the StreamingFileLogger. Notably, this seems to only happen after the first time a new install successfully gets the compressed logs. It will work once, then when you try to get CompressedLogs again it will keep the file locked and crash/exception out on any subsequent attempts
I've seen a few GitHub threads where someone might have the same issue, but with no responses: text text
Note: I have since added try/catch/finally statements to ensure that all the memory streams and archives are disposed of after use
GetCompressedLogs() break and the file has been locked by a previous attempt:
private async Task SaveWindowsLogs()
{
// This breaks!
MemoryStream memoryStream = await _logController.GetCompressedLogs() ?? new MemoryStream();
if (null == memoryStream || memoryStream.Length == 0)
{
_ = Toast.Make("No compressed logs are available").Show();
return;
}
using (memoryStream)
{
FileSaverResult fileSaverReult = await FileSaver.Default.SaveAsync("Logs.zip", memoryStream);
string toastMsg;
if (fileSaverReult.IsSuccessful)
{
toastMsg = $"Logs Succesfully saved at {fileSaverReult.FilePath}";
}
else
{
toastMsg = $"Log Share Failed \n {fileSaverReult.Exception.Message}";
}
_ = Toast.Make(toastMsg).Show();
}
}
It works fine using the InMemoryLogger:
// This works great!
private async Task<List<string>> GetSessionLogs()
{
return await _logController.GetLogList() ?? new List<string>();
}
I was wondering if anyone had any ideas how I could prevent this from happening.
Here are my logger settings in MauiProgram.cs:
builder.Logging
#if DEBUG
.AddTraceLogger(options =>
{
options.MinLevel = LogLevel.Trace;
options.MaxLevel = LogLevel.Critical;
}) // Will write to the Debug Output
.AddDebug().SetMinimumLevel(LogLevel.Trace)
#endif
.AddStreamingFileLogger(
options =>
{
options.RetainDays = 1;
options.MinLevel = LogLevel.Debug;
options.MaxLevel = LogLevel.Critical;
})
.AddInMemoryLogger(options =>
{
options.MaxLines = MauiConstants.MAX_LOGS;
options.MinLevel = LogLevel.Debug;
options.MaxLevel = LogLevel.Critical;
});
I figured it out down to several levels, and have well formed work-around if not a true fix.
As mentioned in my Update edit and in the comment, the root cause was that ultimately, when I closed my application the process did not stop. It stuck around as a background process. This would cause the previous PID of MetroLog to hold on to the log file. When reopening the app, an additional instance of my app would spin up, and be unable to access the Log file that was locked by the original PID.
By using Process Explorer from SysInternals toolset, I was able to find that the app was not closing due to a specific uiohook thread, that was being initiated by my use of SharpHook:
Task.Run(() =>
{
TaskPoolGlobalHook hook = new TaskPoolGlobalHook();
hook.KeyPressed += (sender, e) =>
{
ReceiveKey(e.Data.KeyCode);
};
hook.RunAsync();
}).ConfigureAwait(false);
The RunAsync was making this hook run on a separate thread that the main app could not end when I closed my application. I could not find a way to make this a background thread (closable by main) or to dispose of the hook properly when I needed it running alongside the app.
My workaround was to configure a Lifecycle event that would run a force close of the environment on stop in my MauiProgram.cs:
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.ConfigureLifecycleEvents(AppLifeCycle =>
{
#if WINDOWS
AppLifeCycle.AddWindows(windows =>
{
windows.OnClosed((app, e) =>
{
Environment.Exit(0);
});
});
#endif
})