UIInspector, dynamic memory allocation and pointer arithmetic

Hi community,

I came back from Melbourne a couple of days ago, to attend and present at DDD Sydney, however, the previous night it was raining heavily and I had to stay at the hotel; so I began to write another demo for my presso and I wrote the  UIInspector, a class that allows to extract information from the children elements within a window  (for instance, a textbox or label). The code snippet is shown below 

   1: public static List<UIElement> GetUIElements(string pidOrImageName) {

   2:             int testPid = 0;

   3:             bool hasData = false;

   4:             string xmlAsString = string.Empty;

   5:             List<UIElement> retval = new List<UIElement>();

   6:             IntPtr uiXml = Marshal.AllocHGlobal(XML_ALLOCATED_BYTES);

   7:             GCHandle pinnedPtr = GCHandle.Alloc(uiXml, GCHandleType.Pinned);

   8:             EnumChildProc enumCallback = new EnumChildProc(EnumChildWindowProcedure);

   9:             GCHandle safeFunctor = GCHandle.Alloc(enumCallback);

  10:             WriteMemory(XML_START, uiXml);

  11:  

  12:             if (int.TryParse(pidOrImageName, out testPid) &&

  13:                 Process.GetProcesses().AsQueryable().Where(x => x.Id.Equals(testPid)).Count() > 0) {

  14:                 using (Process targetProcess = Process.GetProcessById(testPid))

  15:                     EnumChildWindows(targetProcess.MainWindowHandle, (EnumChildProc)safeFunctor.Target, uiXml.ToInt32());

  16:                 hasData = true;

  17:             } else {

  18:                 if (!string.IsNullOrEmpty(pidOrImageName)) {

  19:                     var targetProcess = Process.GetProcessesByName(pidOrImageName).DefaultIfEmpty();

  20:  

  21:                     if (targetProcess.Any()) {

  22:                         EnumChildWindows(targetProcess.First().MainWindowHandle, (EnumChildProc)safeFunctor.Target, uiXml.ToInt32());

  23:                         hasData = true;

  24:                     }

  25:                 }

  26:             }

  27:  

  28:             xmlAsString = Marshal.PtrToStringAnsi(uiXml);

  29:             pinnedPtr.Free();

  30:             safeFunctor.Free();

  31:             Marshal.FreeHGlobal(uiXml);

  32:  

  33:             if (!string.IsNullOrEmpty(xmlAsString) && hasData)

  34:                 retval = ConvertToList(xmlAsString);

  35:  

  36:             return retval;

  37:         }

The aforementioned method, expects to receive a string with a numerical representation (Process Identifier) as well as the name of the process to be inspected. The method returns a List<UIElement> (Struct containing information about the child elements found) which can be filtered through a LINQ query.

The first point of interest in this method is Marshal.AllocHGlobal, which at the same time invokes the VirtualAlloc function, in order to dynamically allocate “these many bytes” in memory (buffer). This is required to avoid  incurring in a potential “C0000005 Exception – Access Violation”, then we have to make use of the GCHandle struct to prevent the GC from moving that memory address, and we do the same with the delegate responsible for enumerating the children elements.

It is well-known that a pointer is just a memory address therefore it’s a number; so it’s not hard to manipulate this memory through pointer arithmetic as shown below:

  • (a_pointer + (a_offset * sizeof(*apointer)))

That formula allows us to write in the following memory address (block) given by a pointer, in “theory” we don’t have pointers in C#, however, there are times where a direct memory mainpulation is required as depicted below

   1: private static void WriteMemory(string stringToWrite, IntPtr memoryAddress) {

   2:     int offset = 0;

   3:  

   4:     if (!string.IsNullOrEmpty(stringToWrite)) {

   5:         stringToWrite.AsEnumerable().ToList().ForEach(x => {

   6:             Marshal.WriteByte(new IntPtr(memoryAddress.ToInt64() + (offset * Marshal.SizeOf(typeof(byte)))), (byte)x);

   7:             offset += 1;

   8:         });

   9:     }

  10: }

The method responsible for extracting information from the children elements is shown below, where a WM_GETTEXT message is sent to the child element based on its hWnd, and its contents is then copied into a buffer that is translated into an Unicode through the Marshal.PtrToStringUni method

   1: private static string GetControlOrWindowText(IntPtr hWnd) {

   2:     string retval = string.Empty;

   3:     IntPtr buffer = Marshal.AllocHGlobal(MAX_TEXT_SIZE);

   4:     SendMessage(hWnd, WM_GETTEXT, new IntPtr(MAX_TEXT_SIZE), buffer);

   5:     retval = Marshal.PtrToStringUni(buffer);

   6:     Marshal.FreeHGlobal(buffer);

   7:  

   8:     return retval;

   9: }

The source code can be fully downloaded from here

Regards,

Angel

Leave a Reply

Your email address will not be published. Required fields are marked *