mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-01-18 22:43:31 +08:00
Create better logging dedicated for Programs plugin
This commit is contained in:
parent
0eb3a5cab3
commit
3cf4b4ed8b
122
Plugins/Wox.Plugin.Program/Logger/ProgramLogger.cs
Normal file
122
Plugins/Wox.Plugin.Program/Logger/ProgramLogger.cs
Normal file
@ -0,0 +1,122 @@
|
||||
using NLog;
|
||||
using NLog.Config;
|
||||
using NLog.Targets;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security;
|
||||
using Wox.Infrastructure;
|
||||
|
||||
namespace Wox.Plugin.Program.Logger
|
||||
{
|
||||
/// <summary>
|
||||
/// The Program plugin has seen many issues recorded in the Wox 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.
|
||||
/// </summary>
|
||||
internal static class ProgramLogger
|
||||
{
|
||||
public const string DirectoryName = "Logs";
|
||||
|
||||
static ProgramLogger()
|
||||
{
|
||||
var path = Path.Combine(Constant.DataDirectory, DirectoryName, Constant.Version);
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
Directory.CreateDirectory(path);
|
||||
}
|
||||
|
||||
var configuration = new LoggingConfiguration();
|
||||
var target = new FileTarget();
|
||||
configuration.AddTarget("file", target);
|
||||
target.FileName = path.Replace(@"\", "/") + "/${shortdate}.txt";
|
||||
#if DEBUG
|
||||
var rule = new LoggingRule("*", LogLevel.Debug, target);
|
||||
#else
|
||||
var rule = new LoggingRule("*", LogLevel.Error, target);
|
||||
#endif
|
||||
configuration.LoggingRules.Add(rule);
|
||||
LogManager.Configuration = configuration;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 Wox should continue
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.Synchronized)]
|
||||
internal static void LogException(string message, Exception e)
|
||||
{
|
||||
//Index 0 is always empty.
|
||||
var parts = message.Split('|');
|
||||
var classname = parts[1];
|
||||
var callingMethodName = parts[2];
|
||||
var loadingProgramPath = parts[3];
|
||||
var interpretationMessage = parts[4];
|
||||
|
||||
Debug.WriteLine($"ERROR{message}");
|
||||
|
||||
var logger = LogManager.GetLogger("");
|
||||
|
||||
var innerExceptionNumber = 1;
|
||||
|
||||
var possibleResolution = "Not yet known";
|
||||
var errorStatus = "UNKNOWN";
|
||||
|
||||
logger.Error("------------- BEGIN Wox.Plugin.Program exception -------------");
|
||||
|
||||
do
|
||||
{
|
||||
if (IsKnownWinProgramError(e, callingMethodName) || IsKnownUWPProgramError(e, callingMethodName))
|
||||
{
|
||||
possibleResolution = "Can be ignored and Wox 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 Wox.Plugin.Program exception -------------");
|
||||
}
|
||||
|
||||
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"))
|
||||
return true;
|
||||
|
||||
if (callingMethodName == "XmlNamespaces")
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ using Windows.Management.Deployment;
|
||||
using AppxPackaing;
|
||||
using Shell;
|
||||
using Wox.Infrastructure;
|
||||
using Wox.Infrastructure.Logger;
|
||||
using Wox.Plugin.Program.Logger;
|
||||
using IStream = AppxPackaing.IStream;
|
||||
using Rect = System.Windows.Rect;
|
||||
|
||||
@ -84,7 +84,8 @@ namespace Wox.Plugin.Program.Programs
|
||||
else
|
||||
{
|
||||
var e = Marshal.GetExceptionForHR((int)hResult);
|
||||
Log.Exception($"|UWP.InitializeAppInfo|SHCreateStreamOnFileEx on path <{path}> failed with HResult <{hResult}> and location <{Location}>.", e);
|
||||
ProgramLogger.LogException($"|UWP|InitializeAppInfo|{path}" +
|
||||
"|Error caused while trying to get the details of the UWP program", e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,7 +109,9 @@ namespace Wox.Plugin.Program.Programs
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|UWP.XmlNamespaces|can't find namespaces for <{path}>");
|
||||
ProgramLogger.LogException($"|UWP|XmlNamespaces|{path}" +
|
||||
$"|Error occured while trying to get the XML from {path}", new ArgumentNullException());
|
||||
|
||||
return new string[] { };
|
||||
}
|
||||
}
|
||||
@ -131,15 +134,13 @@ namespace Wox.Plugin.Program.Programs
|
||||
}
|
||||
}
|
||||
|
||||
Log.Error($"|UWP.InitPackageVersion| Unknown Appmanifest version UWP <{FullName}> with location <{Location}>.");
|
||||
ProgramLogger.LogException($"|UWP|XmlNamespaces|{Location}" +
|
||||
"|Trying to get the package version of the UWP program, but a unknown UWP appmanifest version "
|
||||
+ $"{FullName} from location {Location} is returned.", new FormatException());
|
||||
|
||||
Version = PackageVersion.Unknown;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static Application[] All()
|
||||
{
|
||||
var windows10 = new Version(10, 0);
|
||||
@ -153,11 +154,20 @@ namespace Wox.Plugin.Program.Programs
|
||||
{
|
||||
u = new UWP(p);
|
||||
}
|
||||
#if !DEBUG
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|UWP.All|Can't convert Package to UWP for <{p.Id.FullName}>:", e);
|
||||
ProgramLogger.LogException("|UWP|All|An unexpected error occured and "
|
||||
+ $"unable to convert Package to UWP for {p.Id.FullName}", e);
|
||||
return new Application[] { };
|
||||
}
|
||||
#endif
|
||||
#if DEBUG //make developer aware and implement handling
|
||||
catch(Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
#endif
|
||||
return u.Apps;
|
||||
}).ToArray();
|
||||
|
||||
@ -186,18 +196,12 @@ namespace Wox.Plugin.Program.Programs
|
||||
ps = ps.Where(p =>
|
||||
{
|
||||
bool valid;
|
||||
try
|
||||
{
|
||||
var f = p.IsFramework;
|
||||
var d = p.IsDevelopmentMode;
|
||||
var path = p.InstalledLocation.Path;
|
||||
valid = !f && !d && !string.IsNullOrEmpty(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|UWP.CurrentUserPackages|Can't get package info for <{p.Id.FullName}>", e);
|
||||
valid = false;
|
||||
}
|
||||
|
||||
var f = p.IsFramework;
|
||||
var d = p.IsDevelopmentMode;
|
||||
var path = p.InstalledLocation.Path;
|
||||
valid = !f && !d && !string.IsNullOrEmpty(path);
|
||||
|
||||
return valid;
|
||||
});
|
||||
return ps;
|
||||
@ -382,7 +386,8 @@ namespace Wox.Plugin.Program.Programs
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|UWP.ResourceFromPri|Can't load null or empty result pri <{source}> with uwp location <{Package.Location}>.");
|
||||
ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Can't load null or empty result "
|
||||
+ $"pri {source} in uwp location {Package.Location}", new NullReferenceException());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
@ -395,7 +400,7 @@ namespace Wox.Plugin.Program.Programs
|
||||
// Microsoft.MicrosoftOfficeHub_17.7608.23501.0_x64__8wekyb3d8bbwe: ms-resource://Microsoft.MicrosoftOfficeHub/officehubintl/AppManifest_GetOffice_Description
|
||||
// Microsoft.BingFoodAndDrink_3.0.4.336_x64__8wekyb3d8bbwe: ms-resource:AppDescription
|
||||
var e = Marshal.GetExceptionForHR((int)hResult);
|
||||
Log.Exception($"|UWP.ResourceFromPri|Load pri failed <{source}> with HResult <{hResult}> and location <{Package.Location}>.", e);
|
||||
ProgramLogger.LogException($"|UWP|ResourceFromPri|{Package.Location}|Load pri failed {source} with HResult {hResult} and location {Package.Location}", e);
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
@ -474,13 +479,16 @@ namespace Wox.Plugin.Program.Programs
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|UWP.LogoPathFromUri| <{UserModelId}> can't find logo uri for <{uri}>, Package location <{Package.Location}>.");
|
||||
ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
|
||||
$"|{UserModelId} can't find logo uri for {uri} in package location: {Package.Location}", new FileNotFoundException());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|UWP.LogoPathFromUri| <{UserModelId}> cantains can't find extension for <{uri}> Package location <{Package.Location}>.");
|
||||
ProgramLogger.LogException($"|UWP|LogoPathFromUri|{Package.Location}" +
|
||||
$"|Unable to find extension from {uri} for {UserModelId} " +
|
||||
$"in package location {Package.Location}", new FileNotFoundException());
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
@ -506,7 +514,9 @@ namespace Wox.Plugin.Program.Programs
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|UWP.ImageFromPath|Can't get logo for <{UserModelId}> with path <{path}> and location <{Package.Location}>");
|
||||
ProgramLogger.LogException($"|UWP|ImageFromPath|{path}" +
|
||||
$"|Unable to get logo for {UserModelId} from {path} and" +
|
||||
$" located in {Package.Location}", new FileNotFoundException());
|
||||
return new BitmapImage(new Uri(Constant.ErrorIcon));
|
||||
}
|
||||
}
|
||||
@ -553,7 +563,10 @@ namespace Wox.Plugin.Program.Programs
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"|UWP.PlatedImage| Can't convert background string <{BackgroundColor}> to color for <{Package.Location}>.");
|
||||
ProgramLogger.LogException($"|UWP|PlatedImage|{Package.Location}" +
|
||||
$"|Unable to convert background string {BackgroundColor} " +
|
||||
$"to color for {Package.Location}", new InvalidOperationException());
|
||||
|
||||
return new BitmapImage(new Uri(Constant.ErrorIcon));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@ -10,7 +9,7 @@ using System.Text;
|
||||
using Microsoft.Win32;
|
||||
using Shell;
|
||||
using Wox.Infrastructure;
|
||||
using Wox.Infrastructure.Logger;
|
||||
using Wox.Plugin.Program.Logger;
|
||||
|
||||
namespace Wox.Plugin.Program.Programs
|
||||
{
|
||||
@ -125,18 +124,28 @@ namespace Wox.Plugin.Program.Programs
|
||||
|
||||
private static Win32 Win32Program(string path)
|
||||
{
|
||||
var p = new Win32
|
||||
try
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(path),
|
||||
IcoPath = path,
|
||||
FullPath = path,
|
||||
UniqueIdentifier = path,
|
||||
ParentDirectory = Directory.GetParent(path).FullName,
|
||||
Description = string.Empty,
|
||||
Valid = true,
|
||||
Enabled = true
|
||||
};
|
||||
return p;
|
||||
var p = new Win32
|
||||
{
|
||||
Name = Path.GetFileNameWithoutExtension(path),
|
||||
IcoPath = path,
|
||||
FullPath = path,
|
||||
UniqueIdentifier = path,
|
||||
ParentDirectory = Directory.GetParent(path).FullName,
|
||||
Description = string.Empty,
|
||||
Valid = true,
|
||||
Enabled = true
|
||||
};
|
||||
return p;
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32|Win32Program|{path}" +
|
||||
$"|Permission denied when trying to load the program from {path}", e);
|
||||
|
||||
return new Win32() { Valid = false, Enabled = false };
|
||||
}
|
||||
}
|
||||
|
||||
private static Win32 LnkProgram(string path)
|
||||
@ -184,27 +193,43 @@ namespace Wox.Plugin.Program.Programs
|
||||
catch (COMException e)
|
||||
{
|
||||
// C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\MiracastView.lnk always cause exception
|
||||
Log.Exception($"|Win32.LnkProgram|COMException when parsing shortcut <{path}> with HResult <{e.HResult}>", e);
|
||||
ProgramLogger.LogException($"|Win32|LnkProgram|{path}"+
|
||||
"|Error caused likely due to trying to get the description of the program", e);
|
||||
|
||||
program.Valid = false;
|
||||
return program;
|
||||
}
|
||||
#if !DEBUG //Only do a catch all in production. This is so make developer aware of any unhandled exception and add the exception handling in.
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Exception($"|Win32.LnkProgram|Exception when parsing shortcut <{path}>", e);
|
||||
ProgramLogger.LogException($"|Win32|LnkProgram|{path}" +
|
||||
"|An unexpected error occurred in the calling method LnkProgram", e);
|
||||
|
||||
program.Valid = false;
|
||||
return program;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private static Win32 ExeProgram(string path)
|
||||
{
|
||||
var program = Win32Program(path);
|
||||
var info = FileVersionInfo.GetVersionInfo(path);
|
||||
if (!string.IsNullOrEmpty(info.FileDescription))
|
||||
try
|
||||
{
|
||||
program.Description = info.FileDescription;
|
||||
var program = Win32Program(path);
|
||||
var info = FileVersionInfo.GetVersionInfo(path);
|
||||
if (!string.IsNullOrEmpty(info.FileDescription))
|
||||
{
|
||||
program.Description = info.FileDescription;
|
||||
}
|
||||
return program;
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32|ExeProgram|{path}" +
|
||||
$"|Permission denied when trying to load the program from {path}", e);
|
||||
|
||||
return new Win32() { Valid = false, Enabled = false };
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
private static IEnumerable<string> ProgramPaths(string directory, string[] suffixes)
|
||||
@ -227,14 +252,15 @@ namespace Wox.Plugin.Program.Programs
|
||||
}
|
||||
catch (DirectoryNotFoundException e)
|
||||
{
|
||||
Log.Exception($"|Program.Win32.ProgramPaths|skip directory(<{currentDirectory}>)", e);
|
||||
continue;
|
||||
ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" +
|
||||
"|The directory trying to load the program from does not exist", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
Log.Exception($"|Program.Win32.ProgramPaths|Don't have permission on <{currentDirectory}>", e);
|
||||
ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" +
|
||||
$"|Permission denied when trying to load programs from {currentDirectory}", e);
|
||||
}
|
||||
|
||||
try
|
||||
@ -246,7 +272,8 @@ namespace Wox.Plugin.Program.Programs
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
Log.Exception($"|Program.Win32.ProgramPaths|Don't have permission on <{currentDirectory}>", e);
|
||||
ProgramLogger.LogException($"|Win32|ProgramPaths|{currentDirectory}" +
|
||||
$"|Permission denied when trying to load programs from {currentDirectory}", e);
|
||||
}
|
||||
} while (folderQueue.Any());
|
||||
return files;
|
||||
@ -348,21 +375,30 @@ namespace Wox.Plugin.Program.Programs
|
||||
private static string GetProgramPathFromRegistrySubKeys(RegistryKey root, string subkey)
|
||||
{
|
||||
var path = string.Empty;
|
||||
|
||||
using (var key = root.OpenSubKey(subkey))
|
||||
try
|
||||
{
|
||||
if (key == null)
|
||||
using (var key = root.OpenSubKey(subkey))
|
||||
{
|
||||
if (key == null)
|
||||
return string.Empty;
|
||||
|
||||
var defaultValue = string.Empty;
|
||||
path = key.GetValue(defaultValue) as string;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return string.Empty;
|
||||
|
||||
var defaultValue = string.Empty;
|
||||
path = key.GetValue(defaultValue) as string;
|
||||
// fix path like this: ""\"C:\\folder\\executable.exe\""
|
||||
return path = path.Trim('"', ' ');
|
||||
}
|
||||
catch (Exception e) when (e is SecurityException || e is UnauthorizedAccessException)
|
||||
{
|
||||
ProgramLogger.LogException($"|Win32|GetProgramPathFromRegistrySubKeys|{path}" +
|
||||
$"|Permission denied when trying to load the program from {path}", e);
|
||||
|
||||
if (string.IsNullOrEmpty(path))
|
||||
return string.Empty;
|
||||
|
||||
// fix path like this: ""\"C:\\folder\\executable.exe\""
|
||||
return path = path.Trim('"', ' ');
|
||||
}
|
||||
}
|
||||
|
||||
private static Win32 GetProgramFromPath(string path)
|
||||
@ -383,23 +419,41 @@ namespace Wox.Plugin.Program.Programs
|
||||
|
||||
public static Win32[] All(Settings settings)
|
||||
{
|
||||
var programs = new List<Win32>().AsParallel();
|
||||
|
||||
var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes);
|
||||
programs = programs.Concat(unregistered);
|
||||
if (settings.EnableRegistrySource)
|
||||
try
|
||||
{
|
||||
var appPaths = AppPathsPrograms(settings.ProgramSuffixes);
|
||||
programs = programs.Concat(appPaths);
|
||||
}
|
||||
var programs = new List<Win32>().AsParallel();
|
||||
|
||||
if (settings.EnableStartMenuSource)
|
||||
var unregistered = UnregisteredPrograms(settings.ProgramSources, settings.ProgramSuffixes);
|
||||
programs = programs.Concat(unregistered);
|
||||
if (settings.EnableRegistrySource)
|
||||
{
|
||||
var appPaths = AppPathsPrograms(settings.ProgramSuffixes);
|
||||
programs = programs.Concat(appPaths);
|
||||
}
|
||||
|
||||
if (settings.EnableStartMenuSource)
|
||||
{
|
||||
var startMenu = StartMenuPrograms(settings.ProgramSuffixes);
|
||||
programs = programs.Concat(startMenu);
|
||||
}
|
||||
|
||||
return programs.ToArray();
|
||||
}
|
||||
#if DEBUG //This is to make developer aware of any unhandled exception and add in handling.
|
||||
catch (Exception e)
|
||||
{
|
||||
var startMenu = StartMenuPrograms(settings.ProgramSuffixes);
|
||||
programs = programs.Concat(startMenu);
|
||||
throw e;
|
||||
}
|
||||
#endif
|
||||
|
||||
return programs.ToArray();
|
||||
#if !DEBUG //Only do a catch all in production.
|
||||
catch (Exception e)
|
||||
{
|
||||
ProgramLogger.LogException("|Win32|All|Not available|An unexpected error occurred", e);
|
||||
|
||||
return new Win32[0];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,6 +49,9 @@
|
||||
<HintPath>..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
|
||||
<HintPath>..\..\packages\NLog.4.2.0\lib\net45\NLog.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="PresentationCore" />
|
||||
<Reference Include="PresentationFramework" />
|
||||
<Reference Include="ShObjIdlTlb">
|
||||
@ -71,6 +74,7 @@
|
||||
<Compile Include="AddProgramSource.xaml.cs">
|
||||
<DependentUpon>AddProgramSource.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="Logger\ProgramLogger.cs" />
|
||||
<Compile Include="Views\Commands\ProgramSettingDisplay.cs" />
|
||||
<Compile Include="FileChangeWatcher.cs" />
|
||||
<Compile Include="Views\Models\ProgramSource.cs" />
|
||||
|
@ -2,6 +2,7 @@
|
||||
<packages>
|
||||
<package id="JetBrains.Annotations" version="10.3.0" targetFramework="net452" />
|
||||
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net452" />
|
||||
<package id="NLog" version="4.2.0" targetFramework="net452" />
|
||||
<package id="System.Runtime" version="4.0.0" targetFramework="net452" />
|
||||
<package id="UwpDesktop" version="10.0.14393.3" targetFramework="net452" />
|
||||
</packages>
|
Loading…
Reference in New Issue
Block a user