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");
}
}
Related
I'm trying to get information about the programs installed on remote devices. At the moment, I found a way to output all installed programs. Is there a way to get the version and date of installation?
List<string> programs = new List<string>();
ConnectionOptions connectionOptions = new ConnectionOptions();
connectionOptions.Username = "USERNAME";
connectionOptions.Password = "USERPASS";
//connectionOptions.Impersonation = ImpersonationLevel.Impersonate;
string devicename = textBox8.Text;
ManagementScope scope = new ManagementScope("\\\\" + devicename + "\\root\\CIMV2", connectionOptions);
scope.Connect();
string softwareRegLoc = #"Software\Microsoft\Windows\CurrentVersion\Uninstall";
ManagementClass registry = new ManagementClass(scope, new ManagementPath("StdRegProv"), null);
ManagementBaseObject inParams = registry.GetMethodParameters("EnumKey");
inParams["hDefKey"] = 0x80000002;//HKEY_LOCAL_MACHINE
inParams["sSubKeyName"] = softwareRegLoc;
// Read Registry Key Names
ManagementBaseObject outParams = registry.InvokeMethod("EnumKey", inParams, null);
string[] programGuids = outParams["sNames"] as string[];
foreach (string subKeyName in programGuids)
{
inParams = registry.GetMethodParameters("GetStringValue");
inParams["hDefKey"] = 0x80000002;//HKEY_LOCAL_MACHINE
inParams["sSubKeyName"] = softwareRegLoc + #"\" + subKeyName;
inParams["sValueName"] = "DisplayName";
// Read Registry Value
outParams = registry.InvokeMethod("GetStringValue", inParams, null);
if (outParams.Properties["sValue"].Value != null)
{
string softwareName = outParams.Properties["sValue"].Value.ToString();
programs.Add(softwareName);
}
}
foreach (string softwareName in programs)
{
int n = dataGridView8.Rows.Add();
dataGridView8.Rows[n].DefaultCellStyle.BackColor = Color.LightGreen;
dataGridView8.Rows[n].Cells[0].Value = devicename;
dataGridView8.Rows[n].Cells[1].Value = softwareName;
dataGridView8.FirstDisplayedScrollingRowIndex = dataGridView8.RowCount - 1;
}
}
catch
{
MessageBox.Show("Something went wrong");
}
}
Welcome to Stack Overflow. Use ORMi library to get that information easily:
1) Install via NuGet:
Install-Package ORMi
2) Use the library:
using ORMi;
3) Instanciate and use:
WMIHelper helper = new WMIHelper("root\CimV2");
var programs = helper.Query("SELECT Name, Version, InstallDate FROM Win32_Product").ToList();
foreach(var p in programs)
{
Console.WriteLine(p.Name);
}
And that´s it. Then if you want to do some more advanced working you can define your model classes and query in a simpler way. You can read the docs on GitHub.
I'm trying to install a msi on a remote machine , i changed DCOM Config to get access from my machine when using methode install of win32_product it fails with return output 1601 ( that means that Windows Installer service could not be accessed )
i did some research in the net , and run the service manually using services.msc
and did msiexec / unreg and msiexec /register , still not working
ps: both machines a running on windows 10
here is my code
Ps : i used the same code for win32_Bios to get SerialNumber and it works
try
{
\\machine is the name of the computer
string PackageLocation = #"\\" + machine + msi;
ConnectionOptions connection = new ConnectionOptions();
connection.Username = username;
connection.Password = password;
connection.Impersonation = ImpersonationLevel.Impersonate;
connection.Authentication = AuthenticationLevel.Default;
connection.EnablePrivileges = true;
//define the WMI root name space
ManagementScope scope =
new ManagementScope(#"\\" + machine + #"\root\CIMV2", connection);
//define path for the WMI class
ManagementPath p =
new ManagementPath("Win32_Product");
//define new instance
ManagementClass classInstance = new ManagementClass(scope, p, null);
// Obtain in-parameters for the method
ManagementBaseObject inParams = classInstance.GetMethodParameters("Install");
// Add the input parameters.
inParams["AllUsers"] = true; //to install for all users
inParams["Options"] = ""; //paramters must be in the format “property=setting“
inParams["PackageLocation"] = #"c:\install\installer.msi";
//source file must be on the remote machine
// Execute the method and obtain the return values.
ManagementBaseObject outParams = classInstance.InvokeMethod("Install", inParams, null);
// List outParams
string retVal = outParams["ReturnValue"].ToString();
string msg = null;
switch (retVal)
{
case "0":
msg = "The installation completed successfully.";
break;
case "2":
msg = "The system cannot find the specified file. \n\r\n\r" + msi;
break;
case "3":
msg = "The system cannot find the path specified. \n\r\n\r" + msi;
break;
case "1619":
msg = "This installation package \n\r\n\r " + msi + "\n\r\n\rcould not be opened, please verify that it is accessible.";
break;
case "1620":
msg = "This installation package \n\r\n\r " + msi + "\n\r\n\rcould not be opened, please verify that it is a valid MSI package.";
break;
default:
msg = "Please see... \n\r\n\r http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/error_codes.asp \n\r\n\rError code: " + retVal;
break;
}
// Display outParams
Console.WriteLine(msg, "Installation report");
}
catch (ManagementException me)
{
Console.WriteLine(me.Message + "Management Exception");
}
catch (COMException ioe)
{
Console.WriteLine(ioe.Message, "COM Exception");
}
}
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);
}
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.
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"