Wednesday, September 30, 2009

Screen Scraping with C#

Something I have been messing around with lately is the ability to read other applications controls... or screen scraping. In general, screen scraping is a bad solution, using shared memory or some other IPC is much cleaner. However, there are times when screen scraping is the only practical solution (generally if the target application is closed source and the developers don't want to export the data).

So, how would we go about reading some values from another application? Windows actually makes it fair easy. As you may have seen in an earlier post, we have to use some Win32 API functions, so lets get the DllImport stuff out of the way first:


[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, int wMsg, int wParam, StringBuilder lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetWindowText(int hWnd, StringBuilder lpString, int length);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern int GetWindowTextLength(int hWnd);
[DllImport("user32.dll")]
static extern bool EnumChildWindows(IntPtr hWndParent, WindowEnumDelegate lpEnumFunc, int lParam);
[DllImport("user32.dll")]
static extern IntPtr FindWindow(StringBuilder lpClassName, StringBuilder lpWindowName);
[DllImport("user32.dll")]
static extern uint RealGetWindowClass(IntPtr hwnd, StringBuilder pszType, uint cchType);

const int WM_GETTEXT = 13;
const int WM_GETTEXTLENGTH = 14;


Okay, so if we want to find a particular window, we can use FindWindow(), like this:

StringBuilder name = new StringBuilder("Notepad");

hWnd = FindWindow(null, name);


Easy! we now have a handle for the window (or null if we couldn't find it). Now its a simple matter of finding all the child handles and getting their window text!


public delegate bool WindowEnumDelegate(IntPtr hwnd, int lParam);

private bool WindowEnumProc(IntPtr handle, int lParam)
{
int textLen;
StringBuilder text;
StringBuilder className = new StringBuilder(1024);
RealGetWindowClass(handle, className, 1024);

textLen = SendMessage(handle, WM_GETTEXTLENGTH, 0, null);
if (textLen != 0)
{
text = new StringBuilder(textLen);
SendMessage(handle, WM_GETTEXT, (textLen + 1), text);
}

...

return true;
}

...

WindowEnumDelegate del = new WindowEnumDelegate(WindowEnumProc);
EnumChildWindows(hWnd, del, 0);


The above function will be called once for each child of the window we found earlier, and it will find the type of object (className), and send a WM_GETTEXT message which will attempt retrieve text from the objects.

And there we have it! Code for a very basic screen scraper. There is, however, some issues with it. Some objects do not respond in useful ways to WM_GETTEXT messages. Some objects have a whole bunch of data contained under a single handle. For example, a TreeView object will return nothing when presented with a WM_GETTEXT message... special handling code is needed for this, and several other objects, but I'll leave that for another day (I have TreeView handling code I will post about later).

Thursday, September 24, 2009

GPU-Z reader.... cause I can!

While I was looking for something, I found the data structure that GPU-Z uses in its shared memory file... and given I was doing this type of thing anyway, I figured why not?

So the following is more or less identical code to the SpeedFan code.. at least, identical in function. There were a number of little gotchas that made it a little harder than I was expecting. The documented structure is here. Kudos to the author for making it available. It is, of course, in C, and we want to access it in C#. So, we need to translate the structure into something that the Marshalling code will understand.

First a couple of constants. Nothing tricky here.

const String SHMEM_NAME="GPUZShMem";
const int MAX_RECORDS = 128;


Next up, the first structure, the 'record':

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public struct GPUZ_RECORD
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string key;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string value;
};

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public struct GPUZ_SENSOR_RECORD
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
public string name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string unit;

public UInt32 digits;
public double value;
};

A few important things to note here. The original type for key, value etc was WCHAR[], which means its a unicode string, not just a normal C string. So, its very important that we specify the CharSet explicitly. We haven't had to do that yet, but in this case, we need it.

Next, the WCHAR array is a fixed length and not just null terminated. So, to ensure we use enough bytes, we specify SizeConst, which indicates the number of array elements (not the number of bytes).

Lastly, we use string here rather than StringBuilder. We have specified the size, so the Marshalling code knows how many bytes to read, and knows how to create a string.

Now the main data structure:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class GPUZ_SH_MEM
{
public UInt32 version; // Version number, 1 for the struct here
public Int32 busy; // Is data being accessed?
public UInt32 lastUpdate; // GetTickCount() of last update
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_RECORDS)]
public GPUZ_RECORD[] data;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_RECORDS)]
public GPUZ_SENSOR_RECORD[] sensors;

public GPUZ_SH_MEM()
{
data = new GPUZ_RECORD[MAX_RECORDS];
sensors = new GPUZ_SENSOR_RECORD[MAX_RECORDS];
}
}

This isn't actually much different. No strings here, so we don't need to specify the CharSet, but once again we need to use SizeConst for the fixed length arrays. We also supply a constructor, to ensure the memory is allocated for the member arrays.

And that, believe it not, was the difficult bit. A failure in PtrToStruct can have some strange affects... presumably you end up overwriting random bits of memory if the sizes are not matched up, so its doubly important to get it right, as it could potentially mean random crashes in your application, without any indication why.

Full code is here. Note that in both this and the SpeedFan code, GetData() just returns the data structure. This isn't exactly the most encapsulated way to do it... but it keeps the example simple.

Wednesday, September 23, 2009

SpeedFanLogger

Today I wrote a bunch of code, but none of it very interesting. Basically I took the code I spoke about yesterday, called it once a second, and updated a form... and also, optionally, wrote the data to a file. In short, I created a program that will log SpeedFan data!

Yes yes, I am well aware that SpeedFan already has a logging capability! But I have bigger plans than just this...

Here is the full code for the SpeedFanReader class. It really doesn't do much... and could be re-factored to return the data a little better than just a 'Data' blob. But it serves its purpose.

Tuesday, September 22, 2009

Reading SpeedFan shared memory with C#

Building on what I spoke about in my previous post, lets say we want to access the data that SpeedFan provides from a C# application. As a small aside, reading information from the SMBus and other low level interfaces can only be done from the kernel. So applications like SpeedFan (HWMonitor, Everest, etc etc) generally run a driver at kernel level and then a front-end GUI to present the information.

In the case of SpeedFan, shared memory (actually its technically a memory mapped file on Windows I think) is used to communicate between the kernel driver and the userspace GUI application. Even better, the format of this file has been made public by the author of SpeedFan. So, enough talk, lets see some code!

First, we are going to need to access some Windows API functions that are not available via .net:

public const int PROCESS_ALL_ACCESS = 0x1F0FFF;
public const int FILE_MAP_READ = 0x0004;

[DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr OpenFileMapping(int dwDesiredAccess,
bool bInheritHandle, StringBuilder lpName);

[DllImport("Kernel32.dll")]
internal static extern IntPtr MapViewOfFile(IntPtr hFileMapping,
int dwDesiredAccess, int dwFileOffsetHigh, int dwFileOffsetLow,
int dwNumberOfBytesToMap);

[DllImport("Kernel32.dll")]
internal static extern bool UnmapViewOfFile(IntPtr map);

[DllImport("kernel32.dll")]
internal static extern bool CloseHandle(IntPtr hObject);


No dramas there.

Now lets do the dirty and actually access the file:


StringBuilder sharedMemFile = new StringBuilder("SFSharedMemory_ALM");
IntPtr handle = OpenFileMapping(FILE_MAP_READ, false, sharedMemFile);
SpeedFanSharedMem sm;
IntPtr mem = MapViewOfFile(handle , FILE_MAP_READ, 0, 0, Marshal.SizeOf((Type)typeof(SpeedFanSharedMem)));
if (mem == IntPtr.Zero)
{
throw new Exception("Unable to read shared memory.");
}

sm = (SpeedFanSharedMem) Marshal.PtrToStructure(mem, typeof(SpeedFanSharedMem));
UnmapViewOfFile(handle);
CloseHandle(handle);


So there is a bit in there, lets break it down. First, we need to know the named of the file that has we are accessing. In this case its "SFSharedMemory_ALM". Next thing
to note is that we have a structure called 'SpeedFanSharedMem' which is specific to the file in question. So not only do you need to know the file name, but you also need to know the structure that resides there. Assuming you do, its pretty straight forward. Don't forget to tidy up once you are done!


The SpeedFan specific structure looks like this:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
private class SpeedFanSharedMem
{
ushort version;
ushort flags;
Int32 size;
Int32 handle;
ushort numTemps;
ushort numFans;
ushort numVolts;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public Int32[] temps;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public Int32[] fans;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
public Int32[] volts;

public SpeedFanSharedMem()
{
temps = new Int32[32];
fans = new Int32[32];
volts = new Int32[32];
}
}


Things to note here are the attribute at the start, where we define the structure layout in memory, and the way we define the arrays with their appropriate attributes.
Nothing too tricky, just ensuring they get marshalled correctly.

That's it! Well nearly. Don't forget you need appropriate permissions to access the file (in this case, normal user permissions should be fine as we are only opening the mapping for reading).

C#, yes really!

Okay, so its been a while since I have blogged, and the main reason for that has simply been that I haven't done any Objective C code lately (let alone iPhone programming). So, at risk of becoming yet another programmer's blog that meanders all over the place, I figured I may as well talk about what I have been doing.

Firstly, its been on Windows, and secondly, in C#. Why C#? Mainly just because. After 10 years of gcc and vim, objective-C introduced me to a reasonable IDE... but IDE's for C/C++ mostly suck. Mainly because C/C++ has some annoying features (from an IDE point of view). Also, for quick prototyping or GUI apps, I want something fast and easy. C# fits those categories. And given what I've been working on, limiting myself to Windows is a non-issue.

So blah blah blah, lets get to some code! I want to write some code that pokes around in another application and summarises the output into a Windows Live Gadget, and, more importantly, log the data to a file. Some applications make this easy (documented shared memory), some make it difficult and we have to rely on hacks. The first thing we need to do however, is work around one of the limitations of C# (oh no! already!). In retrospect this whole project probably would have been easier in C/C++... ah well, where is the fun in that!

C# gives us the DllImport attribute, so we can call C functions. Handy when what you want is in the Windows API and not available through a C# library.
   [DllImport("kernel32.dll")]
static extern bool CloseHandle(IntPtr hObject);

So this is pretty straight forward. We say which DLL we are using, and then specify the function. We need to match C# types, with C types... which is sometimes easy (in the above, hObject is a handle, so just a number. Would be an int in C, here we use IntPtr... although int would work too).

What about passing a C-string? Well we have a couple of options. First is easy:

[DllImport("user32.dll")]
static extern IntPtr FindWindow(StringBuilder lpClassName, StringBuilder lpWindowName);
Pretty straight forward. We want to pass some strings, so we use StringBuilder. A call would look like this:
    StringBuilder name = new StringBuilder("Notepad");
IntPtr hWnd;

hWnd = FindWindow(null, name);

C# figures out how to Marshal the StringBuilder class to a char * and back again. Too easy! But wait, sometimes its not that simple. What if can't use the StringBuilder type? What if we need to allocate some memory from the heap, and do stuff with it? Or if we have a function that returns a pointer to one of several possible types? For example:

[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, IntPtr wMsg, IntPtr wParam, IntPtr lParam);

Hmm. So two issues here. First is, how do we pass something to that type of function?

IntPtr blob = Marshal.AllocHGlobal(blobSize);

There! A blob of memory allocated, which can be passed directly. What if its actually a structure we want to fill in?

IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(structItem)) ;
Marshal.StructureToPtr(structItem, ptr, false);

This step is effectively what happens when we define the function prototype to accept a StringBuilder (or any other type). This marshalling will occur, but knowing this, allows us some flexibility. Of course the other direction works too:

IntPtr ptr = SomeFunctionThatReturnsIntPtr();
StructItem structItem = Marshal.PtrToStructure(ptr, (Type)typeof(StructItem));

Ok, that's all I want to talk about today. There is some more detailed stuff on the way which builds on the above, and is more likely to be useful to someone... but I'd rather try and keep each posts short-ish.