Uninstall msi/service remotely with administrator rights - c#

I have implemented code to uninstall a msi. The problem is, when I try to uninstall it remotely using WMI it doesnt get happen and even don't throw any exception. I tried on local and found that when I run command "MsiExec.exe /x {Product Code} /qn"; in cmd as administrator the service gets uninstall & even when I try to debug code in Visual Studio (as administrator) the code works too.
But unfortunately this has to get unistall at remote machine. Is there a way where I can run this code or command as administrator remotely?. I googled and found many answers but nothing worked. Any other approach?
private void UnInstall(string ipAddress, long discoveredMachineId)
{
ipAddress = ipAddress.Trim();
ConnectionOptions connection = new ConnectionOptions();
connection.Username = this.userName;
connection.Password = this.password;
ManagementScope scope = new ManagementScope(#"\\" + ipAddress + "\\root\\CIMV2");
try
{
scope.Connect();
if (scope.IsConnected == true)
{
//Start Uninstalling
try
{
var checkKey = Registry.LocalMachine.OpenSubKey(#"Software\Microsoft\Windows\CurrentVersion\Uninstall\{Product Code}");
if (checkKey == null)
{
// Key does not exist
Console.WriteLine("Not Installed");
}
else
{
// Key exist
var wmiProcess = new ManagementClass(scope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
ManagementBaseObject inParams = wmiProcess.GetMethodParameters("Create");
inParams["CommandLine"] = "MsiExec.exe /x {Product Code} /qn";
ManagementBaseObject outParams = wmiProcess.InvokeMethod("Create", inParams, null);
Console.WriteLine("Creation of the process returned: " + outParams["returnValue"]);
Console.WriteLine("Process ID: " + outParams["processId"]);
Console.WriteLine("UnInstalled Successfully...");
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}

Related

How to fix "COMException: A required privilege is not held by the client" error in C#?

I'm working on remote computer control by WMI using C#. I can connect after I have made the necessary settings on the remote computer. There is no problem so far. But when I shut down a remote computer with the Win32ShutDown command, after reboot the remote computer I received the following error:
System.Runtime.InteropServices.COMException (0x80070522): A required privilege is not held by the client. (Exception from HRESULT: 0x80070522).
How can I solve this?
My connection function:
public static ManagementScope Connect(string ip, string userName = "", string password = "")
{
ManagementScope scope = null;
try
{
ConnectionOptions opts;
scope = new ManagementScope(string.Format("\\\\{0}\\root\\cimv2", ip.Trim()));
if (!string.IsNullOrEmpty(userName))
{
opts = new ConnectionOptions
{
Username = userName,
Password = password,
EnablePrivileges = true,
Impersonation = ImpersonationLevel.Impersonate
};
scope.Options = opts;
}
scope.Connect(); //Error is thrown here
return scope;
}
catch (Exception ex)
{
scope = null;
throw ex;
}
}
My shutdown function:
static void ShutDown()
{
try
{
ConnectionOptions options = new ConnectionOptions
{
Username = "Administrator",
Password = "123",
EnablePrivileges = true,
Impersonation = ImpersonationLevel.Impersonate
};
ManagementScope scope = new ManagementScope(string.Format("\\\\{0}\\root\\cimv2", "192.168.1.50", options));
scope.Connect(); //Error is thrown here
ManagementClass mcWin32 = new ManagementClass("Win32_OperatingSystem") { Scope = scope };
mcWin32.Get();
ManagementBaseObject mboShutdownParams = mcWin32.GetMethodParameters("Win32Shutdown");
mboShutdownParams["Flags"] = 5;
ManagementBaseObject mboShutdown = null;
foreach (ManagementObject manObj in mcWin32.GetInstances())
{
mboShutdown = manObj.InvokeMethod("Win32Shutdown", mboShutdownParams, null);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
Console.ReadLine();
}
}

Power up Hyper_V client in C# using WMI

I am new to coding the Hyper-V within WMI, and always welcome to a learning opportunity in this area.
There is a need for me to create a winform application that lists all VMs available within a computer. When a user clicks on one VM, it will launch the Hyper-V client window.
My codes below could pretty much start or stop any specific VM. However, it doesn't launch the hyper-v client window.
Here are my prototype codes (in command lines for now):
using System;
using System.Management;
namespace HyperVSamples
{
public class RequestStateChangeClass
{
public static void RequestStateChange(string vmName, string action)
{
ManagementScope scope = new ManagementScope(#"\\.\root\virtualization\v2", null);
ManagementObject vm = Utility.GetTargetComputer(vmName, scope);
if (null == vm)
{
throw new ArgumentException(
string.Format(
"The virtual machine '{0}' could not be found.",
vmName));
}
ManagementBaseObject inParams = vm.GetMethodParameters("RequestStateChange");
const int Enabled = 2;
const int Disabled = 3;
if (action.ToLower() == "start")
{
inParams["RequestedState"] = Enabled;
}
else if (action.ToLower() == "stop")
{
inParams["RequestedState"] = Disabled;
}
else
{
throw new Exception("Wrong action is specified");
}
ManagementBaseObject outParams = vm.InvokeMethod(
"RequestStateChange",
inParams,
null);
if ((UInt32)outParams["ReturnValue"] == ReturnCode.Started)
{
if (Utility.JobCompleted(outParams, scope))
{
Console.WriteLine(
"{0} state was changed successfully.",
vmName);
}
else
{
Console.WriteLine("Failed to change virtual system state");
}
}
else if ((UInt32)outParams["ReturnValue"] == ReturnCode.Completed)
{
Console.WriteLine(
"{0} state was changed successfully.",
vmName);
}
else
{
Console.WriteLine(
"Change virtual system state failed with error {0}",
outParams["ReturnValue"]);
}
}
public static void Main(string[] args)
{
if (args != null && args.Length != 2)
{
Console.WriteLine("Usage: <application> vmName action");
Console.WriteLine("action: start|stop");
return;
}
RequestStateChange(args[0], args[1]);
}
}
}
Given:
The computer has Hyper-V manager installed with several pre-populated VMs.
Question:
How would I fire up the hyper-v client window from a winform?
Thanks
After taking some research, it appears firing up the hyper-v client is quite simple.... Below is the full function just in case anyone looks for it in the future...
public static string ConnectVM(string VMName)
{
var error = string.Empty;
var runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
//create a pipeline
var path = ConfigurationManager.AppSettings["VMConnectPath"];
var pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript($"& \"{path}\" localhost '{VMName}'");
try
{
pipeline.Invoke();
}
catch (Exception e)
{
error = e.Message;
}
runspace.Close();
return error;
}

share folder c# - not work

I try to share a local folder in my computer - The code is shown below , taken from the Internet and appear in many places, but not working .
Am I missing something ?
private void button1_Click(object sender, EventArgs e)
{
// Create a ManagementClass object
ManagementClass managementClass = new
ManagementClass("Win32_Share");
// Create ManagementBaseObjects for in and out parameters
ManagementBaseObject inParams =
managementClass.GetMethodParameters("Create");
ManagementBaseObject outParams;
// Set the input parameters
inParams["Description"] = "My Files Share";
inParams["Name"] = "My Files Share";
inParams["Path"] = #"C:\folder";
inParams["Type"] = 0x0; // Disk Drive
// Invoke the method on the ManagementClass object
outParams = managementClass.InvokeMethod("Create", inParams,
null);
//Check to see if the method invocation was successful
if ((uint)(outParams.Properties["ReturnValue"].Value) != 0)
{
throw new Exception("Unable to share directory.");
}
}
First of all , the problem was a permission. I'v tried to do net share through CMD and it also did not work, only when I run CMD as administrator share operation worked.
The same thing in C # , Visual Studio you MUST run as an administrator, then it works. Here's my code:
try
{
ManagementClass managementClass = new ManagementClass("Win32_Share");
ManagementBaseObject inParams = managementClass.GetMethodParameters("Create");
inParams["Description"] = Description;
inParams["Name"] = ShareName;
inParams["Path"] = folderPath;
inParams["Type"] = 0; //Disk Drive
inParams["MaximumAllowed"] = null;
inParams["Password"] = null;
inParams["Access"] = null;
ManagementBaseObject outParams;
outParams = managementClass.InvokeMethod("Create", inParams, null);
if ((uint)(outParams.Properties["ReturnValue"].Value) != 0)
throw new Exception();
ManagementObject share = new ManagementObject(managementClass.Path + ".Name='" + ShareName + "'");
}
catch
{
MessageBox.Show("You must run the program as administrator", "N.U.C", MessageBoxButtons.OK, MessageBoxIcon.Information);
Environment.Exit(1);
}
The code it's ok. If what you want it's to debug the program with your IDE it's pretty simple, if you are using Visual Studio as IDE, just run VS with Administrator right and open your project.

Restart remote server from client

I have accessed remote server but it can be some problem. So i want to restart the remote via client side using c#. Is that possible to restart?
EDIT: See #amitdayama's answer below for a more reasonable approach
Yes this is possible.
First, add this using namespace statements:
using System.Diagnostics;
using System.Runtime.InteropServices;
To shut down your computer, use:
Process.Start("shutdown","/s /t 0"); // starts the shutdown application
// the argument /s is to shut down the computer
// the argument /t 0 is to tell the process that
// the specified operation needs to be completed
// after 0 seconds
To restart your computer, use:
Process.Start("shutdown","/r /t 0"); // the argument /r is to restart the computer
Source: Codeproject.com
using System;
using System.Management;
namespace WMI3
{
class Class1
{
static void Main(string[] args)
{
Console.WriteLine("Computer details retrieved using Windows Management Instrumentation (WMI)");
//Connect to the remote computer
ConnectionOptions co = new ConnectionOptions();
co.Username = "username";
co.Password = "Pass";
string serverName="servername";
System.Management.ManagementScope ms = new System.Management.ManagementScope(servername + "\\root\\cimv2", co);
//Query remote computer across the connection
System.Management.ObjectQuery oq = new System.Management.ObjectQuery("SELECT * FROM Win32_OperatingSystem");
ManagementObjectSearcher query1 = new ManagementObjectSearcher(ms,oq);
ManagementObjectCollection queryCollection1 = query1.Get();
foreach( ManagementObject mo in queryCollection1 )
{
string[] ss={""};
mo.InvokeMethod("Reboot",ss);
Console.WriteLine(mo.ToString());
}
}
}
}
This is my solution which supports silent mode, "fire and forget" and delayed reboot. In can simply become enhanced with an individual logon for the process start.
public static bool RebootRemoteMachineSOVersion(ContentControl parentControl, string remoteHostNameOrIp, int waitSeconds = 60, bool silent = false, bool waitForExit = true)
{
waitSeconds = Math.Max(0, waitSeconds);
if (!silent && MessageBox.Show($"Reboot remote computer ({ remoteHostNameOrIp }) in { waitSeconds } seconds?", "Reboot remote machine", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No) == MessageBoxResult.No)
{
return false;
//<-----------
}
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.FileName = "shutdown.exe";
processInfo.Arguments = $#"-r -t { waitSeconds } -m \\{ remoteHostNameOrIp }";
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
processInfo.CreateNoWindow = true;
Process proc;
try
{
proc = Process.Start(processInfo);
if (waitForExit) proc.WaitForExit();
else return true;
//<----------
}
catch (Exception ex)
{
if (!silent) MessageBox.Show($"An error happened:\n\n{ ex.Message }", "Reboot remote machine", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
//<-----------
}
{
string message = "";
const int ERROR_BAD_NETPATH = 53;
const int ERROR_SHUTDOWN_IN_PROGRESS = 1115;
const int RPC_S_UNKNOWN_IF = 1717;
switch (proc.ExitCode)
{
case 0:
if (!silent) MessageBox.Show($"Remote computer is rebooting ({ remoteHostNameOrIp }) in { waitSeconds } seconds.", "Reboot remote computer", MessageBoxButton.OK, MessageBoxImage.Information);
return true;
//<----------
case ERROR_BAD_NETPATH:
message = $"Remote computer not found ({ remoteHostNameOrIp })";
break;
case ERROR_SHUTDOWN_IN_PROGRESS:
message = $"A shutdown is already in progress ({ remoteHostNameOrIp })";
break;
case RPC_S_UNKNOWN_IF:
message = $"Remote computer does not accept shutdown. Probably it is currently booting. ({ remoteHostNameOrIp })";
break;
default:
message = $"Could not shut down - errorcode: { proc.ExitCode } ({ remoteHostNameOrIp })";
break;
}
if (!silent) MessageBox.Show($"{ message }", "Reboot remote computer", MessageBoxButton.OK, MessageBoxImage.Error);
return false;
}
}

Starting a process on a remote machine c#

I am using the code below to start a process on a remote machine. It does start the process, but it is running under the Admin user instead of the current user that is logged in. I need the current user to be able to interact with the program. If I use their credential in my code instead of admin then I will get an access denied. How do I specify to run under user "JohnDoe" ? I know PSSuite is an option, but I would rather use the ManagementScope class if possible.
class Program
{
static void Main(string[] args)
{
try
{
object[] theProcessToRun = { "notepad.exe" };
ConnectionOptions theConnection = new ConnectionOptions();
theConnection.Username = #"MyDomain\Admin";
theConnection.Password = "mypassword";
ManagementScope theScope = new ManagementScope("\\\\" + "BMBM14" + "\\root\\cimv2", theConnection);
ManagementClass theClass = new ManagementClass(theScope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
theClass.InvokeMethod("Create", theProcessToRun);
Console.WriteLine("Success");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Console.ReadLine();
}
Console.ReadLine();
}
}

Categories