first commit
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Drawing;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
|
||||
namespace LibreHardwareMonitor.Utilities;
|
||||
|
||||
public class EmbeddedResources
|
||||
{
|
||||
public static Image GetImage(string name)
|
||||
{
|
||||
name = "LibreHardwareMonitor.Resources." + name;
|
||||
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
|
||||
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
if (names[i].Replace('\\', '.') == name)
|
||||
{
|
||||
using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(names[i]))
|
||||
{
|
||||
// "You must keep the stream open for the lifetime of the Image."
|
||||
Image image = Image.FromStream(stream);
|
||||
|
||||
// so we just create a copy of the image
|
||||
Bitmap bitmap = new Bitmap(image);
|
||||
|
||||
// and dispose it right here
|
||||
image.Dispose();
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Bitmap(1, 1);
|
||||
}
|
||||
|
||||
public static Icon GetIcon(string name)
|
||||
{
|
||||
name = "LibreHardwareMonitor.Resources." + name;
|
||||
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
if (names[i].Replace('\\', '.') == name)
|
||||
{
|
||||
using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(names[i]))
|
||||
{
|
||||
return new Icon(stream);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,754 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using LibreHardwareMonitor.Hardware;
|
||||
using LibreHardwareMonitor.UI;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace LibreHardwareMonitor.Utilities;
|
||||
|
||||
public class HttpServer
|
||||
{
|
||||
private readonly HttpListener _listener;
|
||||
private readonly Node _root;
|
||||
private Thread _listenerThread;
|
||||
|
||||
public HttpServer(Node node, string ip, int port, bool authEnabled = false, string userName = "", string password = "")
|
||||
{
|
||||
_root = node;
|
||||
ListenerIp = ip;
|
||||
ListenerPort = port;
|
||||
AuthEnabled = authEnabled;
|
||||
UserName = userName;
|
||||
Password = password;
|
||||
|
||||
try
|
||||
{
|
||||
_listener = new HttpListener { IgnoreWriteExceptions = true };
|
||||
}
|
||||
catch (PlatformNotSupportedException)
|
||||
{
|
||||
_listener = null;
|
||||
}
|
||||
}
|
||||
|
||||
~HttpServer()
|
||||
{
|
||||
if (PlatformNotSupported)
|
||||
return;
|
||||
|
||||
StopHttpListener();
|
||||
_listener.Abort();
|
||||
}
|
||||
|
||||
public bool AuthEnabled { get; set; }
|
||||
|
||||
public string ListenerIp { get; set; }
|
||||
|
||||
public int ListenerPort { get; set; }
|
||||
|
||||
public string Password
|
||||
{
|
||||
get { return PasswordSHA256; }
|
||||
set { PasswordSHA256 = ComputeSHA256(value); }
|
||||
}
|
||||
|
||||
public bool PlatformNotSupported
|
||||
{
|
||||
get { return _listener == null; }
|
||||
}
|
||||
|
||||
public string UserName { get; set; }
|
||||
|
||||
private string PasswordSHA256 { get; set; }
|
||||
|
||||
public bool StartHttpListener()
|
||||
{
|
||||
if (PlatformNotSupported)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
if (_listener.IsListening)
|
||||
return true;
|
||||
|
||||
// validate that the selected IP exists (it could have been previously selected before switching networks)
|
||||
IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
|
||||
bool ipFound = false;
|
||||
foreach (IPAddress ip in host.AddressList)
|
||||
{
|
||||
if (ListenerIp == ip.ToString())
|
||||
{
|
||||
ipFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ipFound)
|
||||
{
|
||||
// default to behavior of previous version if we don't know what interface to use.
|
||||
ListenerIp = "+";
|
||||
}
|
||||
|
||||
string prefix = "http://" + ListenerIp + ":" + ListenerPort + "/";
|
||||
|
||||
_listener.Prefixes.Clear();
|
||||
_listener.Prefixes.Add(prefix);
|
||||
_listener.Realm = "Libre Hardware Monitor";
|
||||
_listener.AuthenticationSchemes = AuthEnabled ? AuthenticationSchemes.Basic : AuthenticationSchemes.Anonymous;
|
||||
_listener.Start();
|
||||
|
||||
if (_listenerThread == null)
|
||||
{
|
||||
_listenerThread = new Thread(HandleRequests);
|
||||
_listenerThread.Start();
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool StopHttpListener()
|
||||
{
|
||||
if (PlatformNotSupported)
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
_listenerThread?.Abort();
|
||||
_listener.Stop();
|
||||
_listenerThread = null;
|
||||
}
|
||||
catch (HttpListenerException)
|
||||
{ }
|
||||
catch (ThreadAbortException)
|
||||
{ }
|
||||
catch (NullReferenceException)
|
||||
{ }
|
||||
catch (Exception)
|
||||
{ }
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void HandleRequests()
|
||||
{
|
||||
while (_listener.IsListening)
|
||||
{
|
||||
IAsyncResult context = _listener.BeginGetContext(ListenerCallback, _listener);
|
||||
context.AsyncWaitHandle.WaitOne();
|
||||
}
|
||||
}
|
||||
|
||||
public static IDictionary<string, string> ToDictionary(NameValueCollection col)
|
||||
{
|
||||
IDictionary<string, string> dict = new Dictionary<string, string>();
|
||||
foreach (string k in col.AllKeys)
|
||||
{
|
||||
dict.Add(k, col[k]);
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
public SensorNode FindSensor(Node node, string id)
|
||||
{
|
||||
if (node is SensorNode sNode)
|
||||
{
|
||||
if (sNode.Sensor.Identifier.ToString() == id)
|
||||
return sNode;
|
||||
}
|
||||
|
||||
foreach (Node child in node.Nodes)
|
||||
{
|
||||
SensorNode s = FindSensor(child, id);
|
||||
if (s != null)
|
||||
{
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetSensorControlValue(SensorNode sNode, string value)
|
||||
{
|
||||
if (sNode.Sensor.Control == null)
|
||||
{
|
||||
throw new ArgumentException("Specified sensor '" + sNode.Sensor.Identifier + "' can not be set");
|
||||
}
|
||||
|
||||
if (value == "null")
|
||||
{
|
||||
sNode.Sensor.Control.SetDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
sNode.Sensor.Control.SetSoftware(float.Parse(value, CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
|
||||
//Handles "/Sensor" requests.
|
||||
//Parameters are taken from the query part of the URL.
|
||||
//Get:
|
||||
//http://localhost:8085/Sensor?action=Get&id=/some/node/path/0
|
||||
//The output is either:
|
||||
//{"result":"fail","message":"Some error message"}
|
||||
//or:
|
||||
//{"result":"ok","value":42.0, "format":"{0:F2} RPM"}
|
||||
//
|
||||
//Set:
|
||||
//http://localhost:8085/Sensor?action=Set&id=/some/node/path/0&value=42.0
|
||||
//http://localhost:8085/Sensor?action=Set&id=/some/node/path/0&value=null
|
||||
//The output is either:
|
||||
//{"result":"fail","message":"Some error message"}
|
||||
//or:
|
||||
//{"result":"ok"}
|
||||
private void HandleSensorRequest(HttpListenerRequest request, JObject result)
|
||||
{
|
||||
IDictionary<string, string> dict = ToDictionary(HttpUtility.ParseQueryString(request.Url.Query));
|
||||
|
||||
if (dict.ContainsKey("action"))
|
||||
{
|
||||
if (dict.ContainsKey("id"))
|
||||
{
|
||||
SensorNode sNode = FindSensor(_root, dict["id"]);
|
||||
|
||||
if (sNode == null)
|
||||
{
|
||||
throw new ArgumentException("Unknown id " + dict["id"] + " specified");
|
||||
}
|
||||
|
||||
switch (dict["action"])
|
||||
{
|
||||
case "Set" when dict.ContainsKey("value"):
|
||||
SetSensorControlValue(sNode, dict["value"]);
|
||||
break;
|
||||
case "Set":
|
||||
throw new ArgumentNullException("No value provided");
|
||||
case "Get":
|
||||
result["value"] = sNode.Sensor.Value;
|
||||
result["min"] = sNode.Sensor.Min;
|
||||
result["max"] = sNode.Sensor.Max;
|
||||
result["format"] = sNode.Format;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("Unknown action type " + dict["action"]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentNullException("No id provided");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentNullException("No action provided");
|
||||
}
|
||||
}
|
||||
|
||||
//Handles http POST requests in a REST like manner.
|
||||
//Currently the only supported base URL is http://localhost:8085/Sensor.
|
||||
private string HandlePostRequest(HttpListenerRequest request)
|
||||
{
|
||||
JObject result = new() { ["result"] = "ok" };
|
||||
|
||||
try
|
||||
{
|
||||
if (request.Url.Segments.Length == 2)
|
||||
{
|
||||
if (request.Url.Segments[1] == "Sensor")
|
||||
{
|
||||
HandleSensorRequest(request, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Invalid URL ('" + request.Url.Segments[1] + "'), possible values: ['Sensor']");
|
||||
}
|
||||
}
|
||||
else
|
||||
throw new ArgumentException("Empty URL, possible values: ['Sensor']");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
result["result"] = "fail";
|
||||
result["message"] = e.ToString();
|
||||
}
|
||||
#if DEBUG
|
||||
return result.ToString(Newtonsoft.Json.Formatting.Indented);
|
||||
#else
|
||||
return result.ToString(Newtonsoft.Json.Formatting.None);
|
||||
#endif
|
||||
}
|
||||
|
||||
private void ListenerCallback(IAsyncResult result)
|
||||
{
|
||||
HttpListener listener = (HttpListener)result.AsyncState;
|
||||
if (listener == null || !listener.IsListening)
|
||||
return;
|
||||
|
||||
// Call EndGetContext to complete the asynchronous operation.
|
||||
HttpListenerContext context;
|
||||
try
|
||||
{
|
||||
context = listener.EndGetContext(result);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
HttpListenerRequest request = context.Request;
|
||||
|
||||
bool authenticated;
|
||||
|
||||
if (AuthEnabled)
|
||||
{
|
||||
try
|
||||
{
|
||||
HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;
|
||||
authenticated = (identity.Name == UserName) & (ComputeSHA256(identity.Password) == Password);
|
||||
}
|
||||
catch
|
||||
{
|
||||
authenticated = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
authenticated = true;
|
||||
}
|
||||
|
||||
if (authenticated)
|
||||
{
|
||||
switch (request.HttpMethod)
|
||||
{
|
||||
case "POST":
|
||||
{
|
||||
string postResult = HandlePostRequest(request);
|
||||
|
||||
Stream output = context.Response.OutputStream;
|
||||
byte[] utfBytes = Encoding.UTF8.GetBytes(postResult);
|
||||
|
||||
context.Response.AddHeader("Cache-Control", "no-cache");
|
||||
context.Response.ContentLength64 = utfBytes.Length;
|
||||
context.Response.ContentType = "application/json";
|
||||
|
||||
output.Write(utfBytes, 0, utfBytes.Length);
|
||||
output.Close();
|
||||
|
||||
break;
|
||||
}
|
||||
case "GET":
|
||||
{
|
||||
string requestedFile = request.RawUrl.Substring(1);
|
||||
|
||||
if (requestedFile == "data.json")
|
||||
{
|
||||
SendJson(context.Response, request);
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestedFile.Contains("images_icon"))
|
||||
{
|
||||
ServeResourceImage(context.Response,
|
||||
requestedFile.Replace("images_icon/", string.Empty));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (requestedFile.Contains("Sensor"))
|
||||
{
|
||||
JObject sensorResult = new();
|
||||
HandleSensorRequest(request, sensorResult);
|
||||
SendJsonSensor(context.Response, sensorResult);
|
||||
return;
|
||||
}
|
||||
|
||||
// default file to be served
|
||||
if (string.IsNullOrEmpty(requestedFile))
|
||||
requestedFile = "index.html";
|
||||
|
||||
string[] splits = requestedFile.Split('.');
|
||||
string ext = splits[splits.Length - 1];
|
||||
ServeResourceFile(context.Response, "Web." + requestedFile.Replace('/', '.'), ext);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
context.Response.StatusCode = 404;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Response.StatusCode = 401;
|
||||
}
|
||||
|
||||
if (context.Response.StatusCode == 401)
|
||||
{
|
||||
const string responseString = @"<HTML><HEAD><TITLE>401 Unauthorized</TITLE></HEAD>
|
||||
<BODY><H4>401 Unauthorized</H4>
|
||||
Authorization required.</BODY></HTML> ";
|
||||
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
|
||||
context.Response.ContentLength64 = buffer.Length;
|
||||
context.Response.StatusCode = 401;
|
||||
Stream output = context.Response.OutputStream;
|
||||
output.Write(buffer, 0, buffer.Length);
|
||||
output.Close();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
context.Response.Close();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// client closed connection before the content was sent
|
||||
}
|
||||
}
|
||||
|
||||
private void ServeResourceFile(HttpListenerResponse response, string name, string ext)
|
||||
{
|
||||
// resource names do not support the hyphen
|
||||
name = "LibreHardwareMonitor.Resources." +
|
||||
name.Replace("custom-theme", "custom_theme");
|
||||
|
||||
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
|
||||
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
if (names[i].Replace('\\', '.') == name)
|
||||
{
|
||||
using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(names[i]);
|
||||
|
||||
response.ContentType = GetContentType("." + ext);
|
||||
response.ContentLength64 = stream.Length;
|
||||
byte[] buffer = new byte[512 * 1024];
|
||||
try
|
||||
{
|
||||
Stream output = response.OutputStream;
|
||||
int len;
|
||||
while ((len = stream.Read(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
output.Write(buffer, 0, len);
|
||||
}
|
||||
|
||||
output.Flush();
|
||||
output.Close();
|
||||
response.Close();
|
||||
}
|
||||
catch (HttpListenerException)
|
||||
{ }
|
||||
catch (InvalidOperationException)
|
||||
{ }
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
response.StatusCode = 404;
|
||||
response.Close();
|
||||
}
|
||||
|
||||
private void ServeResourceImage(HttpListenerResponse response, string name)
|
||||
{
|
||||
name = "LibreHardwareMonitor.Resources." + name;
|
||||
|
||||
string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames();
|
||||
|
||||
for (int i = 0; i < names.Length; i++)
|
||||
{
|
||||
if (names[i].Replace('\\', '.') == name)
|
||||
{
|
||||
using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(names[i]);
|
||||
|
||||
Image image = Image.FromStream(stream);
|
||||
response.ContentType = "image/png";
|
||||
try
|
||||
{
|
||||
Stream output = response.OutputStream;
|
||||
using (MemoryStream ms = new())
|
||||
{
|
||||
image.Save(ms, ImageFormat.Png);
|
||||
ms.WriteTo(output);
|
||||
}
|
||||
|
||||
output.Close();
|
||||
}
|
||||
catch (HttpListenerException)
|
||||
{ }
|
||||
|
||||
image.Dispose();
|
||||
response.Close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
response.StatusCode = 404;
|
||||
response.Close();
|
||||
}
|
||||
|
||||
private void SendJson(HttpListenerResponse response, HttpListenerRequest request = null)
|
||||
{
|
||||
JObject json = new();
|
||||
|
||||
int nodeIndex = 0;
|
||||
|
||||
json["id"] = nodeIndex++;
|
||||
json["Text"] = "Sensor";
|
||||
json["Min"] = "Min";
|
||||
json["Value"] = "Value";
|
||||
json["Max"] = "Max";
|
||||
json["ImageURL"] = string.Empty;
|
||||
|
||||
json["Children"] = new JArray { GenerateJsonForNode(_root, ref nodeIndex) };
|
||||
|
||||
#if DEBUG
|
||||
string responseContent = json.ToString(Newtonsoft.Json.Formatting.Indented);
|
||||
#else
|
||||
string responseContent = json.ToString(Newtonsoft.Json.Formatting.None);
|
||||
#endif
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(responseContent);
|
||||
|
||||
bool acceptGzip;
|
||||
try
|
||||
{
|
||||
acceptGzip = (request != null) && (request.Headers["Accept-Encoding"].ToLower().IndexOf("gzip", StringComparison.OrdinalIgnoreCase) >= 0);
|
||||
}
|
||||
catch
|
||||
{
|
||||
acceptGzip = false;
|
||||
}
|
||||
|
||||
if (acceptGzip)
|
||||
response.AddHeader("Content-Encoding", "gzip");
|
||||
|
||||
response.AddHeader("Cache-Control", "no-cache");
|
||||
response.AddHeader("Access-Control-Allow-Origin", "*");
|
||||
response.ContentType = "application/json";
|
||||
try
|
||||
{
|
||||
if (acceptGzip)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
using (var zip = new GZipStream(ms, CompressionMode.Compress, true))
|
||||
zip.Write(buffer, 0, buffer.Length);
|
||||
|
||||
buffer = ms.ToArray();
|
||||
}
|
||||
|
||||
response.ContentLength64 = buffer.Length;
|
||||
Stream output = response.OutputStream;
|
||||
output.Write(buffer, 0, buffer.Length);
|
||||
output.Close();
|
||||
}
|
||||
catch (HttpListenerException)
|
||||
{ }
|
||||
|
||||
response.Close();
|
||||
}
|
||||
|
||||
private void SendJsonSensor(HttpListenerResponse response, JObject sensorData)
|
||||
{
|
||||
// Convert the JObject to a JSON string
|
||||
string responseContent = sensorData.ToString(Newtonsoft.Json.Formatting.None);
|
||||
byte[] buffer = Encoding.UTF8.GetBytes(responseContent);
|
||||
|
||||
// Add headers and set content type
|
||||
response.AddHeader("Cache-Control", "no-cache");
|
||||
response.AddHeader("Access-Control-Allow-Origin", "*");
|
||||
response.ContentType = "application/json";
|
||||
|
||||
// Write the response content to the output stream
|
||||
try
|
||||
{
|
||||
response.ContentLength64 = buffer.Length;
|
||||
Stream output = response.OutputStream;
|
||||
output.Write(buffer, 0, buffer.Length);
|
||||
output.Close();
|
||||
}
|
||||
catch (HttpListenerException)
|
||||
{ }
|
||||
|
||||
// Close the response
|
||||
response.Close();
|
||||
}
|
||||
|
||||
private JObject GenerateJsonForNode(Node n, ref int nodeIndex)
|
||||
{
|
||||
JObject jsonNode = new()
|
||||
{
|
||||
["id"] = nodeIndex++,
|
||||
["Text"] = n.Text,
|
||||
["Min"] = string.Empty,
|
||||
["Value"] = string.Empty,
|
||||
["Max"] = string.Empty
|
||||
};
|
||||
|
||||
switch (n)
|
||||
{
|
||||
case SensorNode sensorNode:
|
||||
jsonNode["SensorId"] = sensorNode.Sensor.Identifier.ToString();
|
||||
jsonNode["Type"] = sensorNode.Sensor.SensorType.ToString();
|
||||
jsonNode["Min"] = sensorNode.Min;
|
||||
jsonNode["Value"] = sensorNode.Value;
|
||||
jsonNode["Max"] = sensorNode.Max;
|
||||
jsonNode["ImageURL"] = "images/transparent.png";
|
||||
break;
|
||||
case HardwareNode hardwareNode:
|
||||
jsonNode["ImageURL"] = "images_icon/" + GetHardwareImageFile(hardwareNode);
|
||||
break;
|
||||
case TypeNode typeNode:
|
||||
jsonNode["ImageURL"] = "images_icon/" + GetTypeImageFile(typeNode);
|
||||
break;
|
||||
default:
|
||||
jsonNode["ImageURL"] = "images_icon/computer.png";
|
||||
break;
|
||||
}
|
||||
|
||||
JArray children = [];
|
||||
foreach (Node child in n.Nodes)
|
||||
{
|
||||
children.Add(GenerateJsonForNode(child, ref nodeIndex));
|
||||
}
|
||||
|
||||
jsonNode["Children"] = children;
|
||||
|
||||
return jsonNode;
|
||||
}
|
||||
|
||||
private static string GetContentType(string extension)
|
||||
{
|
||||
switch (extension)
|
||||
{
|
||||
case ".avi": return "video/x-msvideo";
|
||||
case ".css": return "text/css";
|
||||
case ".doc": return "application/msword";
|
||||
case ".gif": return "image/gif";
|
||||
case ".htm":
|
||||
case ".html": return "text/html";
|
||||
case ".jpg":
|
||||
case ".jpeg": return "image/jpeg";
|
||||
case ".js": return "application/x-javascript";
|
||||
case ".mp3": return "audio/mpeg";
|
||||
case ".png": return "image/png";
|
||||
case ".pdf": return "application/pdf";
|
||||
case ".ppt": return "application/vnd.ms-powerpoint";
|
||||
case ".zip": return "application/zip";
|
||||
case ".txt": return "text/plain";
|
||||
default: return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetHardwareImageFile(HardwareNode hn)
|
||||
{
|
||||
switch (hn.Hardware.HardwareType)
|
||||
{
|
||||
case HardwareType.Cpu:
|
||||
return "cpu.png";
|
||||
case HardwareType.GpuNvidia:
|
||||
return "nvidia.png";
|
||||
case HardwareType.GpuAmd:
|
||||
return "ati.png";
|
||||
case HardwareType.GpuIntel:
|
||||
return "intel.png";
|
||||
case HardwareType.Storage:
|
||||
return "hdd.png";
|
||||
case HardwareType.Motherboard:
|
||||
return "mainboard.png";
|
||||
case HardwareType.SuperIO:
|
||||
return "chip.png";
|
||||
case HardwareType.Memory:
|
||||
return "ram.png";
|
||||
case HardwareType.Cooler:
|
||||
return "fan.png";
|
||||
case HardwareType.Network:
|
||||
return "nic.png";
|
||||
case HardwareType.Psu:
|
||||
return "power-supply.png";
|
||||
case HardwareType.Battery:
|
||||
return "battery.png";
|
||||
default:
|
||||
return "cpu.png";
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetTypeImageFile(TypeNode tn)
|
||||
{
|
||||
switch (tn.SensorType)
|
||||
{
|
||||
case SensorType.Voltage:
|
||||
case SensorType.Current:
|
||||
return "voltage.png";
|
||||
case SensorType.Clock:
|
||||
return "clock.png";
|
||||
case SensorType.Load:
|
||||
return "load.png";
|
||||
case SensorType.Temperature:
|
||||
return "temperature.png";
|
||||
case SensorType.Fan:
|
||||
return "fan.png";
|
||||
case SensorType.Flow:
|
||||
return "flow.png";
|
||||
case SensorType.Control:
|
||||
return "control.png";
|
||||
case SensorType.Level:
|
||||
return "level.png";
|
||||
case SensorType.Power:
|
||||
return "power.png";
|
||||
case SensorType.Noise:
|
||||
return "loudspeaker.png";
|
||||
case SensorType.Conductivity:
|
||||
return "voltage.png";
|
||||
case SensorType.Throughput:
|
||||
return "throughput.png";
|
||||
case SensorType.Humidity:
|
||||
return "flow.png";
|
||||
default:
|
||||
return "power.png";
|
||||
}
|
||||
}
|
||||
|
||||
private string ComputeSHA256(string text)
|
||||
{
|
||||
using SHA256 hash = SHA256.Create();
|
||||
return string.Concat(hash
|
||||
.ComputeHash(Encoding.UTF8.GetBytes(text))
|
||||
.Select(item => item.ToString("x2")));
|
||||
}
|
||||
|
||||
public void Quit()
|
||||
{
|
||||
if (PlatformNotSupported)
|
||||
return;
|
||||
|
||||
StopHttpListener();
|
||||
_listener.Abort();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace LibreHardwareMonitor.Utilities;
|
||||
|
||||
public static class IconFactory
|
||||
{
|
||||
private struct BITMAPINFOHEADER
|
||||
{
|
||||
public readonly uint Size;
|
||||
public readonly int Width;
|
||||
public readonly int Height;
|
||||
public readonly ushort Planes;
|
||||
public readonly ushort BitCount;
|
||||
public readonly uint Compression;
|
||||
public readonly uint SizeImage;
|
||||
public readonly int XPelsPerMeter;
|
||||
public readonly int YPelsPerMeter;
|
||||
public readonly uint ClrUsed;
|
||||
public readonly uint ClrImportant;
|
||||
|
||||
public BITMAPINFOHEADER(int width, int height, int bitCount)
|
||||
{
|
||||
Size = 40;
|
||||
Width = width;
|
||||
Height = height;
|
||||
Planes = 1;
|
||||
BitCount = (ushort)bitCount;
|
||||
Compression = 0;
|
||||
SizeImage = 0;
|
||||
XPelsPerMeter = 0;
|
||||
YPelsPerMeter = 0;
|
||||
ClrUsed = 0;
|
||||
ClrImportant = 0;
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(Size);
|
||||
bw.Write(Width);
|
||||
bw.Write(Height);
|
||||
bw.Write(Planes);
|
||||
bw.Write(BitCount);
|
||||
bw.Write(Compression);
|
||||
bw.Write(SizeImage);
|
||||
bw.Write(XPelsPerMeter);
|
||||
bw.Write(YPelsPerMeter);
|
||||
bw.Write(ClrUsed);
|
||||
bw.Write(ClrImportant);
|
||||
}
|
||||
}
|
||||
|
||||
private struct ICONIMAGE
|
||||
{
|
||||
public BITMAPINFOHEADER Header;
|
||||
public readonly byte[] Colors;
|
||||
public readonly int MaskSize;
|
||||
|
||||
public ICONIMAGE(int width, int height, byte[] colors)
|
||||
{
|
||||
Header = new BITMAPINFOHEADER(width, height << 1, (8 * colors.Length) / (width * height));
|
||||
Colors = colors;
|
||||
MaskSize = (width * height) >> 3;
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
Header.Write(bw);
|
||||
int stride = Header.Width << 2;
|
||||
for (int i = (Header.Height >> 1) - 1; i >= 0; i--)
|
||||
bw.Write(Colors, i * stride, stride);
|
||||
for (int i = 0; i < 2 * MaskSize; i++)
|
||||
bw.Write((byte)0);
|
||||
}
|
||||
}
|
||||
|
||||
private struct ICONDIRENTRY
|
||||
{
|
||||
public readonly byte Width;
|
||||
public readonly byte Height;
|
||||
public readonly byte ColorCount;
|
||||
public readonly byte Reserved;
|
||||
public readonly ushort Planes;
|
||||
public readonly ushort BitCount;
|
||||
public readonly uint BytesInRes;
|
||||
public uint ImageOffset;
|
||||
|
||||
public ICONDIRENTRY(ICONIMAGE image, int imageOffset)
|
||||
{
|
||||
Width = (byte)image.Header.Width;
|
||||
Height = (byte)(image.Header.Height >> 1);
|
||||
ColorCount = 0;
|
||||
Reserved = 0;
|
||||
Planes = image.Header.Planes;
|
||||
BitCount = image.Header.BitCount;
|
||||
BytesInRes = (uint)(image.Header.Size + image.Colors.Length + image.MaskSize + image.MaskSize);
|
||||
ImageOffset = (uint)imageOffset;
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(Width);
|
||||
bw.Write(Height);
|
||||
bw.Write(ColorCount);
|
||||
bw.Write(Reserved);
|
||||
bw.Write(Planes);
|
||||
bw.Write(BitCount);
|
||||
bw.Write(BytesInRes);
|
||||
bw.Write(ImageOffset);
|
||||
}
|
||||
|
||||
public uint Size
|
||||
{
|
||||
get { return 16; }
|
||||
}
|
||||
}
|
||||
|
||||
private struct ICONDIR
|
||||
{
|
||||
public readonly ushort Reserved;
|
||||
public readonly ushort Type;
|
||||
public readonly ushort Count;
|
||||
public readonly ICONDIRENTRY[] Entries;
|
||||
|
||||
public ICONDIR(ICONDIRENTRY[] entries)
|
||||
{
|
||||
Reserved = 0;
|
||||
Type = 1;
|
||||
Count = (ushort)entries.Length;
|
||||
Entries = entries;
|
||||
}
|
||||
|
||||
public void Write(BinaryWriter bw)
|
||||
{
|
||||
bw.Write(Reserved);
|
||||
bw.Write(Type);
|
||||
bw.Write(Count);
|
||||
for (int i = 0; i < Entries.Length; i++)
|
||||
Entries[i].Write(bw);
|
||||
}
|
||||
|
||||
public uint Size
|
||||
{
|
||||
get
|
||||
{
|
||||
return (uint)(6 + Entries.Length * (Entries.Length > 0 ? Entries[0].Size : 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly BinaryWriter BinaryWriter = new BinaryWriter(new MemoryStream());
|
||||
|
||||
public static Icon Create(byte[] colors, int width, int height, PixelFormat format)
|
||||
{
|
||||
if (format != PixelFormat.Format32bppArgb)
|
||||
throw new NotImplementedException();
|
||||
|
||||
ICONIMAGE image = new ICONIMAGE(width, height, colors);
|
||||
ICONDIR dir = new ICONDIR(new[] { new ICONDIRENTRY(image, 0) });
|
||||
dir.Entries[0].ImageOffset = dir.Size;
|
||||
|
||||
BinaryWriter.BaseStream.Position = 0;
|
||||
dir.Write(BinaryWriter);
|
||||
image.Write(BinaryWriter);
|
||||
|
||||
BinaryWriter.BaseStream.Position = 0;
|
||||
Icon icon = new Icon(BinaryWriter.BaseStream);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
[DllImport("user32", SetLastError = true)]
|
||||
static extern bool DestroyIcon(IntPtr handle);
|
||||
|
||||
public static Icon Create(Bitmap bitmap)
|
||||
{
|
||||
IntPtr hIcon = bitmap.GetHicon();
|
||||
Icon icon = Icon.FromHandle(hIcon);
|
||||
|
||||
return icon;
|
||||
}
|
||||
|
||||
public static void Destroy(this Icon icon)
|
||||
{
|
||||
DestroyIcon(icon.Handle);
|
||||
icon.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using LibreHardwareMonitor.Hardware;
|
||||
|
||||
namespace LibreHardwareMonitor.Utilities;
|
||||
|
||||
public class Logger
|
||||
{
|
||||
private const string FileNameFormat = "LibreHardwareMonitorLog-{0:yyyy-MM-dd}{1}.csv";
|
||||
|
||||
private readonly IComputer _computer;
|
||||
|
||||
private DateTime _day = DateTime.MinValue;
|
||||
private string _fileName;
|
||||
private string[] _identifiers;
|
||||
private ISensor[] _sensors;
|
||||
private DateTime _lastLoggedTime = DateTime.MinValue;
|
||||
|
||||
public LoggerFileRotation FileRotationMethod = LoggerFileRotation.PerSession;
|
||||
|
||||
public Logger(IComputer computer)
|
||||
{
|
||||
_computer = computer;
|
||||
_computer.HardwareAdded += HardwareAdded;
|
||||
_computer.HardwareRemoved += HardwareRemoved;
|
||||
}
|
||||
|
||||
private void HardwareRemoved(IHardware hardware)
|
||||
{
|
||||
hardware.SensorAdded -= SensorAdded;
|
||||
hardware.SensorRemoved -= SensorRemoved;
|
||||
|
||||
foreach (ISensor sensor in hardware.Sensors)
|
||||
SensorRemoved(sensor);
|
||||
|
||||
foreach (IHardware subHardware in hardware.SubHardware)
|
||||
HardwareRemoved(subHardware);
|
||||
}
|
||||
|
||||
private void HardwareAdded(IHardware hardware)
|
||||
{
|
||||
foreach (ISensor sensor in hardware.Sensors)
|
||||
SensorAdded(sensor);
|
||||
|
||||
hardware.SensorAdded += SensorAdded;
|
||||
hardware.SensorRemoved += SensorRemoved;
|
||||
|
||||
foreach (IHardware subHardware in hardware.SubHardware)
|
||||
HardwareAdded(subHardware);
|
||||
}
|
||||
|
||||
private void SensorAdded(ISensor sensor)
|
||||
{
|
||||
if (_sensors == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < _sensors.Length; i++)
|
||||
{
|
||||
if (sensor.Identifier.ToString() == _identifiers[i])
|
||||
_sensors[i] = sensor;
|
||||
}
|
||||
}
|
||||
|
||||
private void SensorRemoved(ISensor sensor)
|
||||
{
|
||||
if (_sensors == null)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < _sensors.Length; i++)
|
||||
{
|
||||
if (sensor == _sensors[i])
|
||||
_sensors[i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetFileName(DateTime date, uint sessionNumber = 0)
|
||||
{
|
||||
return AppDomain.CurrentDomain.BaseDirectory + Path.DirectorySeparatorChar
|
||||
+ string.Format(FileNameFormat, date, sessionNumber == 0 ? "" : "-" + sessionNumber);
|
||||
}
|
||||
|
||||
private bool OpenExistingLogFile()
|
||||
{
|
||||
if (!File.Exists(_fileName))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
string line;
|
||||
using (StreamReader reader = new StreamReader(_fileName))
|
||||
line = reader.ReadLine();
|
||||
|
||||
if (string.IsNullOrEmpty(line))
|
||||
return false;
|
||||
|
||||
_identifiers = line.Split(',').Skip(1).ToArray();
|
||||
}
|
||||
catch
|
||||
{
|
||||
_identifiers = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_identifiers.Length == 0)
|
||||
{
|
||||
_identifiers = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
_sensors = new ISensor[_identifiers.Length];
|
||||
SensorVisitor visitor = new SensorVisitor(sensor =>
|
||||
{
|
||||
for (int i = 0; i < _identifiers.Length; i++)
|
||||
if (sensor.Identifier.ToString() == _identifiers[i])
|
||||
_sensors[i] = sensor;
|
||||
});
|
||||
visitor.VisitComputer(_computer);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void CreateNewLogFile()
|
||||
{
|
||||
IList<ISensor> list = new List<ISensor>();
|
||||
SensorVisitor visitor = new SensorVisitor(sensor =>
|
||||
{
|
||||
list.Add(sensor);
|
||||
});
|
||||
visitor.VisitComputer(_computer);
|
||||
_sensors = list.ToArray();
|
||||
_identifiers = _sensors.Select(s => s.Identifier.ToString()).ToArray();
|
||||
|
||||
using (StreamWriter writer = new StreamWriter(_fileName, false))
|
||||
{
|
||||
writer.Write(",");
|
||||
for (int i = 0; i < _sensors.Length; i++)
|
||||
{
|
||||
writer.Write(_sensors[i].Identifier);
|
||||
if (i < _sensors.Length - 1)
|
||||
writer.Write(",");
|
||||
else
|
||||
writer.WriteLine();
|
||||
}
|
||||
|
||||
writer.Write("Time,");
|
||||
for (int i = 0; i < _sensors.Length; i++)
|
||||
{
|
||||
writer.Write('"');
|
||||
writer.Write(_sensors[i].Name);
|
||||
writer.Write('"');
|
||||
if (i < _sensors.Length - 1)
|
||||
writer.Write(",");
|
||||
else
|
||||
writer.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public TimeSpan LoggingInterval { get; set; }
|
||||
|
||||
public void Log()
|
||||
{
|
||||
DateTime now = DateTime.Now;
|
||||
|
||||
if (_lastLoggedTime + LoggingInterval - new TimeSpan(5000000) > now)
|
||||
return;
|
||||
|
||||
switch (FileRotationMethod)
|
||||
{
|
||||
case LoggerFileRotation.PerSession:
|
||||
// Create file if it does not exist or the logging interval has passed (+ some margin)
|
||||
if (!File.Exists(_fileName) || now - _lastLoggedTime > (LoggingInterval + TimeSpan.FromMilliseconds(100)))
|
||||
{
|
||||
uint sessionNumber = 1;
|
||||
do {
|
||||
_fileName = GetFileName(DateTime.Now, sessionNumber);
|
||||
sessionNumber++;
|
||||
} while (File.Exists(_fileName));
|
||||
CreateNewLogFile();
|
||||
}
|
||||
break;
|
||||
case LoggerFileRotation.Daily:
|
||||
// Create a new file if the day has changed or the file does not exist
|
||||
if (_day != now.Date || !File.Exists(_fileName))
|
||||
{
|
||||
_day = now.Date;
|
||||
_fileName = GetFileName(_day);
|
||||
if (!OpenExistingLogFile())
|
||||
CreateNewLogFile();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using (StreamWriter writer = new StreamWriter(new FileStream(_fileName, FileMode.Append, FileAccess.Write, FileShare.ReadWrite)))
|
||||
{
|
||||
writer.Write(now.ToString("G", CultureInfo.InvariantCulture));
|
||||
writer.Write(",");
|
||||
for (int i = 0; i < _sensors.Length; i++)
|
||||
{
|
||||
if (_sensors[i] != null)
|
||||
{
|
||||
float? value = _sensors[i].Value;
|
||||
if (value.HasValue)
|
||||
writer.Write(value.Value.ToString("R", CultureInfo.InvariantCulture));
|
||||
}
|
||||
if (i < _sensors.Length - 1)
|
||||
writer.Write(",");
|
||||
else
|
||||
writer.WriteLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException) { }
|
||||
|
||||
_lastLoggedTime = now;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace LibreHardwareMonitor.Utilities
|
||||
{
|
||||
public enum LoggerFileRotation
|
||||
{
|
||||
// Keep the same file for the entire record session
|
||||
PerSession = 0,
|
||||
|
||||
// Create a new file every day
|
||||
Daily,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
// Copyright (C) LibreHardwareMonitor and Contributors.
|
||||
// Partial Copyright (C) Michael Möller <mmoeller@openhardwaremonitor.org> and Contributors.
|
||||
// All Rights Reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using LibreHardwareMonitor.Hardware;
|
||||
|
||||
namespace LibreHardwareMonitor.Utilities;
|
||||
|
||||
public class PersistentSettings : ISettings
|
||||
{
|
||||
private readonly IDictionary<string, string> _settings = new Dictionary<string, string>();
|
||||
|
||||
public void Load(string fileName)
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
try
|
||||
{
|
||||
doc.Load(fileName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(fileName);
|
||||
}
|
||||
catch { }
|
||||
|
||||
string backupFileName = fileName + ".backup";
|
||||
try
|
||||
{
|
||||
doc.Load(backupFileName);
|
||||
}
|
||||
catch
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(backupFileName);
|
||||
}
|
||||
catch { }
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
XmlNodeList list = doc.GetElementsByTagName("appSettings");
|
||||
foreach (XmlNode node in list)
|
||||
{
|
||||
XmlNode parent = node.ParentNode;
|
||||
if (parent != null && parent.Name == "configuration" && parent.ParentNode is XmlDocument)
|
||||
{
|
||||
foreach (XmlNode child in node.ChildNodes)
|
||||
{
|
||||
if (child.Name == "add")
|
||||
{
|
||||
XmlAttributeCollection attributes = child.Attributes;
|
||||
XmlAttribute keyAttribute = attributes["key"];
|
||||
XmlAttribute valueAttribute = attributes["value"];
|
||||
if (keyAttribute != null && valueAttribute != null && keyAttribute.Value != null)
|
||||
{
|
||||
_settings.Add(keyAttribute.Value, valueAttribute.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Save(string fileName)
|
||||
{
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.AppendChild(doc.CreateXmlDeclaration("1.0", "utf-8", null));
|
||||
XmlElement configuration = doc.CreateElement("configuration");
|
||||
doc.AppendChild(configuration);
|
||||
XmlElement appSettings = doc.CreateElement("appSettings");
|
||||
configuration.AppendChild(appSettings);
|
||||
foreach (KeyValuePair<string, string> keyValuePair in _settings)
|
||||
{
|
||||
XmlElement add = doc.CreateElement("add");
|
||||
add.SetAttribute("key", keyValuePair.Key);
|
||||
add.SetAttribute("value", keyValuePair.Value);
|
||||
appSettings.AppendChild(add);
|
||||
}
|
||||
|
||||
byte[] file;
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
using (var writer = new StreamWriter(memory, Encoding.UTF8))
|
||||
{
|
||||
doc.Save(writer);
|
||||
}
|
||||
file = memory.ToArray();
|
||||
}
|
||||
|
||||
string backupFileName = fileName + ".backup";
|
||||
if (File.Exists(fileName))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(backupFileName);
|
||||
}
|
||||
catch { }
|
||||
try
|
||||
{
|
||||
File.Move(fileName, backupFileName);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
using (var stream = new FileStream(fileName, FileMode.Create, FileAccess.Write))
|
||||
{
|
||||
stream.Write(file, 0, file.Length);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(backupFileName);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
public bool Contains(string name)
|
||||
{
|
||||
return _settings.ContainsKey(name);
|
||||
}
|
||||
|
||||
public void SetValue(string name, string value)
|
||||
{
|
||||
_settings[name] = value;
|
||||
}
|
||||
|
||||
public string GetValue(string name, string value)
|
||||
{
|
||||
if (_settings.TryGetValue(name, out string result))
|
||||
return result;
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void Remove(string name)
|
||||
{
|
||||
_settings.Remove(name);
|
||||
}
|
||||
|
||||
public void SetValue(string name, int value)
|
||||
{
|
||||
_settings[name] = value.ToString();
|
||||
}
|
||||
|
||||
public int GetValue(string name, int value)
|
||||
{
|
||||
if (_settings.TryGetValue(name, out string str))
|
||||
{
|
||||
if (int.TryParse(str, out int parsedValue))
|
||||
return parsedValue;
|
||||
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void SetValue(string name, float value)
|
||||
{
|
||||
_settings[name] = value.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public float GetValue(string name, float value)
|
||||
{
|
||||
if (_settings.TryGetValue(name, out string str))
|
||||
{
|
||||
if (float.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out float parsedValue))
|
||||
return parsedValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
|
||||
}
|
||||
|
||||
public double GetValue(string name, double value)
|
||||
{
|
||||
if (_settings.TryGetValue(name, out string str))
|
||||
{
|
||||
if (double.TryParse(str, NumberStyles.Float, CultureInfo.InvariantCulture, out double parsedValue))
|
||||
return parsedValue;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void SetValue(string name, bool value)
|
||||
{
|
||||
_settings[name] = value ? "true" : "false";
|
||||
}
|
||||
|
||||
public bool GetValue(string name, bool value)
|
||||
{
|
||||
if (_settings.TryGetValue(name, out string str))
|
||||
{
|
||||
return str == "true";
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
public void SetValue(string name, Color color)
|
||||
{
|
||||
_settings[name] = color.ToArgb().ToString("X8");
|
||||
}
|
||||
|
||||
public Color GetValue(string name, Color value)
|
||||
{
|
||||
if (_settings.TryGetValue(name, out string str))
|
||||
{
|
||||
if (int.TryParse(str, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out int parsedValue))
|
||||
return Color.FromArgb(parsedValue);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user