Execute .bat File on a Server in C# - c#

I am attempting to execute a .bat file on a remote machine (a server) that ends a process and kicks the user out of a database system. When executed on the server, the file works correctly and ends the process on the client machine.
However, when executed through C#, the command is entered into the command prompt and cannot execute. Here is the .bat file;
psexec \\lp-100 msg * Hi Dan - your being removed!
pskill \\lp-100 sdcdatabase.vshost.exe
pause
The issue arises when executed in C#, this is the error message:
What this leads me to believe is that actually the .bat file is not being executed on the server (that does have PsTools installed), but instead the contents is being copied over and executed on the client machine instead.
Here is the code I am using to execute the .bat file;
Process proc = null;
try
{
string batDir = string.Format(#"\\hqdb1\sdc_sqldb\pstools\");
proc = new Process();
proc.StartInfo.WorkingDirectory = batDir;
proc.StartInfo.FileName = "removeuser.bat";
proc.StartInfo.CreateNoWindow = false;
proc.Start();
proc.WaitForExit();
MessageBox.Show("Bat file executed !!");
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace.ToString());
}
Is this executing the .bat file on the server, kicking the client off, or executing on the client and not finding PsTools?

Years ago I wrote this code, try if it's still working:
ConnectionOptions options = new ConnectionOptions();
options.Password = args[2];
options.Username = args[1];
string machine= args[0];
ManagementScope scope = new ManagementScope();
scope.Options = options;
scope.Path = new ManagementPath(#"\\" + machine + #"\root\cimv2");
scope.Connect();
ObjectQuery query = new ObjectQuery("Select * from Win32_Process Where Name = '" + args[3] + "'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach (ManagementObject m in queryCollection)
{
object y = m.GetPropertyValue("ProcessID");
int pID = Convert.ToInt32(y);
m.InvokeMethod("Terminate", null);
}

Related

How can I get the script name of the script that launched my program in batch and powershell?

So I have a situation where I'll run a script, let's call it test.bat, and this script will at some point run my C# program, myprogram.exe. I need a way to obtain the name of the script that called myprogram.exe, so that when I see the output of that function, it is test.bat.
Right now I am using this code:
string GetParentProcessName()
{
int myId = Process.GetCurrentProcess().Id;
string query = string.Format("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", myId);
ManagementObjectSearcher search = new ManagementObjectSearcher("root\\CIMV2", query);
var results = search.Get().GetEnumerator();
results.MoveNext();
var queryObj = results.Current;
var parentId = (uint)queryObj["ParentProcessId"];
Process parent = Process.GetProcessById((int)parentId);
Console.WriteLine("My parent process name is: {0}", parent.ProcessName);
return parent.ProcessName;
}
However the output when I run test.bat in the command line is "cmd", not "test" or "test.bat".
How can I do this?

Creating an accurate progress bar for remote installer application in C#

Very new to C# here, and I'm learning a lot as I go along.
I'm creating a Winforms app that installs patches remotely. Basically, you give it a file (.msi or .exe) and a computer list, and it goes down the list until all of the patches have been installed. It seems to be working for what I want it to do. Just a click and go thing for vuln/patch management. For the record, I use PSexec and powershell to do the same task, and they're wonderful. Just fiddling around with my own, hoping to do some learning in the process.
I want to create an accurate progress bar for the application, so the admin can have a general idea of what processes are being handled at the time. There is a very wide spread in the amount of systems that may need to be patched, anywhere from 10 systems to 1K+.
I have looked at tutorials of progress bar implementation, but most are based on the code writer estimating the time of task completion for specific jobs. Being that every patch size, install time, and amount of computers are different, that doesn't seem to help me much.
private void Start_Click(object sender, EventArgs e)
{
string complist = openFileDialog2.FileName;
string patch = textBox2.Text;
string fileName = patch;
string patchfile = Path.GetFileName(fileName);
foreach (string line in File.ReadLines(complist))
{
//Checks if C:\PatchUpdates exists on remote machine. If not, a folder is created.
if (!Directory.Exists(#"\\" + line + #"\C$\PatchUpdates"))
{
Directory.CreateDirectory(#"\\" + line + #"\C$\PatchUpdates");
}
//XCOPY patch to every computer
System.Diagnostics.Process processCopy = new System.Diagnostics.Process();
ProcessStartInfo StartInfo = new ProcessStartInfo();
StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
StartInfo.FileName = "cmd";
StartInfo.Arguments = string.Format("/c xcopy " + patch + " " + #"\\{0}\C$\PatchUpdates /K /Y", line);
processCopy.StartInfo = StartInfo;
processCopy.Start();
processCopy.WaitForExit();
//Checks filename and installs according to file type.
if (patch.Contains(".msi"))
{
//Uses WMI to execute a remote command to install using msiexec.
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
ManagementScope WMIscope = new ManagementScope(
string.Format("\\\\{0}\\root\\cimv2", line));
WMIscope.Connect();
ManagementClass WMIprocess = new ManagementClass(
WMIscope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
object[] processmsi = { #"cmd.exe /c msiexec /qn /i " + #"C:\PatchUpdates\" + patchfile + #" /norestart" };
object result = WMIprocess.InvokeMethod("Create", processmsi);
}
else if (patch.Contains(".exe"))
{
//Uses WMI to execute a remote command to install using commandline.
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
ManagementScope WMIscope = new ManagementScope(
string.Format("\\\\{0}\\root\\cimv2", line));
WMIscope.Connect();
ManagementClass WMIprocess = new ManagementClass(
WMIscope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
object[] processexe = { #"cmd.exe /c C:\PatchUpdates\" + patchfile + #" /silent /norestart" };
object result = WMIprocess.InvokeMethod("Create", processexe);
}
else if (patch.Contains(".msu"))
{
//Uses WMI to execute a remote command to install using WUSA.
ConnectionOptions options = new ConnectionOptions();
options.Impersonation = System.Management.ImpersonationLevel.Impersonate;
ManagementScope WMIscope = new ManagementScope(
string.Format("\\\\{0}\\root\\cimv2", line));
WMIscope.Connect();
ManagementClass WMIprocess = new ManagementClass(
WMIscope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
object[] processmsu = { #"wusa " + patchfile + " /quiet /norestart" };
object result = WMIprocess.InvokeMethod("Create", processmsu);
}
}
}
The code above is where most of the work is done. When the user clicks "Start", the patch is copied to C:\PatchUpdates on every machine and is installed using WMI.
How could I make a progress bar that is based on the calculation of time taken to do each task, and finishes at 100% when the last computers are being patched?
I'm assuming a lot of work is needed to do this.
Any help is appreciated.
You need to first get the amount of lines (you systems count).
If you are using .Net 4.0 or later you can use
var lineCount = File.ReadLines(#"C:\file.txt").Count();
else you can do first
var lineCount = 0;
using (var reader = File.OpenText(#"C:\file.txt"))
{
while (reader.ReadLine() != null)
{
lineCount++;
}
}
After that you set the progressbars minimum to 0 and the Maximum to this count of systems. In the foreach you need only to do a progressbar.PerformStep.
public void loadFiles()
{
// Sets the progress bar's minimum value to a number representing
// no operations complete -- in this case, no files read.
progressBar1.Minimum = 0;
// Sets the progress bar's maximum value to a number representing
// all operations complete -- in this case, all five files read.
progressBar1.Maximum = Convert.ToInt(lineCount); // in our case to the number of systems
// Sets the Step property to amount to increase with each iteration.
// In this case, it will increase by one with every file read.
progressBar1.Step = 1;
// Uses a for loop to iterate through the operations to be
// completed. In this case, five files are to be copied into memory,
// so the loop will execute 5 times.
for (int i = 0; i <= 4; i++)
{
// Inserts code to copy a file
progressBar1.PerformStep();
// Updates the label to show that a file was read.
label1.Text = "# of Files Read = " + progressBar1.Value.ToString();
}
}

ManagementObjectSearcher Get() method returns no results

I'm trying to terminate a process on a remote machine with WMI / C# on .NET 4.5. In the code below, when the Get method is called on the ManagementObjectSearcher instance nothing is returned, so the line inside the foreach is not reached. The ManagementScope is connected and the query variable contains the name of the process for termination.
Thx for any help.
try
{
ConnectionOptions connOptions = new ConnectionOptions();
connOptions.Impersonation = ImpersonationLevel.Impersonate;
connOptions.EnablePrivileges = true;
ManagementScope manScope = new ManagementScope(String.Format(#"\\{0}\ROOT\CIMV2", NetworkName), connOptions);
manScope.Connect();
var query = new SelectQuery("select * from Win32_process where name = '" + ProcessName + "'");
using (var searcher = new ManagementObjectSearcher(manScope, query))
{
foreach (ManagementObject process in searcher.Get())
{
process.InvokeMethod("Terminate", null);
}
}
}
catch (ManagementException err)
{
//Do something with error message here
}
As outlined in my comment above, for completeness here's the code with my changes that are as follows.
try
{
ConnectionOptions connOptions = new ConnectionOptions();
connOptions.Impersonation = ImpersonationLevel.Impersonate;
connOptions.EnablePrivileges = true;
ManagementScope manScope = new ManagementScope(String.Format(#"\\{0}\ROOT\CIMV2", NetworkName), connOptions);
manScope.Connect();
ProcessName = ProcessName + ".exe";
using (var searcher = new ManagementObjectSearcher(manScope, new SelectQuery("select * from Win32_Process where Name = '" + ProcessName + "'")))
{
foreach (ManagementObject process in searcher.Get())
{
process.InvokeMethod("Terminate", null);
}
}
}
catch (ManagementException err)
{
//Do something with error message here
}
In my case i was unable to receive CPU utilization value remotely using WMI query:
SELECT PercentProcessorTime FROM Win32_PerfFormattedData_PerfOS_Processor WHERE Name='_Total'
I changed project build platform target from Any CPU to x64 to match my system bitness, and problem was solved. Another way is uncheck Prefer 32-bit checkbox when Any CPU is selected.
Use the Count property to check, whether it contains any record. That is, if(searcher.Get().count == 0) returns true, means no record is present.

Creating and deleting files on remote machine

I want to create and delete a file on a remote machine of which i have admin username and password.
I am using this code
ConnectionOptions options = new ConnectionOptions();
options.Username = "admin";
options.Password = "12345";
ManagementScope scope = null;
ObjectQuery query = null;
ManagementObjectSearcher searcher = null;
try
{
scope = new ManagementScope(#"\\192.168.3.125\root\CIMV2", options);
scope.Connect();
query = new ObjectQuery(#"SELECT * FROM CIM_Datafile WHERE name = 'c:\\c$\\Testing\\Test.txt'");
searcher = new ManagementObjectSearcher(scope, query); // EDIT forgot to include 'scope' previously
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
return;
}
foreach(ManagementObject mo in searcher.Get())
{
uint returnCode = (uint)mo.InvokeMethod("Delete", null);
if (returnCode == 0)
Console.WriteLine("File was successfully deleted");
else
Console.WriteLine("Deletion failed due to return code " + returnCode);
}
But it is giving me invalid query error and also i want to know how to Create a file on Remote machine.
and i even cant access the path \\192.168.3.125\C$\Testing\Test.txt
My file location is c:\Testing\Test.txt
Firstly can you access the file via the windows explorer from the machine (start -> run -> \192.168.3.125\C$\Testing\Test.txt)
If so what's wrong with
File.Delete(#"\\192.168.3.125\C$\Testing\Test.txt");
In my case, when I was connecting two windows based computers, you could just put:
#"\\PC-NAME\NEXT-FOLDER\NEXT-FOLDER\test.txt"

Remotely Install an Application from a FileShare using WMI

So far my code will start a process (Install an application) with command line arguments on a target computer and wait for the process to finish, IF I copy the install files to that computer.
My goal now is to:
Start a process with command line arguments (Install an application) on the remote computer.
NOT copy the files to the remote computer. The installer files will be located on a network share that both the sender computer and the remote computer have access to.
Wait for the process to finish.
Any help is much appreciated!
private void StartAppAction(string PCName, string Params)
{
//Example of Params \\Server\Folder\Application.EXE /s
ConnectionOptions conn = new ConnectionOptions();
conn.Impersonation = ImpersonationLevel.Impersonate;
conn.Authentication = AuthenticationLevel.Default;
conn.EnablePrivileges = true;
ManagementScope manScope = new ManagementScope(String.Format(#"\\{0}\ROOT\CIMV2", PCName), conn);
try
{
manScope.Connect();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
ObjectGetOptions objOpt = new ObjectGetOptions();
ManagementPath manPath = new ManagementPath("Win32_Process");
ManagementClass manClass = new ManagementClass(manScope, manPath, objOpt);
ManagementBaseObject inParams = manClass.GetMethodParameters("Create");
inParams["CommandLine"] = Params;
ManagementBaseObject outParams = manClass.InvokeMethod("Create", inParams, null);
string query = String.Format("SELECT * FROM __InstanceDeletionEvent WITHIN 3 WHERE TargetInstance ISA 'Win32_Process' AND TargetInstance.ProcessID = '{0}'", outParams["ProcessId"].ToString());
string scope = #"\\" + PCName + #"\root\CIMV2";
EventWatcherOptions evOp = new EventWatcherOptions(null, new TimeSpan(1, 0, 0), 1);
ManagementEventWatcher manWatch = new ManagementEventWatcher(scope, query, evOp);
try
{
ManagementBaseObject watcher = manWatch.WaitForNextEvent();
var ID = ((ManagementBaseObject)watcher["TargetInstance"]);
//Process Ended
}
catch
{
MessageBox.Show("Unable to watch for the remote process to finish");
}
}

Categories