Executing Process on a remote Machine c# - c#

i connected over WMI on a remote Machine.
username and password is correctly set.
var options = new ConnectionOptions();
servicePath = "\\\\Testserver\\root\\cimv2";
options.EnablePrivileges = true;
options.Username = username;
options.Password = pwd;
serviceScope = new ManagementScope(servicePath, options);
serviceScope.Connect();
this is the code sequence i want to run on the remote machine
Process process = new Process();
process.StartInfo.FileName = "diskpart.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.StandardInput.WriteLine("list volume");
process.StandardInput.WriteLine("exit");
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// extract information from output
string table = output.Split(new string[] { "DISKPART>" }, StringSplitOptions.None)[1];
var rows = table.Split(new string[] { "\n" }, StringSplitOptions.None);
for (int i = 3; i < rows.Length; i++)
{
if (rows[i].Contains("Volume"))
{
int index = Int32.Parse(rows[i].Split(new string[] { " " }, StringSplitOptions.None)[3]);
string label = rows[i].Split(new string[] { " " }, StringSplitOptions.None)[8];
Console.WriteLine($#"Volume {index} {label}:\");
}
}
if i am going to call the process over wmi like this...
object[] theProcessToRun = { "diskpart" };
using (var managementClass = new ManagementClass(serviceScope, new ManagementPath("Win32_Process"), new ObjectGetOptions()))
{
managementClass.InvokeMethod("Create", theProcessToRun);
}
i can call the process, but havent the possibilites to hand over the commands i would like to execute in this process...
How to solve this ?

You can use a script to be passed in as part of the command line arguments that will contain your command.
Reference: https://www.computerhope.com/diskpart.htm
Also you should redirect your output to a file using cmd as you should get your output directly either.
For script, make sure you use a unc that other machine/user has access to. For output file, make sure you use a unc that other machine/user can write to and you can read from via the program.

Related

C# and diskpart: how to select by disk label and not by a number?

I have a C# application that install Windows image. I have to choose the disk where the system will copied (C: or D: or ...) on the user interface. For that, it's ok.
Then i have to format the disk. I have to select with diskpart.exe the good physical disk associed to C:. But with diskpart, we choose the disk with number: select disk 0 or 1 or ...
How to make the connection between the good disk number and the letter choosen by users on the interface ?
I found Nothing on google. I tried to find an information with wmi Win32_DiskDrive but nothing in common with diskpart detail disk .
Thank's
Another solution instead of using ManagementObjectSearcher is using DiskPart.exe programatically, but my code is rather a static solution (would be better with regex) but will work a long time.
It requires a manifest file with higher execution privileges (Add new element > Application Manifest File and change requestedExecutionLevel to <requestedExecutionLevel level="requireAdministrator" uiAccess="false" />. for further information: https://stackoverflow.com/a/43941461/5830773)
Then you can use following code to get the drive list with DiskPart.exe:
// execute DiskPart programatically
Process process = new Process();
process.StartInfo.FileName = "diskpart.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.StandardInput.WriteLine("list volume");
process.StandardInput.WriteLine("exit");
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// extract information from output
string table = output.Split(new string[] { "DISKPART>" }, StringSplitOptions.None)[1];
var rows = table.Split(new string[] { "\n" }, StringSplitOptions.None);
for (int i = 3; i < rows.Length; i++)
{
if (rows[i].Contains("Volume"))
{
int index = Int32.Parse(rows[i].Split(new string[] { " " }, StringSplitOptions.None)[3]);
string label = rows[i].Split(new string[] { " " }, StringSplitOptions.None)[8];
Console.WriteLine($#"Volume {index} {label}:\");
}
}
This gives following output like from DiskPart but you can customise it for your needs:
Volume 0 C:\
Volume 1 D:\
Volume 2 F:\
Volume 3 G:\
Volume 4 I:\
Volume 5 H:\
Now searching by drive letter is obvious:
public int GetIndexOfDrive(string drive)
{
drive = drive.Replace(":", "").Replace(#"\", "");
// execute DiskPart programatically
Process process = new Process();
process.StartInfo.FileName = "diskpart.exe";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.StandardInput.WriteLine("list volume");
process.StandardInput.WriteLine("exit");
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// extract information from output
string table = output.Split(new string[] { "DISKPART>" }, StringSplitOptions.None)[1];
var rows = table.Split(new string[] { "\n" }, StringSplitOptions.None);
for (int i = 3; i < rows.Length; i++)
{
if (rows[i].Contains("Volume"))
{
int index = Int32.Parse(rows[i].Split(new string[] { " " }, StringSplitOptions.None)[3]);
string label = rows[i].Split(new string[] { " " }, StringSplitOptions.None)[8];
if (label.Equals(drive))
{
return index;
}
}
}
return -1;
}
Usage:
Console.WriteLine(GetIndexOfDrive(#"D:\")); // returns 1 on my computer
To map between disk and logical drive(C:, D:, etc), you have to get the information from:
Win32_LogicalDisk
Win32_LogicalDiskToPartition
Win32_DiskPartition
in any order. These tables will have all information. It can be easily used using System.Management in c# .

How can i execute msg.exe by C# in windows?

Is there a way to display the windows popup msg by using C#?
I mean by using the windows msg.exe program that can be used in cmd, for example:" msg * Hello "
PD: I know that i can use MessageBox.Show() instead. But i want to know if this is possible :(
I wrote 2 ways to do it but none worked:
Process.Start("cmd.exe","/C msg * Hello");
and...
Process cmd = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = "/C msg * Hello",
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
}
};
cmd.Start();
Did you try adding msg.exe directly?
Process cmd = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = #"msg.exe",
Arguments = #"* /v Hello",
WorkingDirectory = Environment.SystemDirectory;
WindowStyle = ProcessWindowStyle.Normal
}
};
cmd.Start();
I encountered the same problem.
This was because the project was configured as "AnyCPU" but the "Prefer 32-bit" option was checked in the "Build" tab of the project configuration. Uncheck that option and the problem will disappear.
Edit: personnaly, I use the following function to locate the binary according to the executable and OS platform (32/64 bits):
public static bool LocateMsgExe(out string returnedMsgPath)
{
returnedMsgPath = null;
string[] msgPaths = new string[] { Environment.ExpandEnvironmentVariables(#"%windir%\system32\msg.exe"),
Environment.ExpandEnvironmentVariables(#"%windir%\sysnative\msg.exe") };
foreach (string msgPath in msgPaths)
{
if (File.Exists(msgPath))
{
returnedMsgPath = msgPath;
return true;
}
}
return false;
}
And for invoking it :
if (LocateMsgExe(out string strMsgPath))
{
Process.Start(strMsgPath, "* \"Hello world!\"");
}
Regards,
damien.
This is my solution. It consists of a webpage (.aspx) with a listbox (lstComputers), a textbox (txtMessageToSend), a dropdownlist to select the OU that contains the computers that will receive the message and a button (btnSendMessage).
This is the code for btnSendMessage on the aspx.cs
protected void btnSendMessage_Click(object sender, EventArgs e)
{
string searchbase = ddlZaal.SelectedItem.Text; //This is a dropdownlist to select an OU
DirectoryEntry entry = new DirectoryEntry("LDAP://OU=" + searchbase + ",OU=YourOU,OU=YourSubOU," + Variables.Domain + ""); //Variables.Domain is specified in the Web.config
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = ("(objectClass=computer)");
foreach (SearchResult result in mySearcher.FindAll())
{
DirectoryEntry directoryObject = result.GetDirectoryEntry();
string computernaam = directoryObject.Properties["Name"].Value.ToString();
lstComputers.Items.Add(computernaam); //This is a listbox that shows the computernames. To each computer a message is sent.
string pingnaam = computernaam + "dns.suffix"; //Might be necessary for connecting to the computes in the domain
string MessageToSend = txtMessageToSend.Text; //The text in this textbox will be the messagetext
Process process = new Process();
ProcessStartInfo psi = new ProcessStartInfo(#"C:\inetpub\wwwroot\PsExec.exe"); //Location of PsExec.exe on the webserver that hosts the web-application.
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.RedirectStandardInput = true;
psi.WindowStyle = ProcessWindowStyle.Minimized;
psi.CreateNoWindow = true;
psi.Arguments = "/accepteula -s -i \\\\" + pingnaam + " cmd /c msg.exe * " + MessageToSend;
process.StartInfo = psi;
process.Start();
}
}

Trying to run same command in command prompt not working

I am making a program that seeks out secured PDFs in a folder and converting them to PNG files using ImageMagick. Below is my code.
string WorkDir = #"C:\Users\rwong\Desktop\TestFiles";
Directory.SetCurrentDirectory(WorkDir);
String[] SubWorkDir = Directory.GetDirectories(WorkDir);
foreach (string subdir in SubWorkDir)
{
string[] filelist = Directory.GetFiles(subdir);
for(int f = 0; f < filelist.Length; f++)
{
if (filelist[f].ToLower().EndsWith(".pdf") || filelist[f].EndsWith(".PDF"))
{
PDFReader reader = new Pdfreader(filelist[f]);
bool PDFCheck = reader.IsOpenedWithFullPermissions;
reader.CLose();
if(PDFCheck)
{
//do nothing
}
else
{
string PNGPath = Path.ChangeExtension(filelistf], ".png");
string PDFfile = '"' + filelist[f] + '"';
string PNGfile = '"' + PNGPath + '"';
string arguments = string.Format("{0} {1}", PDFfile, PNGfile);
ProcessStartInfo startInfo = new ProcessStartInfo(#"C:\Program Files\ImageMagick-6.9.2-Q16\convert.exe");
startInfo.Arguments = arguments;
Process.Start(startInfo);
}
}
}
I have ran the raw command in command prompt and it worked so the command isn't the issue. Sample command below
"C:\Program Files\ImageMagick-6.9.2-Q16\convert.exe" "C:\Users\rwong\Desktop\TestFiles\Test_File File_10.PDF" "C:\Users\rwong\Desktop\TestFiles\Test_File File_10.png"
I looked around SO and there has been hints that spaces in my variable can cause an issue, but most of those threads talk about hardcoding the argument names and they only talk about 1 argument. I thought adding double quotes to each variable would solve the issue but it didn't. I also read that using ProcessStartInfo would have helped but again, no dice. I'm going to guess it is the way I formatted the 2 arguments and how I call the command, or I am using ProcessStartInto wrong. Any thoughts?
EDIT: Based on the comments below I did the extra testing testing by waiting for the command window to exit and I found the following error.
Side note: I wouldn't want to use GhostScript just yet because I feel like I am really close to an answer using ImageMagick.
Solution:
string PNGPath = Path.ChangeExtension(Loan_list[f], ".png");
string PDFfile = PNGPath.Replace("png", "pdf");
string PNGfile = PNGPath;
Process process = new Process();
process.StartInfo.FileName = #"C:\Program Files\ImageMagick-6.9.2 Q16\convert.exe";
process.StartInfo.Arguments = "\"" + PDFfile + "\"" +" \"" + PNGPath +"\""; // Note the /c command (*)
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.Start();
//* Read the output (or the error)
string output = process.StandardOutput.ReadToEnd();
Console.WriteLine(output);
string err = process.StandardError.ReadToEnd();
Console.WriteLine(err);
process.WaitForExit();
It didn't like the way I was formatting the argument string.
This would help you to run you command in c# and also you can get the result of the Console in your C#.
string WorkDir = #"C:\Users\rwong\Desktop\TestFiles";
Directory.SetCurrentDirectory(WorkDir);
String[] SubWorkDir = Directory.GetDirectories(WorkDir);
foreach (string subdir in SubWorkDir)
{
string[] filelist = Directory.GetFiles(subdir);
for(int f = 0; f < filelist.Length; f++)
{
if (filelist[f].ToLower().EndsWith(".pdf") || filelist[f].EndsWith(".PDF"))
{
PDFReader reader = new Pdfreader(filelist[f]);
bool PDFCheck = reader.IsOpenedWithFullPermissions;
reader.CLose()l
if(!PDFCheck)
{
string PNGPath = Path.ChangeExtension(filelistf], ".png");
string PDFfile = '"' + filelist[f] + '"';
string PNGfile = '"' + PNGPath + '"';
string arguments = string.Format("{0} {1}", PDFfile, PNGfile);
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.EnableRaisingEvents = true;
p.StartInfo.CreateNoWindow = true;
p.startInfo.FileName = "C:\Program Files\ImageMagick-6.9.2-Q16\convert.exe";
p.startInfo.Arguments = arguments;
p.OutputDataReceived += new DataReceivedEventHandler(Process_OutputDataReceived);
//You can receive the output provided by the Command prompt in Process_OutputDataReceived
p.Start();
}
}
}
private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
string s = e.Data.ToString();
s = s.Replace("\0", string.Empty);
//Show s
Console.WriteLine(s);
}
}

Running exe with parameters doesn't work

var p = Process.Start(#"c:\PsTools\PsExec.exe", #"C:\Windows\System32\notepad.exe");
var err = p.StandardError.ReadToEnd();
var msg = p.StandardOutput.ReadToEnd();
lblStatusResponse.Text = "Err: " + err + "Msg: " + msg;
Why is my code not working?
I getting error:
System.InvalidOperationException: StandardError has not been redirected.
But when I add following:
p.StartInfo.RedirectStandardError = true;
var p = Process.Start(#"c:\PsTools\PsExec.exe", #"C:\Windows\System32\notepad.exe");)
it still gets the same error.
The main problem is that I wanna execute a exe with arguments, but I can't get it to work.
The following code generates a new p, this ignoring the settings you change in the previous instance:
var p = Process.Start(#"c:\PsTools\PsExec.exe", #"C:\Windows\System32\notepad.exe");)
So it doesn't really matter whether you initialize p like this
p.StartInfo.RedirectStandardError = true;
or not.
What you need to do
You need to create a ProcessStartInfo object, configure it and then pass it to Process.Start.
ProcessStartInfo p = new ProcessStartInfo(#"c:\PsTools\PsExec.exe", #"C:\Windows\System32\notepad.exe");
p.UseShellExecute = false;
p.RedirectStandardError = true;
p.RedirectStandardOutput = true;
Process proc = Process.Start(p);
var err = proc.StandardError.ReadToEnd();
var msg = proc.StandardOutput.ReadToEnd();
Taken from MSDN: https://msdn.microsoft.com/en-us/library/system.diagnostics.process.standardoutput(v=vs.110).aspx
The StandardOutput stream has not been defined for redirection; ensure ProcessStartInfo.RedirectStandardOutput is set to true and ProcessStartInfo.UseShellExecute is set to false.
So remember to set those flags as instructed by MS.
Process proc = new Process();
proc.StartInfo.FileName = "cmd.exe";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.Arguments = "/C " + command; //Enter your own command
proc.Start();
string output =proc.StandardOutput.ReadToEnd();
I know this is not the same code you have but this is was the only working code I have , that will run external command/process in C# , and return all of it output/errors to the application main window
public void Processing()
{
//Create and start the ffmpeg process
System.Diagnostics.ProcessStartInfo psi = new ProcessStartInfo("ffmpeg")
{ // this is fully command argument you can make it according to user input
Arguments = "-y -i '/mnt/Disk2/Video/Antina03.jpg' pp.mp4 ",
RedirectStandardOutput = true,
WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardError=true,
RedirectStandardInput=true
};
System.Diagnostics.Process ischk;
ischk = System.Diagnostics.Process.Start(psi);
ischk.WaitForExit();
////Create a streamreader to capture the output of ischk
System.IO.StreamReader ischkout = ischk.StandardOutput;
ischk.WaitForExit();
if (ischk.HasExited) // this condition very important to make asynchronous output
{
string output = ischkout.ReadToEnd();
out0 = output;
}
/// in case you got error message
System.IO.StreamReader iserror = ischk.StandardError;
ischk.WaitForExit();
if (ischk.HasExited)
{
string output = iserror.ReadToEnd();
out0 = output;
}
}
if you want to run this process just call the function Processing() BTW out0 are global variable so it can use out the function .
credit
I'm using MonoDevlop "C# devloping tool on Linux " and I get the output this way :-
public MainWindow() : base(Gtk.WindowType.Toplevel)
{
Build();
Processing();
textview2.Buffer.Text = out0;
}

How to add authentication property for login to directory path when running batch file in WCF?

I have class in my WCF service to execute batch file. when I test to run the batch file in shared directory, everything is fine, the batch was executed, but when I try to run the batch file from secure diretory, I get error "ACCESS DENIED". How to add login property so I can access my secured directory to execute my batch file?
here is my code:
public string ExecuteBat()
{
string hasil = "";
ProcessStartInfo processInfo = new ProcessStartInfo(#"D:\Secure\command.bat");
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
Process process = Process.Start(processInfo);
process.WaitForExit();
if (process.ExitCode == 0)
{
hasil = "BAT EXECUTED!";
}
else
{
hasil = "EXECUTE BAT FAILED";
}
return hasil;
}
The ProcessStartInfo class has properties for Domain,UserName and Password that, when set, start the process under those credentials, something like this:
ProcessStartInfo processInfo = new ProcessStartInfo(#"D:\Rpts\SSIS_WeeklyFlash_AAF_1.bat");
processInfo.CreateNoWindow = true;
processInfo.UseShellExecute = false;
processInfo.Domain= "MyCompanyDomain";
processInfo.UserName = "username";
//Secure string is an odd beast, so you need something like this:
SecureString ss = new SecureString();
string password = "p#$$w0rd";
foreach (char c in password)
{
ss.AppendChar(c);
}
processInfo.Password = ss;
...

Categories