From 59d7d0c6accd77ecaba7eddb0bffb4c689d385f2 Mon Sep 17 00:00:00 2001 From: Shiroi Neko Date: Wed, 26 Nov 2025 06:50:08 +0700 Subject: [PATCH 1/6] move to better namespace Co-Authored-By: VLTA of @TheFlightSims <176574466+anhvlt-2k6@users.noreply.github.com> --- ConnectForm.xaml.cs | 71 ----------- .../ConnectForm.xaml | 53 +++++---- DefaultUI/ConnectForm.xaml.cs | 110 ++++++++++++++++++ DefaultUI/ExceptionView.xaml | 47 ++++++++ DefaultUI/ExceptionView.xaml.cs | 102 ++++++++++++++++ 5 files changed, 292 insertions(+), 91 deletions(-) delete mode 100644 ConnectForm.xaml.cs rename ConnectForm.xaml => DefaultUI/ConnectForm.xaml (62%) create mode 100644 DefaultUI/ConnectForm.xaml.cs create mode 100644 DefaultUI/ExceptionView.xaml create mode 100644 DefaultUI/ExceptionView.xaml.cs diff --git a/ConnectForm.xaml.cs b/ConnectForm.xaml.cs deleted file mode 100644 index dafdbca..0000000 --- a/ConnectForm.xaml.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Security; -using System.Windows; - -namespace GetWMIBasic -{ - public partial class ConnectForm : Window - { - /* - * Global properties - */ - private bool isConnectLocally; - - public ConnectForm() - { - isConnectLocally = false; - InitializeComponent(); - } - - /* - * Button-based methods - */ - - private void ConnetToExternal_Button(object sender, RoutedEventArgs e) - { - if ( - ComputerName_TextBox.Text.Length == 0 || - UserName_TextBox.Text.Length == 0 || - Password_TextBox.Password.Length == 0) - { - MessageBox.Show("Please don't leave anything empty", "Error", MessageBoxButton.OK, MessageBoxImage.Warning); - } - else - { - DialogResult = true; - } - } - - private void ConnectToLocal_Button(object sender, RoutedEventArgs e) - { - isConnectLocally = true; - DialogResult = true; - } - - private void Cancel_Button(object sender, RoutedEventArgs e) - { - Close(); - } - - /* - * Non-button methods - */ - public (string, string, SecureString) ReturnValue() - { - ShowDialog(); - - string computerName = (isConnectLocally) ? "localhost" : ComputerName_TextBox.Text; - string userName = (isConnectLocally) ? "" : UserName_TextBox.Text; - SecureString password = new SecureString(); - - if (!isConnectLocally) - { - foreach (char c in Password_TextBox.Password) - { - password.AppendChar(c); - } - } - - return (computerName, userName, password); - } - } -} diff --git a/ConnectForm.xaml b/DefaultUI/ConnectForm.xaml similarity index 62% rename from ConnectForm.xaml rename to DefaultUI/ConnectForm.xaml index cb70948..bb83f5d 100644 --- a/ConnectForm.xaml +++ b/DefaultUI/ConnectForm.xaml @@ -1,4 +1,4 @@ - - - - - + @@ -20,38 +18,53 @@ - + + + + - + + + + - - + + - + + + + - - + + - + + + + - - + + - + + + + - - - + + + diff --git a/DefaultUI/ConnectForm.xaml.cs b/DefaultUI/ConnectForm.xaml.cs new file mode 100644 index 0000000..7e8cc71 --- /dev/null +++ b/DefaultUI/ConnectForm.xaml.cs @@ -0,0 +1,110 @@ +using System.Security; +using System.Windows; + +/* The Default UI includes + * 1. ConnectForm.xaml - A form to connect to a local or remote computer + * 2. ExceptionView.xaml - A window to handle exception messages + */ +namespace GetWMIBasic.DefaultUI +{ + /* + * Connection Form class + * It is used to get connection information from the user + * Expected behaviour: + * 1. User enter the computer name, username, and password to connect to a remote computer + * 2. The application tries verify the connection information + * 3. If what user enters is valid, the form closes and returns the information + */ + public partial class ConnectForm : Window + { + /* + * Global properties + * isConnectLocally: A boolean to indicate whether the user wants to connect to the local computer + * - false when the user wants to connect to a remote computer + * - true when the user wants to connect to the local computer + */ + private bool isConnectLocally; + + // Constructor of the ConnectForm class + public ConnectForm() + { + // Set the default value of isConnectLocally to false + isConnectLocally = false; + + // Initialize the components of the form + InitializeComponent(); + } + + // Button-based methods + + /* + * Connect to External Button Click Event - The "Connect" button + * Validates the input fields and sets the DialogResult to true if valid + */ + private void ConnetToExternal_Button(object sender, RoutedEventArgs e) + { + // Validate that none of the input fields are empty + if ( + ComputerName_TextBox.Text.Length == 0 || + UserName_TextBox.Text.Length == 0 || + Password_TextBox.Password.Length == 0) + { + _ = MessageBox.Show("Please don't leave anything empty", "Error", MessageBoxButton.OK, MessageBoxImage.Warning); + } + else + { + // Close the form with a DialogResult of true + DialogResult = true; + } + } + + /* + * Connect to Local Button Click Event - The "Connect to Local" button + * Sets the isConnectLocally flag to true and sets the DialogResult to true + */ + private void ConnectToLocal_Button(object sender, RoutedEventArgs e) + { + isConnectLocally = true; + DialogResult = true; + } + + /* + * Cancel Button Click Event - The "Cancel" button + * Closes the form without setting a DialogResult + */ + private void Cancel_Button(object sender, RoutedEventArgs e) + { + Close(); + } + + + // Non-button methods + + /* + * Return Value Method + * Returns a tuple containing the computer name, username, and password as a SecureString + */ + public (string, string, SecureString) ReturnValue() + { + // Show the dialog and wait for user interaction + _ = ShowDialog(); + + // Prepare the return values based on whether connecting locally or remotely + string computerName = (isConnectLocally) ? "localhost" : ComputerName_TextBox.Text; + string userName = (isConnectLocally) ? "" : UserName_TextBox.Text; + SecureString password = new SecureString(); + + // Only populate the password if connecting remotely + if (!isConnectLocally) + { + foreach (char c in Password_TextBox.Password) + { + password.AppendChar(c); + } + } + + // Return the collected values as a tuple + return (computerName, userName, password); + } + } +} diff --git a/DefaultUI/ExceptionView.xaml b/DefaultUI/ExceptionView.xaml new file mode 100644 index 0000000..3bebf69 --- /dev/null +++ b/DefaultUI/ExceptionView.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + An exception has occurred in the application. + + You may choose to close the application or ignore the error. + + + + + + + + + + + + + + + + + + + + + + diff --git a/DefaultUI/ExceptionView.xaml.cs b/DefaultUI/ExceptionView.xaml.cs new file mode 100644 index 0000000..0b31914 --- /dev/null +++ b/DefaultUI/ExceptionView.xaml.cs @@ -0,0 +1,102 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management; +using System.Runtime.InteropServices; +using System.Windows; +using GetWMIBasic.WMIMethods; + +/* The Default UI includes + * 1. ConnectForm.xaml - A form to connect to a local or remote computer + * 2. ExceptionView.xaml - A window to handle exception messages + */ +namespace GetWMIBasic.DefaultUI +{ + /* + * Exception View class + * It is used to handle exception messages + * + * Excepted behaviour: + * 1. An exception is passed to the HandleException method + * 2. If the exception is of an allowed type, show the exception message + * and give the user the option to ignore it; else, force close the application + */ + public partial class ExceptionView : Window + { + /* + * Global properties + * allowedExceptionTypes: A list of exception types that are allowed to be ignored + */ + private readonly HashSet allowedExceptionTypes = new HashSet + { + typeof(UnauthorizedAccessException), + typeof(COMException), + typeof(ManagementException), + typeof(CommonException) + }; + + // Constructor of the Exception View class + public ExceptionView() + { + InitializeComponent(); + } + + // Button-based methods + + /* + * Click Ignore Exception Button - The "Ignore" button + * Let user ignore the exception and continue using the application + */ + private void Click_IgnoreException(object sender, RoutedEventArgs e) + { + Close(); + } + + /* + * Click Close Application Button - The "Close Application" button + * Close the application in case of a critical or unhandled exception + */ + private void Click_CloseApp(object sender, RoutedEventArgs e) + { + Application.Current.Shutdown(); + } + + // Non-button methods + + /* + * Handle Exception Method + * Get the exception info, and decide whether to allow the user to ignore it + */ + public void HandleException(Exception e) + { + // If the exception is null, ignore it + if (e == null) + { + return; + } + + // Check if the exception type is in the allowed list + bool isAllowed = this.allowedExceptionTypes.Any(t => t.IsInstanceOfType(e)); + + // If not allowed, change the message and hide the ignore button + if (!isAllowed) + { + // Reset the exception message + ExceptionMessage_TextBlock.Text = "An unexpected error has occurred.\n" + + "You should close the application and check for the system"; + // Hide the ignore button + Button_Ignore.Visibility = Visibility.Collapsed; + } + + // Show the detailed exception info +#if !DEBUG + DetailedExceptionDetail_TextBox.Text = e.Message; +#else + DetailedExceptionDetail_TextBox.Text = e.ToString(); +#endif + + // Show the exception view window + ShowDialog(); + } + } +} From e7bad701eee60add3935cff07bca5299b6b1de01 Mon Sep 17 00:00:00 2001 From: Shiroi Neko Date: Wed, 26 Nov 2025 06:50:36 +0700 Subject: [PATCH 2/6] exceptions handlers Co-Authored-By: VLTA of @TheFlightSims <176574466+anhvlt-2k6@users.noreply.github.com> --- WMIMethods/CommonException.cs | 31 +++++++++++++++++++++++++++++++ WMIMethods/CriticalException.cs | 31 +++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 WMIMethods/CommonException.cs create mode 100644 WMIMethods/CriticalException.cs diff --git a/WMIMethods/CommonException.cs b/WMIMethods/CommonException.cs new file mode 100644 index 0000000..861c0c1 --- /dev/null +++ b/WMIMethods/CommonException.cs @@ -0,0 +1,31 @@ +using System; + +/* + * The default WMI method class includes: + * 1. CriticalException.cs - A custom exception class for critical WMI errors + * 2. CommonException.cs - A custom exception class for common WMI errors + * 3. MachineMethods.cs - A base class for WMI operations on local and remote machines + */ +namespace GetWMIBasic.WMIMethods +{ + /* + * Common Exception class + * It is used to test common exceptions in WMI operations + */ + public class CommonException : SystemException + { + // Default constructor - No custom message (use default message) + public CommonException() + { + // Throw a new exception with a default message + throw new Exception("This is a custom exception for WMI-related errors."); + } + + // Default constructor - with custom message + public CommonException(string message) : base(message) + { + // Throw a new exception with custom message + throw new Exception(message); + } + } +} diff --git a/WMIMethods/CriticalException.cs b/WMIMethods/CriticalException.cs new file mode 100644 index 0000000..8971ddc --- /dev/null +++ b/WMIMethods/CriticalException.cs @@ -0,0 +1,31 @@ +using System; + +/* + * The default WMI method class includes: + * 1. CriticalException.cs - A custom exception class for critical WMI errors + * 2. CommonException.cs - A custom exception class for common WMI errors + * 3. MachineMethods.cs - A base class for WMI operations on local and remote machines + */ +namespace GetWMIBasic.WMIMethods +{ + /* + * Critical Exception class + * It is used to test critical exceptions in WMI operations + */ + public class CriticalException : SystemException + { + // Default constructor - No custom message (use default message) + public CriticalException() + { + // Throw a new exception with a default message + throw new Exception("A critical exception has occurred in the WMI operations."); + } + + // Default constructor - with custom message + public CriticalException(string message) : base(message) + { + // Throw a new exception with custom message + throw new Exception(message); + } + } +} From c435413f8e52db38adb6e30ea699b109b9a9be3d Mon Sep 17 00:00:00 2001 From: Shiroi Neko Date: Wed, 26 Nov 2025 06:50:49 +0700 Subject: [PATCH 3/6] update for new files Co-Authored-By: VLTA of @TheFlightSims <176574466+anhvlt-2k6@users.noreply.github.com> --- Get WMI Basic.csproj | 13 +++- MainWindow.xaml | 4 ++ MainWindow.xaml.cs | 119 +++++++++++------------------------ WMIMethods/MachineMethods.cs | 14 ++++- 4 files changed, 63 insertions(+), 87 deletions(-) diff --git a/Get WMI Basic.csproj b/Get WMI Basic.csproj index bb5bc06..1e7d691 100644 --- a/Get WMI Basic.csproj +++ b/Get WMI Basic.csproj @@ -99,8 +99,17 @@ MSBuild:Compile Designer + + ExceptionView.xaml + + + - + + Designer + MSBuild:Compile + + Designer MSBuild:Compile @@ -112,7 +121,7 @@ App.xaml Code - + ConnectForm.xaml diff --git a/MainWindow.xaml b/MainWindow.xaml index 238a08c..7d39ae5 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -29,6 +29,10 @@ + + + + diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 4d0f7fe..964be61 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -1,13 +1,12 @@ -using System; +using GetWMIBasic.DefaultUI; +using GetWMIBasic.WMIMethods; +using System; using System.Linq; using System.Management; -using System.Runtime.InteropServices; using System.Security; using System.Threading.Tasks; using System.Windows; -using GetWMIBasic.WMIMethods; - namespace GetWMIBasic { public partial class MainWindow : Window @@ -41,14 +40,7 @@ private async void ConnectToAnotherComputer_Button(object sender, RoutedEventArg if (userCredential.computerName != "") { machine = null; - if (!userCredential.computerName.Equals("localhost")) - { - machine = new MachineMethods(userCredential); - } - else - { - machine = new MachineMethods(); - } + machine = !userCredential.computerName.Equals("localhost") ? new MachineMethods(userCredential) : new MachineMethods(); await Refresh(); } @@ -75,30 +67,9 @@ private async void Restart_Button(object sender, RoutedEventArgs e) machine.Connect("root\\cimv2"); await machine.CallMethod("Win32_OperatingSystem", "*", "Win32Shutdown", new object[] { 6 }); } - catch (UnauthorizedAccessException ex) + catch (Exception ex) { -#if DEBUG - MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#else - MessageBox.Show($"Failed to catch the Authenticate with {machine.GetComputerName()}: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#endif - } - catch (COMException ex) - { -#if DEBUG - MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#else - MessageBox.Show($"Failed to reach {machine.GetComputerName()}: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#endif - } - - catch (ManagementException ex) - { -#if DEBUG - MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#else - MessageBox.Show($"Failed to catch the Management Method: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#endif + exView.HandleException(ex); } finally { @@ -123,30 +94,9 @@ private async void Shutdown_Button(object sender, RoutedEventArgs e) machine.Connect("root\\cimv2"); await machine.CallMethod("Win32_OperatingSystem", "*", "Win32Shutdown", new object[] { 5 }); } - catch (UnauthorizedAccessException ex) + catch (Exception ex) { -#if DEBUG - MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#else - MessageBox.Show($"Failed to catch the Authenticate with {machine.GetComputerName()}: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#endif - } - catch (COMException ex) - { -#if DEBUG - MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#else - MessageBox.Show($"Failed to reach {machine.GetComputerName()}: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#endif - } - - catch (ManagementException ex) - { -#if DEBUG - MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#else - MessageBox.Show($"Failed to catch the Management Method: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#endif + (new ExceptionView()).HandleException(ex); } finally { @@ -160,6 +110,30 @@ private async void Refresh_Button(object sender, RoutedEventArgs e) await Refresh(); } + private void RegularException_Click(object sender, RoutedEventArgs e) + { + try + { + throw new CommonException("This is a common exception example."); + } + catch (Exception ex) + { + exView.HandleException(ex); + } + } + + private void CriticalException_Click(object sender, RoutedEventArgs e) + { + try + { + throw new CriticalException("This is a critical exception example."); + } + catch (Exception ex) + { + exView.HandleException(ex); + } + } + /* * Non-button methods */ @@ -171,7 +145,7 @@ private async Task Refresh() { machine.Connect("root\\cimv2"); - var biosProperties = machine.GetObjects("Win32_BIOS", "*"); + Task biosProperties = machine.GetObjects("Win32_BIOS", "Manufacturer, Name, SerialNumber, Version"); foreach (ManagementObject biosProperty in (await biosProperties).Cast()) { BIOS_Manufacturer.Text = biosProperty["Manufacturer"]?.ToString() ?? string.Empty; @@ -180,7 +154,7 @@ private async Task Refresh() BIOS_Version.Text = biosProperty["Version"]?.ToString() ?? string.Empty; } - var osProperties = machine.GetObjects("Win32_ComputerSystem", "*"); + Task osProperties = machine.GetObjects("Win32_ComputerSystem", "Name, Domain, TotalPhysicalMemory, SystemType"); foreach (ManagementObject osProperty in (await osProperties).Cast()) { Computer_Name.Text = osProperty["Name"]?.ToString() ?? string.Empty; @@ -189,30 +163,9 @@ private async Task Refresh() Computer_SysType.Text = osProperty["SystemType"]?.ToString() ?? string.Empty; } } - catch (UnauthorizedAccessException ex) - { -#if DEBUG - MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#else - MessageBox.Show($"Failed to catch the Authenticate with {machine.GetComputerName()}: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#endif - } - catch (COMException ex) - { -#if DEBUG - MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#else - MessageBox.Show($"Failed to reach {machine.GetComputerName()}: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#endif - } - - catch (ManagementException ex) + catch (Exception ex) { -#if DEBUG - MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#else - MessageBox.Show($"Failed to catch the Management Method: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); -#endif + exView.HandleException(ex); } finally { diff --git a/WMIMethods/MachineMethods.cs b/WMIMethods/MachineMethods.cs index 038f336..2b6fd80 100644 --- a/WMIMethods/MachineMethods.cs +++ b/WMIMethods/MachineMethods.cs @@ -3,8 +3,18 @@ using System.Security; using System.Threading.Tasks; +/* + * The default WMI method class includes: + * 1. CriticalException.cs - A custom exception class for critical WMI errors + * 2. CommonException.cs - A custom exception class for common WMI errors + * 3. MachineMethods.cs - A base class for WMI operations on local and remote machines + */ namespace GetWMIBasic.WMIMethods { + /* + * Machine Method class + * It is used to connect and perform WMI tasks to local and remote machines through WMI + */ public class MachineMethods { /* @@ -90,11 +100,11 @@ public async Task CallMethod(string className, string fields, string methodName, ObjectQuery query = new ObjectQuery($"SELECT {fields} FROM {className}"); await Task.Run(() => { - using (var searcher = new ManagementObjectSearcher(scope, query)) + using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query)) { foreach (ManagementObject manageObject in searcher.Get().Cast()) { - manageObject.InvokeMethod(methodName, args); + _ = manageObject.InvokeMethod(methodName, args); } } }); From 0cfc1be627aa7ffc2862349352a26a439338b2ee Mon Sep 17 00:00:00 2001 From: shiroinekotfs Date: Thu, 27 Nov 2025 23:10:56 +0700 Subject: [PATCH 4/6] Update MachineMethods.cs --- WMIMethods/MachineMethods.cs | 96 +++++++++++++++++++++++++++++++----- 1 file changed, 83 insertions(+), 13 deletions(-) diff --git a/WMIMethods/MachineMethods.cs b/WMIMethods/MachineMethods.cs index 2b6fd80..a562225 100644 --- a/WMIMethods/MachineMethods.cs +++ b/WMIMethods/MachineMethods.cs @@ -14,13 +14,31 @@ namespace GetWMIBasic.WMIMethods /* * Machine Method class * It is used to connect and perform WMI tasks to local and remote machines through WMI + * + * Security Note: + * 1. When connecting to remote machines, ensure that the credentials are handled securely. + * 2. Use SecureString for passwords to enhance security. + * 3. The application should run with appropriate permissions to access WMI on the target machines. + * For example, locally it may require administrative privileges and do not run with external credentials. */ public class MachineMethods { + + //////////////////////////////////////////////////////////////// + /// Global Properties and Constructors Region + /// This region contains global properties and constructors + /// for the MachineMethods class. + //////////////////////////////////////////////////////////////// + /* - * Class properties + * Global properties + * userCredential: A tuple to store computer name, username, and password + * credential: A ConnectionOptions object to store WMI connection options + * scope: A ManagementScope object to define the WMI scope + * searcher: A ManagementObjectSearcher object to perform WMI queries + * scopePath: A string to store the WMI scope path + * isLocal: A boolean to indicate whether the connection is local or remote */ - protected (string computerName, string username, SecureString password) userCredential; protected ConnectionOptions credential; protected ManagementScope scope; @@ -30,15 +48,19 @@ public class MachineMethods protected bool isLocal; /* - * Machine Method - * 1. Empty - Local - * 2. Or Connect through WMI + * Constructor for local connection + * Note: Local connections do not require username and password. + * It uses current user context. */ - public MachineMethods() { + // Set default computer name to localhost userCredential.computerName = "localhost"; + + // Set local connection flag isLocal = true; + + // Set default connection options for local connection credential = new ConnectionOptions { Impersonation = ImpersonationLevel.Impersonate, @@ -47,12 +69,21 @@ public MachineMethods() }; } + /* + * Constructor for remote connection + * Note: Remote connections require username and password. + */ public MachineMethods((string computerName, string username, SecureString password) userCredential) { + // Set user credentials for remote connection this.userCredential.computerName = userCredential.computerName; this.userCredential.username = userCredential.username; this.userCredential.password = userCredential.password; + + // Set remote connection flag isLocal = false; + + // Set connection options for remote connection credential = new ConnectionOptions { Username = userCredential.username, @@ -63,8 +94,32 @@ public MachineMethods((string computerName, string username, SecureString passwo }; } + //////////////////////////////////////////////////////////////// + /// Connect to a name space region + /// This region contains methods to connect to a WMI name space. + /// For example, "root\\cimv2". + //////////////////////////////////////////////////////////////// + + public void Connect(string nameSpace) + { + // Set the scope path based on local or remote connection + scopePath = $"\\\\{userCredential.computerName}\\{nameSpace}"; + + // Create the ManagementScope object and connect + scope = (isLocal) ? new ManagementScope(scopePath) : new ManagementScope(scopePath, credential); + + // Connect to the WMI scope + scope.Connect(); + } + + //////////////////////////////////////////////////////////////// + /// Get methods region (connection setting, not the WMI data) + /// Various get methods to retrieve information about the machine + /// For example, computer name, current user name, etc. + //////////////////////////////////////////////////////////////// + /* - * Get value + * Get methods for computer name */ public string GetComputerName() { @@ -72,38 +127,53 @@ public string GetComputerName() } /* - * Connection method + * Get methods for current user name */ - - public void Connect(string nameSpace) + public string GetUsername() { - scopePath = $"\\\\{userCredential.computerName}\\{nameSpace}"; - scope = (isLocal) ? new ManagementScope(scopePath) : new ManagementScope(scopePath, credential); - scope.Connect(); + return userCredential.username; } + //////////////////////////////////////////////////////////////// + /// Get methods region (for WMI data) + //////////////////////////////////////////////////////////////// + /* * Get WMI objects */ public async Task GetObjects(string className, string fields) { + // Create the WMI query ObjectQuery query = new ObjectQuery($"SELECT {fields} FROM {className}"); + + // Create the ManagementObjectSearcher object searcher = new ManagementObjectSearcher(scope, query); + + // Execute the query and return the results asynchronously return await Task.Run(() => searcher?.Get()); } + //////////////////////////////////////////////////////////////// + /// Set methods region (for WMI object) + //////////////////////////////////////////////////////////////// + /* * Call WMI Method */ public async Task CallMethod(string className, string fields, string methodName, object[] args) { + // Create the WMI query ObjectQuery query = new ObjectQuery($"SELECT {fields} FROM {className}"); + // Execute the method asynchronously await Task.Run(() => { + // Create the ManagementObjectSearcher object using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query)) { + // Invoke the method on each object returned by the query foreach (ManagementObject manageObject in searcher.Get().Cast()) { + // Invoke the specified method with arguments _ = manageObject.InvokeMethod(methodName, args); } } From dff70fa09b91687fc5921987c615b197b9b7b4b7 Mon Sep 17 00:00:00 2001 From: shiroinekotfs Date: Thu, 27 Nov 2025 23:10:58 +0700 Subject: [PATCH 5/6] Update MainWindow.xaml.cs --- MainWindow.xaml.cs | 139 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 127 insertions(+), 12 deletions(-) diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 964be61..8f8fc53 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -7,109 +7,195 @@ using System.Threading.Tasks; using System.Windows; +/* + * Primary namespace for the application + * It includes the MainWindow class which is the main UI window of the application + * and additional UI for each featuring + */ namespace GetWMIBasic { + /* + * Main Window class + * It is used to display the main UI of the application + */ + public partial class MainWindow : Window { + //////////////////////////////////////////////////////////////// + /// Global Properties and Constructors Region + /// This region contains global properties and constructors + /// for the MachineMethods class. + //////////////////////////////////////////////////////////////// + /* - * Global properties + * Global properties + * machine: An instance of the MachineMethods class to perform WMI operations */ protected MachineMethods machine; + /* + * Constructor of the MainWindow class + * Initializes the components and sets up event handlers + */ public MainWindow() { + // Initialize components InitializeComponent(); + + // Initialize the MachineMethods instance for local machine machine = new MachineMethods(); + + // Set up the sync loading event handler. See the method below for further info. Loaded += MainWindow_Loaded; } - + + /* + * Main Windows async loading method. + * It is used prevent long loading times on the UI thread + */ private async void MainWindow_Loaded(object sender, RoutedEventArgs e) { + // Call the Refresh method to load initial data await Refresh(); } - /* - * Button-based methods + //////////////////////////////////////////////////////////////// + /// User Action Methods Region + /// This region contains methods that handle user actions. + /// For example, button clicks, changes in order. + //////////////////////////////////////////////////////////////// + + /* + * Actions for button "Connect to Another Computer" */ private async void ConnectToAnotherComputer_Button(object sender, RoutedEventArgs e) { + // Initialize the ConnectForm instance ConnectForm connectForm = new ConnectForm(); + // Get the user credentials from the ConnectForm (string computerName, string username, SecureString password) userCredential = connectForm.ReturnValue(); + // If the computer name is not empty, create a new MachineMethods instance if (userCredential.computerName != "") { - machine = null; machine = !userCredential.computerName.Equals("localhost") ? new MachineMethods(userCredential) : new MachineMethods(); - - await Refresh(); } + + // Refresh the data on the UI + await Refresh(); } + /* + * Actons for button "Exit Application" + */ private void Exit_Button(object sender, RoutedEventArgs e) { + // Shut down the application Application.Current.Shutdown(); } + /* + * + */ private async void Restart_Button(object sender, RoutedEventArgs e) { + // Confirm the restart action with the user if (MessageBox.Show($"Do you want to restart the computer {machine.GetComputerName()}?", "Proceed?", MessageBoxButton.YesNo) == MessageBoxResult.Yes) { + // Update the status bar to indicate loading StatusBarChange("Loading", true); + /* + * Try to restart the remote machine using WMI, with the target computer name + * Catch the exception and display it using the ExceptionView if any error occurs + * In any case, no matter if it succeeds or fails, update the status bar to "Done" + */ try { + // Since the local machine cannot be restarted remotely, throw an exception if the target is localhost + // It should be done via the user context menu instead if (machine.GetComputerName() == "localhost") { throw new ManagementException("Local Host cannot be restarted"); } + // Connect to the WMI namespace machine.Connect("root\\cimv2"); + + // Call the Win32Shutdown method with the restart flag (6) await machine.CallMethod("Win32_OperatingSystem", "*", "Win32Shutdown", new object[] { 6 }); } catch (Exception ex) { - exView.HandleException(ex); + // Handle any exceptions using the ExceptionView + (new ExceptionView()).HandleException(ex); } finally { + // Update the status bar to indicate completion StatusBarChange("Done", false); } } } + /* + * Actions for button "Shutdown Computer" + */ private async void Shutdown_Button(object sender, RoutedEventArgs e) { + // Confirm the shutdown action with the user if (MessageBox.Show($"Do you want to shutdown the computer {machine.GetComputerName()}?", "Proceed?", MessageBoxButton.YesNo) == MessageBoxResult.Yes) { + // Update the status bar to indicate loading StatusBarChange("Loading", true); + /* + * Try to shutdown the remote machine using WMI, with the target computer name + * Catch the exception and display it using the ExceptionView if any error occurs + * In any case, no matter if it succeeds or fails, update the status bar to "Done" + */ try { + // Since the local machine cannot be shutdown remotely, throw an exception if the target is localhost + // It should be done via the user context menu instead if (machine.GetComputerName() == "localhost") { throw new ManagementException("Local Host cannot be shut down"); } + // Connect to the WMI namespace machine.Connect("root\\cimv2"); + + // Call the Win32Shutdown method with the shutdown flag (5) await machine.CallMethod("Win32_OperatingSystem", "*", "Win32Shutdown", new object[] { 5 }); } catch (Exception ex) { + // Handle any exceptions using the ExceptionView (new ExceptionView()).HandleException(ex); } finally { + // Update the status bar to indicate completion StatusBarChange("Done", false); } } } + /* + * Actions for button "Refresh Information" + */ private async void Refresh_Button(object sender, RoutedEventArgs e) { + // Call the Refresh method to update the data on the UI await Refresh(); } + /* + * Actions for button "Regular Exception Example" + * (It will throw and catch a common exception to demonstrate the ExceptionView) + */ private void RegularException_Click(object sender, RoutedEventArgs e) { try @@ -118,10 +204,14 @@ private void RegularException_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - exView.HandleException(ex); + (new ExceptionView()).HandleException(ex); } } + /* + * Actions for button "Critical Exception Example" + * (It will throw and catch a critical exception to demonstrate the ExceptionView) + */ private void CriticalException_Click(object sender, RoutedEventArgs e) { try @@ -130,21 +220,39 @@ private void CriticalException_Click(object sender, RoutedEventArgs e) } catch (Exception ex) { - exView.HandleException(ex); + (new ExceptionView()).HandleException(ex); } } + //////////////////////////////////////////////////////////////// + /// Non-User Action Methods Region + /// + /// This region contains methods that do not handle user actions. + /// + /// Think about this is the back-end section. + /// It should not be in a seperated class, because it directly interacts with the UI elements. + //////////////////////////////////////////////////////////////// + /* - * Non-button methods + * Refresh method to update the information displayed on the UI */ private async Task Refresh() { + // Update the status bar to indicate loading StatusBarChange("Loading", true); + /* + * Try to get info from the remote machine using WMI, with the target computer name + * Catch the exception and display it using the ExceptionView if any error occurs + * In any case, no matter if it succeeds or fails, update the status bar to "Done" + */ try { + // Connect to the WMI namespace machine.Connect("root\\cimv2"); + // Get BIOS properties + // In case the property is null, set the text to an empty string Task biosProperties = machine.GetObjects("Win32_BIOS", "Manufacturer, Name, SerialNumber, Version"); foreach (ManagementObject biosProperty in (await biosProperties).Cast()) { @@ -154,6 +262,8 @@ private async Task Refresh() BIOS_Version.Text = biosProperty["Version"]?.ToString() ?? string.Empty; } + // Get Operating System properties + // In case the property is null, set the text to an empty string Task osProperties = machine.GetObjects("Win32_ComputerSystem", "Name, Domain, TotalPhysicalMemory, SystemType"); foreach (ManagementObject osProperty in (await osProperties).Cast()) { @@ -165,14 +275,19 @@ private async Task Refresh() } catch (Exception ex) { - exView.HandleException(ex); + // Handle any exceptions using the ExceptionView + (new ExceptionView()).HandleException(ex); } finally { + // Update the status bar to indicate completion StatusBarChange("Done", false); } } + /* + * Set the status bar text and progress bar state + */ protected void StatusBarChange(string label, bool progressbarLoading) { Bottom_Label.Text = label; From 75ee12e071de20e42fcf963641249d2c19303572 Mon Sep 17 00:00:00 2001 From: shiroinekotfs Date: Fri, 28 Nov 2025 00:12:26 +0700 Subject: [PATCH 6/6] update for correctness Co-Authored-By: VLTA of @TheFlightSims <176574466+anhvlttfs@users.noreply.github.com> --- DefaultUI/ConnectForm.xaml | 53 +++++++++++++-------------------- DefaultUI/ExceptionView.xaml.cs | 3 +- Get WMI Basic.csproj | 2 -- MainWindow.xaml | 48 +++++++++++++++-------------- MainWindow.xaml.cs | 10 ++----- WMIMethods/CommonException.cs | 31 ------------------- WMIMethods/CriticalException.cs | 31 ------------------- WMIMethods/MachineMethods.cs | 18 +++++------ 8 files changed, 57 insertions(+), 139 deletions(-) delete mode 100644 WMIMethods/CommonException.cs delete mode 100644 WMIMethods/CriticalException.cs diff --git a/DefaultUI/ConnectForm.xaml b/DefaultUI/ConnectForm.xaml index bb83f5d..6dd79a4 100644 --- a/DefaultUI/ConnectForm.xaml +++ b/DefaultUI/ConnectForm.xaml @@ -6,17 +6,19 @@ xmlns:local="clr-namespace:GetWMIBasic" mc:Ignorable="d" Title="Connect To Another Computer" - Height="160" - Width="400" + Height="180" + Width="400" + MinHeight="180" + MinWidth="400" + MaxHeight="180" + MaxWidth="400" ResizeMode="NoResize" SizeToContent="WidthAndHeight"> - - - - - + + + @@ -32,34 +34,21 @@ - - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/DefaultUI/ExceptionView.xaml.cs b/DefaultUI/ExceptionView.xaml.cs index 0b31914..d50bc86 100644 --- a/DefaultUI/ExceptionView.xaml.cs +++ b/DefaultUI/ExceptionView.xaml.cs @@ -31,8 +31,7 @@ public partial class ExceptionView : Window { typeof(UnauthorizedAccessException), typeof(COMException), - typeof(ManagementException), - typeof(CommonException) + typeof(ManagementException) }; // Constructor of the Exception View class diff --git a/Get WMI Basic.csproj b/Get WMI Basic.csproj index 1e7d691..24f2f35 100644 --- a/Get WMI Basic.csproj +++ b/Get WMI Basic.csproj @@ -102,8 +102,6 @@ ExceptionView.xaml - - Designer diff --git a/MainWindow.xaml b/MainWindow.xaml index 7d39ae5..3dad6af 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -6,15 +6,17 @@ xmlns:local="clr-namespace:GetWMIBasic" mc:Ignorable="d" Title="Get WMI Basic" - Height="192" + Height="200" Width="800" + MinWidth="600" + MaxWidth="1000" ResizeMode="NoResize" SizeToContent="WidthAndHeight"> - - - + + + @@ -37,18 +39,18 @@ - + - - + + - - + + @@ -57,13 +59,13 @@ - + - + - + - + @@ -71,8 +73,8 @@ - - + + @@ -81,26 +83,26 @@ - + - + - + - + - + - - + + - Welcome + Welcome diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs index 8f8fc53..3239dc6 100644 --- a/MainWindow.xaml.cs +++ b/MainWindow.xaml.cs @@ -70,11 +70,8 @@ private async void MainWindow_Loaded(object sender, RoutedEventArgs e) */ private async void ConnectToAnotherComputer_Button(object sender, RoutedEventArgs e) { - // Initialize the ConnectForm instance - ConnectForm connectForm = new ConnectForm(); - // Get the user credentials from the ConnectForm - (string computerName, string username, SecureString password) userCredential = connectForm.ReturnValue(); + (string computerName, string username, SecureString password) userCredential = (new ConnectForm()).ReturnValue(); // If the computer name is not empty, create a new MachineMethods instance if (userCredential.computerName != "") @@ -91,7 +88,6 @@ private async void ConnectToAnotherComputer_Button(object sender, RoutedEventArg */ private void Exit_Button(object sender, RoutedEventArgs e) { - // Shut down the application Application.Current.Shutdown(); } @@ -200,7 +196,7 @@ private void RegularException_Click(object sender, RoutedEventArgs e) { try { - throw new CommonException("This is a common exception example."); + throw new ManagementException("Unable to manage (example exception handler)."); } catch (Exception ex) { @@ -216,7 +212,7 @@ private void CriticalException_Click(object sender, RoutedEventArgs e) { try { - throw new CriticalException("This is a critical exception example."); + throw new Exception("Critical exception (example exception handler)."); } catch (Exception ex) { diff --git a/WMIMethods/CommonException.cs b/WMIMethods/CommonException.cs deleted file mode 100644 index 861c0c1..0000000 --- a/WMIMethods/CommonException.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; - -/* - * The default WMI method class includes: - * 1. CriticalException.cs - A custom exception class for critical WMI errors - * 2. CommonException.cs - A custom exception class for common WMI errors - * 3. MachineMethods.cs - A base class for WMI operations on local and remote machines - */ -namespace GetWMIBasic.WMIMethods -{ - /* - * Common Exception class - * It is used to test common exceptions in WMI operations - */ - public class CommonException : SystemException - { - // Default constructor - No custom message (use default message) - public CommonException() - { - // Throw a new exception with a default message - throw new Exception("This is a custom exception for WMI-related errors."); - } - - // Default constructor - with custom message - public CommonException(string message) : base(message) - { - // Throw a new exception with custom message - throw new Exception(message); - } - } -} diff --git a/WMIMethods/CriticalException.cs b/WMIMethods/CriticalException.cs deleted file mode 100644 index 8971ddc..0000000 --- a/WMIMethods/CriticalException.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; - -/* - * The default WMI method class includes: - * 1. CriticalException.cs - A custom exception class for critical WMI errors - * 2. CommonException.cs - A custom exception class for common WMI errors - * 3. MachineMethods.cs - A base class for WMI operations on local and remote machines - */ -namespace GetWMIBasic.WMIMethods -{ - /* - * Critical Exception class - * It is used to test critical exceptions in WMI operations - */ - public class CriticalException : SystemException - { - // Default constructor - No custom message (use default message) - public CriticalException() - { - // Throw a new exception with a default message - throw new Exception("A critical exception has occurred in the WMI operations."); - } - - // Default constructor - with custom message - public CriticalException(string message) : base(message) - { - // Throw a new exception with custom message - throw new Exception(message); - } - } -} diff --git a/WMIMethods/MachineMethods.cs b/WMIMethods/MachineMethods.cs index a562225..d822809 100644 --- a/WMIMethods/MachineMethods.cs +++ b/WMIMethods/MachineMethods.cs @@ -162,22 +162,18 @@ public async Task GetObjects(string className, strin */ public async Task CallMethod(string className, string fields, string methodName, object[] args) { - // Create the WMI query - ObjectQuery query = new ObjectQuery($"SELECT {fields} FROM {className}"); - // Execute the method asynchronously - await Task.Run(() => - { - // Create the ManagementObjectSearcher object - using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query)) + // Get the WMI objects asynchronously + using (Task manageObject = GetObjects(className, fields)) { + await Task.Run(() => { // Invoke the method on each object returned by the query - foreach (ManagementObject manageObject in searcher.Get().Cast()) + foreach (ManagementObject obj in manageObject.Result.Cast()) { // Invoke the specified method with arguments - _ = manageObject.InvokeMethod(methodName, args); + _ = obj.InvokeMethod(methodName, args); } - } - }); + }); + } } } }