diff --git a/ColorControl.sln b/ColorControl.sln
index db1f57d..c50f372 100644
--- a/ColorControl.sln
+++ b/ColorControl.sln
@@ -5,7 +5,7 @@ VisualStudioVersion = 17.0.32014.148
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ColorControl", "ColorControl\ColorControl.csproj", "{A6005D08-70F0-44CB-A384-9219EBA84F21}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nspector", "Nspector\Nspector.csproj", "{76797290-48F5-4A82-86C3-FAD25DAE4500}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nspector", "Nspector\Nspector.csproj", "{76797290-48F5-4A82-86C3-FAD25DAE4500}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/ColorControl/ColorControl.csproj b/ColorControl/ColorControl.csproj
index 7e3690c..332c52e 100644
--- a/ColorControl/ColorControl.csproj
+++ b/ColorControl/ColorControl.csproj
@@ -1,32 +1,33 @@
- net6.0-windows10.0.20348.0
- WinExe
- false
- publish\
- true
- Disk
- false
- Foreground
- 7
- Days
- false
- false
- false
- https://www.maassoft.com/downloads/ColorControl/
- ColorControl
- Maassoft
+ 10.0
+ net6.0-windows10.0.20348.0
+ WinExe
+ false
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ false
+ https://www.maassoft.com/downloads/ColorControl/
+ ColorControl
+ Maassoft
Maassoft
0
- 7.3.0.0
- 7.3.0.0
+ 8.0.0.0
+ 8.0.0.0
false
- true
- false
- true
- true
- false
- true
+ true
+ false
+ true
+ true
+ false
+ true
pngbarn.ico
10.0.20348.0
2020-2022 Maassoft
@@ -40,68 +41,70 @@
ColorControl_TemporaryKey.pfx
- bin\x64\Debug\
- MinimumRecommendedRules.ruleset
+ bin\x64\Debug\
+ MinimumRecommendedRules.ruleset
- bin\x64\Release\
- MinimumRecommendedRules.ruleset
+ bin\x64\Release\
+ MinimumRecommendedRules.ruleset
-
- True
- True
- Settings.settings
-
-
- Form
-
-
- UserControl
-
-
-
-
-
- PreserveNewest
-
+
+ True
+ True
+ Settings.settings
+
+
+ Form
+
+
+ UserControl
+
+
+
+
+
+ PreserveNewest
+
-
+
-
+
-
- {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}
- 1
- 0
- 0
- tlbimp
- False
- True
-
+
+ {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B}
+ 1
+ 0
+ 0
+ tlbimp
+ False
+ True
+
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
- SettingsSingleFileGenerator
- Settings.Designer.cs
-
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
\ No newline at end of file
diff --git a/ColorControl/Common/PipeUtils.cs b/ColorControl/Common/PipeUtils.cs
new file mode 100644
index 0000000..7b300fb
--- /dev/null
+++ b/ColorControl/Common/PipeUtils.cs
@@ -0,0 +1,83 @@
+using ColorControl.Svc;
+using Newtonsoft.Json;
+using System;
+using System.IO.Pipes;
+using System.Security.Principal;
+
+namespace ColorControl.Common
+{
+ public static class PipeUtils
+ {
+ private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
+
+ public static string SendMessage(string message)
+ {
+ var pipeClient = new NamedPipeClientStream(".", "testpipe", PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.None);
+ try
+ {
+ pipeClient.Connect(500);
+ try
+ {
+ var ss = new StreamString(pipeClient);
+
+ ss.WriteString(message);
+
+ var result = ss.ReadString();
+
+ return result;
+ }
+ finally
+ {
+ pipeClient.Close();
+ }
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex, "PipeUtils.SendMessage");
+
+ return null;
+ }
+ }
+
+ public static SvcResultMessage SendMessage(SvcMessage message)
+ {
+ var messageJson = JsonConvert.SerializeObject(message);
+
+ var resultJson = SendMessage(messageJson);
+
+ if (resultJson == null)
+ {
+ return null;
+ }
+
+ var resultMessage = JsonConvert.DeserializeObject(resultJson);
+
+ return resultMessage;
+ }
+
+ public static SvcResultMessage SendMessage(SvcMessageType messageType)
+ {
+ var message = new SvcMessage { MessageType = messageType };
+
+ return SendMessage(message);
+ }
+
+ public static bool? SendRpcMessage(string command)
+ {
+ var message = new SvcMessage { MessageType = SvcMessageType.ExecuteRpc, Data = command };
+
+ var messageJson = JsonConvert.SerializeObject(message);
+
+ var resultJson = SendMessage(messageJson);
+
+ if (resultJson == null)
+ {
+ return null;
+ }
+
+ var resultMessage = JsonConvert.DeserializeObject(resultJson);
+
+ return resultMessage.Result;
+ }
+ }
+}
diff --git a/ColorControl/Common/StreamString.cs b/ColorControl/Common/StreamString.cs
new file mode 100644
index 0000000..4aa829b
--- /dev/null
+++ b/ColorControl/Common/StreamString.cs
@@ -0,0 +1,51 @@
+using System;
+using System.IO;
+using System.Text;
+
+namespace ColorControl.Common
+{
+ public class StreamString
+ {
+ private Stream ioStream;
+ private UnicodeEncoding streamEncoding;
+
+ public StreamString(Stream ioStream)
+ {
+ this.ioStream = ioStream;
+ streamEncoding = new UnicodeEncoding();
+ }
+
+ public string ReadString()
+ {
+ var lenBytes = new byte[4];
+
+ var read = ioStream.Read(lenBytes, 0, lenBytes.Length);
+
+ if (read < lenBytes.Length)
+ {
+ return null;
+ }
+
+ var len = BitConverter.ToInt32(lenBytes);
+
+ var inBuffer = new byte[len];
+ ioStream.Read(inBuffer, 0, len);
+
+ return streamEncoding.GetString(inBuffer);
+ }
+
+ public int WriteString(string outString)
+ {
+ var outBuffer = streamEncoding.GetBytes(outString);
+ var len = outBuffer.Length;
+
+ var lenBytes = BitConverter.GetBytes(len);
+
+ ioStream.Write(lenBytes, 0, lenBytes.Length);
+ ioStream.Write(outBuffer, 0, len);
+ ioStream.Flush();
+
+ return outBuffer.Length + lenBytes.Length;
+ }
+ }
+}
diff --git a/ColorControl/Common/Utils.cs b/ColorControl/Common/Utils.cs
index f599500..481a9e7 100644
--- a/ColorControl/Common/Utils.cs
+++ b/ColorControl/Common/Utils.cs
@@ -14,9 +14,12 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
+using System.Net.NetworkInformation;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Security.Principal;
+using System.ServiceProcess;
+using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -49,6 +52,23 @@ namespace ColorControl.Common
private static bool WinKeyDown = false;
private static Keys[] KeysWithoutModifiers = new[] { Keys.F13, Keys.F14, Keys.F15, Keys.F16, Keys.F17, Keys.F18, Keys.F19, Keys.F20, Keys.F21, Keys.F22, Keys.F23, Keys.F24 };
+ private static string SERVICE_NAME = "Color Control Service";
+
+ public static string ELEVATION_MSG = @"Elevation is needed in some cases where ColorControl needs administrator rights.
+Some operations like installing a service, changing the priority of a process or creating a temporary IP-route for improved WOL-functionality will not work without those rights.
+
+These methods are available:
+
+- None: no administrator operations can be executed (unless program already manually is started as admin)
+
+- Run as admin: only available when starting automatically after login (configures scheduled task with highest privileges)
+
+- Use Windows Service: this will install a Windows Service that handles the operations which need admin rights. This is the preferred method.
+
+- Use dedicated elevated process: a second ColorControl-process will be spawned that is run as administrator.
+
+The best and suggested method to provide this is via a Windows Service. Only when installing the service a User Account Control window may popup.";
+
public static Bitmap SubPixelShift(Bitmap bitmap)
{
Bitmap bitmap2 = (Bitmap)bitmap.Clone();
@@ -170,15 +190,34 @@ namespace ColorControl.Common
return keys;
}
- static bool IsAdministrator()
+ private static bool? _IsAdministrator;
+
+ public static bool IsAdministrator()
{
+ if (_IsAdministrator.HasValue)
+ {
+ return _IsAdministrator.Value;
+ }
+
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
- return principal.IsInRole(WindowsBuiltInRole.Administrator);
+ _IsAdministrator = principal.IsInRole(WindowsBuiltInRole.Administrator);
+
+ return _IsAdministrator.Value;
}
- internal static int ExecuteElevated(string args, bool wait = true, bool skipDedicated = false)
+ internal static int ExecuteElevated(string args, bool wait = true, bool skipDedicated = false, bool skipService = false)
{
+ if (!skipService && IsServiceRunning())
+ {
+ var result = PipeUtils.SendRpcMessage(args);
+
+ if (result.HasValue)
+ {
+ return 0;
+ }
+ }
+
if (UseDedicatedElevatedProcess && !skipDedicated)
{
CheckElevatedProcess();
@@ -188,7 +227,9 @@ namespace ColorControl.Common
return 0;
}
- var info = new ProcessStartInfo(Process.GetCurrentProcess().MainModule.FileName, args)
+ var fileName = Process.GetCurrentProcess().MainModule.FileName;
+
+ var info = new ProcessStartInfo(fileName, args)
{
Verb = "runas", // indicates to elevate privileges
UseShellExecute = true,
@@ -225,11 +266,24 @@ namespace ColorControl.Common
if (ElevatedProcessWindowHandle == IntPtr.Zero)
{
- ExecuteElevated(StartUpParams.StartElevatedParam, false, true);
+ ExecuteElevated(StartUpParams.StartElevatedParam, false, true, true);
- Thread.Sleep(500);
+ var delay = 1000;
- ElevatedProcessWindowHandle = NativeMethods.FindWindowW(null, "ElevatedForm");
+ while (delay >= 0)
+ {
+ Thread.Sleep(100);
+
+ ElevatedProcessWindowHandle = NativeMethods.FindWindowW(null, "ElevatedForm");
+
+ if (ElevatedProcessWindowHandle != IntPtr.Zero)
+ {
+ Thread.Sleep(100);
+ break;
+ }
+
+ delay -= 100;
+ }
}
}
@@ -494,11 +548,11 @@ namespace ColorControl.Common
return list;
}
- public static void RegisterTask(string taskName, bool enabled)
+ public static void RegisterTask(string taskName, bool enabled, TaskRunLevel runLevel = TaskRunLevel.LUA)
{
if (!IsAdministrator())
{
- ExecuteElevated(enabled ? StartUpParams.EnableAutoStartParam : StartUpParams.DisableAutoStartParam);
+ ExecuteElevated(enabled ? $"{StartUpParams.EnableAutoStartParam} {(int)runLevel}" : StartUpParams.DisableAutoStartParam, skipService: true);
return;
}
@@ -511,8 +565,10 @@ namespace ColorControl.Common
{
if (enabled)
{
- TaskDefinition td = ts.NewTask();
+ var td = ts.NewTask();
+
td.RegistrationInfo.Description = "Start ColorControl";
+ td.Principal.RunLevel = runLevel;
td.Triggers.Add(new LogonTrigger { UserId = WindowsIdentity.GetCurrent().Name });
@@ -528,32 +584,18 @@ namespace ColorControl.Common
}
catch (Exception e)
{
- //MessageForms.ErrorOk("Could not create/delete task: " + e.Message);
+ Logger.Error($"Could not create/delete task: {e.Message}");
}
}
- public static bool TaskExists(string taskName, bool update)
+ public static bool TaskExists(string taskName)
{
var file = Assembly.GetExecutingAssembly().Location;
using (TaskService ts = new TaskService())
{
var task = ts.RootFolder.Tasks.FirstOrDefault(x => x.Name.Equals(taskName));
- if (task != null)
- {
- //var action = task.Definition.Actions.FirstOrDefault(x => x.ActionType == TaskActionType.Execute);
- //if (action != null)
- //{
- // var execAction = action as ExecAction;
- // if (!execAction.Path.Equals(file))
- // {
- // RegisterTask(taskName, false);
- // RegisterTask(taskName, true);
- // }
- //}
- return true;
- }
- return false;
+ return task != null;
}
}
@@ -871,5 +913,216 @@ namespace ColorControl.Common
{
return str1.Trim().Replace(" ", string.Empty).Equals(str2.Trim().Replace(" ", string.Empty), StringComparison.OrdinalIgnoreCase);
}
+
+ public static void StartService()
+ {
+ if (!IsAdministrator())
+ {
+ ExecuteElevated(StartUpParams.StartServiceParam);
+ return;
+ }
+
+ var controller = new ServiceController(SERVICE_NAME);
+
+ if (controller.Status == ServiceControllerStatus.Stopped)
+ {
+ controller.Start();
+ }
+ }
+
+ public static void StopService()
+ {
+ if (!IsAdministrator())
+ {
+ ExecuteElevated(StartUpParams.StopServiceParam);
+ return;
+ }
+
+ var controller = new ServiceController(SERVICE_NAME);
+
+ if (controller.Status == ServiceControllerStatus.Running)
+ {
+ controller.Stop();
+ }
+ }
+
+ public static bool IsServiceInstalled()
+ {
+ return GetServiceStatus() != 0;
+ }
+
+ public static bool IsServiceRunning()
+ {
+ return GetServiceStatus() == ServiceControllerStatus.Running;
+ }
+
+ public static ServiceControllerStatus GetServiceStatus()
+ {
+ var controller = new ServiceController(SERVICE_NAME);
+
+ try
+ {
+ return controller.Status;
+ }
+ catch (Exception)
+ {
+ return 0;
+ }
+ }
+
+ internal static void InstallService()
+ {
+ if (!IsAdministrator())
+ {
+ ExecuteElevated(StartUpParams.InstallServiceParam, skipService: true);
+
+ return;
+ }
+
+
+ if (!IsServiceInstalled())
+ {
+ StartProcess("sc.exe", @$"create ""{SERVICE_NAME}"" binpath=""{Process.GetCurrentProcess().MainModule.FileName}"" start=auto", wait: true);
+ StartProcess("sc.exe", @$"description ""{SERVICE_NAME}"" ""Executes tasks for Color Control that require elevation. If this service is stopped, functions like WOL might be impacted.""", wait: true);
+ }
+
+ StartService();
+ }
+
+ internal static void UninstallService()
+ {
+ if (!IsAdministrator())
+ {
+ ExecuteElevated(StartUpParams.UninstallServiceParam, skipService: true);
+
+ return;
+ }
+
+ if (!IsServiceInstalled())
+ {
+ return;
+ }
+
+ StopService();
+
+ StartProcess("sc.exe", @$"delete ""{SERVICE_NAME}""", wait: true);
+ }
+
+ public static bool PingHost(string nameOrAddress)
+ {
+ try
+ {
+ using var pinger = new Ping();
+ var reply = pinger.Send(nameOrAddress);
+ return reply.Status == IPStatus.Success;
+ }
+ catch (PingException)
+ {
+ // Discard PingExceptions and return false;
+ return false;
+ }
+ }
+
+ public static bool WriteText(string fileName, string data)
+ {
+ var doWrite = !File.Exists(fileName);
+
+ if (!doWrite)
+ {
+ var writtenData = File.ReadAllText(fileName);
+
+ doWrite = !writtenData.Equals(data);
+ }
+
+ if (doWrite)
+ {
+ try
+ {
+ File.WriteAllText(fileName, data);
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e.ToLogString());
+ }
+ }
+
+ return doWrite;
+ }
+
+ public static string ReadText(string fileName, bool reverse = false)
+ {
+ var exists = File.Exists(fileName);
+
+ if (!exists)
+ {
+ return null;
+ }
+
+ try
+ {
+ var lines = new StringBuilder();
+
+ using var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+
+ using var reader = new StreamReader(fs);
+
+ string line;
+
+ while ((line = reader.ReadLine()) != null)
+ {
+ if (reverse)
+ {
+ lines.Insert(0, line + "\r\n");
+ continue;
+ }
+
+ lines.AppendLine(line);
+ }
+
+ return lines.ToString();
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e.ToLogString());
+ }
+
+ return null;
+ }
+
+ public static bool WriteObject(string fileName, object value)
+ {
+ var data = JsonConvert.SerializeObject(value);
+
+ return WriteText(fileName, data);
+ }
+
+ public static T DeserializeJson(string fileName)
+ {
+ try
+ {
+ var json = File.ReadAllText(fileName);
+ var data = JsonConvert.DeserializeObject(json);
+
+ return data;
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e.ToLogString());
+
+ return default;
+ }
+ }
+
+ public static string Base64Encode(string plainText)
+ {
+ var plainTextBytes = Encoding.UTF8.GetBytes(plainText);
+ return Convert.ToBase64String(plainTextBytes);
+ }
+
+ public static string Base64Decode(string base64EncodedData)
+ {
+ var base64EncodedBytes = Convert.FromBase64String(base64EncodedData);
+ return Encoding.UTF8.GetString(base64EncodedBytes);
+ }
}
}
diff --git a/ColorControl/Common/WOL.cs b/ColorControl/Common/WOL.cs
index 16ccf48..20802b7 100644
--- a/ColorControl/Common/WOL.cs
+++ b/ColorControl/Common/WOL.cs
@@ -1,67 +1,97 @@
using NLog;
using System;
-using System.Net.NetworkInformation;
+using System.Linq;
using System.Net;
+using System.Net.NetworkInformation;
using System.Net.Sockets;
+using Vanara.PInvoke;
+using static Vanara.PInvoke.IpHlpApi;
namespace ColorControl.Common
{
class WOL
{
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
-
+
private const string ArgumentExceptionInvalidMacAddressLength = "Invalid MAC address length.";
private const string ArgumentExceptionInvalidPasswordLength = "Invalid password length.";
private const int DefaultWolPort = 9;
- public static bool WakeFunction(string macAddress)
+ public static bool WakeFunction(string macAddress, string ipAddress = null)
{
- return WakeFunctionToAllNics(macAddress);
+ return WakeFunctionToAllNics(macAddress, ipAddress);
}
- public static bool WakeFunctionToAllNics(string macAddress)
+ public static bool WakeFunctionCheckAdmin(string macAddress, string ipAddress = null)
+ {
+ if (Utils.IsAdministrator())
+ {
+ return WakeFunction(macAddress, ipAddress);
+ }
+
+ var result = PipeUtils.SendRpcMessage($"{StartUpParams.SendWakeOnLanParam} {macAddress} {ipAddress}");
+
+ // Execute local WOL anyway because pipe failed
+ if (!result.HasValue)
+ {
+ result = WakeFunction(macAddress, ipAddress);
+ }
+
+ return result.Value;
+ }
+
+ public static bool WakeFunctionToAllNics(string macAddress, string ipAddressString)
{
var result = false;
Logger.Debug($"Sending WOL packet to MAC-address {macAddress}");
try
{
- var address = PhysicalAddress.Parse(macAddress);
- var addressBytes = address.GetAddressBytes();
+ var physicalAddress = PhysicalAddress.Parse(macAddress);
+ var addressBytes = physicalAddress.GetAddressBytes();
var data = GetWolPacket(addressBytes);
+ var ipAddress = ipAddressString != null ? IPAddress.Parse(ipAddressString) : null;
var interfaces = NetworkInterface.GetAllNetworkInterfaces();
foreach (var ni in interfaces)
{
- if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || ni.Name.Contains("VirtualBox"))
+ if (ni.NetworkInterfaceType == NetworkInterfaceType.Loopback || ni.Name.Contains("VirtualBox") || ni.Name.Contains("vEthernet") || ni.Description.Contains("Virtual"))
{
continue;
}
Logger.Debug($"Found network: {ni.Name} ({ni.Description}), type: {ni.NetworkInterfaceType}");
try
{
- if (/*ni.OperationalStatus == OperationalStatus.Up &&*/ ni.SupportsMulticast && ni.GetIPProperties().GetIPv4Properties() != null)
+ if (ni.SupportsMulticast && ni.GetIPProperties().GetIPv4Properties() == null)
{
- foreach (var uip in ni.GetIPProperties().UnicastAddresses)
- {
- if (uip.Address.ToString().StartsWith("169.254"))
- {
- continue;
- }
- if (uip.Address.AddressFamily == AddressFamily.InterNetwork)
- {
- Logger.Debug($"Broadcast WOL in network: {ni.Name} ({ni.Description}), local address: {uip.Address}");
- var local = new IPEndPoint(uip.Address, 0);
- var udpc = new UdpClient(local);
- udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
- udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
- var target = new IPEndPoint(IPAddress.Broadcast, DefaultWolPort);
- udpc.Send(data, data.Length, target);
+ continue;
+ }
- result = true;
- }
+ foreach (var uip in ni.GetIPProperties().UnicastAddresses)
+ {
+ if (uip.Address.ToString().StartsWith("169.254") || uip.Address.AddressFamily != AddressFamily.InterNetwork)
+ {
+ continue;
}
+
+ Logger.Debug($"Broadcast WOL in network: {ni.Name} ({ni.Description}), local address: {uip.Address}, destination IP-address: {ipAddressString ?? "broadcast"}");
+
+ if (ipAddress != null)
+ {
+ var localEP = new IPEndPoint(uip.Address, 0);
+
+ SendWolToIpAddress(uip.Address, ipAddress, data, physicalAddress);
+ }
+
+ BroadcastWol(uip.Address, IPAddress.Broadcast, data);
+
+ var parts = uip.Address.ToString().Split(".").ToList();
+ parts[3] = "0";
+ var broadcastAddress = IPAddress.Parse(string.Join(".", parts));
+ BroadcastWol(uip.Address, broadcastAddress, data);
+
+ result = true;
}
}
catch (Exception ex)
@@ -110,5 +140,117 @@ namespace ColorControl.Common
}
return packet;
}
- }
+
+ private static void BroadcastWol(IPAddress localAddress, IPAddress broadCastAddress, byte[] data)
+ {
+ //var localEP = new IPEndPoint(localAddress, 0);
+ var udpc = new UdpClient();
+
+ udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
+ //udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, 1);
+
+ var target = new IPEndPoint(broadCastAddress, DefaultWolPort);
+
+ udpc.Send(data, data.Length, target);
+ }
+
+ private static void SendWolToIpAddress(IPAddress localAddress, IPAddress remoteAddress, byte[] data, PhysicalAddress physicalAddress)
+ {
+ //var localEP = new IPEndPoint(localAddress, 0);
+
+ var netLuid = CreateTransientLocalNetEntry(remoteAddress, physicalAddress);
+ try
+ {
+ var udpc = new UdpClient();
+ udpc.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
+
+ var target = new IPEndPoint(remoteAddress, DefaultWolPort);
+
+ udpc.Send(data, data.Length, target);
+ }
+ finally
+ {
+ DeleteNetEntry(netLuid, remoteAddress);
+ }
+ }
+
+ private static NET_LUID CreateTransientLocalNetEntry(IPAddress ipAddress, PhysicalAddress macAddress)
+ {
+ var ipBytes = ipAddress.GetAddressBytes();
+
+ var address = new Ws2_32.SOCKADDR_INET
+ {
+ si_family = Ws2_32.ADDRESS_FAMILY.AF_INET,
+ Ipv4 = new Ws2_32.SOCKADDR_IN(new Ws2_32.IN_ADDR(ipBytes), DefaultWolPort),
+ };
+
+ var net = new NET_LUID();
+ var addr = new Ws2_32.SOCKADDR_INET();
+
+ MIB_IPFORWARD_ROW2 forwardRow;
+ var result = GetBestRoute2(net, 0, addr, address, 0, out forwardRow, out _);
+
+ if (result != Win32Error.ERROR_SUCCESS)
+ {
+ Logger.Debug($"Cannot find best route: {result}");
+
+ var luidDummy = new NET_LUID();
+
+ return luidDummy;
+ }
+ var luid = forwardRow.InterfaceLuid;
+
+ var macAddressBytes = macAddress.GetAddressBytes();
+ var bytes = new byte[32];
+
+ macAddressBytes.CopyTo(bytes, 0);
+
+ var row = new MIB_IPNET_ROW2
+ {
+ Address = address,
+ InterfaceLuid = luid,
+ PhysicalAddress = bytes,
+ PhysicalAddressLength = 6
+ };
+
+ result = CreateIpNetEntry2(ref row);
+
+ if (result != Win32Error.ERROR_SUCCESS)
+ {
+ Logger.Debug($"Cannot create ip net entry: {result}");
+ }
+
+ return luid;
+ }
+
+ private static void DeleteNetEntry(NET_LUID luid, IPAddress ipAddress)
+ {
+ if (luid.Value == 0)
+ {
+ return;
+ }
+
+ var ipBytes = ipAddress.GetAddressBytes();
+
+ var address = new Ws2_32.SOCKADDR_INET
+ {
+ si_family = Ws2_32.ADDRESS_FAMILY.AF_INET,
+ Ipv4 = new Ws2_32.SOCKADDR_IN(new Ws2_32.IN_ADDR(ipBytes), DefaultWolPort)
+
+ };
+
+ var row = new MIB_IPNET_ROW2
+ {
+ Address = address,
+ InterfaceLuid = luid,
+ };
+
+ var result = DeleteIpNetEntry2(ref row);
+
+ if (result != Win32Error.ERROR_SUCCESS)
+ {
+ Logger.Debug($"Cannot delete ip net entry: {result}");
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/ColorControl/Config.cs b/ColorControl/Config.cs
index 6be9acf..788fe61 100644
--- a/ColorControl/Config.cs
+++ b/ColorControl/Config.cs
@@ -2,6 +2,14 @@
namespace ColorControl
{
+ public enum ElevationMethod
+ {
+ None = 0,
+ RunAsAdmin = 1,
+ UseService = 2,
+ UseElevatedProcess = 3
+ }
+
public class ListViewSortState
{
public SortOrder SortOrder { get; set; }
@@ -31,6 +39,8 @@ namespace ColorControl
public string LgQuickAccessShortcut { get; set; }
public string GameQuickAccessShortcut { get; set; }
public bool UseDedicatedElevatedProcess { get; set; }
+ public ElevationMethod ElevationMethod { get; set; }
+ public bool ElevationMethodAsked { get; set; }
public Config()
{
@@ -43,6 +53,8 @@ namespace ColorControl
FixChromeFonts = false;
UseGdiScaling = true;
UseDedicatedElevatedProcess = false;
+ ElevationMethod = ElevationMethod.None;
+ ElevationMethodAsked = false;
LgPresetsSortState = new ListViewSortState();
NvPresetsSortState = new ListViewSortState();
AmdPresetsSortState = new ListViewSortState();
diff --git a/ColorControl/Forms/ElevatedForm.cs b/ColorControl/Forms/ElevatedForm.cs
index 044b3f6..f54cc50 100644
--- a/ColorControl/Forms/ElevatedForm.cs
+++ b/ColorControl/Forms/ElevatedForm.cs
@@ -49,7 +49,7 @@ namespace ColorControl.Forms
var container = (WinApi.COPYDATASTRUCT)m.GetLParam(typeof(WinApi.COPYDATASTRUCT));
var message = Marshal.PtrToStringAnsi(container.lpData);
- File.AppendAllLines("d:\\Msg.txt", new[] { "Message received: " + message });
+ //File.AppendAllLines("d:\\Msg.txt", new[] { "Message received: " + message });
HandleMessage(message);
@@ -69,7 +69,14 @@ namespace ColorControl.Forms
var startUpParams = StartUpParams.Parse(args);
- Program.HandleStartupParams(startUpParams, null);
+ try
+ {
+ Program.HandleStartupParams(startUpParams, null);
+ }
+ catch (Exception)
+ {
+
+ }
}
private void CheckMutexAsync()
diff --git a/ColorControl/MainForm.Designer.cs b/ColorControl/MainForm.Designer.cs
index fa80a6f..3969130 100644
--- a/ColorControl/MainForm.Designer.cs
+++ b/ColorControl/MainForm.Designer.cs
@@ -231,6 +231,13 @@
this.edtDelayDisplaySettings = new System.Windows.Forms.NumericUpDown();
this.label6 = new System.Windows.Forms.Label();
this.grpGeneralOptions = new System.Windows.Forms.GroupBox();
+ this.btnStartStopService = new System.Windows.Forms.Button();
+ this.btnElevationInfo = new System.Windows.Forms.Button();
+ this.rbElevationService = new System.Windows.Forms.RadioButton();
+ this.rbElevationProcess = new System.Windows.Forms.RadioButton();
+ this.rbElevationAdmin = new System.Windows.Forms.RadioButton();
+ this.label8 = new System.Windows.Forms.Label();
+ this.rbElevationNone = new System.Windows.Forms.RadioButton();
this.chkGdiScaling = new System.Windows.Forms.CheckBox();
this.chkCheckForUpdates = new System.Windows.Forms.CheckBox();
this.chkMinimizeToSystemTray = new System.Windows.Forms.CheckBox();
@@ -238,6 +245,8 @@
this.chkStartMinimized = new System.Windows.Forms.CheckBox();
this.chkStartAfterLogin = new System.Windows.Forms.CheckBox();
this.tabLog = new System.Windows.Forms.TabPage();
+ this.lblShowLog = new System.Windows.Forms.Label();
+ this.cbxLogType = new System.Windows.Forms.ComboBox();
this.btnClearLog = new System.Windows.Forms.Button();
this.edtLog = new System.Windows.Forms.TextBox();
this.tabInfo = new System.Windows.Forms.TabPage();
@@ -251,7 +260,6 @@
this.mnuNvSettings = new System.Windows.Forms.ContextMenuStrip(this.components);
this.miNvProfileInspector = new System.Windows.Forms.ToolStripMenuItem();
this.miNvSettings = new System.Windows.Forms.ToolStripMenuItem();
- this.chkOptionsDedicatedElevatedProcess = new System.Windows.Forms.CheckBox();
this.tcMain.SuspendLayout();
this.tabNVIDIA.SuspendLayout();
this.mnuNvPresets.SuspendLayout();
@@ -2237,11 +2245,11 @@
this.grpNvidiaOptions.Controls.Add(this.cbxDitheringBitDepth);
this.grpNvidiaOptions.Controls.Add(this.chkDitheringEnabled);
this.grpNvidiaOptions.Controls.Add(this.pbGradient);
- this.grpNvidiaOptions.Location = new System.Drawing.Point(481, 7);
+ this.grpNvidiaOptions.Location = new System.Drawing.Point(537, 7);
this.grpNvidiaOptions.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.grpNvidiaOptions.Name = "grpNvidiaOptions";
this.grpNvidiaOptions.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
- this.grpNvidiaOptions.Size = new System.Drawing.Size(625, 351);
+ this.grpNvidiaOptions.Size = new System.Drawing.Size(569, 351);
this.grpNvidiaOptions.TabIndex = 6;
this.grpNvidiaOptions.TabStop = false;
this.grpNvidiaOptions.Text = "NVIDIA options - test dithering";
@@ -2312,7 +2320,7 @@
this.pbGradient.Location = new System.Drawing.Point(7, 106);
this.pbGradient.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.pbGradient.Name = "pbGradient";
- this.pbGradient.Size = new System.Drawing.Size(611, 236);
+ this.pbGradient.Size = new System.Drawing.Size(555, 236);
this.pbGradient.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
this.pbGradient.TabIndex = 0;
this.pbGradient.TabStop = false;
@@ -2324,11 +2332,11 @@
this.grpMiscellaneousOptions.Controls.Add(this.edtBlankScreenSaverShortcut);
this.grpMiscellaneousOptions.Controls.Add(this.lblFixChromeFontsDescription);
this.grpMiscellaneousOptions.Controls.Add(this.chkFixChromeFonts);
- this.grpMiscellaneousOptions.Location = new System.Drawing.Point(7, 332);
+ this.grpMiscellaneousOptions.Location = new System.Drawing.Point(7, 365);
this.grpMiscellaneousOptions.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.grpMiscellaneousOptions.Name = "grpMiscellaneousOptions";
this.grpMiscellaneousOptions.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
- this.grpMiscellaneousOptions.Size = new System.Drawing.Size(467, 150);
+ this.grpMiscellaneousOptions.Size = new System.Drawing.Size(522, 150);
this.grpMiscellaneousOptions.TabIndex = 5;
this.grpMiscellaneousOptions.TabStop = false;
this.grpMiscellaneousOptions.Text = "Miscellaneous";
@@ -2399,11 +2407,11 @@
this.grpLGOptions.Controls.Add(this.lblLgMaxPowerOnRetriesDescription);
this.grpLGOptions.Controls.Add(this.edtLgMaxPowerOnRetries);
this.grpLGOptions.Controls.Add(this.lblLgMaxPowerOnRetries);
- this.grpLGOptions.Location = new System.Drawing.Point(7, 136);
+ this.grpLGOptions.Location = new System.Drawing.Point(7, 169);
this.grpLGOptions.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.grpLGOptions.Name = "grpLGOptions";
this.grpLGOptions.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
- this.grpLGOptions.Size = new System.Drawing.Size(470, 190);
+ this.grpLGOptions.Size = new System.Drawing.Size(522, 190);
this.grpLGOptions.TabIndex = 4;
this.grpLGOptions.TabStop = false;
this.grpLGOptions.Text = "LG controller";
@@ -2411,7 +2419,7 @@
// lblGameBarShortcut
//
this.lblGameBarShortcut.AutoSize = true;
- this.lblGameBarShortcut.Location = new System.Drawing.Point(201, 127);
+ this.lblGameBarShortcut.Location = new System.Drawing.Point(266, 127);
this.lblGameBarShortcut.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
this.lblGameBarShortcut.Name = "lblGameBarShortcut";
this.lblGameBarShortcut.Size = new System.Drawing.Size(108, 15);
@@ -2420,7 +2428,7 @@
//
// edtLgGameBarShortcut
//
- this.edtLgGameBarShortcut.Location = new System.Drawing.Point(307, 121);
+ this.edtLgGameBarShortcut.Location = new System.Drawing.Point(386, 121);
this.edtLgGameBarShortcut.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.edtLgGameBarShortcut.Name = "edtLgGameBarShortcut";
this.edtLgGameBarShortcut.ReadOnly = true;
@@ -2447,7 +2455,7 @@
0,
0,
0});
- this.edtLgOptionShutdownDelay.Location = new System.Drawing.Point(349, 68);
+ this.edtLgOptionShutdownDelay.Location = new System.Drawing.Point(428, 68);
this.edtLgOptionShutdownDelay.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.edtLgOptionShutdownDelay.Maximum = new decimal(new int[] {
5000,
@@ -2507,7 +2515,7 @@
//
// edtLgMaxPowerOnRetries
//
- this.edtLgMaxPowerOnRetries.Location = new System.Drawing.Point(349, 20);
+ this.edtLgMaxPowerOnRetries.Location = new System.Drawing.Point(428, 20);
this.edtLgMaxPowerOnRetries.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.edtLgMaxPowerOnRetries.Maximum = new decimal(new int[] {
25,
@@ -2535,11 +2543,11 @@
//
this.grpHDROptions.Controls.Add(this.edtDelayDisplaySettings);
this.grpHDROptions.Controls.Add(this.label6);
- this.grpHDROptions.Location = new System.Drawing.Point(481, 365);
+ this.grpHDROptions.Location = new System.Drawing.Point(537, 365);
this.grpHDROptions.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.grpHDROptions.Name = "grpHDROptions";
this.grpHDROptions.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
- this.grpHDROptions.Size = new System.Drawing.Size(467, 83);
+ this.grpHDROptions.Size = new System.Drawing.Size(569, 83);
this.grpHDROptions.TabIndex = 3;
this.grpHDROptions.TabStop = false;
this.grpHDROptions.Text = "HDR";
@@ -2578,7 +2586,13 @@
//
// grpGeneralOptions
//
- this.grpGeneralOptions.Controls.Add(this.chkOptionsDedicatedElevatedProcess);
+ this.grpGeneralOptions.Controls.Add(this.btnStartStopService);
+ this.grpGeneralOptions.Controls.Add(this.btnElevationInfo);
+ this.grpGeneralOptions.Controls.Add(this.rbElevationService);
+ this.grpGeneralOptions.Controls.Add(this.rbElevationProcess);
+ this.grpGeneralOptions.Controls.Add(this.rbElevationAdmin);
+ this.grpGeneralOptions.Controls.Add(this.label8);
+ this.grpGeneralOptions.Controls.Add(this.rbElevationNone);
this.grpGeneralOptions.Controls.Add(this.chkGdiScaling);
this.grpGeneralOptions.Controls.Add(this.chkCheckForUpdates);
this.grpGeneralOptions.Controls.Add(this.chkMinimizeToSystemTray);
@@ -2589,15 +2603,101 @@
this.grpGeneralOptions.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.grpGeneralOptions.Name = "grpGeneralOptions";
this.grpGeneralOptions.Padding = new System.Windows.Forms.Padding(4, 3, 4, 3);
- this.grpGeneralOptions.Size = new System.Drawing.Size(467, 122);
+ this.grpGeneralOptions.Size = new System.Drawing.Size(522, 156);
this.grpGeneralOptions.TabIndex = 2;
this.grpGeneralOptions.TabStop = false;
this.grpGeneralOptions.Text = "General";
//
+ // btnStartStopService
+ //
+ this.btnStartStopService.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnStartStopService.Location = new System.Drawing.Point(474, 103);
+ this.btnStartStopService.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.btnStartStopService.Name = "btnStartStopService";
+ this.btnStartStopService.Size = new System.Drawing.Size(40, 27);
+ this.btnStartStopService.TabIndex = 65;
+ this.btnStartStopService.Text = "Start";
+ this.btnStartStopService.UseVisualStyleBackColor = true;
+ this.btnStartStopService.Click += new System.EventHandler(this.btnStartStopService_Click);
+ //
+ // btnElevationInfo
+ //
+ this.btnElevationInfo.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
+ this.btnElevationInfo.Location = new System.Drawing.Point(329, 41);
+ this.btnElevationInfo.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.btnElevationInfo.Name = "btnElevationInfo";
+ this.btnElevationInfo.Size = new System.Drawing.Size(22, 27);
+ this.btnElevationInfo.TabIndex = 64;
+ this.btnElevationInfo.Text = "?";
+ this.btnElevationInfo.UseVisualStyleBackColor = true;
+ this.btnElevationInfo.Click += new System.EventHandler(this.btnElevationInfo_Click);
+ //
+ // rbElevationService
+ //
+ this.rbElevationService.AutoSize = true;
+ this.rbElevationService.Location = new System.Drawing.Point(216, 107);
+ this.rbElevationService.Name = "rbElevationService";
+ this.rbElevationService.Size = new System.Drawing.Size(136, 19);
+ this.rbElevationService.TabIndex = 63;
+ this.rbElevationService.TabStop = true;
+ this.rbElevationService.Tag = "2";
+ this.rbElevationService.Text = "Use Windows Service";
+ this.rbElevationService.UseVisualStyleBackColor = true;
+ this.rbElevationService.CheckedChanged += new System.EventHandler(this.rbElevationNone_CheckedChanged);
+ //
+ // rbElevationProcess
+ //
+ this.rbElevationProcess.AutoSize = true;
+ this.rbElevationProcess.Location = new System.Drawing.Point(216, 128);
+ this.rbElevationProcess.Name = "rbElevationProcess";
+ this.rbElevationProcess.Size = new System.Drawing.Size(189, 19);
+ this.rbElevationProcess.TabIndex = 62;
+ this.rbElevationProcess.TabStop = true;
+ this.rbElevationProcess.Tag = "3";
+ this.rbElevationProcess.Text = "Use dedicated elevated process";
+ this.rbElevationProcess.UseVisualStyleBackColor = true;
+ this.rbElevationProcess.CheckedChanged += new System.EventHandler(this.rbElevationNone_CheckedChanged);
+ //
+ // rbElevationAdmin
+ //
+ this.rbElevationAdmin.AutoSize = true;
+ this.rbElevationAdmin.Location = new System.Drawing.Point(216, 86);
+ this.rbElevationAdmin.Name = "rbElevationAdmin";
+ this.rbElevationAdmin.Size = new System.Drawing.Size(97, 19);
+ this.rbElevationAdmin.TabIndex = 61;
+ this.rbElevationAdmin.TabStop = true;
+ this.rbElevationAdmin.Tag = "1";
+ this.rbElevationAdmin.Text = "Run as admin";
+ this.rbElevationAdmin.UseVisualStyleBackColor = true;
+ this.rbElevationAdmin.CheckedChanged += new System.EventHandler(this.rbElevationNone_CheckedChanged);
+ //
+ // label8
+ //
+ this.label8.AutoSize = true;
+ this.label8.Location = new System.Drawing.Point(216, 47);
+ this.label8.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ this.label8.Name = "label8";
+ this.label8.Size = new System.Drawing.Size(105, 15);
+ this.label8.TabIndex = 60;
+ this.label8.Text = "Elevation-method:";
+ //
+ // rbElevationNone
+ //
+ this.rbElevationNone.AutoSize = true;
+ this.rbElevationNone.Location = new System.Drawing.Point(216, 65);
+ this.rbElevationNone.Name = "rbElevationNone";
+ this.rbElevationNone.Size = new System.Drawing.Size(54, 19);
+ this.rbElevationNone.TabIndex = 10;
+ this.rbElevationNone.TabStop = true;
+ this.rbElevationNone.Tag = "0";
+ this.rbElevationNone.Text = "None";
+ this.rbElevationNone.UseVisualStyleBackColor = true;
+ this.rbElevationNone.CheckedChanged += new System.EventHandler(this.rbElevationNone_CheckedChanged);
+ //
// chkGdiScaling
//
this.chkGdiScaling.AutoSize = true;
- this.chkGdiScaling.Location = new System.Drawing.Point(238, 72);
+ this.chkGdiScaling.Location = new System.Drawing.Point(216, 22);
this.chkGdiScaling.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.chkGdiScaling.Name = "chkGdiScaling";
this.chkGdiScaling.Size = new System.Drawing.Size(197, 19);
@@ -2609,7 +2709,7 @@
// chkCheckForUpdates
//
this.chkCheckForUpdates.AutoSize = true;
- this.chkCheckForUpdates.Location = new System.Drawing.Point(238, 47);
+ this.chkCheckForUpdates.Location = new System.Drawing.Point(7, 122);
this.chkCheckForUpdates.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.chkCheckForUpdates.Name = "chkCheckForUpdates";
this.chkCheckForUpdates.Size = new System.Drawing.Size(197, 19);
@@ -2621,7 +2721,7 @@
// chkMinimizeToSystemTray
//
this.chkMinimizeToSystemTray.AutoSize = true;
- this.chkMinimizeToSystemTray.Location = new System.Drawing.Point(238, 22);
+ this.chkMinimizeToSystemTray.Location = new System.Drawing.Point(7, 97);
this.chkMinimizeToSystemTray.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.chkMinimizeToSystemTray.Name = "chkMinimizeToSystemTray";
this.chkMinimizeToSystemTray.Size = new System.Drawing.Size(152, 19);
@@ -2667,6 +2767,8 @@
//
// tabLog
//
+ this.tabLog.Controls.Add(this.lblShowLog);
+ this.tabLog.Controls.Add(this.cbxLogType);
this.tabLog.Controls.Add(this.btnClearLog);
this.tabLog.Controls.Add(this.edtLog);
this.tabLog.Location = new System.Drawing.Point(4, 24);
@@ -2678,9 +2780,33 @@
this.tabLog.Text = "Log";
this.tabLog.UseVisualStyleBackColor = true;
//
+ // lblShowLog
+ //
+ this.lblShowLog.AutoSize = true;
+ this.lblShowLog.Location = new System.Drawing.Point(8, 14);
+ this.lblShowLog.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0);
+ this.lblShowLog.Name = "lblShowLog";
+ this.lblShowLog.Size = new System.Drawing.Size(88, 15);
+ this.lblShowLog.TabIndex = 33;
+ this.lblShowLog.Text = "Show log from:";
+ //
+ // cbxLogType
+ //
+ this.cbxLogType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.cbxLogType.FormattingEnabled = true;
+ this.cbxLogType.Items.AddRange(new object[] {
+ "Main application",
+ "Service"});
+ this.cbxLogType.Location = new System.Drawing.Point(102, 9);
+ this.cbxLogType.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
+ this.cbxLogType.Name = "cbxLogType";
+ this.cbxLogType.Size = new System.Drawing.Size(189, 23);
+ this.cbxLogType.TabIndex = 32;
+ this.cbxLogType.SelectedIndexChanged += new System.EventHandler(this.cbxLogType_SelectedIndexChanged);
+ //
// btnClearLog
//
- this.btnClearLog.Location = new System.Drawing.Point(7, 7);
+ this.btnClearLog.Location = new System.Drawing.Point(299, 6);
this.btnClearLog.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
this.btnClearLog.Name = "btnClearLog";
this.btnClearLog.Size = new System.Drawing.Size(88, 27);
@@ -2824,18 +2950,6 @@
this.miNvSettings.Text = "Settings";
this.miNvSettings.Click += new System.EventHandler(this.miNvSettings_Click);
//
- // chkOptionsDedicatedElevatedProcess
- //
- this.chkOptionsDedicatedElevatedProcess.AutoSize = true;
- this.chkOptionsDedicatedElevatedProcess.Location = new System.Drawing.Point(7, 97);
- this.chkOptionsDedicatedElevatedProcess.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3);
- this.chkOptionsDedicatedElevatedProcess.Name = "chkOptionsDedicatedElevatedProcess";
- this.chkOptionsDedicatedElevatedProcess.Size = new System.Drawing.Size(190, 19);
- this.chkOptionsDedicatedElevatedProcess.TabIndex = 8;
- this.chkOptionsDedicatedElevatedProcess.Text = "Use dedicated elevated process";
- this.chkOptionsDedicatedElevatedProcess.UseVisualStyleBackColor = true;
- this.chkOptionsDedicatedElevatedProcess.CheckedChanged += new System.EventHandler(this.chkOptionsDedicatedElevatedProcess_CheckedChanged);
- //
// MainForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
@@ -3124,7 +3238,15 @@
private System.Windows.Forms.ContextMenuStrip mnuGameOptions;
private System.Windows.Forms.ToolStripMenuItem miGameProcessorAffinity;
private System.Windows.Forms.ToolStripMenuItem miGameProcessPriority;
- private System.Windows.Forms.CheckBox chkOptionsDedicatedElevatedProcess;
+ private System.Windows.Forms.RadioButton rbElevationService;
+ private System.Windows.Forms.RadioButton rbElevationProcess;
+ private System.Windows.Forms.RadioButton rbElevationAdmin;
+ private System.Windows.Forms.Label label8;
+ private System.Windows.Forms.RadioButton rbElevationNone;
+ private System.Windows.Forms.Button btnElevationInfo;
+ private System.Windows.Forms.Button btnStartStopService;
+ private System.Windows.Forms.Label lblShowLog;
+ private System.Windows.Forms.ComboBox cbxLogType;
}
}
diff --git a/ColorControl/MainForm.cs b/ColorControl/MainForm.cs
index 645a6f6..aa8eb5f 100644
--- a/ColorControl/MainForm.cs
+++ b/ColorControl/MainForm.cs
@@ -7,8 +7,8 @@ using ColorControl.Services.Common;
using ColorControl.Services.GameLauncher;
using ColorControl.Services.LG;
using ColorControl.Services.NVIDIA;
+using ColorControl.Svc;
using LgTv;
-using Newtonsoft.Json;
using NLog;
using nspector;
using nspector.Common;
@@ -28,7 +28,6 @@ using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
-using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
@@ -77,7 +76,6 @@ namespace ColorControl
private bool _updatingDitherSettings;
private LgGameBar _gameBarForm;
- private QuickAccessForm _gameQuickAccessForm;
private GameService _gameService;
@@ -125,7 +123,7 @@ namespace ColorControl
_trayIcon.ContextMenuStrip.Opened += trayIconContextMenu_Popup;
_trayIcon.BalloonTipClicked += trayIconBalloonTip_Clicked;
- chkStartAfterLogin.Checked = Utils.TaskExists(Program.TS_TASKNAME, true);
+ chkStartAfterLogin.Checked = Utils.TaskExists(Program.TS_TASKNAME);
chkFixChromeFonts.Enabled = Utils.IsChromeInstalled();
if (chkFixChromeFonts.Enabled)
@@ -140,12 +138,15 @@ namespace ColorControl
_shortcuts.Add(SHORTCUTID_SCREENSAVER, StartScreenSaver);
+ //Utils.StartService();
+
InitLgService();
InitNvService();
InitAmdService();
InitGameService();
InitInfo();
+ UpdateServiceInfo();
UserSessionInfo.Install();
_screenStateNotify = WinApi.RegisterPowerSettingNotification(Handle, ref Utils.GUID_CONSOLE_DISPLAY_STATE, 0);
@@ -154,9 +155,36 @@ namespace ColorControl
_initialized = true;
+ //Task.Run(() => Utils.StartPipeAsync());
+
AfterInitialized();
}
+ private void UpdateServiceInfo()
+ {
+ var text = "Use Windows Service";
+ btnStartStopService.Enabled = true;
+
+ if (Utils.IsServiceRunning())
+ {
+ text += " (running)";
+ btnStartStopService.Text = "Stop";
+ }
+ else if (Utils.IsServiceInstalled())
+ {
+ text += " (installed, not running)";
+ btnStartStopService.Text = "Start";
+ }
+ else
+ {
+ text += " (not installed)";
+ btnStartStopService.Text = "Start";
+ btnStartStopService.Enabled = false;
+ }
+
+ rbElevationService.Text = text;
+ }
+
private void InitNvService()
{
try
@@ -361,6 +389,11 @@ namespace ColorControl
Text = Application.ProductName + " " + Application.ProductVersion;
//}
+ if (Utils.IsAdministrator())
+ {
+ Text += " (administrator)";
+ }
+
lblInfo.Text = Text + " - " + _currentVersionInfo.LegalCopyright;
lbPlugins.Items.Add("lgtv.net by gr4b4z");
@@ -817,7 +850,13 @@ namespace ColorControl
return;
}
var enabled = chkStartAfterLogin.Checked;
- Utils.RegisterTask(Program.TS_TASKNAME, enabled);
+ RegisterScheduledTask(enabled);
+
+ rbElevationAdmin.Enabled = enabled;
+ if (!enabled && rbElevationAdmin.Checked)
+ {
+ rbElevationNone.Checked = true;
+ }
}
private void LoadConfig()
@@ -829,7 +868,6 @@ namespace ColorControl
edtDelayDisplaySettings.Value = _config.DisplaySettingsDelay;
edtBlankScreenSaverShortcut.Text = _config.ScreenSaverShortcut;
chkGdiScaling.Checked = _config.UseGdiScaling;
- chkOptionsDedicatedElevatedProcess.Checked = _config.UseDedicatedElevatedProcess;
if (!string.IsNullOrEmpty(_config.ScreenSaverShortcut))
{
@@ -857,15 +895,7 @@ namespace ColorControl
_config.FormHeight = Height;
}
- try
- {
- var data = JsonConvert.SerializeObject(_config);
- File.WriteAllText(Program.ConfigFilename, data);
- }
- catch (Exception e)
- {
- Logger.Error(e.ToLogString());
- }
+ Utils.WriteObject(Program.ConfigFilename, _config);
}
private void ShowControls(Control parent, bool show = true, Control exclude = null)
@@ -907,6 +937,44 @@ namespace ColorControl
InitSelectedTab();
CheckForUpdates();
+
+ CheckElevationMethod();
+ }
+
+ private void CheckElevationMethod()
+ {
+ if (Utils.IsAdministrator())
+ {
+ return;
+ }
+
+ if (_config.ElevationMethodAsked)
+ {
+ if (_config.ElevationMethod == ElevationMethod.UseService && !Utils.IsServiceInstalled() &&
+ (MessageForms.QuestionYesNo("The elevation method is set to Windows Service but it is not installed. Do you want to install it now?") == DialogResult.Yes))
+ {
+ Utils.InstallService();
+ }
+ else if (_config.ElevationMethod == ElevationMethod.UseService && !Utils.IsServiceRunning() &&
+ (MessageForms.QuestionYesNo("The elevation method is set to Windows Service but it is not running. Do you want to start it now?") == DialogResult.Yes))
+ {
+ Utils.StartService();
+ }
+
+ return;
+ }
+
+ var result = MessageForms.QuestionYesNo(Utils.ELEVATION_MSG + @"
+
+Do you want to install and start the Windows Service now? You can always change this on the Options-tab page.
+
+NOTE: installing the service may cause a User Account Control popup.");
+ if (result == DialogResult.Yes)
+ {
+ SetElevationMethod(ElevationMethod.UseService);
+ }
+
+ _config.ElevationMethodAsked = true;
}
protected override void SetVisibleCore(bool value)
@@ -1692,6 +1760,24 @@ namespace ColorControl
UpdateDitherSettings();
}
+
+ _initialized = false;
+ try
+ {
+ var _ = _config.ElevationMethod switch
+ {
+ ElevationMethod.None => rbElevationNone.Checked = true,
+ ElevationMethod.RunAsAdmin => rbElevationAdmin.Checked = true,
+ ElevationMethod.UseService => rbElevationService.Checked = true,
+ ElevationMethod.UseElevatedProcess => rbElevationProcess.Checked = true
+ };
+ }
+ finally
+ {
+ _initialized = true;
+ }
+
+ UpdateServiceInfo();
}
private void UpdateDitherSettings()
@@ -2059,36 +2145,44 @@ namespace ColorControl
private void LoadLog()
{
- var filename = Path.Combine(_dataDir, "LogFile.txt");
+ var logType = cbxLogType.SelectedIndex;
- var lines = new List { "No log file found" };
-
- try
+ if (logType == -1)
{
- if (File.Exists(filename))
+ cbxLogType.SelectedIndex = 0;
+ return;
+ }
+
+ string text;
+
+ if (logType == 0)
+ {
+ var filename = Path.Combine(_dataDir, "LogFile.txt");
+
+ text = Utils.ReadText(filename);
+
+ text ??= "No log file found";
+ }
+ else
+ {
+ if (Utils.IsServiceRunning())
{
- using var fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
+ var message = new SvcMessage { MessageType = SvcMessageType.GetLog };
- using var reader = new StreamReader(fs);
+ var result = PipeUtils.SendMessage(message);
- String line;
-
- while ((line = reader.ReadLine()) != null)
- {
- lines.Add(line);
- }
-
- //lines = File.ReadAllLines(filename);
+ text = result?.Data ?? "Cannot get log from service";
+ }
+ else
+ {
+ text = "The service is not installed or not running";
}
}
- catch (Exception ex)
- {
- lines = new List { $"Cannot load log file: {ex.Message}" };
- }
- var reversedLines = lines.ToList();
- var builder = new StringBuilder();
- reversedLines.ForEach(line => builder.AppendLine(line));
- edtLog.Text = builder.ToString();
+
+ edtLog.Text = text;
+
+ edtLog.SelectionStart = text.Length;
+ edtLog.ScrollToCaret();
}
private void LoadInfo()
@@ -2401,12 +2495,26 @@ Do you want to continue?";
private void btnClearLog_Click(object sender, EventArgs e)
{
- var filename = Path.Combine(_dataDir, "LogFile.txt");
- if (File.Exists(filename))
+ var logType = cbxLogType.SelectedIndex;
+
+ if (logType == -1)
{
- File.Delete(filename);
- edtLog.Clear();
+ return;
}
+
+ if (logType == 0)
+ {
+ var filename = Program.LogFilename;
+ if (File.Exists(filename))
+ {
+ File.Delete(filename);
+ }
+ }
+ else
+ {
+ PipeUtils.SendMessage(SvcMessageType.ClearLog);
+ }
+ edtLog.Clear();
}
private void edtLgDeviceFilter_TextChanged(object sender, EventArgs e)
@@ -3652,7 +3760,8 @@ The InStart and Software Update items are now visible under the Expert-button."
var fileName = Path.GetFileNameWithoutExtension(preset.Path);
- preset.name = versionInfo.FileDescription ?? versionInfo.ProductName ?? fileName;
+ preset.name = !string.IsNullOrEmpty(versionInfo.FileDescription) ? versionInfo.FileDescription :
+ !string.IsNullOrEmpty(versionInfo.ProductName) ? versionInfo.ProductName : fileName;
}
AddOrUpdateItemGame(preset);
@@ -4066,13 +4175,150 @@ The InStart and Software Update items are now visible under the Expert-button."
var enumValue = Enum.Parse(typeof(GamePriorityClass), value);
preset.ProcessPriorityClass = (uint)(int)enumValue;
-
}
- private void chkOptionsDedicatedElevatedProcess_CheckedChanged(object sender, EventArgs e)
+ private void rbElevationNone_CheckedChanged(object sender, EventArgs e)
{
- _config.UseDedicatedElevatedProcess = chkOptionsDedicatedElevatedProcess.Checked;
- Utils.UseDedicatedElevatedProcess = _config.UseDedicatedElevatedProcess;
+ if (!_initialized)
+ {
+ return;
+ }
+
+
+ if (sender is not RadioButton button || !button.Checked)
+ {
+ return;
+ }
+
+ SetElevationMethod((ElevationMethod)Utils.ParseInt((string)button.Tag));
+ }
+
+ private void SetElevationMethod(ElevationMethod elevationMethod)
+ {
+ _config.ElevationMethod = elevationMethod;
+
+ Utils.UseDedicatedElevatedProcess = _config.ElevationMethod == ElevationMethod.UseElevatedProcess;
+
+ var _ = _config.ElevationMethod switch
+ {
+ ElevationMethod.None => SetElevationMethodNone(),
+ ElevationMethod.RunAsAdmin => SetElevationMethodRunAsAdmin(),
+ ElevationMethod.UseService => SetElevationMethodUseService(),
+ ElevationMethod.UseElevatedProcess => SetElevationMethodUseElevatedProcess(),
+ _ => true
+ };
+
+ UpdateServiceInfo();
+ }
+
+ private bool SetElevationMethodNone()
+ {
+ if (chkStartAfterLogin.Enabled)
+ {
+ RegisterScheduledTask();
+ }
+
+ Utils.UninstallService();
+
+ return true;
+ }
+
+ private bool SetElevationMethodRunAsAdmin()
+ {
+ if (chkStartAfterLogin.Enabled)
+ {
+ RegisterScheduledTask(false);
+ RegisterScheduledTask();
+ }
+
+ Utils.UninstallService();
+
+ return true;
+ }
+
+ private bool SetElevationMethodUseService()
+ {
+ Utils.InstallService();
+
+ if (Utils.IsServiceInstalled())
+ {
+ MessageForms.InfoOk("Service installed successfully.");
+ return true;
+ }
+
+ MessageForms.ErrorOk("Service could not be installed. Check the logs.");
+
+ return false;
+ }
+
+ private bool SetElevationMethodUseElevatedProcess()
+ {
+ if (chkStartAfterLogin.Enabled)
+ {
+ RegisterScheduledTask();
+ }
+
+ Utils.UninstallService();
+
+ return true;
+ }
+
+ private void RegisterScheduledTask(bool enabled = true)
+ {
+ Utils.RegisterTask(Program.TS_TASKNAME, enabled, _config.ElevationMethod == ElevationMethod.RunAsAdmin ? Microsoft.Win32.TaskScheduler.TaskRunLevel.Highest : Microsoft.Win32.TaskScheduler.TaskRunLevel.LUA);
+ }
+
+ private void btnElevationInfo_Click(object sender, EventArgs e)
+ {
+ MessageForms.InfoOk(Utils.ELEVATION_MSG + @$"
+
+NOTE: if ColorControl itself already runs as administrator this is indicated in the title: ColorControl {Application.ProductVersion} (administrator).
+
+Currently ColorControl is {(Utils.IsAdministrator() ? "" : "not ")}running as administrator.
+");
+ }
+
+ private void btnStartStopService_Click(object sender, EventArgs e)
+ {
+ btnStartStopService.Enabled = false;
+ try
+ {
+ var start = btnStartStopService.Text == "Start";
+
+ if (start)
+ {
+ Utils.StartService();
+ }
+ else
+ {
+ Utils.StopService();
+ }
+
+ var wait = 1000;
+
+ while (wait > 0)
+ {
+ Utils.WaitForTask(Task.Delay(100));
+
+ if (start == Utils.IsServiceRunning())
+ {
+ break;
+ }
+
+ wait -= 100;
+ }
+ }
+ finally
+ {
+ btnStartStopService.Enabled = true;
+ }
+
+ UpdateServiceInfo();
+ }
+
+ private void cbxLogType_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ LoadLog();
}
}
}
\ No newline at end of file
diff --git a/ColorControl/Program.cs b/ColorControl/Program.cs
index 10701d4..744bd2c 100644
--- a/ColorControl/Program.cs
+++ b/ColorControl/Program.cs
@@ -3,13 +3,18 @@ using ColorControl.Forms;
using ColorControl.Services.AMD;
using ColorControl.Services.LG;
using ColorControl.Services.NVIDIA;
+using ColorControl.Svc;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
using Newtonsoft.Json;
using NLog;
using NWin32;
using System;
using System.Diagnostics;
using System.IO;
+using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using System.Windows.Forms;
namespace ColorControl
@@ -19,6 +24,7 @@ namespace ColorControl
public const string TS_TASKNAME = "ColorControl";
public static string DataDir { get; private set; }
public static string ConfigFilename { get; private set; }
+ public static string LogFilename { get; private set; }
public static Config Config { get; private set; }
public static AppContext AppContext { get; private set; }
@@ -32,6 +38,20 @@ namespace ColorControl
[STAThread]
static void Main(string[] args)
{
+ DataDir = Utils.GetDataPath();
+
+ var runAsService = args.Contains("--service") || Process.GetCurrentProcess().Parent()?.ProcessName?.Equals("services", StringComparison.InvariantCultureIgnoreCase) == true;
+
+ InitLogger(runAsService);
+
+ Logger.Debug("Parent process: " + Process.GetCurrentProcess().Parent()?.ProcessName);
+
+ if (runAsService)
+ {
+ Utils.WaitForTask(RunService(args));
+ return;
+ }
+
MutexId = $"Global\\{typeof(MainForm).GUID}";
var currentDomain = AppDomain.CurrentDomain;
@@ -40,9 +60,6 @@ namespace ColorControl
// Handler for exceptions in threads behind forms.
Application.ThreadException += GlobalThreadExceptionHandler;
- DataDir = Utils.GetDataPath();
- InitLogger();
-
LoadConfig();
if (Config.UseGdiScaling)
@@ -88,10 +105,19 @@ namespace ColorControl
mutex.WaitOne();
try
{
+ if (Debugger.IsAttached)
+ {
+ Utils.StartService();
+ }
+
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm(AppContext));
+ if (Debugger.IsAttached)
+ {
+ Utils.StopService();
+ }
}
catch (Exception ex)
{
@@ -108,12 +134,17 @@ namespace ColorControl
}
}
- private static void InitLogger()
+ private static void InitLogger(bool runAsService)
{
var config = new NLog.Config.LoggingConfiguration();
// Targets where to log to: File and Console
- var logfile = new NLog.Targets.FileTarget("logfile") { FileName = Path.Combine(DataDir, "LogFile.txt") };
+ var logFileAppendix = runAsService ? "_svc" : "";
+
+ LogFilename = Path.Combine(DataDir, $"LogFile{logFileAppendix}.txt");
+
+ var logfile = new NLog.Targets.FileTarget("logfile") { FileName = LogFilename };
+
//var logconsole = new NLog.Targets.ConsoleTarget("logconsole");
// Rules for mapping loggers to targets
@@ -154,7 +185,7 @@ namespace ColorControl
}
if (startUpParams.EnableAutoStart || startUpParams.DisableAutoStart)
{
- Utils.RegisterTask(TS_TASKNAME, startUpParams.EnableAutoStart);
+ Utils.RegisterTask(TS_TASKNAME, startUpParams.EnableAutoStart, startUpParams.AutoStartRunLevel);
return true;
}
if (startUpParams.SetProcessAffinity)
@@ -172,6 +203,30 @@ namespace ColorControl
StartElevated();
return true;
}
+ if (startUpParams.SendWol)
+ {
+ return WOL.WakeFunction(startUpParams.WolMacAddress, startUpParams.WolIpAddress);
+ }
+ if (startUpParams.InstallService)
+ {
+ Utils.InstallService();
+ return true;
+ }
+ if (startUpParams.UninstallService)
+ {
+ Utils.UninstallService();
+ return true;
+ }
+ if (startUpParams.StartService)
+ {
+ Utils.StartService();
+ return true;
+ }
+ if (startUpParams.StopService)
+ {
+ Utils.StopService();
+ return true;
+ }
var useConsole = startUpParams.NoGui || existingProcess != null;
@@ -263,6 +318,24 @@ namespace ColorControl
}
}
+ private static async Task RunService(string[] args)
+ {
+ Logger.Debug("RUNNING SERVICE");
+
+ using IHost host = Host.CreateDefaultBuilder(args)
+ .UseWindowsService(options =>
+ {
+ options.ServiceName = "ColorControl Service";
+ })
+ .ConfigureServices(services =>
+ {
+ services.AddHostedService();
+ })
+ .Build();
+
+ await host.RunAsync();
+ }
+
public static int EnumThreadWindows(IntPtr handle, IntPtr param)
{
NativeMethods.SendMessageW(handle, Utils.WM_BRINGTOFRONT, UIntPtr.Zero, IntPtr.Zero);
diff --git a/ColorControl/Services/AMD/AmdService.cs b/ColorControl/Services/AMD/AmdService.cs
index f647bd3..e6671a0 100644
--- a/ColorControl/Services/AMD/AmdService.cs
+++ b/ColorControl/Services/AMD/AmdService.cs
@@ -1,10 +1,8 @@
using ATI.ADL;
using ColorControl.Common;
using ColorControl.Services.Common;
-using Newtonsoft.Json;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Linq;
using System.Windows.Forms;
@@ -52,15 +50,7 @@ namespace ColorControl.Services.AMD
private void SavePresets()
{
- try
- {
- var json = JsonConvert.SerializeObject(_presets);
- File.WriteAllText(_presetsFilename, json);
- }
- catch (Exception e)
- {
- Logger.Error(e.ToLogString());
- }
+ Utils.WriteObject(_presetsFilename, _presets);
}
public void GlobalSave()
diff --git a/ColorControl/Services/GameLauncher/GameService.cs b/ColorControl/Services/GameLauncher/GameService.cs
index 1041e41..3551b8a 100644
--- a/ColorControl/Services/GameLauncher/GameService.cs
+++ b/ColorControl/Services/GameLauncher/GameService.cs
@@ -1,9 +1,7 @@
using ColorControl.Common;
using ColorControl.Services.Common;
-using Newtonsoft.Json;
using System;
using System.Collections.Generic;
-using System.IO;
using System.Threading.Tasks;
namespace ColorControl.Services.GameLauncher
@@ -51,15 +49,7 @@ namespace ColorControl.Services.GameLauncher
private void SavePresets()
{
- try
- {
- var json = JsonConvert.SerializeObject(_presets);
- File.WriteAllText(_presetsFilename, json);
- }
- catch (Exception e)
- {
- Logger.Error(e.ToLogString());
- }
+ Utils.WriteObject(_presetsFilename, _presets);
}
public void GlobalSave()
diff --git a/ColorControl/Services/LG/LgDevice.cs b/ColorControl/Services/LG/LgDevice.cs
index 83fba78..79e0130 100644
--- a/ColorControl/Services/LG/LgDevice.cs
+++ b/ColorControl/Services/LG/LgDevice.cs
@@ -759,7 +759,7 @@ namespace ColorControl.Services.LG
if (MacAddress != null)
{
- result = WOL.WakeFunction(MacAddress);
+ result = WOL.WakeFunctionCheckAdmin(MacAddress, IpAddress);
_justWokeUp = true;
}
else
diff --git a/ColorControl/Services/LG/LgService.cs b/ColorControl/Services/LG/LgService.cs
index b012f31..f977831 100644
--- a/ColorControl/Services/LG/LgService.cs
+++ b/ColorControl/Services/LG/LgService.cs
@@ -1,6 +1,7 @@
using ColorControl.Common;
using ColorControl.Forms;
using ColorControl.Services.Common;
+using ColorControl.Svc;
using LgTv;
using Microsoft.Win32;
using Newtonsoft.Json;
@@ -127,6 +128,8 @@ namespace ColorControl.Services.LG
SaveConfig();
SavePresets();
SaveRemoteControlButtons();
+
+ SendConfigToService();
}
protected override List GetDefaultPresets()
@@ -257,29 +260,19 @@ namespace ColorControl.Services.LG
private void SavePresets()
{
- try
- {
- var json = JsonConvert.SerializeObject(_presets);
- File.WriteAllText(_presetsFilename, json);
- }
- catch (Exception e)
- {
- Logger.Error(e.ToLogString());
- }
+ Utils.WriteObject(_presetsFilename, _presets);
}
private void SaveConfig()
{
Config.PowerOnAfterStartup = Devices?.Any(d => d.PowerOnAfterStartup) ?? false;
- var json = JsonConvert.SerializeObject(Config);
- File.WriteAllText(_configFilename, json);
+ Utils.WriteObject(_configFilename, Config);
}
private void SaveRemoteControlButtons()
{
- var json = JsonConvert.SerializeObject(_remoteControlButtons);
- File.WriteAllText(_rcButtonsFilename, json);
+ Utils.WriteObject(_rcButtonsFilename, _remoteControlButtons);
}
public async Task RefreshDevices(bool connect = true, bool afterStartUp = false)
@@ -937,5 +930,23 @@ namespace ColorControl.Services.LG
Utils.StartProcess(resumeScript, hidden: true);
}
}
+
+ private void SendConfigToService()
+ {
+ if (!Utils.IsServiceRunning())
+ {
+ return;
+ }
+
+ var json = JsonConvert.SerializeObject(Config);
+
+ var message = new SvcMessage
+ {
+ MessageType = SvcMessageType.SetLgConfig,
+ Data = json
+ };
+
+ PipeUtils.SendMessage(message);
+ }
}
}
\ No newline at end of file
diff --git a/ColorControl/Services/NVIDIA/NvService.cs b/ColorControl/Services/NVIDIA/NvService.cs
index dab957e..f1e5fc9 100644
--- a/ColorControl/Services/NVIDIA/NvService.cs
+++ b/ColorControl/Services/NVIDIA/NvService.cs
@@ -1,6 +1,5 @@
using ColorControl.Common;
using ColorControl.Services.Common;
-using Newtonsoft.Json;
using nspector.Common;
using nspector.Common.Meta;
using NvAPIWrapper.Display;
@@ -10,7 +9,6 @@ using NWin32.NativeTypes;
using System;
using System.Collections.Generic;
using System.ComponentModel;
-using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
@@ -187,15 +185,7 @@ namespace ColorControl.Services.NVIDIA
private void SavePresets()
{
- try
- {
- var json = JsonConvert.SerializeObject(_presets);
- File.WriteAllText(_presetsFilename, json);
- }
- catch (Exception e)
- {
- Logger.Error(e.ToLogString());
- }
+ Utils.WriteObject(_presetsFilename, _presets);
}
private void SetCurrentDisplay(NvPreset preset)
@@ -509,11 +499,11 @@ namespace ColorControl.Services.NVIDIA
return false;
}
- var timing = display.DisplayDevice.CurrentTiming;
var desktopRect = display.DisplayDevice.ScanOutInformation.SourceDesktopRectangle;
if (refreshRate == 0)
{
+ var timing = display.DisplayDevice.CurrentTiming;
refreshRate = (uint)timing.Extra.RefreshRate;
}
@@ -523,11 +513,6 @@ namespace ColorControl.Services.NVIDIA
resolutionHeight = (uint)desktopRect.Height;
}
- if (timing.Extra.RefreshRate == refreshRate && desktopRect.Width == resolutionWidth && desktopRect.Height == resolutionHeight)
- {
- return true;
- }
-
var portrait = new[] { Rotate.Degree90, Rotate.Degree270 }.Contains(display.DisplayDevice.ScanOutInformation.SourceToTargetRotation);
return SetRefreshRateInternal(display.Name, (int)refreshRate, portrait, (int)resolutionWidth, (int)resolutionHeight, updateRegistry);
diff --git a/ColorControl/StartUpParams.cs b/ColorControl/StartUpParams.cs
index 5bc7f0a..aaacc42 100644
--- a/ColorControl/StartUpParams.cs
+++ b/ColorControl/StartUpParams.cs
@@ -1,4 +1,6 @@
-using System.Collections.Generic;
+using ColorControl.Common;
+using Microsoft.Win32.TaskScheduler;
+using System.Collections.Generic;
namespace ColorControl
{
@@ -17,6 +19,11 @@ namespace ColorControl
public const string SetProcessAffinityParam = "--set-process-affinity";
public const string SetProcessPriorityParam = "--set-process-priority";
public const string StartElevatedParam = "--elevated";
+ public const string SendWakeOnLanParam = "--send-wol";
+ public const string InstallServiceParam = "--install-service";
+ public const string UninstallServiceParam = "--uninstall-service";
+ public const string StartServiceParam = "--start-service";
+ public const string StopServiceParam = "--stop-service";
public bool RunningFromScheduledTask { get; private set; }
public bool ActivateChromeFontFix { get; private set; }
@@ -30,6 +37,7 @@ namespace ColorControl
public string LgPresetName { get; private set; }
public bool NoGui { get; set; }
public bool EnableAutoStart { get; private set; }
+ public TaskRunLevel AutoStartRunLevel { get; private set; } = TaskRunLevel.LUA;
public bool DisableAutoStart { get; private set; }
public bool SetProcessAffinity { get; private set; }
public int ProcessId { get; private set; }
@@ -37,6 +45,13 @@ namespace ColorControl
public bool SetProcessPriority { get; private set; }
public uint PriorityClass { get; private set; }
public bool StartElevated { get; private set; }
+ public bool SendWol { get; private set; }
+ public string WolMacAddress { get; private set; }
+ public string WolIpAddress { get; private set; }
+ public bool InstallService { get; private set; }
+ public bool UninstallService { get; private set; }
+ public bool StartService { get; private set; }
+ public bool StopService { get; private set; }
public static StartUpParams Parse(IEnumerable args)
{
@@ -85,6 +100,22 @@ namespace ColorControl
settings.PriorityClass = uint.Parse(arg);
break;
}
+ case SendWakeOnLanParam:
+ {
+ if (settings.WolMacAddress == null)
+ {
+ settings.WolMacAddress = arg;
+ continue;
+ }
+
+ settings.WolIpAddress = arg;
+ break;
+ }
+ case EnableAutoStartParam:
+ {
+ settings.AutoStartRunLevel = (TaskRunLevel)Utils.ParseInt(arg, 0);
+ break;
+ }
}
parseNameParam = string.Empty;
@@ -140,6 +171,7 @@ namespace ColorControl
case EnableAutoStartParam:
{
settings.EnableAutoStart = true;
+ parseNameParam = EnableAutoStartParam;
break;
}
case DisableAutoStartParam:
@@ -164,6 +196,32 @@ namespace ColorControl
settings.StartElevated = true;
break;
}
+ case SendWakeOnLanParam:
+ {
+ settings.SendWol = true;
+ parseNameParam = SendWakeOnLanParam;
+ break;
+ }
+ case InstallServiceParam:
+ {
+ settings.InstallService = true;
+ break;
+ }
+ case UninstallServiceParam:
+ {
+ settings.UninstallService = true;
+ break;
+ }
+ case StartServiceParam:
+ {
+ settings.StartService = true;
+ break;
+ }
+ case StopServiceParam:
+ {
+ settings.StopService = true;
+ break;
+ }
}
}
return settings;
diff --git a/ColorControl/Svc/ColorControlBackgroundService.cs b/ColorControl/Svc/ColorControlBackgroundService.cs
new file mode 100644
index 0000000..d3607d4
--- /dev/null
+++ b/ColorControl/Svc/ColorControlBackgroundService.cs
@@ -0,0 +1,193 @@
+using ColorControl.Common;
+using ColorControl.Services.LG;
+using Microsoft.Extensions.Hosting;
+using Newtonsoft.Json;
+using NLog;
+using System;
+using System.IO;
+using System.IO.Pipes;
+using System.Linq;
+using System.Security.AccessControl;
+using System.Security.Principal;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace ColorControl.Svc
+{
+ public sealed class ColorControlBackgroundService : BackgroundService
+ {
+ private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
+ private static string LgConfigFile = "LgConfig.json";
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ try
+ {
+ LgConfigFile = Path.Combine(Program.DataDir, LgConfigFile);
+
+ //var lgService = new LgService(Program.DataDir, true);
+ //lgService.InstallEventHandlers(true);
+
+ await WakeDevicesAsync();
+
+ var securityIdentifier = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
+
+ var ps = new PipeSecurity();
+ ps.AddAccessRule(new PipeAccessRule(securityIdentifier, PipeAccessRights.ReadWrite, AccessControlType.Allow));
+
+ Logger.Debug("EXECUTING BACKGROUND");
+
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ Logger.Debug("WAITING FOR MESSAGE...");
+
+ var pipeServer = NamedPipeServerStreamAcl.Create("testpipe", PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1024, 1024, ps);
+
+ await pipeServer.WaitForConnectionAsync(stoppingToken);
+
+ var ss = new StreamString(pipeServer);
+
+ var message = ss.ReadString();
+
+ //Logger.Debug("MESSAGE RECEIVED: " + message);
+
+ try
+ {
+ var result = HandleMessage(message);
+
+ var resultJson = JsonConvert.SerializeObject(result);
+
+ //Logger.Debug("SENDING RESULT: " + resultJson);
+
+ ss.WriteString(resultJson);
+ }
+ catch (Exception ex)
+ {
+ Logger.Error($"While handling message: {message}");
+
+ var result = SvcResultMessage.FromResult(false, ex.Message);
+ var resultJson = JsonConvert.SerializeObject(result);
+ ss.WriteString(resultJson);
+ }
+
+ pipeServer.Close();
+ //pipeServer.Disconnect();
+ }
+ Logger.Debug("STOPPING...");
+ }
+ catch (OperationCanceledException)
+ {
+ Logger.Debug("Service stopping");
+ }
+ catch (Exception ex)
+ {
+ Logger.Error(ex, "{Message}", ex.Message);
+
+ // Terminates this process and returns an exit code to the operating system.
+ // This is required to avoid the 'BackgroundServiceExceptionBehavior', which
+ // performs one of two scenarios:
+ // 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
+ // 2. When set to "StopHost": will cleanly stop the host, and log errors.
+ //
+ // In order for the Windows Service Management system to leverage configured
+ // recovery options, we need to terminate the process with a non-zero exit code.
+ //Environment.Exit(1);
+ }
+ }
+
+ private SvcResultMessage HandleMessage(string json)
+ {
+ var message = JsonConvert.DeserializeObject(json);
+
+ var result = message.MessageType switch
+ {
+ SvcMessageType.SetLgConfig => HandleSetLgConfigMessage(message),
+ SvcMessageType.GetLog => HandleGetLogMessage(message),
+ SvcMessageType.ClearLog => HandleClearLogMessage(message),
+ SvcMessageType.ExecuteRpc => HandleExecuteRpcCommand(message)
+ };
+
+ return result;
+ }
+
+ private SvcResultMessage HandleExecuteRpcCommand(SvcMessage message)
+ {
+ var args = message.Data.Split(' ');
+
+ var startUpParams = StartUpParams.Parse(args);
+
+ return SvcResultMessage.FromResult(Program.HandleStartupParams(startUpParams, null));
+ }
+
+ private SvcResultMessage HandleSetLgConfigMessage(SvcMessage message)
+ {
+ var data = message.Data;
+
+ Utils.WriteText(LgConfigFile, data);
+
+ return SvcResultMessage.FromResult(true);
+ }
+
+ private SvcResultMessage HandleGetLogMessage(SvcMessage _)
+ {
+ var logFile = Program.LogFilename;
+
+ var data = Utils.ReadText(logFile);
+
+ return SvcResultMessage.FromResult(data);
+ }
+
+ private SvcResultMessage HandleClearLogMessage(SvcMessage _)
+ {
+ var logFile = Program.LogFilename;
+
+ if (File.Exists(logFile))
+ {
+ File.Delete(logFile);
+ }
+
+ return SvcResultMessage.FromResult(true);
+ }
+
+ private async Task WakeDevicesAsync()
+ {
+ if (!UserSessionInfo.UserLocalSession)
+ {
+ return;
+ }
+
+ if (!File.Exists(LgConfigFile))
+ {
+ return;
+ }
+
+ var config = Utils.DeserializeJson(LgConfigFile);
+
+ if (config == null)
+ {
+ return;
+ }
+
+ var wakeDevices = config.Devices.Where(d => d.PowerOnAfterStartup).ToList();
+
+ foreach (var wakeDevice in wakeDevices)
+ {
+ for (var i = 0; i < config.PowerOnRetries; i++)
+ {
+ WOL.WakeFunction(wakeDevice.MacAddress, wakeDevice.IpAddress);
+
+ await Task.Delay(1000);
+
+ var result = Utils.PingHost(wakeDevice.IpAddress);
+
+ if (result)
+ {
+ Logger.Debug($"Device {wakeDevice.Name} is pingable, stopping WOL");
+
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/ColorControl/Svc/SvcMessage.cs b/ColorControl/Svc/SvcMessage.cs
new file mode 100644
index 0000000..6e9a499
--- /dev/null
+++ b/ColorControl/Svc/SvcMessage.cs
@@ -0,0 +1,17 @@
+namespace ColorControl.Svc
+{
+ public enum SvcMessageType
+ {
+ SetLgConfig = 1,
+ GetLog = 2,
+ ClearLog = 3,
+ ExecuteRpc = 10,
+ }
+
+ public class SvcMessage
+ {
+ public SvcMessageType MessageType { get; set; }
+
+ public string Data { get; set; }
+ }
+}
diff --git a/ColorControl/Svc/SvcResultMessage.cs b/ColorControl/Svc/SvcResultMessage.cs
new file mode 100644
index 0000000..1be9b0a
--- /dev/null
+++ b/ColorControl/Svc/SvcResultMessage.cs
@@ -0,0 +1,15 @@
+namespace ColorControl.Svc
+{
+ public class SvcResultMessage
+ {
+ public bool Result { get; set; }
+
+ public string Data { get; set; }
+
+ public string ErrorMessage { get; set; }
+
+
+ public static SvcResultMessage FromResult(bool result, string errorMessage = null) => new SvcResultMessage { Result = result, ErrorMessage = errorMessage };
+ public static SvcResultMessage FromResult(string data) => new SvcResultMessage { Result = true, Data = data };
+ }
+}