I'm redesigning one of my programs which performs certain actions on processes of interest (known as "Monitored Processes" in my program).
Some actions I always need to do on those processes are:
- Open process handle
- Get process name
- Close process handle
And some further actions I may need to do on some or all of them are:
- Suspend process
- Resume process
- Set process priority
- Set process affinity
Previously I simply had a "handler" class that opened a handle to the process, checked its name, and performed the requested actions. All of that is done via p/invoke, I don't use .NET's Process
class at all.
However I am trying to redesign it from a SOLID perspective, so I created a "MonitoredProcess" object and have added a number of methods to it to perform the actions.
Here it is in its current state:
public class MonitoredProcess : IMonitoredProcess
{
private readonly IMonitoredProcessConfig config;
private readonly int processId;
private IntPtr handle = IntPtr.Zero;
private uint suspendResumeResult;
private string processName;
public IMonitoredProcessConfig Config { get { return config; } }
public int ProcessId { get { return processId; } }
public uint SuspendResumeResult { get { return suspendResumeResult; } }
public string ProcessName
{
get
{
if (processName == null) PopulateProcessName();
return processName;
}
}
public MonitoredProcess(IMonitoredProcessConfig config, int processId)
{
if (config == null) throw new ArgumentNullException("config");
this.config = config;
this.processId = processId;
}
public bool OpenProcess()
{
var alreadyOpen = handle != IntPtr.Zero;
if (alreadyOpen) return true;
handle = Kernel32.OpenProcess(config.RequiredRights, false, (uint)processId);
return handle != IntPtr.Zero;
}
public bool CloseProcess()
{
var closed = Kernel32.CloseHandle(handle);
if (closed) handle = IntPtr.Zero;
return closed;
}
public uint Resume()
{
suspendResumeResult = Ntdll.NtResumeProcess(handle);
return suspendResumeResult;
}
public uint Suspend()
{
suspendResumeResult = Ntdll.NtSuspendProcess(handle);
return suspendResumeResult;
}
private void PopulateProcessName()
{
var buffer = new StringBuilder((int)Kernel32.GeneralConstants.MAX_PATH);
var success = 0 != Psapi.GetModuleFileNameEx(
handle,
IntPtr.Zero,
buffer,
(uint)buffer.Capacity
);
processName = success ? Path.GetFileName(buffer.ToString()) : string.Empty;
}
}
I haven't yet added methods to set the process's priority and affinity. The interface it implements is not yet really defined, it will become whatever the class eventually exposes.
Question:
Is this class doing too much? Should a "process" know how to suspend/resume itself, and set its own priority and CPU affinity? Or should I have other classes that take an IMonitoredProcess
and perform these actions on them (which is how I did it before)?
In the way I did it before, the classes performing the actions implemented them through p/invoke, so they "knew" they could throw and catch Win32Exception
s to get the error message. If this MonitoredProcess
class implements everything via p/invoke internally, the class calling those methods technically shouldn't know how they're implemented, so has no reason to either throw or catch Win32Exception
s.
And I wouldn't want to simply throw them from this class (even wrapped in a domain-specific exception) because in half the places I call the methods I'm not interested in the outcome, but I'd still have to try/catch it every time - and it creates objects unnecessarily.
Which again makes me wonder if this class should really be doing all these actions, ostensibly on itself, but it uses external functions to perform all of them.
SafeHandle
instead of aIntPtr
.