Tuesday, September 22, 2009

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.

No comments:

Post a Comment