I am trying to execute a process in a remote server from an ASP .Net web application (Note that the web application resides on the same server as the processes). The code I am running is the following:
ConnectionOptions conO = new ConnectionOptions();
// conO.Username = txtUser.Text;
// conO.Password = txtPassword.Text;
ManagementPath path = new ManagementPath(#"\\" + txtRemoteComputer.Text + #"\root\cimv2");
System.Management.ManagementScope oMs = new System.Management.ManagementScope(path, conO);
oMs.Connect();
ObjectGetOptions opt = new ObjectGetOptions();
ManagementClass classInstance = new ManagementClass(oMs, path, opt);
ManagementBaseObject inParams = classInstance.GetMethodParameters("Create");
inParams["CommandLine"] = txtPath.Text;
ManagementBaseObject outParams = classInstance.InvokeMethod("Create", inParams, null);
lblInfo.Text = outParams["returnValue"].ToString() + " Process ID: {0}" + outParams["processId"].ToString();
However, I get the following error:
Operation is not valid due to the current state of the object.
Does anyone have a better solution for this problem?
Btw, I have the following scenario:
My client runs a server, in which he has some applications that do many different things (logging, calculations ...). He wants to be able to monitor and if needed to restart these applications from a web based client (even his mobile phone).
What I am trying to accomplish using this application is to kill a process and start the same one. Using System.Diagnostics I was able to query the processes that are currently running, but that obviously is not a solution, since for remote machines System.Diagnostics can only see processes, and not interact with them.
When you deal with WMI and get the error that you listed, generally it means that there was no data to return from your query. Looking at your code, it seems that you never make the call to where the process data is stored. You need to have a call to Win32_Process somewhere in your code. The code to get info on processes goes like this:
ManagementPath path = new ManagementPath(#"\\" + txtRemoteComputer.Text + #"\root\cimv2");
System.Management.ManagementScope oMs = new System.Management.ManagementScope(path, conO);
oMs.Connect();
ObjectQuery query = new ObjectQuery(
"SELECT * FROM Win32_Process");
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(oMs, query);
foreach (ManagementObject queryObj in searcher.Get())
{
After that you do whatever with the code. Please note that this code is only to get the process, I have no experience with sending calls to end a process remotely, but at least with this you can start to check if the remote computer is evening running a certain process.
Related
I'm coding a windows service and in one point I need to know if there is an active interactive session.
I tried using OnSessionChange() and keep in a variable the last SessionChangeReason. When I reach to the mentioned point I compare if it's equal to SessionChangeReason.SessionLogOn. This works with the inconvenient that the service has a delayed start so if the user logs on before the service starts running this information is lost.
I have also seen the System.Environment.Interactive property but as I understand this refers to the process of the current service which is not interactive, so it wouldn't give me the information I need (I might misunderstood this, though).
Is there a way to get this info 'on demand' without having to keep register of the SessionChangeReason?
Edit: Maybe I wasn't clear about this point. Aside from knowing that there is an interactive session I also need to know that it isn't locked.
P/Invoke WTSEnumerateSessions to see if there are additional sessions and what their connected states are. You obviously have to ignore session 0 on Vista+.
You should only do this when your service is started, the session change notification should be used to detect further changes.
Finally I've resigned to knowing specifically that there is a session and isn't locked so we'll work with whether there is an active session or not.
If only knowing there is an active session works for you and you don't want to use pInvoke you can either:
a) Search for the explorer process
Process[] ps = Process.GetProcessesByName("explorer");
bool explorerActive = (ps.Length > 0);
b) Use the following WMI query to get the UserName of the active session:
using System.Management;
ConnectionOptions oConn = new ConnectionOptions();
ManagementScope oMs = new ManagementScope("\\\\localhost", oConn);
ObjectQuery oQuery = new ObjectQuery("select * from Win32_ComputerSystem");
ManagementObjectSearcher oSearcher = new ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection = oSearcher.Get();
foreach (ManagementObject oReturn in oReturnCollection)
{
if (oReturn["UserName"] == null)
{
// No active session
Console.Write("UserName: null");
}
else
{
// Active session
Console.Write("UserName: " + oReturn["UserName"].ToString());
}
}
If you want to use pInvoke see Anders' answer.
I'm developing a server C# application which executes a .exe file through a local domain network on different client computers.
I've chosen to do it via WMI and works fine when the .exe path is local to the remote machine. Searching over other threads here and other forums I've noticed that WMI does not support UNC paths (here comes my problem).
When I call the method below to execute a .exe placed on the remote pc desktop, it just works fine:
var execResult = WmiExecuteRemoteProcess("XPSP3", #"C:\Documents and Settings\user1\Desktop\My_Setup.exe", #"domain\user", "mypass");
Now, when I try to use UNC paths, I get the exit code 2:
var execResult = WmiExecuteRemoteProcess("XPSP3", #"\\server\shared\My_Setup.exe", #"domain\user", "mypass");
The WmiExecuteRemoteProcess method looks like this:
public bool WmiExecuteRemoteProcess(string remoteComputerName, string arguments, string pUser, string pPassword)
{
try
{
ConnectionOptions connOptions = new ConnectionOptions();
connOptions.Username = pUser;
connOptions.Password = pPassword;
connOptions.Impersonation = ImpersonationLevel.Impersonate;
connOptions.EnablePrivileges = true;
ManagementScope manScope = new ManagementScope(string.Format(#"\\{0}\ROOT\CIMV2", remoteComputerName), connOptions);
manScope.Connect();
ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementPath managementPath = new ManagementPath("Win32_Process");
using (ManagementClass processClass = new ManagementClass(manScope, managementPath, objectGetOptions))
{
using (ManagementBaseObject inParams = processClass.GetMethodParameters("Create"))
{
inParams["CommandLine"] = arguments;
using (ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null))
{
return (uint)outParams["returnValue"] == 0;
}
}
}
}
catch (Exception ex)
{
Log.Error(ex.Message);
return false;
}
}
Given this situation I've decided kind of "cheat" it by parsing the arguments parameter as follows:
var args = "cmd.exe /c \"pushd \"\"\\\\server\\shared\"\" && My_Setup.exe && popd\"";
var execResult = WmiExecuteRemoteProcess("XPSP3",args,#"domain\user", "mypass");
What I try to do here is to use the cmd.exe with the commands pushd and popd to map the UNC path into a network drive-based path like "Z:\shared". This way both WMI and cmd.exe don't have to deal with the UNC path.
Result: again, if the .exe is local to the remote machine, it works very well, but when using a UNC path, only the cmd.exe process appears. Maybe it's internally throwing the exit code 2 again, but I'm not able to catch it, even redirecting the output of the cmd execution to a log file.
Perhaps someone experienced in this kind of mechanics can throw some light on this. I'd prefer not to develop an entire service only for this, or to use PsExec (maybe this one as a last resort).
Please let me know if I'm missing any info. Any comments will be much appreciated.
Regards.
Edit: I checked and it's not a matter of permissions to the shared folder or file.
In case anyone faces this or other related issue, this is how I solved it:
The issue is not that WMI cannot deal with UNC paths, but WMI operations are not allowed to access network resources due to security restrictions in Windows. It doesn't matter if you map the paths, it's just not authorized. In this particular case the workaround I ended up with, was to copy the setup.exe to a temporary folder in the remote machine, and finally execute it through WMI by accessing its local path just like I was doing before.
var execResult = WmiExecuteRemoteProcess("XPSP3", #"C:\temp_folder\My_Setup.exe", #"domain\user", "mypass");
How can I start a process on a remote computer in c#, say computer name = "someComputer", using System.Diagnostics.Process class?
I created a small console app on that remote computer that just writes "Hello world" to a txt file, and I would like to call it remotely.
Console app path: c:\MyAppFolder\MyApp.exe
Currently I have this:
ProcessStartInfo startInfo = new ProcessStartInfo(string.Format(#"\\{0}\{1}", someComputer, somePath);
startInfo.UserName = "MyUserName";
SecureString sec = new SecureString();
string pwd = "MyPassword";
foreach (char item in pwd)
{
sec.AppendChar(item);
}
sec.MakeReadOnly();
startInfo.Password = sec;
startInfo.UseShellExecute = false;
Process.Start(startInfo);
I keep getting "Network path was not found".
Can can use PsExec from http://technet.microsoft.com/en-us/sysinternals/bb897553.aspx
Or WMI:
object theProcessToRun() = { "YourFileHere" };
ManagementClass theClass = new ManagementClass(#"\\server\root\cimv2:Win32_Process");
theClass.InvokeMethod("Create", theProcessToRun);
Use one of the following:
(EDIT) Remote Powershell
WMI (see Ivan G's answer)
Task Scheduler API (http://msdn.microsoft.com/en-us/library/windows/desktop/aa383606%28v=vs.85%29.aspx)
PsExec
WshRemote object with a dummy script. Chances are, it works via DCOM, activating some of scripting objects remotely.
Or if you feel like it, inject your own service or COM component. That would be very close to what PsExec does.
Of all these methods, I prefer task scheduler. The cleanest API of them all, I think. Connect to the remote task scheduler, create a new task for the executable, run it. Note: the executable name should be local to that machine. Not \servername\path\file.exe, but c:\path\file.exe. Delete the task if you feel like it.
All those methods require that you have administrative access to the target machine.
ProcessStartInfo is not capable of launching remote processes.
According to MSDN, a Process object only allows access to remote processes not the ability to start or stop remote processes. So to answer your question with respect to using this class, you can't.
An example with WMI and other credentials as the current process, on default it used the same user as the process runs.
var hostname = "server"; //hostname or a IpAddress
var connection = new ConnectionOptions();
//The '.\' is for a local user on the remote machine
//Or 'mydomain\user' for a domain user
connection.Username = #".\Administrator";
connection.Password = "passwordOfAdministrator";
object[] theProcessToRun = { "YourFileHere" }; //for example notepad.exe
var wmiScope = new ManagementScope($#"\\{hostname}\root\cimv2", connection);
wmiScope.Connect();
using (var managementClass = new ManagementClass(wmiScope, new ManagementPath("Win32_Process"), new ObjectGetOptions()))
{
managementClass.InvokeMethod("Create", theProcessToRun);
}
I don't believe you can start a process through a UNC path directly; that is, if System.Process uses the windows comspec to launch the application... how about you test this theory by mapping a drive to "\someComputer\somePath", then changing your creation of the ProcessStartInfo to that? If it works that way, then you may want to consider temporarily mapping a drive programmatically, launch your app, then remove the mapping (much like pushd/popd works from a command window).
I'm trying to get the FreeSpace from the D drive of a remote computer.
Towards the end, I'm getting a ManagementException was unhandled by user code "Not Found"
This is the line that gives me the error: fs = m["FreeSpace"].ToString();
Here's my code:
ConnectionOptions oConn = new ConnectionOptions();
oConn.Username = "username";
oConn.Password = "password";
oConn.Authority = "ntlmdomain:XXX";
ManagementScope scope = new ManagementScope("\\\\Remote_Computer\\root\\CIMV2", oConn);
scope.Connect();
ObjectQuery query = new ObjectQuery("SELECT DeviceID, VolumeName FROM Win32_LogicalDisk where DeviceID = 'D:'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach (ManagementObject m in queryCollection)
{
//error happens here
fs = m["FreeSpace"].ToString();
freeSpace = Convert.ToInt64(fs);
}
I found out what the issue was.
My query was wrong. I replaced it with:
"SELECT FreeSpace FROM Win32_LogicalDisk where DeviceID = 'D:'"
And problem solved.
I believe that this post should have an answer for you!!
Basically, it boils down to one of two methods:
Either you import GetDiskFreeSpaceEx and use it on the path to the drive, or you use WMI on a network drive that you connect beforehand.
So you need to have network access to this drive.
If you're trying to monitor a remote system, you could easily create a small app/service that will run on that machine and continually collect the needed information, then provide it to the monitoring application using something like WCF or even direct connection if you want.
Let me know if this was helpful,
Max
EDIT:
Actually, I misunderstood your question. I thought you were looking for a way to connect in the first place. I'll leave this answer here, though, so that anyone finding this through a search, would maybe find it useful.
I'm creating a watch dog service that will be monitoring other services on various remote servers (all in the same domain). The user that I'm using to connect to the remote servers is not an admin. When I try to enumerate the services in the Win32_Service class, I get an access denied error.
I've given the user 'Remote Enable' & 'Enable Account' persmissions to the Root\CIMV2 namespace in the WMI Control.
I am able to connect to the server with the following code. The object ServiceListItem is just a simple class that contains the server name and the service name:
SecureString secureString = new SecureString();
foreach ( char c in "password" )
{
secureString.AppendChar( c );
}
ConnectionOptions connectionOptions = new ConnectionOptions();
connectionOptions.Username = "domain\\user";
connectionOptions.SecurePassword = secureString;
foreach ( ServiceListItem service in _serviceList )
{
ManagementScope managementScope = new ManagementScope();
managementScope = new ManagementScope( String.Format( #"\\{0}\root\cimv2", service.ServerName ), connectionOptions );
managementScope.Connect();
//RelatedObjectQuery relatedObjectQuery = new RelatedObjectQuery( String.Format( "Win32_Service.Name='{0}'", service.ServiceName ) );
//ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher( managementScope, relatedObjectQuery );
ObjectQuery objectQuery = new ObjectQuery( "SELECT * FROM Win32_Service WHERE Name = '" + service.ServiceName + "'" );
ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher( managementScope, objectQuery );
ManagementObjectCollection objectCollection = objectSearcher.Get();
foreach ( ManagementObject managementObject in objectCollection )
{
serviceStatus = managementObject.Properties["State"].Value.ToString();
Debug.Print(service.ServiceName + " - " + serviceStatus);
//break;
}
}
The managementScope.Connect() runs fine, which means the wmi security on cimv2 is set up correctly. However, when I try to enumerate the objectCollection, I get the 'Access Denied' exception. This tells me (I think) that the user doesn't have permissions to enumerate the Win32_Service class (SC_MANAGER_ENUMERATE_SERVICE).
I just haven't been able to find any good examples on how to enable that permission for a remote user. I'm not very experienced when it comes to coding with Windows api's, so please be as detailed as possible in your answers :)
Trying to find the same answer myself today, I've been doing a lot of googling. After a good half hour of incantations, I found this MSDN article (907460) which uses sc sdet. It seems to work so far, even though the security descriptor is for Windows Server 2003. I've found you can do sc sdshow SCMANAGER to get the current value so when back in the office tomorrow I'll be comparing an contrasting to make sure I've not locked something out I shouldn't have :-)
For completeness, the notes in KB907460 (in case it moves/goes away):
Symptoms: After you install Microsoft Windows Server 2003 Service Pack 1 (SP1), non-administrators cannot remotely access the Service Control Manager.
Cause: Windows Server 2003 SP1 changes the Service Control Manager default security settings.
Resolution:
To resolve this issue, use version 5.2.3790.1830 of the Sc.exe tool.
This tool is located in the %windir%\System32 folder. To do this,
follow these steps:
Click Start, click Run, type cmd, and then click OK.
Type the following command at the command prompt, and then press ENTER:
sc sdset SCMANAGER D:(A;;CCLCRPRC;;;AU)(A;;CCLCRPWPRC;;;SY)(A;;KA;;;BA)S:(AU;FA;KA;;;WD)(AU;OIIOFA;GA;;;WD)
I found myself stuck into a similar problem. In my case it had nothing to do with permissions, which I did set by following this link: http://www.poweradmin.com/help/enableWMI.aspx
So, After hours of wondering lost I found this article that tells how UAC interfere with your set of permissions and how can you fix that:
http://www.solarwinds.com/documentation/apm/docs/APMWMITroubleshooting.pdf
In my case, the registry key didn't existed, so I created it.
Hope this helps also, cheers!