Remote shutdown PC using C# windows form? - c#

I'm trying to shutdown PC remotely programmatically using c# via command prompt and I already done a few search and found out this kind of codes.
System.Diagnostics.Process.Start("shutdown", "/s");
And since it doesn't spicify any pc which to shutdown so I tried changing that codes to this codes which I think satisfy my goal. But it turns out that this doesn't work.
System.Diagnostics.Process.Start("shutdown", "/s /m \\192.168.1.21 /t 5 /c 'Shutdown in 5 seconds'");
NO Exception in C# it just don't shutdown.
I also try this but no luck.
System.Diagnostics.Process.Start("shutdown /s /m \\192.168.1.21 /t 5 /c 'Shutdown in 5 seconds'");
C# Exception "The system cannot find file specified".
EDIT:
I forgot to tell you that I alredy set up my remote computer and server the way that it will not fail to connect to each other such as turning off the firewall, configuring Local system policy and changing network and sharing center.

// Remote Shutdown
public bool RemoteShutdown(string userName, string password, string ip)
{
try
{
ConnectionOptions op = new ConnectionOptions();
op.Username = userName;
op.Password = password;
// Make a connection to a remote computer.
ManagementScope scope = new ManagementScope("\\\\" + ip + "\\root\\cimv2", op);
scope.Connect();
//Query system for Operating System information
ObjectQuery oq = new ObjectQuery("SELECT * FROM Win32_OperatingSystem");
ManagementObjectSearcher query = new ManagementObjectSearcher(scope, oq);
ManagementObjectCollection queryCollection = query.Get();
foreach (ManagementObject obj in queryCollection)
{
obj.InvokeMethod("ShutDown", null); //shutdown
}
return true;
}
catch (Exception)
{
return false;
}
}
// Remote Reboot
public bool RemoteReboot(string userName, string password, string ip)
{
try
{
ConnectionOptions op = new ConnectionOptions();
op.Username = userName;
op.Password = password;
// Make a connection to a remote computer.
ManagementScope scope = new ManagementScope("\\\\" + ip + "\\root\\cimv2", op);
scope.Connect();
//Query system for Operating System information
ObjectQuery oq = new ObjectQuery("SELECT * FROM Win32_OperatingSystem");
ManagementObjectSearcher query = new ManagementObjectSearcher(scope, oq);
ManagementObjectCollection queryCollection = query.Get();
foreach (ManagementObject obj in queryCollection)
{
obj.InvokeMethod("Reboot", null); //shutdown
}
return true;
}
catch (Exception)
{
return false;
}
}

in C#, \\ in string means \
so the parameter interpreted as
/s /m \192.168.1.21 /t 5 /c 'Shutdown in 5 seconds'
you should use \\\\ to represent \\
or add an # mark before the start quote mark like this
System.Diagnostics.Process.Start("shutdown", #"/s /m \\192.168.1.21 /t 5 /c 'Shutdown in 5 seconds'");

You can have a look at the SO post below. It talks about rebooting a remote machine.
WMI to reboot remote machine
If you look at the Win32Shutdown method
Shutdown => 1 (0x1) & Reboot => 2 (0x2)
So in the SO link above you will have to change
// Add the input parameters.
inParams["Flags"] = 2; //Reboot
to
// Add the input parameters.
inParams["Flags"] = 1; //Shutdown

This should work:
System.Diagnostics.Process.Start("shutdown", #"-m \\192.168.1.21 -s -f -t 0");
Flags should be syntaxed like -m , not /m.
Even better is to create a "silent" shutdown (without cmd-window to show):
var shutdown = new ProcessStartInfo("shutdown", #"-m \\192.168.1.8 -s -f -t 0");
shutdown.CreateNoWindow = true;
shutdown.UseShellExecute = false;
Process.Start(shutdown);
Tested in win7, .Net framework 4.03

This is the code I use for shutdown / reboot remote machines:
int waitSeconds = 0;
string remoteHostNameOrIp = "192.168.0.1";
ProcessStartInfo processInfo = new ProcessStartInfo();
processInfo.FileName = "shutdown.exe";
processInfo.Arguments = $#"-s -t { waitSeconds } -m \\{ remoteHostNameOrIp }";
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
processInfo.CreateNoWindow = true;
var proc = Process.Start(processInfo);
// Works without WaitForExit();
// When using this you can check in "proc" if the shutdown was accepted by
// the remote machine
//
// proc.WaitForExit();

You must also enable the "Remote Registry" service on the target computer, AND you must have admin rights on the remote computer.
See here for details.

thanks for the createnowindow, i was already thinking to use it if everything works (but its unusefull if you want to see what is happening)
No comments without explaination about my dashes. as long as i know, you can choose between - or /
try it yourself :-) Start+R (run) you will see it works :)
i think its to much work to solve it.
So i figgerd out that a able to use a file "FILENAME.cmd"
with "shutdown -s -m \IP -t 40" in it
The delay for reminder to close or save stuff (not nessecary)
the -f doesn't work for me
but with the cmd file i can call it and let it run from my application.
So my problem is solved, i know its a little bit.... not perfect. but it works at least.
Thanx to all for quick reply's

Related

How to perform IISReset on multiple remote server using .NET Core?

I'm creating a website and in it I'm giving a link where the user enters his/her Azure VM username and password and then I'm gonna go ahead and restart the machine IIS server.
So I'm writing a .NET Code for implementing the same but no luck yet. I'm not able to restart the IIS server for the remote machines, I have even looked for an alternative approach to achieve the same using Powershell but unable to do so.
I tried remotely restarting the IIS server using WMI and also created code for calling Powershell in .Net Core but I'm not able to achieve the same.
Can someone please help me with how to restart the IIS server remotely using C# code or .NET Core code?
As for this...
Share me the link wherein there is a PowerShell script that restarts
IIS on a remote server using the system credentials.
... a quick search using 'Restart IIS on remote machine'
... will give a list of articles in the topic, some from right here on StackOverflow, since this is not the first time this has been asked. So, your question can be considered a potential duplicate of the below.
Example(s):
about_Remote - PowerShell | Microsoft Docs
Restart IIS on remote machine
Some of the answers, not using PowerShell to do this from the above are:
# Simplest will be
iisreset <servername>
# Run command prompt as admin and execute the command.
# Example : If server name is SRVAPP then command will be iisreset SRVAPP
# You could use sc
sc \\RemoteServer stop iisadmin
sc \\RemoteServer start w3svc
# or SysInternals' psexec. The PsTools suite is useful for these scenarios.
psexec \\RemoteServer iisreset
PowerShell Remoting requires you to be in the local admin group on the target host. You cannot run PowerShell code as SYSTEM unless you are running a scheduled task, even then it is the scheduled task running as whatever credential it was set for and running any script in that task. To run PowerShell code as another user, you must know the username and password.
You can use PowerShell to set up a scheduled task to run. Just search for 'PowerShell scheduled task' for details.
I tried the below codes for restarting IIS remotely but it didn't work.
Method 1:
using System.Diagnostics;
using System.Management;
using System.IO;
using System.Security;
Process myProcess = new Process();
ProcessStartInfo remoteAdmin =
new ProcessStartInfo(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "iisreset.exe"));
remoteAdmin.Arguments = "/restart";
myProcess.StartInfo.Verb = "runas";
var s = new SecureString();
//s.AppendChar('g');
Console.WriteLine("Enter username:");
string userName = Console.ReadLine();
Console.WriteLine(Environment.NewLine);
Console.WriteLine("Enter password:");
string password = Console.ReadLine();
Console.WriteLine(Environment.NewLine);
var securePasswordString = new SecureString();
// Use ToCharArray to convert string to array.
char[] array = password.ToCharArray();
// Loop through array.
for (int i = 0; i < array.Length; i++)
{
// Get character from the array.
securePasswordString.AppendChar(array[i]);
}
remoteAdmin.UserName = userName;
remoteAdmin.Password = securePasswordString;
remoteAdmin.Domain = "localhost";
myProcess.StartInfo = remoteAdmin;
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.Start(); //---ERROR HERE
if (!myProcess.Start())
{
// That didn't work
Console.WriteLine(Environment.NewLine);
Console.WriteLine("Process did not start!!!");
}
myProcess.WaitForExit();
var processExitCode = myProcess.ExitCode;
if (processExitCode == 0)
{
Console.WriteLine("The operation completed successfully.");
}
if (processExitCode != 0)
{
// That didn't work
if (processExitCode == 5)
{
Console.WriteLine(Environment.NewLine);
Console.WriteLine("Access Denied");
}
}
Console.ReadKey();
Method 2:
ConnectionOptions conn = new ConnectionOptions();
conn.Impersonation = ImpersonationLevel.Impersonate;
conn.Username = #"Username";
conn.Password = "";
//ManagementScope theScope = new ManagementScope("\\\\" + txtServerName.Text + "\\root\\cimv2", conn);
theScope.Connect(); //---ERROR HERE
I tried the below code to run powershell script from C# code but I need the script which takes remote server admin credentials and restart the IIS.
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
static void RunPsScriptMethod2()
{
StringBuilder sb = new StringBuilder();
PowerShell psExec = PowerShell.Create();
psExec.AddCommand(#"C:\Users\d92495j\Desktop\test.ps1");
psExec.AddArgument(DateTime.Now);
Collection<PSObject> results;
Collection<ErrorRecord> errors;
results = psExec.Invoke();
errors = psExec.Streams.Error.ReadAll();
if (errors.Count > 0)
{
foreach (ErrorRecord error in errors)
{
sb.AppendLine(error.ToString());
}
}
else
{
foreach (PSObject result in results)
{
sb.AppendLine(result.ToString());
}
}
Console.WriteLine(sb.ToString());
}

Invoke RenamePrinter as a non-administrator user

I'm trying to rename a printer using WMI in C#. I can run queries to select printers, but when I try to invoke the RenamePrinter method, I get an Access Denied result. I've tried running this application as administrator and creating a manifest, but I can't seem to invoke this method unless I'm actually running under the administrator account.
var oSearcher = new ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
foreach (ManagementObject oReturn in oReturnCollection)
{
var objectClass = new ManagementClass("Win32_Printer");
var inParams = objectClass.GetMethodParameters("RenamePrinter");
inParams["NewPrinterName"] = "..."; // something
ManagementBaseObject oResult = oReturn.InvokeMethod("RenamePrinter", inParams, null);
var result = oResult["returnValue"]; // 5 = Access Denied
Is there some way I can invoke RenamePrinter under a normal user's account -- even if it means running as administrator?
I've been trying to do this, and I've found that I can't do this with certain network printers, but with local printers it works.
What I did was launch PowerShell as local admin and run it from there:
public void RenamePrinter(string strCurrentName, string strNewPrinterName)
{
var newProcessInfo = new System.Diagnostics.ProcessStartInfo
{
FileName = #"C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe",
Verb = "runas",
CreateNoWindow = true,
Arguments = "-Command \"$printer = get-wmiobject win32_printer | where { $_.Name -eq '" + strCurrentName + "' }; $printer.RenamePrinter('" + strNewPrinterName + "')\""
};
System.Diagnostics.Process.Start(newProcessInfo);
}
I receive the same "Access Denied" error when I attempt this with network printers (maybe it's a driver thing? I don't know) but this works for local printers.

Execute a process in a remote machine using WMI

I want to open process pon remote machine, this remote machine is inside local network.
I try this command and in the remote machine nothing happen, this user that i connect with have administrators rights.
Both machines running Windows 7
static void Main(string[] args)
{
try
{
//Assign the name of the process you want to kill on the remote machine
string processName = "notepad.exe";
//Assign the user name and password of the account to ConnectionOptions object
//which have administrative privilege on the remote machine.
ConnectionOptions connectoptions = new ConnectionOptions();
connectoptions.Username = #"MyDomain\MyUser";
connectoptions.Password = "12345678";
//IP Address of the remote machine
string ipAddress = "192.168.0.100";
ManagementScope scope = new ManagementScope(#"\\" + ipAddress + #"\root\cimv2", connectoptions);
//Define the WMI query to be executed on the remote machine
SelectQuery query = new SelectQuery("select * from Win32_process where name = '" + processName + "'");
object[] methodArgs = { "notepad.exe", null, null, 0 };
using (ManagementObjectSearcher searcher = new
ManagementObjectSearcher(scope, query))
{
foreach (ManagementObject process in searcher.Get())
{
//process.InvokeMethod("Terminate", null);
process.InvokeMethod("Create", methodArgs);
}
}
Console.ReadLine();
}
catch (Exception ex)
{
//Log exception in exception log.
//Logger.WriteEntry(ex.StackTrace);
Console.WriteLine(ex.StackTrace);
}
}
you are not opening a process with that code but you are enumerating all the running process named "iexplore.exe" and close them.
I think an easier, better way is to use SysInternals PsExec or the Task Scheduler API
If you want to use WMI your code should look like this:
object theProcessToRun = { "YourFileHere" };
ManagementClass theClass = new ManagementClass(#"\\server\root\cimv2:Win32_Process");
theClass.InvokeMethod("Create", theProcessToRun);
----------In reply to your comment------------------
First of all you need to change your attitude and approach to coding and read the code that your are copy/pasting.
Then you should study a little more about programming languages.
No I will not write the code for you. I gave you an hint to point to the right direction. now it is your turn to develop it. Have fun!!
This is script that i did for my company before this using vbs script. can search the net to convert it to C# or etc. Fundamental of the steps and how to start a service using WMI. Have a nice coding and have fun.
sUser = "TESTDomain\T-CL-S"
sPass = "Temp1234"
Set ServiceSet = GetObject("winmgmts:").ExecQuery("Select * from Win32_Service where Name = 'netlogon'")
For Each Service In ServiceSet
Service.StopService
Service.Change "netlogon",Service.PathName, , ,"Automatic",false,sUser,sPass
Service.StartService
Next
Set Service = Nothing
Set ServiceSet = Nothing

C# and psexec process output redirection issues

I'm trying to create a small program to run on a centralized device. This program will run
"psexec \server(s) netstat -na | findstr "LISTENING""
to collect netstat data from remote nodes (should redirect output to string), then parse the data and compare against a known list. I can run the psexec cmd above without any issues from the cmd line, but when I try to run the same command as a process within my C# program, no data is returned to be parsed. I can see that the netstat is being run (cmd window flashes with netstat results), but the process.standardoutput is not catching the stream. If I use ping or pretty much anything other than psexec as an argument, the stream is caught and the results are shown in my text box. I've also tried setting the filename to psexec.exe and specifying the arguments but I get the same results. Last but not least, if I run psexec without any arguments, I get the help kickback info returned in my textbox. This is true if I'm running psexec.exe as the filename OR if I run cmd.exe as filename with "/c psexec" specified as args.
I'm just trying to get psexec output to be caught when executing locally at this point. I'll worry about psexec to remote machines later. Any help would be MUCH appreciated.
Here's the code:
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.FileName = "cmd.exe";
pProcess.StartInfo.Arguments = "/c psexec netstat";
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.Start();
string strOutput = pProcess.StandardOutput.ReadToEnd();
pProcess.WaitForExit();
if (pProcess.HasExited)
{
textBox1.Text = strOutput;
}
else
{
textBox1.Text = "TIMEOUT FAIL";
}
I would recommend also capturing the standard error output in case anything is being reported there.
Also, you may have a disconnect between "bitness" of psexec and your application if you are running on a 64-bit OS. If this is the case, change the platform for the project to match that of psexec rather than building as Any CPU.
Came across a few things to be changed but your recommendation of capturing standard error output was dead on and a good start. Turns out some info was being sent to the error output (even though really wasn't error, just run status 0 from psexec) so I knew at that point psexec wasn't just eating ALL the output. Once I started trying to pass remote hosts as args, I started getting user/pass error data back. Also needed to catch standard input if I wanted to supply credentials for proc run. Threw in some str literals and credentials for the remote exec, works perfectly. Thanks for the help. Here is the updated code--
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.Domain = "domain";
pProcess.StartInfo.UserName = "user with priv";
pProcess.StartInfo.Password = new System.Security.SecureString();
char [] pass = textBox3.Text.ToArray();
for (int x = 0; x < pass.Length; ++x)
{
pProcess.StartInfo.Password.AppendChar(pass[x]);
}
pProcess.StartInfo.FileName = #"psexec.exe";
pProcess.StartInfo.Arguments = #"\\remoteHost netstat -ano";
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardInput = true;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.StartInfo.RedirectStandardError = true;
pProcess.Start();
pProcess.WaitForExit(30000);
if (!pProcess.HasExited)
{
pProcess.Kill();
}
string strOutput = pProcess.StandardOutput.ReadToEnd();
string errOutput = pProcess.StandardError.ReadToEnd();
textBox1.Text = strOutput;
textBox2.Text = errOutput;

How to shutdown machine from ASP.NET

I have a machine running Windows Home Server v1 (WHS is based on Windows Server 2003 and is runningh IIS6) and on here I have a restful webservice running which I build in C# VS2008 (dotnet 3.5). From within the webservice I want to be able to;
1 Check that certain windows services are running;
2 Start certain windows services
3 Stop certain windows services
4 Reboot the machine
5 Shutdown the machine
For 1-3 then I am using impersonation to elevate the ASPNET user to the local administrator (it is only me running this on a local secure network) and then "ServiceController" to control the services. This works perfectly.
For for 4 & 5 I am having issues and can't get it to work.
If I use System.Diagnostics.Process to call the "shutdown.exe" command with the parameters "/s /f" then the process executes without any errors but does not do anything! No shutdown, no exception nothing and I can't work out why. I have tried setting the local admin username & password but it does not help, and the impersonate user call does not help.
My code
string shut_args = "/s /f /t 10";
Process process1 = new Process();
process1.StartInfo.FileName = "shutdown.exe";
process1.StartInfo.WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.System);
SecureString password = new SecureString();
foreach (char c in "mypassword")
password.AppendChar(c);
process1.StartInfo.Password = password;
process1.StartInfo.Domain = "homeserver";
process1.StartInfo.UserName = "Administrator";
process1.StartInfo.Arguments = shut_args;
process1.StartInfo.CreateNoWindow = true;
process1.StartInfo.UseShellExecute = false;
process1.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
impersonateValidUser();
process1.Start();
So instead I tried to use WMI (code taken from another post on here) but here I get an "Privilege not held" error when trying to call InvokeMethod
Mycode
ManagementBaseObject mboShutdown = null; ManagementClass mcWin32 = new ManagementClass ("Win32_OperatingSystem"); mcWin32.Get();
mcWin32.Scope.Options.Impersonation = ImpersonationLevel.Impersonate;
mcWin32.Scope.Options.Authentication = AuthenticationLevel.Connect;
mcWin32.Scope.Options.EnablePrivileges = true;
ManagementBaseObject mboShutdownParams = mcWin32.GetMethodParameters("Win32Shutdown");
// Flag 1 means we want to shut down the system. Use "2" to reboot.
mboShutdownParams["Flags"] = "1";
mboShutdownParams["Reserved"] = "0";
foreach (ManagementObject manObj in mcWin32.GetInstances())
{
mboShutdown = manObj.InvokeMethod("Win32Shutdown", mboShutdownParams, null);
}
I have also been through all of my security settings for the ASPNET and NETWORK_SERVICE user and they have rights to shutdown the server, the WMI security settings are also set-up for these users. But I just can't figure out what is wrong.
Any ideas?
Cheers
Have you tried to run a simple batch file instead of starting up the shutdown process in ASP.NET ?
The whole process is described here: remote shutdown/restart using ASP.NET
Old question, but I wanted to do this myself, and just figured out the answer. So try this:
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "shutdown";
startInfo.Arguments = "/s /t 5";
startInfo.UseShellExecute = true;
startInfo.Verb = "runas";
process.StartInfo = startInfo;
process.Start();

Categories