title: SendMessage TreeView TVM_GETITEM crashes that process
body: <p>I am trying to access a <code>TreeView</code> running in another process. Many of the tree view messages work correctly. However, trying to use the <code>TVM_GETITEM</code> causes that other process to crash.</p>
  
  <p>The code below is a simple program that illustrates the crash. To get it to run, you will need some CHM help file. I'm using a CHM help file because hh.exe uses a <code>TreeView</code> control for the table of contents.</p>
  
  <p>The goal is to be able to get the a tree node's text.</p>
  
  <pre><code>using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.IO;
  using System.Linq;
  using System.Runtime.InteropServices;
  using System.Text;
  using System.Threading;
  using System.Windows.Forms;
  
  namespace TreeViewTest {
  
  public class TVForm : Form {
  
      String chmFile = @"C:\temp\QuickGuide.chm"; // change this to some help file on local computer
  
      Button btnOpenFile = new Button { Text = "Open File", AutoSize = true };
      Button btnDeleteSelected = new Button { Text = "Delete Selected", AutoSize = true };
      Button btnGetCount = new Button { Text = "Get Count", AutoSize = true };
      Button btnGetText = new Button { Text = "Get Text", AutoSize = true }; // causes hh.exe process to crash
      Button btnSelectNext = new Button { Text = "Select Next", AutoSize = true };
      Process process = null;
      IntPtr ptrTreeView = IntPtr.Zero;
  
      public TVForm() {
          FlowLayoutPanel p = new FlowLayoutPanel { FlowDirection = System.Windows.Forms.FlowDirection.TopDown, Dock = DockStyle.Fill };
          p.Controls.Add(btnOpenFile);
          p.Controls.Add(btnGetCount);
          p.Controls.Add(btnDeleteSelected);
          p.Controls.Add(btnGetText);
          p.Controls.Add(btnSelectNext);
          Controls.Add(p);
  
          btnOpenFile.Click += buttonClicked; // works fine
          btnDeleteSelected.Click += buttonClicked; // works fine
          btnGetCount.Click += buttonClicked;
          btnGetText.Click += buttonClicked;
          btnSelectNext.Click += buttonClicked;
      }
  
      void buttonClicked(object sender, EventArgs e) {
          if (!File.Exists(chmFile)) {
              MessageBox.Show("Cannot find CHM file: " + chmFile);
              return;
          }
  
          IntPtr hwndTreeView = GetTreeViewHandle();
          if (hwndTreeView == IntPtr.Zero) {
              MessageBox.Show("Cannot find TreeView handle.");
              return;
          }
  
          if (sender == btnDeleteSelected) {
              // get the handle to the selected node in the tree view
              IntPtr hwndItem = SendMessage(hwndTreeView, (int) TVM.TVM_GETNEXTITEM, (int) TVGN.TVGN_CARET, 0);
              if (hwndItem == IntPtr.Zero)
                  MessageBox.Show("no item selected");
              else
                  SendMessage(hwndTreeView, (int) TVM.TVM_DELETEITEM, IntPtr.Zero, hwndItem);
          }
          else if (sender == btnGetCount) {
              IntPtr count = SendMessage(hwndTreeView, (int) TVM.TVM_GETCOUNT, 0, 0);
              MessageBox.Show(String.Format("There are {0} nodes in the tree view.", count.ToInt32()));
          }
          else if (sender == btnSelectNext) {
              IntPtr hwndItem = SendMessage(hwndTreeView, (int) TVM.TVM_GETNEXTITEM, (int) TVGN.TVGN_CARET, 0);
              if (hwndItem == IntPtr.Zero) {
                  MessageBox.Show("no item selected");
                  return;
              }
              hwndItem = SendMessage(hwndTreeView, (int) TVM.TVM_GETNEXTITEM, new IntPtr((int) TVGN.TVGN_NEXT), hwndItem);
              if (hwndItem == IntPtr.Zero)
                  MessageBox.Show("there is no next item");
              else
                  SendMessage(hwndTreeView, (int) TVM.TVM_SELECTITEM, new IntPtr((int) TVGN.TVGN_CARET), hwndItem);
          }
          else if (sender == btnGetText) { // crashes hh.exe
              IntPtr hwndItem = SendMessage(hwndTreeView, (int) TVM.TVM_GETNEXTITEM, (int) TVGN.TVGN_CARET, 0);
              if (hwndItem == IntPtr.Zero) {
                  MessageBox.Show("no item selected");
                  return;
              }
  
              var item = new TVITEMEX(); // have tried both TVITEM and TVITEMEX
              item.hItem = hwndItem;
              item.mask = (int) (TVIF.TVIF_TEXT | TVIF.TVIF_HANDLE);
              item.cchTextMax = 260;
              item.pszText = Marshal.AllocHGlobal(item.cchTextMax);
  
              IntPtr ptr = Marshal.AllocHGlobal(Marshal.SizeOf(item));
              Marshal.StructureToPtr(item, ptr, false);
  
              // have tried TVM_GETITEM and TVM_GETITEMA
              SendMessage(hwndTreeView, (int) TVM.TVM_GETITEM, IntPtr.Zero, ptr); // this line crashes hh.exe
  
              String text = Marshal.PtrToStringAuto(item.pszText);
              MessageBox.Show(text);
  
              Marshal.FreeHGlobal(item.pszText);
              Marshal.FreeHGlobal(ptr);
          }
      }
  
      public Process GetProcess() {
          if (process != null &amp;&amp; !process.HasExited)
              return process;
  
          ptrTreeView = IntPtr.Zero;
          process = Process.Start(chmFile);
          Thread.Sleep(1000); // sleep a little to give the process time to open (otherwise cannot find the TreeView control)
          return process;
      }
  
      public IntPtr GetTreeViewHandle() {
          if (ptrTreeView != IntPtr.Zero &amp;&amp; !process.HasExited)
              return ptrTreeView;
  
          var list = new List&lt;IntPtr&gt;();
          Process p = GetProcess();
          FindSysTreeView(p.MainWindowHandle, new Hashtable(), list);
          if (list.Count == 0) {
              return IntPtr.Zero;
          }
          ptrTreeView = list[0];
          return ptrTreeView;
      }
  
      private static void FindSysTreeView(IntPtr p, Hashtable ht, List&lt;IntPtr&gt; list) {
          var cn = GetClassName(p);
          //var txt = GetWindowText(p);
          if (cn == "SysTreeView32") {
              if (!list.Contains(p))
                  list.Add(p);
          }
          if (ht[p] == null) {
              ht[p] = p;
              foreach (var c in GetChildWindows(p))
                  FindSysTreeView(c, ht, list);
          }
      }
  
      public static String GetClassName(IntPtr hWnd) {
          StringBuilder sb = new StringBuilder(256);
          GetClassName(hWnd, sb, sb.Capacity);
          return sb.ToString();
      }
  
      public static List&lt;IntPtr&gt; GetChildWindows(IntPtr parent) {
          List&lt;IntPtr&gt; result = new List&lt;IntPtr&gt;();
          GCHandle listHandle = GCHandle.Alloc(result);
          try {
              Win32Callback childProc = new Win32Callback(EnumWindow);
              EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
          } finally {
              if (listHandle.IsAllocated)
                  listHandle.Free();
          }
          return result;
      }
  
      private static bool EnumWindow(IntPtr handle, IntPtr pointer) {
          GCHandle gch = GCHandle.FromIntPtr(pointer);
          List&lt;IntPtr&gt; list = gch.Target as List&lt;IntPtr&gt;;
          if (list == null)
              throw new InvalidCastException("GCHandle Target could not be cast as List&lt;IntPtr&gt;");
          list.Add(handle);
          return true;
      }
  
      [DllImport("user32.dll")]
      static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
  
      [DllImport("user32.Dll")]
      private static extern bool EnumChildWindows(IntPtr parentHandle, Win32Callback callback, IntPtr lParam);
      private delegate bool Win32Callback(IntPtr hwnd, IntPtr lParam);
  
      [DllImport("user32.dll")]
      static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
  
      [DllImport("user32.dll")]
      static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
  
      private const int TV_FIRST = 0x1100;
      public enum TVM {
          TVM_GETNEXTITEM = (TV_FIRST + 10),
          TVM_GETITEMA = (TV_FIRST + 12),
          TVM_GETITEM = (TV_FIRST + 62),
          TVM_GETCOUNT = (TV_FIRST + 5),
          TVM_SELECTITEM = (TV_FIRST + 11),
          TVM_DELETEITEM = (TV_FIRST + 1),
          TVM_EXPAND = (TV_FIRST + 2),
          TVM_GETITEMRECT = (TV_FIRST + 4),
          TVM_GETINDENT = (TV_FIRST + 6),
          TVM_SETINDENT = (TV_FIRST + 7),
          TVM_GETIMAGELIST = (TV_FIRST + 8),
          TVM_SETIMAGELIST = (TV_FIRST + 9),
          TVM_GETISEARCHSTRING = (TV_FIRST + 64),
          TVM_HITTEST = (TV_FIRST + 17),
      }
  
      public enum TVGN {
          TVGN_ROOT = 0x0,
          TVGN_NEXT = 0x1,
          TVGN_PREVIOUS = 0x2,
          TVGN_PARENT = 0x3,
          TVGN_CHILD = 0x4,
          TVGN_FIRSTVISIBLE = 0x5,
          TVGN_NEXTVISIBLE = 0x6,
          TVGN_PREVIOUSVISIBLE = 0x7,
          TVGN_DROPHILITE = 0x8,
          TVGN_CARET = 0x9,
          TVGN_LASTVISIBLE = 0xA
      }
  
      [Flags]
      public enum TVIF {
          TVIF_TEXT = 1,
          TVIF_IMAGE = 2,
          TVIF_PARAM = 4,
          TVIF_STATE = 8,
          TVIF_HANDLE = 16,
          TVIF_SELECTEDIMAGE = 32,
          TVIF_CHILDREN = 64,
          TVIF_INTEGRAL = 0x0080,
          TVIF_DI_SETITEM = 0x1000
      }
  
      [StructLayout(LayoutKind.Sequential)]
      public struct TVITEMEX {
          public uint mask;
          public IntPtr hItem;
          public uint state;
          public uint stateMask;
          public IntPtr pszText;
          public int cchTextMax;
          public int iImage;
          public int iSelectedImage;
          public int cChildren;
          public IntPtr lParam;
          public int iIntegral;
          public uint uStateEx;
          public IntPtr hwnd;
          public int iExpandedImage;
          public int iReserved;
      }
  
      [StructLayout(LayoutKind.Sequential)]
      public struct TVITEM {
          public uint mask;
          public IntPtr hItem;
          public uint state;
          public uint stateMask;
          public IntPtr pszText;
          public int cchTextMax;
          public int iImage;
          public int iSelectedImage;
          public int cChildren;
          public IntPtr lParam;
      }
  
  }
  }
  </code></pre>