My user is in the Administrators group. I am using the .NET WMI API (System.Management) to kill a process using this code:
var scope = new ManagementScope("\root\cimv2");
scope.Connect();
var query = new ObjectQuery(
string.Format("Select * from Win32_Process Where ProcessID = {0}",
processId));
var searcher = new ManagementObjectSearcher(scope, query);
var coll = searcher.Get().GetEnumerator();
coll.MoveNext();
var mgmtObj = (ManagementObject)coll.Current;
var ret = (uint) mgmtObj.InvokeMethod("Terminate");
// ret == 2 here, meaning "Access Denied"
It's failing to kill the process and returning a 2 (Access Denied). However if I use:
Process.Start("cmd", string.Format("/c \"taskkill /f /pid {0}\"", processId));
the process gets killed. (but if I leave out /f it fails).
Is there any way to terminate the process using WMI?
EDIT: I found the following on
http://msdn.microsoft.com/en-us/library/windows/desktop/aa393907(v=vs.85).aspx:
To terminate a process that you do not own, enable the
SeDebugPrivilege privilege.
The page provides VBScript code but how would I do this in C#?
That is only possible with some API calls which are only available via pinvoke - for complete C# source code see here.
Related
From C# (.NET 4.0), we're making a WMI call like this:
var connectOptions = new ConnectionOptions
{
Authority = "ntlmdomain:" + paramValues.Domain,
Username = paramValues.UserName,
Password = paramValues.Password
};
var scopeString = #"\\" + paramValues.Server + #"\root\cimv2";
var scope = new ManagementScope(scopeString, connectOptions);
scope.Connect();
var queryString = String.Format("SELECT * FROM Win32_NTLogEvent WHERE LogFile = 'Security' AND TimeGenerated > '{0:yyyyMMddHHmm00.000000+0000}'";,
paramValues.StartTime);
var query = new ObjectQuery(queryString);
var searcher = new ManagementObjectSearcher(scope, query);
var queryCollection = searcher.Get();
This works fine, with the exception of a failure against only one of our servers. In that case, the searcher.Get() call hangs. Looking at the network traffic through Wireshark reveals that the Event Log entries are being correctly returned, but at some point the remote server simply terminates the TCP connection.
I found a WMI Tester utility online (http://www.paessler.com/tools/wmitester) that appears not to be written against the .NET framework (using DCOM). I can supply the same credentials and the same WMI query using that tool and get back the expected results.
Am I right to suspect that something is different between the .NET code and the DCOM call, or am I chasing the wrong thing? So far this is the only difference between the working and non-working code that I can find.
I have to create an executable which checks if a certain process is running for a certain user (a service account) on a remote machine, the input parameters are 3 strings, machine name, user name and process name.
I have the idea to do this using either System.Diagnostics or WMI, just wanted to double check if anybody has another idea like powershell or even a window functionality which could make the task even easier.
since we want to make sure that process is always running on a dedicated server we will configure a scheduled task to execute a small console application which does this check. Not sure if coding it in C# is the best option or am I ignoring a builtin feature of windows server? Thanks!
I'm pretty sure you can accomplish this with tasklist cmd: tasklist /S \\<server> /V > tasklist.txt. this will give you a file you can grep through.
namespace not referenced
using System.Management;
I have ended up by implementing following solution in C#
this retrieves the username without domain name of the user running processName on machineName
public static string GetProcessOwner()
{
try
{
var resultUserName = string.Empty;
ConnectionOptions opt = new ConnectionOptions();
string path = string.Format(#"\\{0}\root\cimv2", machineName);
ManagementScope scope = new ManagementScope(path, opt);
scope.Connect();
var query = new ObjectQuery(string.Format("Select * From Win32_Process Where Name = '{0}'", processName));
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
var processList = searcher.Get();
foreach (ManagementObject obj in processList)
{
string[] argList = new string[] { string.Empty, string.Empty };
int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
if (returnVal == 0)
{
// return DOMAIN\user
//return argList[1] + "\\" + argList[0];
resultUserName = argList[0];
}
}
return resultUserName;
}
catch (Exception exc)
{
Debug.WriteLine(exc.Message);
return string.Empty;
}
}
GetOwner can return empty array for remote comp, so it may not work
I am trying to create an application that pulls process information and puts it into a SQL table.
I am pulling Process Name, PID, CPU Time, Memory Usage, Page, and Handles.
Everything works fine except for the CPU Time. Here is the code I am using to get the CPU information:
Process[] processes = Process.GetProcesses(machineName);
foreach (Process p in processes)
{
var cpuTime = p.TotalProcessorTime;
}
However I am getting this error:
Feature is not supported for remote machines.
Does anyone have any other way I can get this information and still be able to add it to my SQL table?
How about using WMI ?
string computerName="MyPc";
System.Management.ManagementScope ms = new System.Management.ManagementScope(#"\\" + computerName + #"\root\cimv2");
System.Management.SelectQuery sq = new System.Management.SelectQuery("SELECT * FROM Win32_Process");
System.Management.ManagementObjectSearcher mos = new System.Management.ManagementObjectSearcher(ms,sq);
foreach (System.Management.ManagementObject mo in mos.Get())
{
Console.WriteLine(mo["Name"].ToString());
}
Console.Read();
If I run this from my command prompt it works fine.
psexec \ServerName cscript.exe iisapp.vbs /a AppName /r
I'm trying to do the same thing with C# console app. I'm using the below code but most of the time the application hangs and doesn't complete, and the few times it does it throws an error code. Am I doing this wrong? Does anyone know where I can look up the error or error code?
static void RecycleAppPool(string sServer)
{
Console.Clear();
ProcessStartInfo p = new ProcessStartInfo("psexec.exe", "\\\\" + sServer + " cscript.exe iisapp.vbs /a <AppName> /r");
p.RedirectStandardInput = true;
p.UseShellExecute = false;
Process.Start(p);
}
When it completes with an error, looks like this
"cscript.exe exited with error code -2147024664"
EDIT
Below code working well
static void RecycleAppPool(string sServer)
{
Console.Clear();
ProcessStartInfo p = new ProcessStartInfo("psexec.exe");
p.Arguments = #"\\" + sServer + #" cscript.exe iisapp.vbs /a AppName /r";
p.UseShellExecute = false;
Process.Start(p);
}
VS2003/8/10: Tools->Error Lookup. Paste in the error code in hex. 800700E8. It's "The pipe is being closed." Not very helpful - some issue with redirection i guess.
Do you really have in the ProcessStartInfo parameter, or is that being used to replace what your actual app name is?
Have you tried recycling using appcmd instead of iisapp.vbs?
And, in this thread they recycled a remote application pool using WMI.
If it's IIS7 then you can you the web admin namespace from C#:
using System;
using System.Xml.Serialization;
using Microsoft.Web.Administration;
using System.Linq;
using System.Runtime.InteropServices;
///...
var serverManager = ServerManager.OpenRemote(#"\\myiisserver");
var appPool = serverManager.ApplicationPools["my app pool name"];
appPool.Recycle();
You can learn more about the Web Admin Namespace here. So far it has worked very well for us. BUT must be installed on the client and remote machines.
I struggled with this a lot for the last 2 days trying every solution I found online. I'm trying to recycle an application pool on remote machines on a different domain. The first method I tried with PsExec returned error 3. I tried DirectoryEntry and failed on permissions as well and then tried using ServerManager but the same issue.
Finally, I moved to WMI and it worked:
public static void RecycleIis4(string user, string password, string serverName = "LOCALHOST", string appPoolName = "DefaultAppPool")
{
var processToRun = new[] { #"c:\Windows\system32\inetsrv\appcmd recycle APPPOOL " + appPoolName };
var connection = new ConnectionOptions { Username = user, Password = password };
var wmiScope = new ManagementScope(string.Format(#"\\{0}\root\cimv2", serverName), connection);
var wmiProcess = new ManagementClass(wmiScope, new ManagementPath("Win32_Process"), new ObjectGetOptions());
wmiProcess.InvokeMethod("Create", processToRun);
}
Hope this helps.
I'm using WMI to start a process on a remote machine. The call to create the process returns immediately and I also get the id of the process on the remote machine.
I would like to wait for the remote process to be completed. One option would be to poll whether a process on the remote machine with the given id still exists.
However, I was wondering whether there is a better way to achieve this, maybe using native WinAPI functions?
Just for additional information, this is the code that I am currently using to start the remote process:
ConnectionOptions connOptions = new ConnectionOptions();
connOptions.Impersonation = ImpersonationLevel.Impersonate;
connOptions.EnablePrivileges = true;
connOptions.Username = domainUserName;
connOptions.Password = password;
ManagementScope manScope = new ManagementScope(String.Format(#"\\{0}\ROOT\CIMV2", host), connOptions);
manScope.Connect();
ObjectGetOptions objectGetOptions = new ObjectGetOptions();
ManagementPath managementPath = new ManagementPath("Win32_Process");
ManagementClass processClass = new ManagementClass(manScope, managementPath, objectGetOptions);
ManagementBaseObject inParams = processClass.GetMethodParameters("Create");
inParams["CommandLine"] = commandLine;
ManagementBaseObject outParams = processClass.InvokeMethod("Create", inParams, null);
I don't know how effective this can be, you can use ManagementEventWatcher to watch a query.
Here is something I found on the net.
WqlEventQuery wQuery =
new WqlEventQuery("Select * From __InstanceDeletionEvent Within 1 Where TargetInstance ISA 'Win32_Process'");
using (ManagementEventWatcher wWatcher = new ManagementEventWatcher(scope, wQuery))
{
bool stopped = false;
while (stopped == false)
{
using (ManagementBaseObject MBOobj = wWatcher.WaitForNextEvent())
{
if (((ManagementBaseObject)MBOobj["TargetInstance"])["ProcessID"].ToString() == ProcID)
{
// the process has stopped
stopped = true;
}
}
}
wWatcher.Stop();
}
The native Win32 way of achieving this would be to perform a WaitForSingleObject() on the process handle returned by CreateProcess(), however I don't think this handle is made available to you from WMI.
This article offers another option you could consider - instead of polling the process list and waiting for your process to disappear, it repeatedly queries for process deletion events matching your process ID:
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")
objWMIService.Create "notepad.exe", null, null, intProcessID
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colMonitoredProcesses = objWMIService.ExecNotificationQuery _
("Select * From __InstanceDeletionEvent Within 1 Where TargetInstance ISA 'Win32_Process'")
Do Until i = 1
Set objLatestProcess = colMonitoredProcesses.NextEvent
If objLatestProcess.TargetInstance.ProcessID = intProcessID Then
i = 1
End If
Loop
You could also improve on this by using a ManagementEventWatcher object and its WaitForNextEvent method to avoid having to poll for the deletion events.
If the process on the remote machine is your code then you could open up a socket on the calling machine and let the remote machine 'ping' it when it has finished.
If you want to use this method for any remote process you could have a helper app/service on the remote computer that monitors your process and returns the completed ping.
I havent had chance to check this yet,
int pid = (int)managementBaseObject["processId"];
Process remPrc = Process.GetProcessById(pid, RemoteMachine);
remPrc.WaitForExit();