tags:

views:

252

answers:

2

In a C++ app, I have an hWnd pointing to a window running in a third party process. This window contains controls which extend the COM TreeView control. I am interested in obtaining the CheckState of this control.
I use the hWnd to get an HTREEITEM using TreeView_GetRoot(hwnd) from commctrl.h

hwnd points to the window and hItem is return value from TreeView_GetRoot(hwnd). They are used as follows:

int iCheckState = TreeView_GetCheckState(hwnd,  hItem);
switch (iCheckState)
{
   case 0:
      // (unchecked)
   case 1:
      // checked
   ...
}

I'm looking to port this code into a C# app which does the same thing (switches off the CheckState of the TreeView control). I have never used COM and am quite unfamiliar.

I have tried using the .NET mscomctl but can't find equivalent methods to TreeView_GetRoot or TreeView_GetCheckState. I'm totally stuck and don't know how to recreate this code in C# :(

Suggestions?

+1  A: 

Why are you not using a Windows Forms TreeView control? If you are using this control, set the control's CheckBoxes property to true to enable check boxes, and set the Checked property on the nodes you want to display checked.

To get the collection of root nodes, use the TreeView's Nodes property. This returns a TreeNodeCollection which you can then index or add items to.

Mike Dimmick
The windows is owned by a third party. I have no control over it. The maker of this app has chosen to implement the control using an extension of the COM object.
Kenn
+2  A: 

We have these definitions from CommCtrl.h:

#define TreeView_SetItemState(hwndTV, hti, data, _mask) \
{ TVITEM _ms_TVi;\
  _ms_TVi.mask = TVIF_STATE; \
  _ms_TVi.hItem = (hti); \
  _ms_TVi.stateMask = (_mask);\
  _ms_TVi.state = (data);\
  SNDMSG((hwndTV), TVM_SETITEM, 0, (LPARAM)(TV_ITEM *)&_ms_TVi);\
}

#define TreeView_SetCheckState(hwndTV, hti, fCheck) \
  TreeView_SetItemState(hwndTV, hti, INDEXTOSTATEIMAGEMASK((fCheck)?2:1), TVIS_STATEIMAGEMASK)

We can translate this to C# using PInvoke. First, we implement these macros as functions, and then add whatever other support is needed to make those functions work. Here is my first shot at it. You should double check my code especially when it comes to the marshalling of the struct. Further, you may want to Post the message cross-thread instead of calling SendMessage.

Lastly, I am not sure if this will work at all since I believe that the common controls use WM_USER+ messages. When these messages are sent cross-process, the data parameter's addresses are unmodified and the resulting process may cause an Access Violation. This would be a problem in whatever language you use (C++ or C#), so perhaps I am wrong here (you say you have a working C++ program).

static class Interop {

public static IntPtr TreeView_SetCheckState(HandleRef hwndTV, IntPtr hti, bool fCheck) {
    return TreeView_SetItemState(hwndTV, hti, INDEXTOSTATEIMAGEMASK((fCheck) ? 2 : 1), (uint)TVIS.TVIS_STATEIMAGEMASK);
}

public static IntPtr TreeView_SetItemState(HandleRef hwndTV, IntPtr hti, uint data, uint _mask) {
    TVITEM _ms_TVi = new TVITEM();
    _ms_TVi.mask = (uint)TVIF.TVIF_STATE;
    _ms_TVi.hItem = (hti);
    _ms_TVi.stateMask = (_mask);
    _ms_TVi.state = (data);
    IntPtr p = Marshal.AllocCoTaskMem(Marshal.SizeOf(_ms_TVi));
    Marshal.StructureToPtr(_ms_TVi, p, false);
    IntPtr r = SendMessage(hwndTV, (int)TVM.TVM_SETITEMW, IntPtr.Zero, p);
    Marshal.FreeCoTaskMem(p);
    return r;
}

private static uint INDEXTOSTATEIMAGEMASK(int i) { return ((uint)(i) << 12); }

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(HandleRef hWnd, int msg, IntPtr wParam, IntPtr lParam);

private enum TVIF : uint {
    TVIF_STATE = 0x0008
}

private enum TVIS : uint {
    TVIS_STATEIMAGEMASK = 0xF000
}

private enum TVM : int {
    TV_FIRST = 0x1100,
    TVM_SETITEMA = (TV_FIRST + 13),
    TVM_SETITEMW = (TV_FIRST + 63)
}

private 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;
}
}
Frank Krueger
Yep, this worked.
Kenn