diff --git a/ConnectForm.xaml b/ConnectForm.xaml
deleted file mode 100644
index cb70948..0000000
--- a/ConnectForm.xaml
+++ /dev/null
@@ -1,61 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
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/DefaultUI/ConnectForm.xaml b/DefaultUI/ConnectForm.xaml
new file mode 100644
index 0000000..6dd79a4
--- /dev/null
+++ b/DefaultUI/ConnectForm.xaml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
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..d50bc86
--- /dev/null
+++ b/DefaultUI/ExceptionView.xaml.cs
@@ -0,0 +1,101 @@
+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)
+ };
+
+ // 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();
+ }
+ }
+}
diff --git a/Get WMI Basic.csproj b/Get WMI Basic.csproj
index bb5bc06..24f2f35 100644
--- a/Get WMI Basic.csproj
+++ b/Get WMI Basic.csproj
@@ -99,8 +99,15 @@
MSBuild:CompileDesigner
+
+ ExceptionView.xaml
+
-
+
+ Designer
+ MSBuild:Compile
+
+ DesignerMSBuild:Compile
@@ -112,7 +119,7 @@
App.xamlCode
-
+ ConnectForm.xaml
diff --git a/MainWindow.xaml b/MainWindow.xaml
index 238a08c..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">
-
-
-
+
+
+
-
+
-
-
+
+
-
-
+
+
@@ -53,13 +59,13 @@
-
+
-
+
-
+
-
+
@@ -67,8 +73,8 @@
-
-
+
+
@@ -77,26 +83,26 @@
-
+
-
+
-
+
-
+
-
+
-
-
+
+
- Welcome
+ Welcome
diff --git a/MainWindow.xaml.cs b/MainWindow.xaml.cs
index 4d0f7fe..3239dc6 100644
--- a/MainWindow.xaml.cs
+++ b/MainWindow.xaml.cs
@@ -1,177 +1,255 @@
-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;
-
+/*
+ * 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)
{
- ConnectForm connectForm = new ConnectForm();
-
- (string computerName, string username, SecureString password) userCredential = connectForm.ReturnValue();
+ // Get the user credentials from the ConnectForm
+ (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 != "")
{
- machine = null;
- if (!userCredential.computerName.Equals("localhost"))
- {
- machine = new MachineMethods(userCredential);
- }
- else
- {
- machine = new MachineMethods();
- }
-
- await Refresh();
+ machine = !userCredential.computerName.Equals("localhost") ? new MachineMethods(userCredential) : new MachineMethods();
}
+
+ // Refresh the data on the UI
+ await Refresh();
}
+ /*
+ * Actons for button "Exit Application"
+ */
private void Exit_Button(object sender, RoutedEventArgs e)
{
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 (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
+ // 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 (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
+ // 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();
}
/*
- * Non-button methods
+ * 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
+ {
+ throw new ManagementException("Unable to manage (example exception handler).");
+ }
+ catch (Exception 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
+ {
+ throw new Exception("Critical exception (example exception handler).");
+ }
+ catch (Exception 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.
+ ////////////////////////////////////////////////////////////////
+
+ /*
+ * 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");
- var biosProperties = machine.GetObjects("Win32_BIOS", "*");
+ // 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())
{
BIOS_Manufacturer.Text = biosProperty["Manufacturer"]?.ToString() ?? string.Empty;
@@ -180,7 +258,9 @@ private async Task Refresh()
BIOS_Version.Text = biosProperty["Version"]?.ToString() ?? string.Empty;
}
- var osProperties = machine.GetObjects("Win32_ComputerSystem", "*");
+ // 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())
{
Computer_Name.Text = osProperty["Name"]?.ToString() ?? string.Empty;
@@ -189,37 +269,21 @@ 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)
+ catch (Exception 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
+ // 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;
diff --git a/WMIMethods/MachineMethods.cs b/WMIMethods/MachineMethods.cs
index 038f336..d822809 100644
--- a/WMIMethods/MachineMethods.cs
+++ b/WMIMethods/MachineMethods.cs
@@ -3,14 +3,42 @@
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
+ *
+ * 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;
@@ -20,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,
@@ -37,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,
@@ -53,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()
{
@@ -62,42 +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)
{
- ObjectQuery query = new ObjectQuery($"SELECT {fields} FROM {className}");
- await Task.Run(() =>
- {
- using (var searcher = new ManagementObjectSearcher(scope, query))
+ // Get the WMI objects asynchronously
+ using (Task manageObject = GetObjects(className, fields)) {
+ await Task.Run(() =>
{
- foreach (ManagementObject manageObject in searcher.Get().Cast())
+ // Invoke the method on each object returned by the query
+ foreach (ManagementObject obj in manageObject.Result.Cast())
{
- manageObject.InvokeMethod(methodName, args);
+ // Invoke the specified method with arguments
+ _ = obj.InvokeMethod(methodName, args);
}
- }
- });
+ });
+ }
}
}
}