diff --git a/ControlMyMonitorManagement.sln b/ControlMyMonitorManagement.sln
new file mode 100644
index 0000000..918ed15
--- /dev/null
+++ b/ControlMyMonitorManagement.sln
@@ -0,0 +1,43 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.3.32505.426
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlMyMonitorManagement", "ControlMyMonitorManagement\ControlMyMonitorManagement.csproj", "{DBD5D7D8-9261-40F2-AA31-20DC2E6D2176}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Library", "Library\Library.csproj", "{1281FEF8-86AF-4479-A58A-05E0F212FECC}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Language", "Language\Language.csproj", "{AAED30CF-DEB6-4D06-B24C-C9B5E2113B88}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tester", "Tester\Tester.csproj", "{0D34DD73-3282-40EB-8F59-DF190944BF12}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {DBD5D7D8-9261-40F2-AA31-20DC2E6D2176}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {DBD5D7D8-9261-40F2-AA31-20DC2E6D2176}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {DBD5D7D8-9261-40F2-AA31-20DC2E6D2176}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {DBD5D7D8-9261-40F2-AA31-20DC2E6D2176}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1281FEF8-86AF-4479-A58A-05E0F212FECC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1281FEF8-86AF-4479-A58A-05E0F212FECC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1281FEF8-86AF-4479-A58A-05E0F212FECC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1281FEF8-86AF-4479-A58A-05E0F212FECC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AAED30CF-DEB6-4D06-B24C-C9B5E2113B88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AAED30CF-DEB6-4D06-B24C-C9B5E2113B88}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AAED30CF-DEB6-4D06-B24C-C9B5E2113B88}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AAED30CF-DEB6-4D06-B24C-C9B5E2113B88}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0D34DD73-3282-40EB-8F59-DF190944BF12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0D34DD73-3282-40EB-8F59-DF190944BF12}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0D34DD73-3282-40EB-8F59-DF190944BF12}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0D34DD73-3282-40EB-8F59-DF190944BF12}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {88781872-23CA-43AF-ABEA-F36438B6CB58}
+ EndGlobalSection
+EndGlobal
diff --git a/ControlMyMonitorManagement/App.xaml b/ControlMyMonitorManagement/App.xaml
new file mode 100644
index 0000000..dc96db3
--- /dev/null
+++ b/ControlMyMonitorManagement/App.xaml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ControlMyMonitorManagement/App.xaml.cs b/ControlMyMonitorManagement/App.xaml.cs
new file mode 100644
index 0000000..bf7a075
--- /dev/null
+++ b/ControlMyMonitorManagement/App.xaml.cs
@@ -0,0 +1,34 @@
+using CMM.Library.Config;
+using CMM.Library.Method;
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace CMM.Management
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ internal static XConfig cfg { get; private set; }
+ internal static CMMMgr CMMMgr { get; private set; }
+
+ public App()
+ {
+ //cfg = new();
+ //cfg.Load();
+ CMMMgr = new CMMMgr();
+ Startup += async (s, e) => await App_Startup(s, e);
+ }
+
+ private async Task App_Startup(object sender, StartupEventArgs e)
+ {
+ await CMMMgr.Init();
+ }
+ }
+}
diff --git a/ControlMyMonitorManagement/AssemblyInfo.cs b/ControlMyMonitorManagement/AssemblyInfo.cs
new file mode 100644
index 0000000..8b5504e
--- /dev/null
+++ b/ControlMyMonitorManagement/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Windows;
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
diff --git a/ControlMyMonitorManagement/Control/Loading.xaml b/ControlMyMonitorManagement/Control/Loading.xaml
new file mode 100644
index 0000000..d79f66e
--- /dev/null
+++ b/ControlMyMonitorManagement/Control/Loading.xaml
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ControlMyMonitorManagement/Control/Loading.xaml.cs b/ControlMyMonitorManagement/Control/Loading.xaml.cs
new file mode 100644
index 0000000..fd970e5
--- /dev/null
+++ b/ControlMyMonitorManagement/Control/Loading.xaml.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace CMM.Management.Control
+{
+ ///
+ /// Interaction logic for Loading.xaml
+ ///
+ public partial class Loading : UserControl
+ {
+ public Loading()
+ {
+ InitializeComponent();
+ }
+ }
+}
diff --git a/ControlMyMonitorManagement/Control/MonCtrl.cs b/ControlMyMonitorManagement/Control/MonCtrl.cs
new file mode 100644
index 0000000..d0b85c9
--- /dev/null
+++ b/ControlMyMonitorManagement/Control/MonCtrl.cs
@@ -0,0 +1,55 @@
+using System.Windows.Controls;
+using System.Windows;
+using CMM.Library.ViewModel;
+using CMM.Library.Base;
+using CMM.Library.Method;
+using System.Windows.Data;
+using System;
+
+namespace CMM.Management.Control
+{
+ ///
+ /// 單一顆螢幕
+ ///
+ internal class MonCtrl : System.Windows.Controls.Control
+ {
+ public readonly static DependencyProperty MonProperty;
+ private StackPanel _sp;
+
+ static MonCtrl()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(MonCtrl), new FrameworkPropertyMetadata(typeof(MonCtrl)));
+
+ MonProperty = DependencyProperty.Register(
+ "Monitor",
+ typeof(XMonitor),
+ typeof(MonCtrl),
+ new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnMonChangedCallback));
+ }
+
+ public override void OnApplyTemplate()
+ {
+ _sp = Template.FindName("sp", this) as StackPanel;
+ }
+
+ public XMonitor Mon
+ {
+ get => (XMonitor)GetValue(MonProperty);
+ set => SetValue(MonProperty, value);
+ }
+
+ static void OnMonChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
+ {
+ var me = sender as MonCtrl;
+ if (me != null)
+ {
+ me.OnMonChanged((XMonitor)args.NewValue);
+ }
+ }
+
+ public virtual void OnMonChanged(XMonitor value)
+ {
+
+ }
+ }
+}
diff --git a/ControlMyMonitorManagement/Control/MonitorsControl.cs b/ControlMyMonitorManagement/Control/MonitorsControl.cs
new file mode 100644
index 0000000..0c21a87
--- /dev/null
+++ b/ControlMyMonitorManagement/Control/MonitorsControl.cs
@@ -0,0 +1,61 @@
+using System.Windows.Controls;
+using System.Windows;
+using CMM.Library.ViewModel;
+using CMM.Library.Base;
+using CMM.Library.Method;
+using System.Windows.Data;
+using System;
+
+namespace CMM.Management.Control
+{
+ ///
+ /// 全部螢幕
+ ///
+ internal class MonitorsControl : System.Windows.Controls.Control
+ {
+ public readonly static DependencyProperty MonitorsProperty;
+ private StackPanel _sp;
+
+ static MonitorsControl()
+ {
+ DefaultStyleKeyProperty.OverrideMetadata(typeof(MonitorsControl), new FrameworkPropertyMetadata(typeof(MonitorsControl)));
+
+ MonitorsProperty = DependencyProperty.Register(
+ "Monitors",
+ typeof(ObservableRangeCollection),
+ typeof(MonitorsControl),
+ new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnMonitorsChangedCallback));
+ }
+
+ public override void OnApplyTemplate()
+ {
+ _sp = Template.FindName("sp", this) as StackPanel;
+ }
+
+ public ObservableRangeCollection Monitors
+ {
+ get => (ObservableRangeCollection)GetValue(MonitorsProperty);
+ set => SetValue(MonitorsProperty, value);
+ }
+
+ static void OnMonitorsChangedCallback(DependencyObject sender, DependencyPropertyChangedEventArgs args)
+ {
+ var me = sender as MonitorsControl;
+ if (me != null)
+ {
+ me.OnMonitorsChanged((ObservableRangeCollection)args.NewValue);
+ }
+ }
+
+ public virtual void OnMonitorsChanged(ObservableRangeCollection value)
+ {
+ foreach (var mon in value)
+ {
+ var monCtrl = new MonCtrl();
+ monCtrl.Mon = mon;
+
+ _sp.Children.Add(monCtrl);
+ }
+ }
+ }
+}
diff --git a/ControlMyMonitorManagement/ControlMyMonitorManagement.csproj b/ControlMyMonitorManagement/ControlMyMonitorManagement.csproj
new file mode 100644
index 0000000..04191ea
--- /dev/null
+++ b/ControlMyMonitorManagement/ControlMyMonitorManagement.csproj
@@ -0,0 +1,66 @@
+
+
+
+ WinExe
+ net6.0-windows
+ CMM.Management
+ true
+ Copyright © DangWang $([System.DateTime]::Now.ToString(yyyy))
+ Dang
+ ControlMyMonitorManagement
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(DefaultXamlRuntime)
+ Designer
+
+
+ $(DefaultXamlRuntime)
+ Designer
+
+
+ $(DefaultXamlRuntime)
+ Designer
+
+
+ $(DefaultXamlRuntime)
+ Designer
+
+
+ $(DefaultXamlRuntime)
+ Designer
+
+
+
+
+ 1
+ 0
+ $([System.DateTime]::op_Subtraction($([System.DateTime]::get_Now().get_Date()),$([System.DateTime]::new(2017,9,17))).get_TotalDays())
+ $([System.DateTime]::Now.ToString(Hmm))
+ $([System.DateTime]::Now.ToString(yyyyMMdd))
+ $(Major).$(Minor).$(ProjectStartedDate).$(DaysSinceProjectStarted)
+ 0.0.0.1
+ $(VersionSuffix)
+ 0.0.0.1
+ $(DateTimeSuffix)
+
+
+
diff --git a/ControlMyMonitorManagement/Images/icons.xaml b/ControlMyMonitorManagement/Images/icons.xaml
new file mode 100644
index 0000000..4e978a5
--- /dev/null
+++ b/ControlMyMonitorManagement/Images/icons.xaml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ControlMyMonitorManagement/MainWindow.xaml b/ControlMyMonitorManagement/MainWindow.xaml
new file mode 100644
index 0000000..bb1e9d4
--- /dev/null
+++ b/ControlMyMonitorManagement/MainWindow.xaml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ControlMyMonitorManagement/MainWindow.xaml.cs b/ControlMyMonitorManagement/MainWindow.xaml.cs
new file mode 100644
index 0000000..ff850c6
--- /dev/null
+++ b/ControlMyMonitorManagement/MainWindow.xaml.cs
@@ -0,0 +1,87 @@
+using CMM.Library.WinAPI;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Interop;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace CMM.Management
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+
+ }
+
+ private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
+ {
+
+ }
+
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ EnableBlur();
+ this.DataContext = App.CMMMgr;
+ }
+
+ #region 透明背景
+ private void EnableBlur()
+ {
+ var windowHelper = new WindowInteropHelper(this);
+ var accent = new AccentPolicy();
+ accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
+ var accentStructSize = Marshal.SizeOf(accent);
+ var accentPtr = Marshal.AllocHGlobal(accentStructSize);
+ Marshal.StructureToPtr(accent, accentPtr, false);
+ var data = new WindowCompositionAttributeData();
+ data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
+ data.SizeOfData = accentStructSize;
+ data.Data = accentPtr;
+
+ Win32.SetWindowCompositionAttribute(windowHelper.Handle, ref data);
+
+ Marshal.FreeHGlobal(accentPtr);
+ }
+ #endregion
+
+ private void move(object sender, MouseButtonEventArgs e) => DragMove();
+
+ private void Btn_Click(object sender, RoutedEventArgs e)
+ {
+ var tag = (sender as FrameworkElement).Tag.ToString();
+
+ switch (tag)
+ {
+ case "Exit":
+ Deconstruct();
+ break;
+ case "Minimized":
+ this.WindowState = WindowState.Minimized;
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void Deconstruct()
+ {
+ Environment.Exit(0);
+ }
+ }
+}
diff --git a/ControlMyMonitorManagement/Style/Btn.xaml b/ControlMyMonitorManagement/Style/Btn.xaml
new file mode 100644
index 0000000..c5d30bd
--- /dev/null
+++ b/ControlMyMonitorManagement/Style/Btn.xaml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ControlMyMonitorManagement/Style/Color.xaml b/ControlMyMonitorManagement/Style/Color.xaml
new file mode 100644
index 0000000..f18278b
--- /dev/null
+++ b/ControlMyMonitorManagement/Style/Color.xaml
@@ -0,0 +1,39 @@
+
+
+ #C1272C
+ #66424242
+ #535353
+
+ #232323
+ #8A3D40
+ #FFFFFF
+
+ #B2626262
+ #CCADADAD
+
+ #FF686868
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ControlMyMonitorManagement/Style/Main.xaml b/ControlMyMonitorManagement/Style/Main.xaml
new file mode 100644
index 0000000..4ccd3a9
--- /dev/null
+++ b/ControlMyMonitorManagement/Style/Main.xaml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/ControlMyMonitorManagement/Style/MonCtrlStyle.xaml b/ControlMyMonitorManagement/Style/MonCtrlStyle.xaml
new file mode 100644
index 0000000..debd0d9
--- /dev/null
+++ b/ControlMyMonitorManagement/Style/MonCtrlStyle.xaml
@@ -0,0 +1,18 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/ControlMyMonitorManagement/Style/MonitorsControlStyle.xaml b/ControlMyMonitorManagement/Style/MonitorsControlStyle.xaml
new file mode 100644
index 0000000..e70c415
--- /dev/null
+++ b/ControlMyMonitorManagement/Style/MonitorsControlStyle.xaml
@@ -0,0 +1,17 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/ControlMyMonitorManagement/Style/Slider.xaml b/ControlMyMonitorManagement/Style/Slider.xaml
new file mode 100644
index 0000000..addf73c
--- /dev/null
+++ b/ControlMyMonitorManagement/Style/Slider.xaml
@@ -0,0 +1,278 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Display/Display.csproj b/Display/Display.csproj
new file mode 100644
index 0000000..0ec6dc4
--- /dev/null
+++ b/Display/Display.csproj
@@ -0,0 +1,32 @@
+
+
+
+ net6.0-windows
+ enable
+ CMM.Display
+ ControlMyMonitorManagement
+ true
+ Dang
+ Copyright © DangWang $([System.DateTime]::Now.ToString(yyyy))
+
+
+
+ 1
+ 0
+ $([System.DateTime]::op_Subtraction($([System.DateTime]::get_Now().get_Date()),$([System.DateTime]::new(2017,9,17))).get_TotalDays())
+ $([System.DateTime]::Now.ToString(Hmm))
+ $([System.DateTime]::Now.ToString(yyyyMMdd))
+ $(Major).$(Minor).$(ProjectStartedDate).$(DaysSinceProjectStarted)
+ 0.0.0.1
+ $(VersionSuffix)
+ 0.0.0.1
+ $(DateTimeSuffix)
+
+
+
+
+
+
+
+
+
diff --git a/Display/Images/icons.xaml b/Display/Images/icons.xaml
new file mode 100644
index 0000000..273dd61
--- /dev/null
+++ b/Display/Images/icons.xaml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Display/Style/Btn.xaml b/Display/Style/Btn.xaml
new file mode 100644
index 0000000..d7536fb
--- /dev/null
+++ b/Display/Style/Btn.xaml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Display/Style/Color.xaml b/Display/Style/Color.xaml
new file mode 100644
index 0000000..f18278b
--- /dev/null
+++ b/Display/Style/Color.xaml
@@ -0,0 +1,39 @@
+
+
+ #C1272C
+ #66424242
+ #535353
+
+ #232323
+ #8A3D40
+ #FFFFFF
+
+ #B2626262
+ #CCADADAD
+
+ #FF686868
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Display/Style/Main.xaml b/Display/Style/Main.xaml
new file mode 100644
index 0000000..4ccd3a9
--- /dev/null
+++ b/Display/Style/Main.xaml
@@ -0,0 +1,4 @@
+
+
+
\ No newline at end of file
diff --git a/Display/Style/Slider.xaml b/Display/Style/Slider.xaml
new file mode 100644
index 0000000..fc734df
--- /dev/null
+++ b/Display/Style/Slider.xaml
@@ -0,0 +1,278 @@
+
+
+
+
\ No newline at end of file
diff --git a/Language/CulturesHelper.cs b/Language/CulturesHelper.cs
new file mode 100644
index 0000000..9206b00
--- /dev/null
+++ b/Language/CulturesHelper.cs
@@ -0,0 +1,115 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Windows;
+
+namespace CMM.Language
+{
+ public class CulturesHelper
+ {
+ private bool _isFoundInstalledCultures = false;
+
+ const string LanguageFolder = "/Language;component";
+ const string ResourceLocalPath = "pack://application:,," + LanguageFolder;
+ const string LanguageFileName = "StringResources";
+
+ public List SupportedCultures { get; private set; } = new List();
+
+ public CulturesHelper() => Init();
+
+ private void Init()
+ {
+ if (!_isFoundInstalledCultures)
+ {
+ var cultureInfo = new CultureInfo("");
+
+ var Languages = GetAllLanguageResource();
+
+ GetAllLanguageResource().ForEach(file =>
+ {
+ try
+ {
+ string cultureName = file.Substring(file.IndexOf(".") + 1).Replace(".xaml", "");
+
+ cultureInfo = CultureInfo.GetCultureInfo(cultureName);
+
+ if (cultureInfo != null)
+ {
+ SupportedCultures.Add(cultureInfo);
+ }
+ }
+ catch (ArgumentException) { }
+ });
+
+ _isFoundInstalledCultures = true;
+ }
+ }
+
+ ///
+ /// 增加Language的XMAL檔
+ ///
+ private static List GetAllLanguageResource()
+ {
+ var Languages = new List();
+ string uriPath = ResourceLocalPath + "/" + LanguageFileName;
+
+ Languages.Add(uriPath + ".en-US.xaml");
+ Languages.Add(uriPath + ".zh-TW.xaml");
+
+ return Languages;
+ }
+
+ public void ChangeCulture(string cultureName)
+ {
+ var cultureInfo = CultureInfo.GetCultureInfo(cultureName);
+
+ if (cultureInfo != null)
+ {
+ ChangeCulture(cultureInfo);
+ }
+ }
+
+ ///
+ /// 切換語系
+ ///
+ public void ChangeCulture(CultureInfo culture)
+ {
+ if (SupportedCultures.Contains(culture))
+ {
+ var existsRD = Application.Current.Resources.MergedDictionaries
+ .Where(x => x.Source.OriginalString.StartsWith(ResourceLocalPath, StringComparison.CurrentCultureIgnoreCase) ||
+ x.Source.OriginalString.StartsWith(LanguageFolder, StringComparison.CurrentCultureIgnoreCase))
+ .FirstOrDefault();
+
+ if (existsRD == null) return;
+
+ var resourceFile = $"{ResourceLocalPath}/{LanguageFileName}.{culture.Name}.xaml";
+ var res = new ResourceDictionary()
+ {
+ Source = new Uri(resourceFile, UriKind.Absolute)
+ };
+
+ Application.Current.Resources.MergedDictionaries.Remove(existsRD);
+ Application.Current.Resources.MergedDictionaries.Add(res);
+ }
+ }
+ }
+
+ public static class Lang
+ {
+ public static string Find(string key)
+ {
+ if (Application.Current == null) return null;
+
+ try
+ {
+ return (string)Application.Current.FindResource(key);
+ }
+ catch
+ {
+ return string.Empty;
+ }
+ }
+ }
+}
diff --git a/Language/Language.csproj b/Language/Language.csproj
new file mode 100644
index 0000000..df47c0c
--- /dev/null
+++ b/Language/Language.csproj
@@ -0,0 +1,35 @@
+
+
+
+ net6.0-windows
+ CMM.Language
+ enable
+ true
+ ControlMyMonitorManagement
+
+
+
+ 1
+ 0
+ $([System.DateTime]::op_Subtraction($([System.DateTime]::get_Now().get_Date()),$([System.DateTime]::new(2017,9,17))).get_TotalDays())
+ $([System.DateTime]::Now.ToString(Hmm))
+ $([System.DateTime]::Now.ToString(yyyyMMdd))
+ $(Major).$(Minor).$(ProjectStartedDate).$(DaysSinceProjectStarted)
+ 0.0.0.1
+ $(VersionSuffix)
+ 0.0.0.1
+ $(DateTimeSuffix)
+
+
+
+
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+
+
+ MSBuild:Compile
+
+
+
diff --git a/Language/StringResources.en-US.xaml b/Language/StringResources.en-US.xaml
new file mode 100644
index 0000000..603c9e6
--- /dev/null
+++ b/Language/StringResources.en-US.xaml
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/Language/StringResources.xaml b/Language/StringResources.xaml
new file mode 100644
index 0000000..603c9e6
--- /dev/null
+++ b/Language/StringResources.xaml
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/Language/StringResources.zh-TW.xam b/Language/StringResources.zh-TW.xam
new file mode 100644
index 0000000..603c9e6
--- /dev/null
+++ b/Language/StringResources.zh-TW.xam
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/Library/Base/ObservableRangeCollection.cs b/Library/Base/ObservableRangeCollection.cs
new file mode 100644
index 0000000..406dbf5
--- /dev/null
+++ b/Library/Base/ObservableRangeCollection.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Collections.Specialized;
+
+namespace CMM.Library.Base
+{
+ ///
+ /// Represents a dynamic data collection that provides notifications when items get added, removed, or when the whole list is refreshed.
+ ///
+ ///
+ public class ObservableRangeCollection : ObservableCollection
+ {
+ ///
+ /// Adds the elements of the specified collection to the end of the ObservableCollection(Of T).
+ ///
+ public void AddRange(IEnumerable collection)
+ {
+ if (collection == null) throw new ArgumentNullException("collection");
+
+ foreach (var i in collection) Items.Add(i);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ ///
+ /// Removes the first occurence of each item in the specified collection from ObservableCollection(Of T).
+ ///
+ public void RemoveRange(IEnumerable collection)
+ {
+ if (collection == null) throw new ArgumentNullException("collection");
+
+ foreach (var i in collection) Items.Remove(i);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ ///
+ /// Clears the current collection and replaces it with the specified item.
+ ///
+ public void Replace(T item)
+ {
+ ReplaceRange(new T[] { item });
+ }
+
+ ///
+ /// Clears the current collection and replaces it with the specified collection.
+ ///
+ public void ReplaceRange(IEnumerable collection)
+ {
+ if (collection == null) throw new ArgumentNullException("collection");
+
+ Items.Clear();
+ foreach (var i in collection) Items.Add(i);
+ OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
+ }
+
+ ///
+ /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class.
+ ///
+ public ObservableRangeCollection()
+ : base() { }
+
+ ///
+ /// Initializes a new instance of the System.Collections.ObjectModel.ObservableCollection(Of T) class that contains elements copied from the specified collection.
+ ///
+ /// collection: The collection from which the elements are copied.
+ /// The collection parameter cannot be null.
+ public ObservableRangeCollection(IEnumerable collection)
+ : base(collection) { }
+ }
+}
diff --git a/Library/Base/PropertyBase.cs b/Library/Base/PropertyBase.cs
new file mode 100644
index 0000000..b225fe7
--- /dev/null
+++ b/Library/Base/PropertyBase.cs
@@ -0,0 +1,31 @@
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+
+namespace CMM.Library.Base
+{
+ public class PropertyBase : INotifyPropertyChanged
+ {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ virtual internal protected void OnPropertyChanged(string propertyName)
+ {
+ if (this.PropertyChanged != null)
+ {
+ this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+
+ protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null)
+ {
+ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
+
+ protected void SetProperty(ref T storage, T value, [CallerMemberName] string propertyName = null)
+ {
+ if (object.Equals(storage, value)) return;
+
+ storage = value;
+ this.OnPropertyChanged(propertyName);
+ }
+ }
+}
diff --git a/Library/Config/Config.cs b/Library/Config/Config.cs
new file mode 100644
index 0000000..e93f2dc
--- /dev/null
+++ b/Library/Config/Config.cs
@@ -0,0 +1,76 @@
+using CMM.Language;
+using CMM.Library.Base;
+using CMM.Library.Helpers;
+using CMM.Library.Method;
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Text.Json.Serialization;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace CMM.Library.Config
+{
+ public class XConfig : PropertyBase
+ {
+ [JsonIgnore]
+ public string Version { get; private set; }
+
+ public static string ConfigFileName => Path.Combine(AssemblyData.Path, "Config.cfg");
+
+ #region Language
+ [JsonIgnore]
+ public CultureInfo Culture
+ {
+ get => _Culture;
+ set
+ {
+ SetProperty(ref _Culture, value);
+ LoadCultures();
+ }
+ }
+ CultureInfo _Culture;
+ public string Language { get; set; } = null;
+ CulturesHelper CulturesHelper { get; init; } = new();
+ public void LoadCultures()
+ {
+ if (CulturesHelper == null) return;
+
+ CulturesHelper.ChangeCulture(Culture);
+ }
+ #endregion
+
+ public virtual void Load()
+ {
+ XConfig _base = null;
+ if (new FileInfo(ConfigFileName).Exists)
+ {
+ try
+ {
+ _base = ConfigFileName.JsonFormFile();
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"{Lang.Find("LoadConfigErr")}{ex.Message}", "failed", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+ this.Culture = _base?.Culture ?? new CultureInfo(_base?.Language ?? "en-US", false);
+ this.Version = $"{AssemblyData.AppName} {AssemblyData.AppVersion}";
+ }
+
+ public virtual void Save()
+ {
+ try
+ {
+ this.FileToJson(ConfigFileName);
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"{Lang.Find("SaveConfigErr")}{ex.Message}", "failed", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ }
+ }
+}
diff --git a/Library/Extensions/MonitorExtension.cs b/Library/Extensions/MonitorExtension.cs
new file mode 100644
index 0000000..d2c08cf
--- /dev/null
+++ b/Library/Extensions/MonitorExtension.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CMM.Library.Extensions
+{
+ public static class MonitorExtension
+ {
+
+ }
+}
diff --git a/Library/Helpers/ConsoleHelper.cs b/Library/Helpers/ConsoleHelper.cs
new file mode 100644
index 0000000..30d1fa6
--- /dev/null
+++ b/Library/Helpers/ConsoleHelper.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CMM.Library.Helpers
+{
+ internal class ConsoleHelper
+ {
+ const string cmdFileName = "cmd.exe";
+ private static Process CreatProcess(string fileName) =>
+ new Process()
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ FileName = fileName,
+ UseShellExecute = false,
+ RedirectStandardInput = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ CreateNoWindow = true,
+ }
+ };
+
+ public static async Task CmdCommandAsync(params string[] cmds) =>
+ await CommandAsync(cmdFileName, cmds);
+
+ public static string CmdCommand(params string[] cmds) =>
+ Command(cmdFileName, cmds);
+
+ public static async Task CommandAsync(string fileName, params string[] cmds)
+ {
+ var p = CreatProcess(fileName);
+ p.Start();
+ foreach (var cmd in cmds)
+ {
+ p.StandardInput.WriteLine(cmd);
+ }
+ p.StandardInput.WriteLine("exit");
+ var result = await p.StandardOutput.ReadToEndAsync();
+ var error = await p.StandardError.ReadToEndAsync();
+ if (!string.IsNullOrWhiteSpace(error))
+ result = result + "\r\n:\r\n" + error;
+ await p.WaitForExitAsync();
+ p.Close();
+ Debug.WriteLine(result);
+ return result;
+ }
+
+ public static string Command(string fileName, params string[] cmds)
+ {
+ var p = CreatProcess(fileName);
+ p.Start();
+ foreach (var cmd in cmds)
+ {
+ p.StandardInput.WriteLine(cmd);
+ }
+ p.StandardInput.WriteLine("exit");
+ var result = p.StandardOutput.ReadToEnd();
+ var error = p.StandardError.ReadToEnd();
+ if (!string.IsNullOrWhiteSpace(error))
+ result = result + "\r\n:\r\n" + error;
+ p.WaitForExit();
+ p.Close();
+ Debug.WriteLine(result);
+ return result;
+ }
+ }
+}
diff --git a/Library/Helpers/FileHelper.cs b/Library/Helpers/FileHelper.cs
new file mode 100644
index 0000000..4e61185
--- /dev/null
+++ b/Library/Helpers/FileHelper.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CMM.Library.Helpers
+{
+ internal static class FileHelper
+ {
+ public static byte[] ResourceToByteArray(this string fileName)
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+ var resourceName =
+ assembly.GetManifestResourceNames().
+ Where(str => str.Contains(fileName)).FirstOrDefault();
+ if (resourceName == null) return null;
+
+ using (var stream = assembly.GetManifestResourceStream(resourceName))
+ using (var memoryStream = new MemoryStream())
+ {
+ stream.CopyTo(memoryStream);
+ return memoryStream.ToArray();
+ }
+ }
+ }
+}
diff --git a/Library/Helpers/JsonHelper.cs b/Library/Helpers/JsonHelper.cs
new file mode 100644
index 0000000..b6b1498
--- /dev/null
+++ b/Library/Helpers/JsonHelper.cs
@@ -0,0 +1,131 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Text.Encodings.Web;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace CMM.Library.Helpers
+{
+ static class JsonSerializerExtensions
+ {
+ public static JsonSerializerOptions defaultSettings = new JsonSerializerOptions()
+ {
+ WriteIndented = true,
+ IgnoreNullValues = true,
+ PropertyNamingPolicy = null,
+ Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
+ };
+ }
+
+ internal static class JsonHelper
+ {
+ ///
+ /// 複製整個obj全部結構
+ ///
+ public static T DeepCopy(T RealObject) =>
+ JsonSerializer.Deserialize(JsonSerializer.Serialize(RealObject, JsonSerializerExtensions.defaultSettings));
+
+ public static string JsonFormResource(this string fileName)
+ {
+ var assembly = Assembly.GetExecutingAssembly();
+ var resourceName =
+ assembly.GetManifestResourceNames().
+ Where(str => str.Contains(fileName)).FirstOrDefault();
+ if (resourceName == null) return "";
+
+ using (var stream = assembly.GetManifestResourceStream(resourceName))
+ using (var reader = new StreamReader(stream, Encoding.UTF8))
+ {
+ return reader.ReadToEnd();
+ }
+ }
+
+ public static T JsonFormResource(this string fileName) =>
+ JsonFormString(JsonFormResource(fileName));
+
+ public static T JsonFormFile(this string fileName) =>
+ JsonFormString(Load(fileName));
+
+ public static T JsonFormString(this string json) =>
+ JsonSerializer.Deserialize(json);
+
+ public static void FileToJson(this T payload, string savePath) =>
+ Save(savePath, payload.ToJson());
+
+ public static string ToJson(this T payload) =>
+ JsonSerializer.Serialize(payload, JsonSerializerExtensions.defaultSettings);
+
+ ///
+ /// 從Embedded resource讀string
+ ///
+ /// resource位置,不含副檔名
+ public static string GetResource(this Assembly assembly, string aFileName)
+ {
+ var resourceName = assembly
+ .GetManifestResourceNames()
+ .Where(str => str.Contains(aFileName))
+ .FirstOrDefault();
+ if (resourceName == null) return "";
+
+ using (var stream = assembly.GetManifestResourceStream(resourceName))
+ using (var sr = new StreamReader(stream, Encoding.UTF8))
+ {
+ return sr.ReadToEnd();
+ }
+ }
+
+ public static string Load(string aFileName) =>
+ Load(new FileInfo(aFileName));
+
+ public static string Load(FileInfo aFi)
+ {
+ if (aFi.Exists)
+ {
+ string _Json = string.Empty;
+
+ try
+ {
+ var sr = new StreamReader(aFi.FullName);
+ _Json = sr.ReadToEnd();
+ sr.Close();
+ }
+ catch (IOException) { throw; }
+ catch (Exception) { throw; }
+
+ return _Json;
+ }
+
+ throw new Exception("開檔失敗。");
+ }
+
+ public static void Save(string filePath, string content) =>
+ Save(new FileInfo(filePath), content);
+
+ public static void Save(FileInfo aFi, string aContent)
+ {
+ if (!aFi.Directory.Exists)
+ {
+ aFi.Directory.Create();
+ }
+
+ if (aFi.Exists)
+ {
+ aFi.Delete();
+ }
+
+ aFi.Refresh();
+ if (aFi.Exists) throw new Exception("寫檔失敗,檔案已存在或已開啟。");
+
+ try
+ {
+ File.WriteAllText(aFi.FullName, aContent);
+ }
+ catch (IOException) { throw; }
+ catch (Exception) { throw; }
+ }
+ }
+}
diff --git a/Library/Helpers/UAC.cs b/Library/Helpers/UAC.cs
new file mode 100644
index 0000000..a945830
--- /dev/null
+++ b/Library/Helpers/UAC.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Security.Principal;
+
+namespace CMM.Library.Helpers
+{
+ public class UAC
+ {
+ public static void Check()
+ {
+ var identity = WindowsIdentity.GetCurrent();
+ var principal = new WindowsPrincipal(identity);
+ if (!principal.IsInRole(WindowsBuiltInRole.Administrator))
+ throw new Exception($"Cannot delete task with your current identity '{identity.Name}' permissions level." +
+ "You likely need to run this application 'as administrator' even if you are using an administrator account.");
+
+ }
+ }
+}
diff --git a/Library/Library.csproj b/Library/Library.csproj
new file mode 100644
index 0000000..b03e9e8
--- /dev/null
+++ b/Library/Library.csproj
@@ -0,0 +1,40 @@
+
+
+
+ net6.0-windows
+ enable
+ CMM.Library
+ ControlMyMonitorManagement
+ true
+ Dang
+ Copyright © DangWang $([System.DateTime]::Now.ToString(yyyy))
+
+
+
+ 1
+ 0
+ $([System.DateTime]::op_Subtraction($([System.DateTime]::get_Now().get_Date()),$([System.DateTime]::new(2017,9,17))).get_TotalDays())
+ $([System.DateTime]::Now.ToString(Hmm))
+ $([System.DateTime]::Now.ToString(yyyyMMdd))
+ $(Major).$(Minor).$(ProjectStartedDate).$(DaysSinceProjectStarted)
+ 0.0.0.1
+ $(VersionSuffix)
+ 0.0.0.1
+ $(DateTimeSuffix)
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
+
+
+
+
diff --git a/Library/Method/AssemblyData.cs b/Library/Method/AssemblyData.cs
new file mode 100644
index 0000000..4cc1188
--- /dev/null
+++ b/Library/Method/AssemblyData.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CMM.Library.Method
+{
+ public static class AssemblyData
+ {
+ ///
+ /// 當下Assembly名稱
+ ///
+ public static string AssemblyName => System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
+
+ public static string AppName => AppDomain.CurrentDomain.FriendlyName;
+
+ ///
+ /// 程式根目錄,無視工作目錄
+ ///
+ public static string Path => AppDomain.CurrentDomain.BaseDirectory;
+ ///
+ /// 版本
+ ///
+ public static string AssemblyVersion => GetAssemblyVersion();
+ public static string AppVersion => GetFileVersion(Process.GetCurrentProcess().MainModule.FileName);
+ ///
+ /// CCM 輸出
+ ///
+ public static string smonitors => System.IO.Path.Combine(Path, "smonitors.tmp");
+
+ static string GetAssemblyVersion()
+ {
+ var fi = System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase.Replace(@"file:///", "");
+ return GetFileVersion(fi);
+ }
+
+ public static string GetFileVersion(string filePath)
+ {
+ var fvi = FileVersionInfo.GetVersionInfo(filePath);
+ return $"{fvi.FileMajorPart}." +
+ $"{fvi.FileMinorPart}." +
+ $"{fvi.FileBuildPart}." +
+ $"{fvi.FilePrivatePart}";
+ }
+ }
+}
diff --git a/Library/Method/CMMCommand.cs b/Library/Method/CMMCommand.cs
new file mode 100644
index 0000000..453029f
--- /dev/null
+++ b/Library/Method/CMMCommand.cs
@@ -0,0 +1,177 @@
+using CMM.Library.Base;
+using CMM.Library.Helpers;
+using CMM.Library.ViewModel;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CMM.Library.Method
+{
+ ///
+ /// Control My Monitor Management Command
+ ///
+ public static class CMMCommand
+ {
+ static readonly string CMMTmpFolder = Path.Combine(Path.GetTempPath(), $"CMM");
+ static readonly string CMMexe = Path.Combine(CMMTmpFolder, "ControlMyMonitor.exe");
+ static readonly string CMMsMonitors = Path.Combine(CMMTmpFolder, "smonitors.tmp");
+ static readonly string CMMcfg = Path.Combine(CMMTmpFolder, "ControlMyMonitor.cfg");
+
+ public static async Task ScanMonitor()
+ {
+ await BytesToFileAsync(new(CMMexe));
+ await ConsoleHelper.CmdCommandAsync($"{CMMexe} /smonitors {CMMsMonitors}");
+ }
+
+ public static async Task ScanMonitorInterfaces(IEnumerable monitors)
+ {
+ var taskList = new List();
+ foreach (var mon in monitors)
+ {
+ taskList.Add(Task.Run(async () => await
+ ScanMonitorInterfaces($"{CMMTmpFolder}\\{mon.SerialNumber}.tmp", mon)));
+ }
+
+ await Task.WhenAll(taskList.ToArray());
+ }
+
+ static async Task ScanMonitorInterfaces(string savePath, XMonitor mon)
+ {
+ //await ConsoleHelper.CmdCommandAsync($"{CMMexe} /scomma {savePath} {mon.MonitorID}");
+ await mon.ReadMonitorStatus(savePath);
+ }
+
+ ///
+ /// 取得螢幕狀態
+ ///
+ public static async Task ReadMonitorStatus(this XMonitor @this, string filePath)
+ {
+ var statusColle = new ObservableRangeCollection();
+
+ if (!File.Exists(filePath)) return;
+
+ foreach (var line in await File.ReadAllLinesAsync(filePath))
+ {
+ var sp = line.Split(",");
+ if (sp.Length < 6) continue;
+
+ statusColle.Add(new XMonitorStatus
+ {
+ VCP_Code = StrTrim(sp[0]),
+ VCPCodeName = StrTrim(sp[1]),
+ Read_Write = StrTrim(sp[2]),
+ CurrentValue = TryGetInt(sp[3]),
+ MaximumValue = TryGetInt(sp[4]),
+ PossibleValues = TryGetArrStr(sp),
+ });
+ }
+
+ @this.Status = statusColle;
+
+ string StrTrim(string str)
+ {
+ if (string.IsNullOrEmpty(str)) return null;
+ return str;
+ }
+
+ string TryGetArrStr(string[] strArr)
+ {
+ if (strArr.Length < 7) return null;
+
+ var outStr = string.Join(",", strArr[5..]);
+ outStr = outStr.Substring(1, outStr.Length - 2);
+ return outStr;
+ }
+
+ int? TryGetInt(string str)
+ {
+ if (int.TryParse(str, out var value)) return value;
+ return null;
+ }
+ }
+
+ ///
+ /// 取得螢幕清單
+ ///
+ public static async Task> ReadMonitorsData()
+ {
+ var monitors = new List();
+
+ if (!File.Exists(CMMsMonitors)) return monitors;
+
+ XMonitor mon = null;
+ string context;
+ foreach (var line in await File.ReadAllLinesAsync(CMMsMonitors))
+ {
+ var sp = line.Split(":", StringSplitOptions.RemoveEmptyEntries);
+ try
+ {
+ if (sp.Length != 2 || string.IsNullOrEmpty(sp[1])) continue;
+
+ context = sp[1].Substring(2, sp[1].Length - 3);
+ }
+ catch
+ {
+ continue;
+ }
+
+ if (sp[0].StartsWith("Monitor Device Name"))
+ {
+ mon = new XMonitor();
+ mon.MonitorDeviceName = context;
+ continue;
+ }
+
+ if (sp[0].StartsWith("Monitor Name"))
+ {
+ mon.MonitorName = context;
+ continue;
+ }
+
+ if (sp[0].StartsWith("Serial Number"))
+ {
+ mon.SerialNumber = context;
+ continue;
+ }
+
+ if (sp[0].StartsWith("Adapter Name"))
+ {
+ mon.AdapterName = context;
+ continue;
+ }
+
+ if (sp[0].StartsWith("Monitor ID"))
+ {
+ mon.MonitorID = context;
+ monitors.Add(mon);
+ continue;
+ }
+ }
+
+ return monitors;
+ }
+
+ static void BytesToFile(FileInfo fi)
+ {
+ fi.Refresh();
+ if (fi.Exists) return;
+ if (!fi.Directory.Exists) fi.Directory.Create();
+
+ File.WriteAllBytes(fi.FullName, fi.Name.ResourceToByteArray());
+ }
+
+ static async Task BytesToFileAsync(FileInfo fi)
+ {
+ fi.Refresh();
+ if (fi.Exists) return;
+ if (!fi.Directory.Exists) fi.Directory.Create();
+
+ await File.WriteAllBytesAsync(fi.FullName, fi.Name.ResourceToByteArray());
+ }
+
+ }
+
+}
diff --git a/Library/Method/CMMMgr.cs b/Library/Method/CMMMgr.cs
new file mode 100644
index 0000000..09b482d
--- /dev/null
+++ b/Library/Method/CMMMgr.cs
@@ -0,0 +1,29 @@
+using CMM.Library.Base;
+using CMM.Library.ViewModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CMM.Library.Method
+{
+ public class CMMMgr : PropertyBase
+ {
+ public ObservableRangeCollection Monitors
+ {
+ get => _Monitors;
+ set { SetProperty(ref _Monitors, value); }
+ }
+ ObservableRangeCollection _Monitors = new ();
+
+ public async Task Init()
+ {
+ await CMMCommand.ScanMonitor();
+ var monColle = new ObservableRangeCollection();
+ monColle.AddRange(await CMMCommand.ReadMonitorsData());
+ Monitors = monColle;
+ await CMMCommand.ScanMonitorInterfaces(monColle);
+ }
+ }
+}
diff --git a/Library/Resource/ControlMyMonitor.chm b/Library/Resource/ControlMyMonitor.chm
new file mode 100644
index 0000000..3f9d3a7
Binary files /dev/null and b/Library/Resource/ControlMyMonitor.chm differ
diff --git a/Library/Resource/ControlMyMonitor.exe b/Library/Resource/ControlMyMonitor.exe
new file mode 100644
index 0000000..41405f8
Binary files /dev/null and b/Library/Resource/ControlMyMonitor.exe differ
diff --git a/Library/Resource/readme.txt b/Library/Resource/readme.txt
new file mode 100644
index 0000000..1e34ea4
--- /dev/null
+++ b/Library/Resource/readme.txt
@@ -0,0 +1,393 @@
+
+
+
+ControlMyMonitor v1.35
+Copyright (c) 2017 - 2022 Nir Sofer
+Web site: https://www.nirsoft.net
+
+
+
+Description
+===========
+
+ControlMyMonitor allows you view and modify the settings of your monitor
+(Also known as 'VCP Features'), like brightness, contrast, sharpness,
+red/green/blue color balance, OSD Language, Input Port (VGA , DVI , HDMI
+) and more... You can modify the monitor settings from the GUI and from
+command-line. You can also export all settings of your monitor into a
+configuration file and then later load the same configuration back into
+your monitor.
+
+
+
+System Requirements
+===================
+
+
+* Any version of Windows, starting from Windows Vista and up to Windows
+ 11.
+* Hardware that supports DDC/CI.
+
+
+
+Versions History
+================
+
+
+* Version 1.35:
+ o Added 4 display filter options: Display Read+Write, Display Read
+ Only, Display Write Only, Display Manufacturer Specific.
+
+* Version 1.31:
+ o you can now use 'Secondary' as the monitor string in all
+ command-line options in order to specify the secondary monitor.
+
+* Version 1.30:
+ o When there is a DDC/CI error (Error codes like 0xC0262582,
+ 0xC0262583), the error description is now displayed in the status bar
+ in addition to the error code.
+ o You can also click the error code with the mouse in order to copy
+ the error code and error description to the clipboard.
+
+* Version 1.29:
+ o Added option to choose another font (name and size) to display in
+ the main window.
+
+* Version 1.28:
+ o Fixed some display issues in high DPI mode (Toolbar and status
+ bar).
+
+* Version 1.27:
+ o Added /TurnOff command-line option to turn off the specified
+ monitor.
+ o Added /TurnOn command-line option to turn on the specified
+ monitor.
+ o Added /SwitchOffOn command-line option to switch the specified
+ monitor between on and off state.
+
+* Version 1.26:
+ o When ControlMyMonitor fails to get the current monitor settings,
+ error code is now displayed in the status bar.
+
+* Version 1.25:
+ o Added 'Put Icon On Tray' option.
+
+* Version 1.20:
+ o Added /SwitchValue command-line option, which allows you to
+ switch between multiple values.
+ o For example, in order to switch the monitor off when it's turned
+ on and switch it on when it's turned off, use the following command:
+ (On some monitors you should use 4 instead of 5)
+ ControlMyMonitor.exe /SwitchValue "\\.\DISPLAY1\Monitor0" D6 1 5
+
+* Version 1.17:
+ o When pressing F5 (Refresh) the refresh process is smoother,
+ keeping the last selected item.
+ o Added 'Load Selected Monitor On Start' option.
+
+* Version 1.16:
+ o Added /GetValue command-line option, which returns the current
+ value of the specified VCP Code, for example:
+ ControlMyMonitor.exe /GetValue "\\.\DISPLAY1\Monitor0" 10
+ echo %errorlevel%
+
+* Version 1.15:
+ o Added /SetValueIfNeeded command-line option, which sets a value
+ only if the current value is different from the value you want to set.
+
+* Version 1.12:
+ o Added 'Add Header Line To CSV/Tab-Delimited File' option (Turned
+ on by default).
+
+* Version 1.11:
+ o Added /smonitors command-line option to save the monitors list
+ into a text file.
+ o Added 'Save All Items' option (Shift+Ctrl+S).
+
+* Version 1.10:
+ o Added save command-line options (/scomma, /stab , /shtml, and so
+ on...) to export the current monitor settings into a file.
+ o Added support for JSON file in 'Save Selected Items' option.
+
+* Version 1.05:
+ o Added 'Refresh Monitors List' option (Ctrl+F5).
+
+* Version 1.00 - First release.
+
+
+
+Start Using ControlMyMonitor
+============================
+
+ControlMyMonitor doesn't require any installation process or additional
+DLL files. In order to start using it simply run the executable file -
+ControlMyMonitor.exe
+
+After running ControlMyMonitor, the current settings of your monitor are
+displayed in the main window. If you have multiple monitors, you can
+choose another monitor from the monitor combo-box below the toolbar.
+In order to modify a single item, select the item that you want to
+change, and then double click the item (or press the F6 key). You can
+also increase or decrease the current value by using the 'Increase Value'
+or 'Decrease Value' options (Under the Action menu). You can also
+increase/decrease values by using the mouse wheel, according to the
+selected option in Options -> Change Value With Mouse Wheel. By default,
+the mouse wheel feature is active when you hold down the Ctrl key.
+
+
+
+Restore Factory Defaults
+========================
+
+There are some write-only items that allow you to restore the factory
+defaults of the monitor. In order to activate these items, you have to
+set the value to 1.
+
+
+
+Save/Load Config
+================
+
+ControlMyMonitor allows you to export all read/write properties into a
+simple text file and then later load these properties back to the
+monitor. You can find the save/load config feature under the File menu
+('Save Monitor Config' and 'Load Monitor Config').
+
+
+
+Error 0xc0262582, 0xc0262583 and similar codes
+==============================================
+
+If you get error 0xc0262582 (or similar codes) and the main window of
+ControlMyMonitor is empty, it means that Windows operating system cannot
+connect your monitor using DDC/CI.
+Here's what you can do in order to try to solve the problem:
+* Update the driver of your graphics card.
+* Try to plug your monitor using different type of cable/connector
+ (VGA, DVI, HDMI, DisplayPort).
+* If you use a KVM switch, try to plug your monitor directly to the
+ computer, without the KVM switch.
+
+
+
+
+Before you report a bug...
+==========================
+
+Be aware that if you have a specific setting that ControlMyMonitor fails
+to set properly or ControlMyMonitor fails to connect your monitor
+completely with error code (usually begins with 0xc02625 ), It's not a
+bug or problem in ControlMyMonitor tool, but in your hardware.
+It might be a bug with the chip of your monitor or with the driver of
+your graphics card or a problem with the cable/connector you use. As a
+programmer of ControlMyMonitor, I cannot help you to debug or fix these
+hardware problems. You can try to contact the manufacturers of your
+hardware and ask them to solve the problem.
+
+
+
+Command-Line Options
+====================
+
+You can use 'Primary' as your monitor string in all command-line options
+in order to specify the primary monitor. You can also use 'Secondary' as
+your monitor string in all command-line options in order to specify the
+secondary monitor.
+If you have multiple monitors, you have to find a string that uniquely
+identifies your monitor. Open ControlMyMonitor , select the desired
+monitor and then press Ctrl+M (Copy Monitor Settings). Paste the string
+from the clipboard into notepad or other text editor. You'll see
+something like this:
+
+Monitor Device Name: "\\.\DISPLAY1\Monitor0"
+Monitor Name: "22EA53"
+Serial Number: "402CFEZE1200"
+Adapter Name: "Intel(R) HD Graphics"
+Monitor ID: "MONITOR\GSM59A4\{4d36e96e-e325-11ce-bfc1-08002be10318}\0012"
+
+You can use any string from this list as long as the other monitors on
+your system have different values for the same property.
+
+
+
+/SetValue
+Sets the value of the specified VCP Code for the specified monitor.
+
+Here's some examples:
+
+Set the brightness of primary monitor to 70:
+ControlMyMonitor.exe /SetValue Primary 10 70
+
+Set the contrast of the monitor with serial number 102ABC335 to 65:
+ControlMyMonitor.exe /SetValue "102ABC335" 12 65
+
+Restore factory defaults to the \\.\DISPLAY1\Monitor0 monitor:
+ControlMyMonitor.exe /SetValue "\\.\DISPLAY1\Monitor0" 04 1
+
+Turn on the \\.\DISPLAY2\Monitor0 monitor:
+ControlMyMonitor.exe /SetValue "\\.\DISPLAY2\Monitor0" D6 1
+
+Turn off the \\.\DISPLAY2\Monitor0 monitor: (On some monitors you should
+set the value to 4 instead of 5)
+ControlMyMonitor.exe /SetValue "\\.\DISPLAY2\Monitor0" D6 5
+
+Change the input source of \\.\DISPLAY3\Monitor0 monitor:
+ControlMyMonitor.exe /SetValue "\\.\DISPLAY3\Monitor0" 60 3
+
+/SetValueIfNeeded
+This command is similar to /SetValue , but it actually sets the value
+only if the current value is different from the specified value.
+
+/ChangeValue
+Increases/decreases the value of the specified VCP Code for the specified
+monitor.
+
+Here's some examples:
+
+Increase the brightness of the secondary monitor by 5%:
+ControlMyMonitor.exe /ChangeValue Secondary 10 5
+
+Decrease the contrast of the \\.\DISPLAY1\Monitor0 monitor by 5%:
+ControlMyMonitor.exe /ChangeValue "\\.\DISPLAY1\Monitor0" 12 -5
+
+/SwitchValue ...
+Switch between the specified 2 or more values.
+
+For example, this command switches the monitor between off (5) and on (1)
+state:
+ControlMyMonitor.exe /SwitchValue "\\.\DISPLAY1\Monitor0" D6 1 5
+
+The following command switches between 3 brightness values - 30%, 50%,
+90% :
+ControlMyMonitor.exe /SwitchValue "\\.\DISPLAY1\Monitor0" 10 30 50 90
+
+/GetValue
+Return the value of the specified VCP Code for the specified monitor.
+
+Example for batch file:
+ControlMyMonitor.exe /GetValue "\\.\DISPLAY1\Monitor0" 10
+echo %errorlevel%
+
+/TurnOff
+Turn off the specified monitor.
+
+Example:
+ControlMyMonitor.exe /TurnOff "\\.\DISPLAY3\Monitor0"
+
+/TurnOn
+Turn on the specified monitor.
+
+Example:
+ControlMyMonitor.exe /TurnOn "\\.\DISPLAY1\Monitor0"
+
+/SwitchOffOn
+Switch the specified monitor between on and off state.
+
+Example:
+ControlMyMonitor.exe /SwitchOffOn "\\.\DISPLAY2\Monitor0"
+
+/SaveConfig
+Saves all read+write values of the specified monitor into a file.
+For example:
+ControlMyMonitor.exe /SaveConfig "c:\temp\mon1.cfg" Primary
+ControlMyMonitor.exe /SaveConfig "c:\temp\mon1.cfg"
+"\\.\DISPLAY2\Monitor0"
+
+/LoadConfig
+Loads all values stored in configuration file into the specified monitor.
+For example:
+ControlMyMonitor.exe /LoadConfig "c:\temp\mon1.cfg" Primary
+ControlMyMonitor.exe /LoadConfig "c:\temp\mon1.cfg"
+"\\.\DISPLAY2\Monitor0"
+
+/stext
+Save the current monitor settings into a simple text file.
+
+/stab
+Save the current monitor settings into a tab-delimited text file.
+
+/scomma
+Save the current monitor settings into a comma-delimited text file (csv).
+
+/shtml
+Save the current monitor settings into HTML file (Horizontal).
+
+/sverhtml
+Save the current monitor settings into HTML file (Vertical).
+
+/sxml
+Save the current monitor settings into XML file.
+
+/sjson
+Save the current monitor settings into JSON file.
+
+/smonitors
+Save the current monitors list into a simple text file.
+
+For all save command-line options, you can specify empty filename in
+order to send the data to stdout, for example:
+ControlMyMonitor.exe /scomma "" | more
+
+
+
+Get value of the specified VCP Code using GetNir tool
+=====================================================
+
+There is also an option to get the current value of the specified VCP
+Code with GetNir tool. The value is sent to stdout.
+For example, the following command sends the current monitor brightness
+to stdout:
+ControlMyMonitor.exe /stab "" | GetNir "Current Value" "VCPCode=10"
+
+
+
+Translating ControlMyMonitor to other languages
+===============================================
+
+In order to translate ControlMyMonitor to other language, follow the
+instructions below:
+1. Run ControlMyMonitor with /savelangfile parameter:
+ ControlMyMonitor.exe /savelangfile
+ A file named ControlMyMonitor_lng.ini will be created in the folder of
+ ControlMyMonitor utility.
+2. Open the created language file in Notepad or in any other text
+ editor.
+3. Translate all string entries to the desired language. Optionally,
+ you can also add your name and/or a link to your Web site.
+ (TranslatorName and TranslatorURL values) If you add this information,
+ it'll be used in the 'About' window.
+4. After you finish the translation, Run ControlMyMonitor, and all
+ translated strings will be loaded from the language file.
+ If you want to run ControlMyMonitor without the translation, simply
+ rename the language file, or move it to another folder.
+
+
+
+License
+=======
+
+This utility is released as freeware. You are allowed to freely
+distribute this utility via floppy disk, CD-ROM, Internet, or in any
+other way, as long as you don't charge anything for this and you don't
+sell it or distribute it as a part of commercial product. If you
+distribute this utility, you must include all files in the distribution
+package, without any modification !
+
+
+
+Disclaimer
+==========
+
+The software is provided "AS IS" without any warranty, either expressed
+or implied, including, but not limited to, the implied warranties of
+merchantability and fitness for a particular purpose. The author will not
+be liable for any special, incidental, consequential or indirect damages
+due to loss of data or any other reason.
+
+
+
+Feedback
+========
+
+If you have any problem, suggestion, comment, or you found a bug in my
+utility, you can send a message to nirsofer@yahoo.com
diff --git a/Library/ViewModel/XMonitor.cs b/Library/ViewModel/XMonitor.cs
new file mode 100644
index 0000000..b3e3e35
--- /dev/null
+++ b/Library/ViewModel/XMonitor.cs
@@ -0,0 +1,72 @@
+using CMM.Library.Base;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CMM.Library.ViewModel
+{
+ public class XMonitor : PropertyBase
+ {
+ ///
+ /// 裝置路徑
+ ///
+ public string MonitorDeviceName
+ {
+ get => _MonitorDeviceName;
+ set { SetProperty(ref _MonitorDeviceName, value); }
+ }
+ string _MonitorDeviceName;
+
+ ///
+ /// 裝置名稱
+ ///
+ public string MonitorName
+ {
+ get => _MonitorName;
+ set { SetProperty(ref _MonitorName, value); }
+ }
+ string _MonitorName;
+
+ ///
+ /// 裝置序號
+ ///
+ public string SerialNumber
+ {
+ get => _SerialNumber;
+ set { SetProperty(ref _SerialNumber, value); }
+ }
+ string _SerialNumber;
+
+ ///
+ /// 訊號裝置
+ ///
+ public string AdapterName
+ {
+ get => _AdapterName;
+ set { SetProperty(ref _AdapterName, value); }
+ }
+ string _AdapterName;
+
+ ///
+ /// 裝置識別碼
+ ///
+ public string MonitorID
+ {
+ get => _MonitorID;
+ set { SetProperty(ref _MonitorID, value); }
+ }
+ string _MonitorID;
+
+ ///
+ /// 狀態
+ ///
+ public ObservableRangeCollection Status
+ {
+ get => _Status;
+ set { SetProperty(ref _Status, value); }
+ }
+ ObservableRangeCollection _Status = new();
+ }
+}
diff --git a/Library/ViewModel/XMonitorStatus.cs b/Library/ViewModel/XMonitorStatus.cs
new file mode 100644
index 0000000..e253639
--- /dev/null
+++ b/Library/ViewModel/XMonitorStatus.cs
@@ -0,0 +1,54 @@
+using CMM.Library.Base;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CMM.Library.ViewModel
+{
+ public class XMonitorStatus : PropertyBase
+ {
+ public string VCP_Code
+ {
+ get => _VCP_Code;
+ set { SetProperty(ref _VCP_Code, value); }
+ }
+ string _VCP_Code;
+
+ public string VCPCodeName
+ {
+ get => _VCPCodeName;
+ set { SetProperty(ref _VCPCodeName, value); }
+ }
+ string _VCPCodeName;
+
+ public string Read_Write
+ {
+ get => _Read_Write;
+ set { SetProperty(ref _Read_Write, value); }
+ }
+ string _Read_Write;
+
+ public int? CurrentValue
+ {
+ get => _CurrentValue;
+ set { SetProperty(ref _CurrentValue, value); }
+ }
+ int? _CurrentValue;
+
+ public int? MaximumValue
+ {
+ get => _MaximumValue;
+ set { SetProperty(ref _MaximumValue, value); }
+ }
+ int? _MaximumValue;
+
+ public string PossibleValues
+ {
+ get => _PossibleValues;
+ set { SetProperty(ref _PossibleValues, value); }
+ }
+ string _PossibleValues;
+ }
+}
diff --git a/Library/WinAPI/Blur.cs b/Library/WinAPI/Blur.cs
new file mode 100644
index 0000000..a29ecf1
--- /dev/null
+++ b/Library/WinAPI/Blur.cs
@@ -0,0 +1,44 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace CMM.Library.WinAPI
+{
+ #region 透明背景
+ public enum AccentState
+ {
+ ACCENT_DISABLED = 0,
+ ACCENT_ENABLE_GRADIENT = 1,
+ ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
+ ACCENT_ENABLE_BLURBEHIND = 3,
+ ACCENT_INVALID_STATE = 4
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct AccentPolicy
+ {
+ public AccentState AccentState;
+ public int AccentFlags;
+ public int GradientColor;
+ public int AnimationId;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ public struct WindowCompositionAttributeData
+ {
+ public WindowCompositionAttribute Attribute;
+ public IntPtr Data;
+ public int SizeOfData;
+ }
+
+ public enum WindowCompositionAttribute
+ {
+ // ...
+ WCA_ACCENT_POLICY = 19
+ // ...
+ }
+ #endregion
+}
diff --git a/Library/WinAPI/Win32Api.cs b/Library/WinAPI/Win32Api.cs
new file mode 100644
index 0000000..e1b8c91
--- /dev/null
+++ b/Library/WinAPI/Win32Api.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Drawing;
+using System.Security.Principal;
+
+namespace CMM.Library.WinAPI
+{
+ public static class Win32
+ {
+ public const int WM_SETTEXT = 0x000C;
+ public const int WM_CLICK = 0x00F5;
+ public const int CHILDID_SELF = 0;
+ public const int CHILDID_1 = 1;
+ public const int OBJID_CLIENT = -4;
+
+ [DllImport("user32.dll", EntryPoint = "GetWindowText", CharSet = CharSet.Unicode)]
+ public static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int cch);
+ [DllImport("user32.dll", EntryPoint = "FindWindowEx", CharSet = CharSet.Auto)]
+ public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
+ public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam);
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
+ public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
+ [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, IntPtr lParam);
+ [DllImport("User32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ public static extern int SendMessage(IntPtr hWnd, uint msg, int wParam, StringBuilder lParam);
+ [DllImport("user32.dll", CharSet = CharSet.Auto)]
+ public static extern int PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam);
+ [DllImport("user32.dll", CharSet = CharSet.Auto)]
+ public static extern int MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool BRePaint);
+ [DllImport("user32.dll")]
+ public static extern int GetWindowRect(IntPtr hwnd, out Rectangle lpRect);
+ [DllImport("user32.dll")]
+ public static extern bool SetForegroundWindow(IntPtr hWnd);
+ [DllImport("user32.dll")]
+ public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
+ [DllImport("user32.dll")]
+ public static extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
+
+ public static bool IsUAC()
+ {
+ var principal = new WindowsPrincipal(WindowsIdentity.GetCurrent());
+ return principal.IsInRole(WindowsBuiltInRole.Administrator);
+ }
+ }
+}
diff --git a/Tester/Tester.csproj b/Tester/Tester.csproj
new file mode 100644
index 0000000..65e62a8
--- /dev/null
+++ b/Tester/Tester.csproj
@@ -0,0 +1,21 @@
+
+
+
+ net6.0
+ enable
+ CMM.Tester
+ ControlMyMonitorManagement
+ Dang
+ false
+ Copyright © DangWang $([System.DateTime]::Now.ToString(yyyy))
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tester/UnitTest1.cs b/Tester/UnitTest1.cs
new file mode 100644
index 0000000..b1800b4
--- /dev/null
+++ b/Tester/UnitTest1.cs
@@ -0,0 +1,16 @@
+namespace CMM.Tester
+{
+ public class Tests
+ {
+ [SetUp]
+ public void Setup()
+ {
+ }
+
+ [Test]
+ public void Test1()
+ {
+ Assert.Pass();
+ }
+ }
+}
\ No newline at end of file
diff --git a/Tester/Usings.cs b/Tester/Usings.cs
new file mode 100644
index 0000000..cefced4
--- /dev/null
+++ b/Tester/Usings.cs
@@ -0,0 +1 @@
+global using NUnit.Framework;
\ No newline at end of file