using NLog;
using System;
using System.IO;
using System.Runtime.CompilerServices;
using System.Security;
namespace Flow.Launcher.Plugin.Program.Logger
{
///
/// The Program plugin has seen many issues recorded in the Flow Launcher repo related to various loading of Windows programs.
/// This is a dedicated logger for this Program plugin with the aim to output a more friendlier message and clearer
/// log that will allow debugging to be quicker and easier.
///
internal static class ProgramLogger
{
public const string DirectoryName = "Logs";
///
/// Logs an exception
///
[MethodImpl(MethodImplOptions.Synchronized)]
internal static void LogException(string classname, string callingMethodName, string loadingProgramPath,
string interpretationMessage, Exception e)
{
var logger = LogManager.GetLogger("");
var innerExceptionNumber = 1;
var possibleResolution = "Not yet known";
var errorStatus = "UNKNOWN";
logger.Error("------------- BEGIN Flow.Launcher.Plugin.Program exception -------------");
do
{
if (IsKnownWinProgramError(e, callingMethodName) || IsKnownUWPProgramError(e, callingMethodName))
{
possibleResolution = "Can be ignored and Flow Launcher should still continue, however the program may not be loaded";
errorStatus = "KNOWN";
}
var calledMethod = e.TargetSite != null ? e.TargetSite.ToString() : e.StackTrace;
calledMethod = string.IsNullOrEmpty(calledMethod) ? "Not available" : calledMethod;
logger.Error($"\nException full name: {e.GetType().FullName}"
+ $"\nError status: {errorStatus}"
+ $"\nClass name: {classname}"
+ $"\nCalling method: {callingMethodName}"
+ $"\nProgram path: {loadingProgramPath}"
+ $"\nInnerException number: {innerExceptionNumber}"
+ $"\nException message: {e.Message}"
+ $"\nException error type: HResult {e.HResult}"
+ $"\nException thrown in called method: {calledMethod}"
+ $"\nPossible interpretation of the error: {interpretationMessage}"
+ $"\nPossible resolution: {possibleResolution}");
innerExceptionNumber++;
e = e.InnerException;
} while (e != null);
logger.Error("------------- END Flow.Launcher.Plugin.Program exception -------------");
}
///
/// Please follow exception format: |class name|calling method name|loading program path|user friendly message that explains the error
/// => Example: |Win32|LnkProgram|c:\..\chrome.exe|Permission denied on directory, but Flow Launcher should continue
///
[MethodImpl(MethodImplOptions.Synchronized)]
internal static void LogException(string message, Exception e)
{
//Index 0 is always empty.
var parts = message.Split('|', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length < 4)
{
var logger = LogManager.GetLogger("");
logger.Error(e, $"fail to log exception in program logger, parts length is too small: {parts.Length}, message: {message}");
return;
}
var classname = parts[0];
var callingMethodName = parts[1];
var loadingProgramPath = parts[2];
var interpretationMessage = parts[3];
LogException(classname, callingMethodName, loadingProgramPath, interpretationMessage, e);
}
private static bool IsKnownWinProgramError(Exception e, string callingMethodName)
{
if (e.TargetSite?.Name == "GetDescription" && callingMethodName == "LnkProgram")
return true;
if (e is SecurityException || e is UnauthorizedAccessException || e is DirectoryNotFoundException)
return true;
return false;
}
private static bool IsKnownUWPProgramError(Exception e, string callingMethodName)
{
if (((e.HResult == -2147024774 || e.HResult == -2147009769) && callingMethodName == "ResourceFromPri")
|| (e.HResult == -2147024894 && (callingMethodName == "LogoPathFromUri" || callingMethodName == "ImageFromPath"))
|| (e.HResult == -2147024864 && callingMethodName == "InitializeAppInfo"))
return true;
if (callingMethodName == "XmlNamespaces")
return true;
return false;
}
}
}