Tuesday, July 21, 2009

C# OpenProcess

Certain tasks simply can't be done using only managed code. In order to call a Win32 API (or simply a DLL) using C#, DllImport is used. DllImport resides in the namespace System.Runtime.InteropServices.

If you want to implement the OpenProcess Function in C#. We can see on MSDN that the C++ definition for the function is:
HANDLE WINAPI OpenProcess(
__in  DWORD dwDesiredAccess,
__in  BOOL bInheritHandle,
__in  DWORD dwProcessId
);

In C# thats written:
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(
int dwDesiredAccess,
bool bInheritHandle,
int dwProcessId
);

To not get ahead or myself, this is the code I used to get the handle to the process.  The class Process resides in the namespace System.Diagnostics.
Process[] hProcess = Process.GetProcessesByName("calc");

This line of code returns an array of all the instances of the calculator running on your machine.
IntPtr hReadProcess = OpenProcess(0x0010, true, handle[0].Id);

This line of code opens the first instance of the calculator found, and assigns the handle to hReadProcess. 0x0010 is the process access right specified to open the process with. 0x0010 means PROCESS_VM_READ, and is needed in order to use the ReadProcessMemory function on the process.

On MSDN we can see that if the OpenProcess function succeeds, it return's an open handle. That's of course to be expected.

But if the function fails, it will return null, still not a problem, but we are referred to call GetLastError to get a more specific error message. GetLasError unfortunately doesn't work in C#. Sure, it "works" but the CLR itself could very easily make it's own calls and all of a sudden the last error message isn't what you are after.

In order to fix this problem, all we need to do is add SetLastError=true to our DllImport statement. It should now look like this:
[DllImport("kernel32.dll",SetLastError=true)]
public static extern IntPtr OpenProcess(
int dwDesiredAccess,
bool bInheritHandle,
int dwProcessId
);

Now that this is done, you can call Marshal.GetLastWin32Error, that is found in the System.Runtime.InteropServices, which we need to use anyway because of DllImport.

Now simply use this line of code to get the last error message:
MessageBox.Show(Marshal.GetLastWin32Error().ToString());

No comments: