mirror of
https://github.com/gnh1201/welsonjs.git
synced 2025-02-06 15:04:58 +00:00
fix #154 (Migrate parent.Log() to Microsoft.Extensions.Logging)
Migrate parent.Log() to Microsoft.Extensions.Logging
This commit is contained in:
parent
53bc51a3af
commit
30c0e20eae
|
@ -3,9 +3,9 @@
|
|||
// https://github.com/gnh1201/welsonjs
|
||||
using ClamAV.Net.Client;
|
||||
using ClamAV.Net.Client.Results;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Diagnostics.Eventing.Reader;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.ServiceProcess;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
@ -15,6 +15,7 @@ namespace WelsonJS.Service
|
|||
{
|
||||
private EventLogWatcher eventLogWatcher;
|
||||
private ServiceMain parent;
|
||||
private ILogger logger;
|
||||
private enum EventType: int
|
||||
{
|
||||
FileCreate = 11,
|
||||
|
@ -80,11 +81,16 @@ namespace WelsonJS.Service
|
|||
catch (Exception ex)
|
||||
{
|
||||
clamAvConenctionString = "tcp://127.0.0.1:3310";
|
||||
this.parent.Log($"Failed to read the address because of {ex.Message}. Set default: {clamAvConenctionString}");
|
||||
logger.LogInformation($"Failed to read the address because of {ex.Message}. Set default: {clamAvConenctionString}");
|
||||
}
|
||||
ConnectToClamAv().Start();
|
||||
}
|
||||
|
||||
public void SetLogger(ILogger _logger)
|
||||
{
|
||||
logger = _logger;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
try
|
||||
|
@ -103,7 +109,7 @@ namespace WelsonJS.Service
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parent.Log($"Could not reach to the Sysmon service: {ex.Message}");
|
||||
logger.LogInformation($"Could not reach to the Sysmon service: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -140,8 +146,8 @@ namespace WelsonJS.Service
|
|||
string image = e.EventRecord.Properties[(int)FileCreateEvent.Image]?.Value?.ToString();
|
||||
string fileName = e.EventRecord.Properties[(int)FileCreateEvent.TargetFilename]?.Value?.ToString();
|
||||
|
||||
parent.Log($"> Detected the file creation: {fileName}");
|
||||
parent.Log(parent.DispatchServiceEvent("fileCreated", new string[] {
|
||||
logger.LogInformation($"> Detected the file creation: {fileName}");
|
||||
logger.LogInformation(parent.DispatchServiceEvent("fileCreated", new string[] {
|
||||
ruleName,
|
||||
processId,
|
||||
image,
|
||||
|
@ -150,7 +156,7 @@ namespace WelsonJS.Service
|
|||
|
||||
if (clamAvClient != null)
|
||||
{
|
||||
parent.Log($"> Starting the ClamAV scan: {fileName}");
|
||||
logger.LogInformation($"> Starting the ClamAV scan: {fileName}");
|
||||
Task.Run(async () =>
|
||||
{
|
||||
await ScanWithClamAv(fileName);
|
||||
|
@ -170,8 +176,8 @@ namespace WelsonJS.Service
|
|||
string desinationPort = e.EventRecord.Properties[(int)NetworkConnectionEvent.DestinationPort]?.Value?.ToString();
|
||||
string dstinationAddress = $"{protocol}://{destinationIp}:{desinationPort}";
|
||||
|
||||
parent.Log($"> Detected the network connection: {dstinationAddress}");
|
||||
parent.Log(parent.DispatchServiceEvent("networkConnected", new string[] {
|
||||
logger.LogInformation($"> Detected the network connection: {dstinationAddress}");
|
||||
logger.LogInformation(parent.DispatchServiceEvent("networkConnected", new string[] {
|
||||
ruleName,
|
||||
processId,
|
||||
image,
|
||||
|
@ -191,8 +197,8 @@ namespace WelsonJS.Service
|
|||
string eventType = e.EventRecord.Properties[(int)RegistryEvent.EventType]?.Value?.ToString();
|
||||
string targetObject = e.EventRecord.Properties[(int)RegistryEvent.TargetObject]?.Value?.ToString();
|
||||
|
||||
parent.Log($"> Detected the registry modification: {targetObject}");
|
||||
parent.Log(parent.DispatchServiceEvent("registryModified", new string[] {
|
||||
logger.LogInformation($"> Detected the registry modification: {targetObject}");
|
||||
logger.LogInformation(parent.DispatchServiceEvent("registryModified", new string[] {
|
||||
ruleName,
|
||||
processId,
|
||||
image,
|
||||
|
@ -209,12 +215,12 @@ namespace WelsonJS.Service
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parent.Log($"Failed to process the event bacause of {ex.Message}.");
|
||||
logger.LogInformation($"Failed to process the event bacause of {ex.Message}.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.Log("The event instance was null.");
|
||||
logger.LogInformation("The event instance was null.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,11 +236,11 @@ namespace WelsonJS.Service
|
|||
// Get ClamAV engine and virus database version
|
||||
VersionResult result = await clamAvClient.GetVersionAsync().ConfigureAwait(false);
|
||||
|
||||
parent.Log($"ClamAV version {result.ProgramVersion}, Virus database version {result.VirusDbVersion}");
|
||||
logger.LogInformation($"ClamAV version {result.ProgramVersion}, Virus database version {result.VirusDbVersion}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parent.Log($"Failed to read the address because of {ex.Message}. {clamAvConenctionString}");
|
||||
logger.LogInformation($"Failed to read the address because of {ex.Message}. {clamAvConenctionString}");
|
||||
clamAvClient = null;
|
||||
}
|
||||
}
|
||||
|
@ -243,8 +249,8 @@ namespace WelsonJS.Service
|
|||
{
|
||||
ScanResult res = await clamAvClient.ScanRemotePathAsync(remotePath).ConfigureAwait(false);
|
||||
|
||||
parent.Log($"> Scan result: Infected={res.Infected}, VirusName={res.VirusName}");
|
||||
parent.Log(parent.DispatchServiceEvent("avScanResult", new string[] {
|
||||
logger.LogInformation($"> Scan result: Infected={res.Infected}, VirusName={res.VirusName}");
|
||||
logger.LogInformation(parent.DispatchServiceEvent("avScanResult", new string[] {
|
||||
res.Infected.ToString(),
|
||||
res.VirusName
|
||||
}));
|
||||
|
|
|
@ -9,12 +9,14 @@ using System.ServiceProcess;
|
|||
using Grpc.Net.Client;
|
||||
using Grpc.Net.Client.Web;
|
||||
using System.Net.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
public class HeartbeatClient
|
||||
{
|
||||
private readonly HeartbeatService.HeartbeatServiceClient _client;
|
||||
private ILogger logger;
|
||||
private readonly GrpcChannel _channel;
|
||||
private int HeartbeatInterval;
|
||||
private ServiceMain _parent;
|
||||
|
@ -38,7 +40,7 @@ namespace WelsonJS.Service
|
|||
catch (Exception ex)
|
||||
{
|
||||
serverAddress = "http://localhost:50051";
|
||||
_parent.Log($"Failed to read the address because of {ex.Message}. Set default: {serverAddress}");
|
||||
logger.LogInformation($"Failed to read the address because of {ex.Message}. Set default: {serverAddress}");
|
||||
}
|
||||
|
||||
var httpClientHandler = new HttpClientHandler();
|
||||
|
@ -51,7 +53,12 @@ namespace WelsonJS.Service
|
|||
_client = new HeartbeatService.HeartbeatServiceClient(_channel);
|
||||
|
||||
clientId = GetSystemUUID().ToLower();
|
||||
_parent.Log($"Use the client ID: {clientId}");
|
||||
logger.LogInformation($"Use the client ID: {clientId}");
|
||||
}
|
||||
|
||||
public void SetLogger(ILogger _logger)
|
||||
{
|
||||
logger = _logger;
|
||||
}
|
||||
|
||||
public async Task StartHeartbeatAsync()
|
||||
|
@ -69,14 +76,14 @@ namespace WelsonJS.Service
|
|||
|
||||
await call.RequestStream.WriteAsync(request);
|
||||
await call.RequestStream.CompleteAsync();
|
||||
_parent.Log("Sent heartbeat");
|
||||
logger.LogInformation("Sent heartbeat");
|
||||
|
||||
await Task.Delay(HeartbeatInterval); // Wait for HeartbeatInterval
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_parent.Log("Heartbeat request stream failed: " + ex.Message);
|
||||
logger.LogInformation("Heartbeat request stream failed: " + ex.Message);
|
||||
}
|
||||
|
||||
// 서버 응답을 수신하는 작업
|
||||
|
@ -85,16 +92,16 @@ namespace WelsonJS.Service
|
|||
while (await call.ResponseStream.MoveNext())
|
||||
{
|
||||
var response = call.ResponseStream.Current;
|
||||
_parent.Log("Heartbeat response received: " + response.IsAlive);
|
||||
logger.LogInformation("Heartbeat response received: " + response.IsAlive);
|
||||
}
|
||||
}
|
||||
catch (RpcException ex)
|
||||
{
|
||||
_parent.Log($"gRPC error: {ex.Status.Detail}");
|
||||
logger.LogInformation($"gRPC error: {ex.Status.Detail}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_parent.Log($"Unexpected error: {ex.Message}");
|
||||
logger.LogInformation($"Unexpected error: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -118,7 +125,7 @@ namespace WelsonJS.Service
|
|||
while (await eventCall.ResponseStream.MoveNext())
|
||||
{
|
||||
var response = eventCall.ResponseStream.Current;
|
||||
_parent.Log($"Received event from server: {response.EventType} with args: {string.Join(", ", response.Args)}");
|
||||
logger.LogInformation($"Received event from server: {response.EventType} with args: {string.Join(", ", response.Args)}");
|
||||
}
|
||||
}
|
||||
finally
|
||||
|
@ -148,7 +155,7 @@ namespace WelsonJS.Service
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_parent.Log($"An error occurred while retrieving the system UUID: {ex.Message}");
|
||||
logger.LogInformation($"An error occurred while retrieving the system UUID: {ex.Message}");
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
|
|
63
WelsonJS.Toolkit/WelsonJS.Service/Logging/FileLogger.cs
Normal file
63
WelsonJS.Toolkit/WelsonJS.Service/Logging/FileLogger.cs
Normal file
|
@ -0,0 +1,63 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System.IO;
|
||||
using System;
|
||||
|
||||
namespace WelsonJS.Service.Logging
|
||||
{
|
||||
public class FileLogger : ILogger
|
||||
{
|
||||
private string loggingDirectory;
|
||||
private string categoryName;
|
||||
private static object _lock = new object();
|
||||
|
||||
public FileLogger(string _loggingDirectory, string _categoryName = "welsonjs")
|
||||
{
|
||||
loggingDirectory = _loggingDirectory;
|
||||
categoryName = _categoryName;
|
||||
}
|
||||
|
||||
public IDisposable BeginScope<TState>(TState state)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEnabled(LogLevel logLevel)
|
||||
{
|
||||
//return logLevel == LogLevel.Trace;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
|
||||
{
|
||||
if (formatter != null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!Directory.Exists(loggingDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(loggingDirectory);
|
||||
}
|
||||
|
||||
string path = Path.Combine(loggingDirectory, $"{categoryName}_service.{DateTime.Now.ToString("yyyy-MM-dd")}.log");
|
||||
string nl = Environment.NewLine;
|
||||
string err = "";
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
err = nl + exception.GetType() + ": " + exception.Message + nl + exception.StackTrace + nl;
|
||||
}
|
||||
|
||||
File.AppendAllText(path, logLevel.ToString() + ": [" + DateTime.Now.ToString() + "] " + formatter(state, exception) + nl + err);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(LogLevel.Warning.ToString() + ": [" + DateTime.Now.ToString() + "] Failed to write a log file. " + ex.Message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WelsonJS.Service.Logging
|
||||
{
|
||||
public static class FileLoggerExtensions
|
||||
{
|
||||
public static ILoggerFactory AddDirectory(this ILoggerFactory factory, string loggingDirectory)
|
||||
{
|
||||
factory.AddProvider(new FileLoggerProvider(loggingDirectory));
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace WelsonJS.Service.Logging
|
||||
{
|
||||
public class FileLoggerProvider : ILoggerProvider
|
||||
{
|
||||
private string loggingDirectory;
|
||||
|
||||
public FileLoggerProvider(string _loggingDirectory)
|
||||
{
|
||||
loggingDirectory = _loggingDirectory;
|
||||
}
|
||||
|
||||
public ILogger CreateLogger(string categoryName)
|
||||
{
|
||||
return new FileLogger(loggingDirectory, categoryName);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
// Dispose
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,6 +15,7 @@ using System.Windows.Forms;
|
|||
using System.Linq;
|
||||
using Tesseract;
|
||||
using WelsonJS.Service;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
public class ScreenMatch
|
||||
{
|
||||
|
@ -105,6 +106,7 @@ public class ScreenMatch
|
|||
}
|
||||
|
||||
private ServiceMain parent;
|
||||
private ILogger logger;
|
||||
private List<Bitmap> templateImages;
|
||||
private string templateDirectoryPath;
|
||||
private string outputDirectoryPath;
|
||||
|
@ -130,7 +132,7 @@ public class ScreenMatch
|
|||
private void SetBusy(bool busy)
|
||||
{
|
||||
this.busy = busy;
|
||||
parent.Log($"State changed: busy={busy}");
|
||||
logger.LogInformation($"State changed: busy={busy}");
|
||||
}
|
||||
|
||||
public class TemplateInfo
|
||||
|
@ -198,7 +200,7 @@ public class ScreenMatch
|
|||
{
|
||||
screen_time_mode = null;
|
||||
screen_time_params = null;
|
||||
this.parent.Log($"Failed to read from configration file: {ex.Message}");
|
||||
logger.LogInformation($"Failed to read from configration file: {ex.Message}");
|
||||
}
|
||||
|
||||
if (!String.IsNullOrEmpty(screen_time_params))
|
||||
|
@ -238,14 +240,14 @@ public class ScreenMatch
|
|||
case "backward":
|
||||
{
|
||||
isSearchFromEnd = true;
|
||||
this.parent.Log("Use the backward search when screen time");
|
||||
logger.LogInformation("Use the backward search when screen time");
|
||||
break;
|
||||
}
|
||||
|
||||
case "save":
|
||||
{
|
||||
isSaveToFile = true;
|
||||
this.parent.Log("Will be save an image file when capture the screens");
|
||||
logger.LogInformation("Will be save an image file when capture the screens");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -330,6 +332,11 @@ public class ScreenMatch
|
|||
LoadTemplateImages();
|
||||
}
|
||||
|
||||
public void SetLogger(ILogger _logger)
|
||||
{
|
||||
logger = _logger;
|
||||
}
|
||||
|
||||
public void SetMode(string mode)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(mode))
|
||||
|
@ -358,7 +365,7 @@ public class ScreenMatch
|
|||
catch (Exception ex)
|
||||
{
|
||||
files = new string[]{};
|
||||
parent.Log($"Failed to read the directory structure: {ex.Message}");
|
||||
logger.LogInformation($"Failed to read the directory structure: {ex.Message}");
|
||||
}
|
||||
|
||||
foreach (var file in files)
|
||||
|
@ -370,12 +377,12 @@ public class ScreenMatch
|
|||
if (!String.IsNullOrEmpty(altpath))
|
||||
{
|
||||
realpath = altpath;
|
||||
parent.Log($"Use the alternative image: {realpath}");
|
||||
logger.LogInformation($"Use the alternative image: {realpath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
realpath = file;
|
||||
parent.Log($"Use the default image: {realpath}");
|
||||
logger.LogInformation($"Use the default image: {realpath}");
|
||||
}
|
||||
|
||||
Bitmap bitmap = new Bitmap(realpath)
|
||||
|
@ -452,11 +459,11 @@ public class ScreenMatch
|
|||
Height = image.Height
|
||||
};
|
||||
|
||||
parent.Log($"Trying match the template {templateName} on the screen {i}...");
|
||||
logger.LogInformation($"Trying match the template {templateName} on the screen {i}...");
|
||||
|
||||
if (!String.IsNullOrEmpty(nextTemplateInfo.FileName) && templateName != nextTemplateInfo.FileName)
|
||||
{
|
||||
parent.Log($"Ignored the template {templateName}");
|
||||
logger.LogInformation($"Ignored the template {templateName}");
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -477,7 +484,7 @@ public class ScreenMatch
|
|||
{
|
||||
string out_filepath = Path.Combine(outputDirectoryPath, out_filename);
|
||||
((Bitmap)out_mainImage.Clone()).Save(out_filepath);
|
||||
parent.Log($"Screenshot saved: {out_filepath}");
|
||||
logger.LogInformation($"Screenshot saved: {out_filepath}");
|
||||
}
|
||||
|
||||
// List to store the positions of matched templates in the main image
|
||||
|
@ -488,7 +495,7 @@ public class ScreenMatch
|
|||
{
|
||||
Bitmap outdatedImage = null;
|
||||
|
||||
parent.Log($"Finding a previous screen of {nextTemplateInfo.FileName}...");
|
||||
logger.LogInformation($"Finding a previous screen of {nextTemplateInfo.FileName}...");
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -499,7 +506,7 @@ public class ScreenMatch
|
|||
{
|
||||
if (((SampleInfo)outdatedImage.Tag).FileName == nextTemplateInfo.FileName)
|
||||
{
|
||||
parent.Log($"Found the previous screen of {nextTemplateInfo.FileName}");
|
||||
logger.LogInformation($"Found the previous screen of {nextTemplateInfo.FileName}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -507,7 +514,7 @@ public class ScreenMatch
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parent.Log($"Error finding a previous screen: {ex.Message}");
|
||||
logger.LogInformation($"Error finding a previous screen: {ex.Message}");
|
||||
}
|
||||
|
||||
// Find the matching positions of the outdated image in the main image
|
||||
|
@ -515,16 +522,16 @@ public class ScreenMatch
|
|||
matchPositions = FindTemplate(out_mainImage, outdatedImage);
|
||||
if (matchPositions.Count > 0)
|
||||
{
|
||||
parent.Log("Match found with the outdated image");
|
||||
logger.LogInformation("Match found with the outdated image");
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.Log("No match found with the outdated image");
|
||||
logger.LogInformation("No match found with the outdated image");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.Log("Not found a outdated image");
|
||||
logger.LogInformation("Not found a outdated image");
|
||||
matchPositions = new List<Point>();
|
||||
}
|
||||
}
|
||||
|
@ -553,18 +560,18 @@ public class ScreenMatch
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parent.Log($"Ignore the match. {ex.Message}");
|
||||
logger.LogInformation($"Ignore the match. {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (results.Count > 0)
|
||||
{
|
||||
parent.Log("Match found");
|
||||
logger.LogInformation("Match found");
|
||||
}
|
||||
else
|
||||
{
|
||||
parent.Log($"No match found");
|
||||
logger.LogInformation($"No match found");
|
||||
}
|
||||
|
||||
templateCurrentIndex = ++templateCurrentIndex % templateImages.Count;
|
||||
|
@ -622,24 +629,24 @@ public class ScreenMatch
|
|||
else
|
||||
{
|
||||
outdatedSamples.Enqueue(croppedNodupBitmap);
|
||||
parent.Log($"Added to the image queue. {templateName}");
|
||||
logger.LogInformation($"Added to the image queue. {templateName}");
|
||||
}
|
||||
}
|
||||
|
||||
// if use Clipboard
|
||||
if (sampleClipboard.Contains(templateName))
|
||||
{
|
||||
parent.Log($"Trying to use the clipboard... {templateName}");
|
||||
logger.LogInformation($"Trying to use the clipboard... {templateName}");
|
||||
Thread th = new Thread(new ThreadStart(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Clipboard.SetImage((Bitmap)croppedBitmap.Clone());
|
||||
parent.Log($"Copied the image to Clipboard");
|
||||
logger.LogInformation($"Copied the image to Clipboard");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parent.Log($"Failed to copy to the clipboard: {ex.Message}");
|
||||
logger.LogInformation($"Failed to copy to the clipboard: {ex.Message}");
|
||||
}
|
||||
}));
|
||||
th.SetApartmentState(ApartmentState.STA);
|
||||
|
@ -657,14 +664,14 @@ public class ScreenMatch
|
|||
{
|
||||
text = page.GetText();
|
||||
|
||||
parent.Log($"Mean confidence: {page.GetMeanConfidence()}");
|
||||
parent.Log($"Text (GetText): {text}");
|
||||
logger.LogInformation($"Mean confidence: {page.GetMeanConfidence()}");
|
||||
logger.LogInformation($"Text (GetText): {text}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parent.Log($"Failed to OCR: {ex.Message}");
|
||||
logger.LogInformation($"Failed to OCR: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -680,7 +687,7 @@ public class ScreenMatch
|
|||
EnumDisplaySettings(screen.DeviceName, -1, ref dm);
|
||||
|
||||
var scalingFactor = Math.Round(Decimal.Divide(dm.dmPelsWidth, screen.Bounds.Width), 2);
|
||||
parent.Log($"Resolved the screen scale: {scalingFactor}");
|
||||
logger.LogInformation($"Resolved the screen scale: {scalingFactor}");
|
||||
|
||||
int adjustedWidth = (int)(screenSize.Width * scalingFactor);
|
||||
int adjustedHeight = (int)(screenSize.Height * scalingFactor);
|
||||
|
@ -743,13 +750,13 @@ public class ScreenMatch
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parent.Log($"Ignore the match. {ex.Message}");
|
||||
logger.LogInformation($"Ignore the match. {ex.Message}");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
parent.Log($"Error {ex.Message}");
|
||||
logger.LogInformation($"Error {ex.Message}");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -34,19 +34,22 @@ using System.Collections.Generic;
|
|||
using WelsonJS.TinyINIController;
|
||||
using System.Collections;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using WelsonJS.Service.Logging;
|
||||
using Grpc.Core.Logging;
|
||||
|
||||
namespace WelsonJS.Service
|
||||
{
|
||||
public partial class ServiceMain : ServiceBase
|
||||
{
|
||||
private readonly string appName = "WelsonJS";
|
||||
private readonly static string applicationName = "WelsonJS";
|
||||
private static List<Timer> timers;
|
||||
private Microsoft.Extensions.Logging.ILogger logger;
|
||||
private string workingDirectory;
|
||||
private string scriptName;
|
||||
private string scriptFilePath;
|
||||
private string scriptText;
|
||||
private ScriptControl scriptControl;
|
||||
private string logFilePath;
|
||||
private string[] args;
|
||||
private bool disabledHeartbeat = false;
|
||||
private bool disabledScreenTime = false;
|
||||
|
@ -68,8 +71,10 @@ namespace WelsonJS.Service
|
|||
// set service arguments
|
||||
this.args = args;
|
||||
|
||||
// set the log file path
|
||||
logFilePath = Path.Combine(Path.GetTempPath(), "welsonjs_service.log");
|
||||
// set the logger
|
||||
ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
|
||||
factory.AddDirectory(Path.GetTempPath());
|
||||
logger = factory.CreateLogger(applicationName);
|
||||
|
||||
// mapping arguments to each variables
|
||||
var arguments = ParseArguments(this.args);
|
||||
|
@ -109,13 +114,13 @@ namespace WelsonJS.Service
|
|||
// set working directory
|
||||
if (string.IsNullOrEmpty(workingDirectory))
|
||||
{
|
||||
workingDirectory = Path.Combine(Path.GetTempPath(), appName);
|
||||
Log("Working directory not provided. Using default value: " + workingDirectory);
|
||||
workingDirectory = Path.Combine(Path.GetTempPath(), applicationName);
|
||||
logger.LogInformation("Working directory not provided. Using default value: " + workingDirectory);
|
||||
|
||||
if (!Directory.Exists(workingDirectory))
|
||||
{
|
||||
Directory.CreateDirectory(workingDirectory);
|
||||
Log("Directory created: " + workingDirectory);
|
||||
logger.LogInformation("Directory created: " + workingDirectory);
|
||||
}
|
||||
}
|
||||
Directory.SetCurrentDirectory(workingDirectory);
|
||||
|
@ -135,7 +140,7 @@ namespace WelsonJS.Service
|
|||
}
|
||||
else
|
||||
{
|
||||
Log($"Configuration file not found: {settingsFilePath}");
|
||||
logger.LogInformation($"Configuration file not found: {settingsFilePath}");
|
||||
}
|
||||
|
||||
// read configrations from settings.ini
|
||||
|
@ -174,7 +179,7 @@ namespace WelsonJS.Service
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"{configName} is ignored: {ex.Message}");
|
||||
logger.LogInformation($"{configName} is ignored: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +188,7 @@ namespace WelsonJS.Service
|
|||
if (string.IsNullOrEmpty(scriptName))
|
||||
{
|
||||
scriptName = "defaultService";
|
||||
Log($"Script name not provided. Using default value: {scriptName}");
|
||||
logger.LogInformation($"Script name not provided. Using default value: {scriptName}");
|
||||
}
|
||||
|
||||
// set path of the script
|
||||
|
@ -193,6 +198,7 @@ namespace WelsonJS.Service
|
|||
if (!disabledHeartbeat)
|
||||
{
|
||||
HeartbeatClient heartbeatClient = new HeartbeatClient(this);
|
||||
heartbeatClient.SetLogger(logger);
|
||||
Task.Run(heartbeatClient.StartHeartbeatAsync);
|
||||
Task.Run(heartbeatClient.StartEventListenerAsync);
|
||||
}
|
||||
|
@ -211,10 +217,10 @@ namespace WelsonJS.Service
|
|||
}
|
||||
else
|
||||
{
|
||||
Log("Disabled the User Interactive Mode. (e.g., OnScreenTime)");
|
||||
logger.LogInformation("Disabled the User Interactive Mode. (e.g., OnScreenTime)");
|
||||
}
|
||||
|
||||
Log(appName + " Service Loaded");
|
||||
logger.LogInformation(applicationName + " Service Loaded");
|
||||
}
|
||||
|
||||
public IniFile GetSettingsHandler()
|
||||
|
@ -241,7 +247,7 @@ namespace WelsonJS.Service
|
|||
// Check exists the entry script file
|
||||
if (File.Exists(scriptFilePath))
|
||||
{
|
||||
Log($"Script file found: {scriptFilePath}");
|
||||
logger.LogInformation($"Script file found: {scriptFilePath}");
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -281,35 +287,36 @@ namespace WelsonJS.Service
|
|||
}
|
||||
|
||||
// initialize
|
||||
Log(DispatchServiceEvent("start", startArguments));
|
||||
logger.LogInformation(DispatchServiceEvent("start", startArguments));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"Failed to start because of {ex.Message}");
|
||||
logger.LogInformation($"Failed to start because of {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log($"Script file not found: {scriptFilePath}");
|
||||
logger.LogInformation($"Script file not found: {scriptFilePath}");
|
||||
}
|
||||
|
||||
// Trace a Sysmon file events (If Sysinternals Sysmon installed)
|
||||
if (!disabledFileMonitor)
|
||||
{
|
||||
fileEventMonitor = new FileEventMonitor(this, workingDirectory);
|
||||
fileEventMonitor.SetLogger(logger);
|
||||
fileEventMonitor.Start();
|
||||
|
||||
Log("File Event Monitor Started");
|
||||
logger.LogInformation("File Event Monitor Started");
|
||||
}
|
||||
else
|
||||
{
|
||||
Log("File Event Monitor is Disabled");
|
||||
logger.LogInformation("File Event Monitor is Disabled");
|
||||
}
|
||||
|
||||
// Start all the registered timers
|
||||
timers.ForEach(timer => timer?.Start());
|
||||
|
||||
Log(appName + " Service Started");
|
||||
logger.LogInformation(applicationName + " Service Started");
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
|
@ -323,16 +330,16 @@ namespace WelsonJS.Service
|
|||
// dispatch stop callback
|
||||
try
|
||||
{
|
||||
Log(DispatchServiceEvent("stop"));
|
||||
logger.LogInformation(DispatchServiceEvent("stop"));
|
||||
scriptControl?.Reset();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log("Exception when stop: " + ex.Message);
|
||||
logger.LogInformation("Exception when stop: " + ex.Message);
|
||||
}
|
||||
scriptControl = null;
|
||||
|
||||
Log(appName + " Service Stopped");
|
||||
logger.LogInformation(applicationName + " Service Stopped");
|
||||
}
|
||||
|
||||
private void OnUserInteractiveEnvironment()
|
||||
|
@ -341,13 +348,14 @@ namespace WelsonJS.Service
|
|||
if (GetSystemMetrics(SM_REMOTESESSION) > 0)
|
||||
{
|
||||
disabledScreenTime = true;
|
||||
Log("This application may not work correctly in a remote desktop session");
|
||||
logger.LogInformation("This application may not work correctly in a remote desktop session");
|
||||
}
|
||||
|
||||
// set screen timer
|
||||
if (!disabledScreenTime)
|
||||
{
|
||||
screenMatcher = new ScreenMatch(this, workingDirectory);
|
||||
screenMatcher.SetLogger(logger);
|
||||
|
||||
Timer screenTimer = new Timer
|
||||
{
|
||||
|
@ -356,13 +364,13 @@ namespace WelsonJS.Service
|
|||
screenTimer.Elapsed += OnScreenTime;
|
||||
timers.Add(screenTimer);
|
||||
|
||||
Log("Screen Time Event Enabled");
|
||||
logger.LogInformation("Screen Time Event Enabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
disabledScreenTime = true;
|
||||
|
||||
Log("Screen Time Event Disabled");
|
||||
logger.LogInformation("Screen Time Event Disabled");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -370,11 +378,11 @@ namespace WelsonJS.Service
|
|||
{
|
||||
try
|
||||
{
|
||||
Log(DispatchServiceEvent("elapsedTime"));
|
||||
logger.LogInformation(DispatchServiceEvent("elapsedTime"));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log("Exception when elapsed time: " + ex.Message);
|
||||
logger.LogInformation("Exception when elapsed time: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -385,7 +393,7 @@ namespace WelsonJS.Service
|
|||
List<ScreenMatchResult> matchedResults = screenMatcher.CaptureAndMatch();
|
||||
matchedResults.ForEach(result =>
|
||||
{
|
||||
Log(DispatchServiceEvent("screenTemplateMatched", new string[]
|
||||
logger.LogInformation(DispatchServiceEvent("screenTemplateMatched", new string[]
|
||||
{
|
||||
result.FileName,
|
||||
result.ScreenNumber.ToString(),
|
||||
|
@ -396,7 +404,7 @@ namespace WelsonJS.Service
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"Waiting a next screen time... {ex.Message}");
|
||||
logger.LogInformation($"Waiting a next screen time... {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -416,7 +424,7 @@ namespace WelsonJS.Service
|
|||
}
|
||||
else
|
||||
{
|
||||
Log("InvokeScriptMethod Ignored: " + methodName);
|
||||
logger.LogInformation("InvokeScriptMethod Ignored: " + methodName);
|
||||
}
|
||||
|
||||
return "void";
|
||||
|
@ -467,7 +475,7 @@ namespace WelsonJS.Service
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log($"Use all templates because of {ex.Message}");
|
||||
logger.LogInformation($"Use all templates because of {ex.Message}");
|
||||
}
|
||||
|
||||
return new ScreenMatch.TemplateInfo(templateName, index);
|
||||
|
@ -485,27 +493,5 @@ namespace WelsonJS.Service
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
public void Log(string message)
|
||||
{
|
||||
string _message = $"{DateTime.Now}: {message}";
|
||||
|
||||
if (Environment.UserInteractive)
|
||||
{
|
||||
Console.WriteLine(_message);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (StreamWriter writer = new StreamWriter(logFilePath, true))
|
||||
{
|
||||
writer.WriteLine(_message);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"LOGGING FAILED: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,13 +51,13 @@ namespace WelsonJS.Service
|
|||
}
|
||||
else
|
||||
{
|
||||
parent.Log($"Error parsing line: '{pair}'.");
|
||||
throw new Exception($"Error parsing line: '{pair}'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
parent.Log($"Error loading variable file: {ex.Message}");
|
||||
Console.WriteLine($"Error loading variable file: {ex.Message}");
|
||||
userVariables = new Dictionary<string, string>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -125,12 +125,42 @@
|
|||
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.8.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Extensions.Configuration, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Configuration.8.0.0\lib\net462\Microsoft.Extensions.Configuration.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Configuration.Abstractions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Configuration.Abstractions.8.0.0\lib\net462\Microsoft.Extensions.Configuration.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Configuration.Binder, Version=8.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Configuration.Binder.8.0.2\lib\net462\Microsoft.Extensions.Configuration.Binder.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection, Version=8.0.0.1, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.8.0.1\lib\net462\Microsoft.Extensions.DependencyInjection.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=8.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.8.0.2\lib\net462\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging, Version=8.0.0.1, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Logging.8.0.1\lib\net462\Microsoft.Extensions.Logging.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=8.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Logging.Abstractions.8.0.2\lib\net462\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging.Configuration, Version=8.0.0.1, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Logging.Configuration.8.0.1\lib\net462\Microsoft.Extensions.Logging.Configuration.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Logging.Console, Version=8.0.0.1, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Logging.Console.8.0.1\lib\net462\Microsoft.Extensions.Logging.Console.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Options, Version=8.0.0.2, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Options.8.0.2\lib\net462\Microsoft.Extensions.Options.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Options.ConfigurationExtensions, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Options.ConfigurationExtensions.8.0.0\lib\net462\Microsoft.Extensions.Options.ConfigurationExtensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Extensions.Primitives, Version=8.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Extensions.Primitives.8.0.0\lib\net462\Microsoft.Extensions.Primitives.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="RestSharp, Version=112.1.0.0, Culture=neutral, PublicKeyToken=598062e77f915f75, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\RestSharp.112.1.0\lib\net48\RestSharp.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -138,6 +168,7 @@
|
|||
<Reference Include="System.Buffers, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
<Reference Include="System.Configuration.Install" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=8.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
|
@ -159,6 +190,7 @@
|
|||
<Reference Include="System.Numerics.Vectors, Version=4.1.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -185,6 +217,9 @@
|
|||
<ItemGroup>
|
||||
<Compile Include="FileEventMonitor.cs" />
|
||||
<Compile Include="HeartbeatClient.cs" />
|
||||
<Compile Include="Logging\FileLogger.cs" />
|
||||
<Compile Include="Logging\FileLoggerExtensions.cs" />
|
||||
<Compile Include="Logging\FileLoggerProvider.cs" />
|
||||
<Compile Include="Model\FileMatchResult.cs" />
|
||||
<Compile Include="ServiceMain.cs">
|
||||
<SubType>Component</SubType>
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Extensions.DependencyInjection.Abstractions" publicKeyToken="adb9793829ddae60" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.1" newVersion="8.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.2" newVersion="8.0.0.2" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="RestSharp" publicKeyToken="598062e77f915f75" culture="neutral" />
|
||||
|
@ -87,6 +87,14 @@
|
|||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.0.1" newVersion="4.2.0.1" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Extensions.Options" publicKeyToken="adb9793829ddae60" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.2" newVersion="8.0.0.2" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.Extensions.Configuration.Binder" publicKeyToken="adb9793829ddae60" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.0.0.2" newVersion="8.0.0.2" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
|
|
|
@ -10,8 +10,18 @@
|
|||
<package id="Grpc.Net.Common" version="2.66.0" targetFramework="net48" />
|
||||
<package id="Grpc.Tools" version="2.67.0" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="Microsoft.Bcl.AsyncInterfaces" version="8.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Configuration" version="8.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Configuration.Abstractions" version="8.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Configuration.Binder" version="8.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.DependencyInjection" version="8.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="8.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Logging" version="8.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Logging.Abstractions" version="8.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Logging.Configuration" version="8.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Logging.Console" version="8.0.1" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Options" version="8.0.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Options.ConfigurationExtensions" version="8.0.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Extensions.Primitives" version="8.0.0" targetFramework="net48" />
|
||||
<package id="RestSharp" version="112.1.0" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.5.1" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="8.0.1" targetFramework="net48" />
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user