title: WinApi: Get Control Name of COM Forms
body: <p>I want to replace my current UI automation tool (QTP) with .Net framework. 
  I need to test VB6 (COM) application.</p>
  
  <p>One of the fundamentals of framework is using the form name.
  So far I failed to find a way to get this data using Win API.</p>
  
  <p>There is only one constrain for the solution and its that the solution MUST rely on .Net code - meaning: <strong>no commercials tools allowed</strong>.</p>
  
  <p>Does anyone is familiar with this subject?</p>
  
  <p>These links are my major references:</p>
  
  <ol>
  <li><p><a href="http://msdn.microsoft.com/en-us/library/ms996405(d=printer).aspx" rel="nofollow">http://msdn.microsoft.com/en-us/library/ms996405(d=printer).aspx</a></p></li>
  <li><p><a href="http://blogs.msdn.com/b/brianmcm/archive/2006/01/17/getting-the-winforms-id-of-a-control.aspx" rel="nofollow">http://blogs.msdn.com/b/brianmcm/archive/2006/01/17/getting-the-winforms-id-of-a-control.aspx</a></p></li>
  <li><p><a href="http://blogs.msdn.com/b/brianmcm/archive/2006/01/23/516418.aspx" rel="nofollow">http://blogs.msdn.com/b/brianmcm/archive/2006/01/23/516418.aspx</a></p></li>
  <li><p><a href="http://bytes.com/topic/c-sharp/answers/558930-really-need-help-sendmessage-wm_getcontrolname" rel="nofollow">http://bytes.com/topic/c-sharp/answers/558930-really-need-help-sendmessage-wm_getcontrolname</a></p></li>
  </ol>
  
  <p>All of them suggest to use SendMessage in order to retrieve the form's data, which I failed to do.</p>
  
  <p>I will be happy to for any idea for this issue.</p>
  
  <p>Thank you very much.</p>
  
  <p>The C# code</p>
  
  <pre><code> public static class VbAdapter : IAdapter
  {
      /// &lt;summary&gt;
      /// Gets form internal name (design-time name).
      /// &lt;/summary&gt;
      /// &lt;param name="hWnd"&gt;Form handle&lt;/param&gt;
      /// &lt;returns&gt;string. Form's internal name.&lt;/returns&gt;
      public static string GetFormInternalName(IntPtr hWnd)
      {
          int _ctrlNameMsg = 0;
          //_ctrlNameMsg = NativeMethods.RegisterWindowMessage("WM_GETCONTROLNAME"); //For .Net forms
          _ctrlNameMsg = NativeMethods.RegisterWindowMessage("Get_CONTROLNAME"); //for vb6 forms
  
          return GetControlName(hWnd, _ctrlNameMsg);
      }
  
      /// &lt;summary&gt;
      /// Get control internal name using its handle.
      /// &lt;/summary&gt;
      /// &lt;param name="hWnd"&gt;Control handle&lt;/param&gt;
      /// &lt;param name="msg"&gt;Control Name Message&lt;/param&gt;
      /// &lt;returns&gt;string.&lt;/returns&gt;
      private static string GetControlName(IntPtr hWnd, int msg)
      {
          //vars
  
          uint size = 65536; //size of memory to be allocated
          byte[] byteArray = new byte[size]; //win form internal name buffer
  
          IntPtr bufferMem = IntPtr.Zero; //pointer to memory buffer contain the internal name
          IntPtr written = IntPtr.Zero;          //number of bytes written so far
          IntPtr retHandle = IntPtr.Zero;         //returned handle
          IntPtr hProcess = IntPtr.Zero;       //Process handle
          IntPtr fileHandle = IntPtr.Zero;          //File handle
  
          bool retVal = false;
  
          //in case non Win32Nt OS version - throw exception
          if (Environment.OSVersion.Platform != PlatformID.Win32NT)
              throw new Win32Exception("Oprating System is not supportted for this module.\nThis module is supportted on Win32Nt OS only.");
          try
          {
              uint procId = GetProcessIdFromHWnd(hWnd);
              //get process deatails
              hProcess = NativeMethods.OpenProcess(
                  WindowsConsts.PROCESS_VM_OPERATION |
                  WindowsConsts.PROCESS_VM_READ |
                  WindowsConsts.PROCESS_VM_WRITE,
                  false,
                  procId);
  
              //Todo: Export to OpenProcess Method in native class
              if (hProcess.ToInt64() == 0) throw new Win32Exception();
  
              bufferMem = NativeMethods.VirtualAllocEx(hProcess,
                  IntPtr.Zero,
                  new UIntPtr(size),
                  WindowsConsts.MEM_RESERVE | WindowsConsts.MEM_COMMIT,
                  NativeMethods.PageProtection.ReadWrite);
  
              //Todo: Export to OpenProcess Method in native class
              if (hProcess.ToInt64() == 0) throw new Win32Exception();
  
              //Send message to the control requesting it's name
              retHandle = NativeMethods.SendMessage(hWnd, msg, new IntPtr(size), bufferMem);
  
              //Get TVITEM from shared memory
              if (!NativeMethods.ReadProcessMemory(hProcess, bufferMem, byteArray, new UIntPtr(size), written))
                  throw new Win32Exception();
          }
          catch (Exception)
          {
              throw new Win32Exception();
          }
  
          return ByteArrayToString(byteArray);
      }
  
      /// &lt;summary&gt;
      /// Converts byte array to string.
      /// &lt;/summary&gt;
      /// &lt;param name="byteArray"&gt;The byte array.&lt;/param&gt;
      /// &lt;returns&gt;string.&lt;/returns&gt;
      private static string ByteArrayToString(byte[] byteArray)
      {
          return Encoding.Unicode.GetString(byteArray).TrimEnd('\0');
      }
  
      /// &lt;summary&gt;
      /// Get the process id using its handle.
      /// &lt;/summary&gt;
      /// &lt;param name="hWnd"&gt;The handle&lt;/param&gt;
      /// &lt;returns&gt;uint. The process Id.&lt;/returns&gt;
      private static uint GetProcessIdFromHWnd(IntPtr hWnd)
      {
          uint pId;
          NativeMethods.GetWindowThreadProcessId(hWnd, out pId);
  
          return pId;
      }
  }
   internal class NativeMethods
  {
      [DllImport("kernel32.dll")]
      internal static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle,
          uint dwProcessId);
  
      [DllImport("kernel32.dll")]
      internal static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
          UIntPtr dwSize, uint flAllocationType, PageProtection flProtect);
  
      [DllImport("user32.dll", SetLastError = true)]
      internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
  
      [DllImport("kernel32.dll")]
      internal static extern bool VirtualFreeEx(IntPtr hProcess, IntPtr lpAddress,
          UIntPtr dwSize, uint dwFreeType);
  
      [DllImport("kernel32.dll")]
      internal static extern bool CloseHandle(IntPtr hObject);
  
      [DllImport("kernel32.dll")]
      internal static extern IntPtr MapViewOfFile(IntPtr hFileMappingObject, uint
          dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow,
          UIntPtr dwNumberOfBytesToMap);
  
      [DllImport("kernel32.dll")]
      internal static extern bool UnmapViewOfFile(IntPtr lpBaseAddress);
  
      [DllImport("kernel32.dll", SetLastError = true)]
      internal static extern IntPtr CreateFileMapping(IntPtr hFile,
          IntPtr lpFileMappingAttributes, PageProtection flProtect, int dwMaximumSizeHigh,
          int dwMaximumSizeLow, string lpName);
  
      [DllImport("user32.dll")]
      internal static extern IntPtr SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);
  
      [DllImport("kernel32.dll")]
      internal static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress,
          [Out] byte[] lpBuffer, UIntPtr nSize, IntPtr lpNumberOfBytesRead);
  
      [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
      internal static extern void MoveMemoryFromByte(IntPtr dest, ref byte src, int size);
  
      [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
      internal static extern void MoveMemoryToByte(ref byte dest, IntPtr src, int size);
  
      [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
      internal static extern int RegisterWindowMessage(string lpString);
  
      [Flags]
      internal enum PageProtection : uint
      {
          NoAccess = 0x01,
          Readonly = 0x02,
          ReadWrite = 0x04,
          WriteCopy = 0x08,
          Execute = 0x10,
          ExecuteRead = 0x20,
          ExecuteReadWrite = 0x40,
          ExecuteWriteCopy = 0x80,
          Guard = 0x100,
          NoCache = 0x200,
          WriteCombine = 0x400,
      }
  }
  </code></pre>