C#操作QQ的TreeView控件

image

通过spy++我们可以发现类ATL:30A57F50为treeview控件

我们需实现如下对treeview的操作

1),遍历获取信息 2),单选 3),多选

操作其他进程的步骤这个不区分语言的,一般实现过程如下

1),打开远程进程,这个调用API 函数 OpenProcess来实现

2),分配远程内存,调用API VirtualAllocEx 来实现,当然不是绝对的,当操作是不需要对象,而是通过SendMessage直接返回时就不需要分配

3),内存复制,由本地进程内存复制到远程进程内存,使用WriteProcessMemory来实现,同上不是绝对,只有调用需要读取对象信息时,才有需要

4),发送消息,发送消息就不用说了SendMessage

5),上面发送的消息结果,如果是写入对象的话,那么我们需要读取远程内存到本地和3相反,ReadProcessMemory

6),完了?,没有的.资源没释放,OK,释放资源,一般意义上就完成了

上面所有的步骤在vc里一般都这样实现的

我来讲解一下在C#如何实现操作其他进程的TreeView

1),获取Treeview控件句柄使用API FindWindowEx来实现,下面这个函数是找出所有控件,可以调用之后判断className来获取到控件指针
 public static List<IntPtr> FindControl(IntPtr hwnd, string className, string title = null)
 {
            List<IntPtr> controls = new List<IntPtr>();
            IntPtr handle = IntPtr.Zero;
            while (true)
            {
                IntPtr tmp = handle;
                handle = WinAPIHelper.FindWindowEx(hwnd, tmp, className, title);
                if (handle != IntPtr.Zero)
                {
                    controls.Add(handle);
                }
                else
                    break;
            }
            return controls;
     }

2),读取item

    a),获取根节点

  int retval = WinAPIHelper.SendMessage(hwnd, (int)WinAPIHelper.TV_Messages.TVM_GETNEXTITEM, (int)WinAPIHelper.TVM_GETNEXTITEM.TVGN_ROOT, IntPtr.Zero);

,返回值为节点指针

   b),选择获取到的项

 int r1 = WinAPIHelper.SendMessage(hwnd, (int)WinAPIHelper.TV_Messages.TVM_SELECTITEM,
                  (int)WinAPIHelper.TVM_GETNEXTITEM.TVGN_CARET, new IntPtr(retval));

,返回值为操作结果

   c),展开选中项

 int r2 = WinAPIHelper.SendMessage(hwnd, (int)WinAPIHelper.TV_Messages.TVM_EXPAND,
                        (int)WinAPIHelper.TVM_EXPAND.TVE_EXPAND, new IntPtr(retval));

,返回操作结果

   d),声明ITEM并写入远程内存
 

                    WinAPIHelper.TVITEM tvItem = new WinAPIHelper.TVITEM();
                    int size = Marshal.SizeOf(tvItem);
                    tvItem.mask = WinAPIHelper.TVIF_TEXT;
                    tvItem.hItem = (IntPtr)retval;
                    tvItem.pszText = (IntPtr)(remoteBuffer.ToInt32() + size + 1);
                    tvItem.cchTextMax = 255;
                    localBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(tvItem));
                    Marshal.StructureToPtr(tvItem, localBuffer, false);
                    bSuccess = WinAPIHelper.WriteProcessMemory(hProcess, remoteBuffer, ref tvItem, size, IntPtr.Zero);

关于item定义,大家可查询MSDN

   e),发送获取Item消息

   WinAPIHelper.SendMessage(hwnd, (int)WinAPIHelper.TV_Messages.TVM_GETITEM, 0, remoteBuffer);

    f),读取远程内存

   bSuccess = WinAPIHelper.ReadProcessMemory(hProcess, remoteBuffer, localBuffer, buff_size, IntPtr.Zero);

    g),将指针转为结构

WinAPIHelper.TVITEM retItem = (WinAPIHelper.TVITEM)
                                   Marshal.PtrToStructure(localBuffer, (Type)typeof(WinAPIHelper.TVITEM))

经过上面的操作我们就获取到了选定的某一项的信息,接下来我们使用

  retval = WinAPIHelper.SendMessage(hwnd, (int)WinAPIHelper.TV_Messages.TVM_GETNEXTITEM, (int)WinAPIHelper.TVM_GETNEXTITEM.TVGN_NEXTVISIBLE, new IntPtr(retval));
来选择下一个项在进行上面的操作,直到返回值为空位置

3),多选实现,多选我们需要通过修改ITEM的状态来实现

  a),声明ITEM(操作为更改状态)

                WinAPIHelper.TVITEM tvItem = new WinAPIHelper.TVITEM();
                tvItem.hItem = items[i].Handle;
                tvItem.mask = WinAPIHelper.TVIF_STATE;
                tvItem.state = WinAPIHelper.TVIS_SELECTED;
                tvItem.stateMask = WinAPIHelper.TVIS_SELECTED;

   b),分配远程内存

IntPtr remoteBuffer = WinAPIHelper.VirtualAllocEx(hPro, IntPtr.Zero, buff_size, WinAPIHelper.AllocationType.Commit, WinAPIHelper.MemoryProtection.ExecuteReadWrite);

   c),写入远程内存

   bool result = WinAPIHelper.WriteProcessMemory(hPro, remoteBuffer, ref tvItem, size, IntPtr.Zero);

   d),设置状态

 int retval = WinAPIHelper.SendMessage(hTv, (int)WinAPIHelper.TV_Messages.TVM_SETITEM, 0, remoteBuffer);

  经过上面就实现了为某一项设置选中状态,实际操作中,我们为提高内存使用效率,是先分配缓冲区,在设置(写入)对象,然后发送指令

下面是完整代码

    public class WinTreeViewItem
    {
        public int Index { get; set; }
        public int lpAddress { get; set; }
        public string Text { get; set; }
        public bool OwnChild { get; set; }
        public IntPtr Handle { get; set; }
    }
    public class TreeViewHelper
    {
        const int buff_size = 1024;
        const int MAX_TVMSTRING = 255;        
        static public List<WinTreeViewItem> GetAllItem(IntPtr hTv)
        {
            return GetItems(hTv, null, null, int.MaxValue);
        }
 
        static public WinTreeViewItem GetItemByText(IntPtr hTv, params string[] text)
        {
            List<string> containsList = new List<string>();
            foreach (var item in text)
            {
                if (item != null && item != "")
                    containsList.Add(item);
            }
            List<WinTreeViewItem> items = GetItems(hTv, containsList, null, 1, false);
            if (items.Count > 0)
                return items[0];
            else
                return null;
        }
 
        static public bool SelectItem(IntPtr hTv, WinTreeViewItem item)
        {
            List<WinTreeViewItem> items = new List<WinTreeViewItem>();
            items.Add(item);
            return SelectItems(hTv, items);
        }
 
        static public List<WinTreeViewItem> GetItems(IntPtr hTv, List<string> containsList, List<string> withOutList, int takeCount)
        {
            return GetItems(hTv, containsList, withOutList, takeCount, false);
        }
 
        static public List<WinTreeViewItem> GetItems(IntPtr hTv, List<string> containsList, List<string> withOutList, int takeCount, bool isAddParent, int containsPreferenceCount = 0, int withOutPreferenceCount = 0)
        {
            if (withOutPreferenceCount < 1 && containsPreferenceCount < 1)
            {
                throw new Exception("两个数量级不能同时为空");
            }
            int tryCount = 0;
            IntPtr hPro = WinAPIHelper.OpenProcess(WinAPIHelper.PROCESS_ALL_ACCESS, false, WndHelper.GetProcessId(hTv));
            List<WinTreeViewItem> withOutResultItems = new List<WinTreeViewItem>();
            List<WinTreeViewItem> containsResultItems = new List<WinTreeViewItem>();
            int index = 0;
            int retval = 0;
        GETROOT:
            if (retval == 0)
            {
                tryCount++;
                if (tryCount > SysConfig.TreeViewItemFindTryCount)
                {
                    return null;
                }
                retval = WinAPIHelper.SendMessage(hTv, (int)WinAPIHelper.TV_Messages.TVM_GETNEXTITEM, (int)WinAPIHelper.TVM_GETNEXTITEM.TVGN_ROOT, IntPtr.Zero);
                Console.WriteLine("根节点" + retval);
                Thread.Sleep(500);
                goto GETROOT;
            }
            tryCount = 0;
            int itemCount = 0;
        FINDCOUNT:
            if (itemCount == 0)
            {
                tryCount++;
                if (tryCount > SysConfig.TreeViewItemFindTryCount)
                {
                    return null;
                }
                itemCount = WinAPIHelper.SendMessage(hTv, (int)WinAPIHelper.TV_Messages.TVM_GETCOUNT, 0, IntPtr.Zero);
                Console.WriteLine("可用创建数量" + itemCount);
                Thread.Sleep(500);
                goto FINDCOUNT;
            }
            IntPtr pStrBufferMemory = WinAPIHelper.VirtualAllocEx(hPro, IntPtr.Zero, MAX_TVMSTRING, WinAPIHelper.AllocationType.Commit, WinAPIHelper.MemoryProtection.ReadWrite);
            IntPtr remoteBuffer = WinAPIHelper.VirtualAllocEx(hPro, IntPtr.Zero, Marshal.SizeOf(typeof(WinAPIHelper.TVITEM)), WinAPIHelper.AllocationType.Commit, WinAPIHelper.MemoryProtection.ExecuteReadWrite);
 
            if (remoteBuffer == IntPtr.Zero)
            {
                Console.WriteLine("远程地址分配失败");
                return null;
            }
            if (pStrBufferMemory == IntPtr.Zero)
            {
                Console.WriteLine("远程地址分配失败");
                return null;
            }
            WinAPIHelper.TVITEM tvItem = new WinAPIHelper.TVITEM();
            // int size = Marshal.SizeOf(tvItem);
            tvItem.mask = WinAPIHelper.TVIF_TEXT;
            tvItem.pszText = pStrBufferMemory;
            tvItem.cchTextMax = MAX_TVMSTRING;
            IntPtr localBuffer = Marshal.AllocHGlobal(Marshal.SizeOf(tvItem));
            Marshal.StructureToPtr(tvItem, localBuffer, false);
            int addCount = 0;
            string tmpcontxt = "";
            while (retval != 0)
            {
                if (index + 1 > itemCount) break;
                tvItem.hItem = (IntPtr)retval;
                var item = new WinTreeViewItem()
                {
                    Index = index,
                    lpAddress = retval
                };
                int r1 = WinAPIHelper.SendMessage(hTv, (int)WinAPIHelper.TV_Messages.TVM_SELECTITEM, (int)WinAPIHelper.TVM_GETNEXTITEM.TVGN_CARET, new IntPtr(retval));
                if (r1 == 0)
                {
                    Console.WriteLine("选择项失败");
                    continue;
                }
                //Thread.Sleep(100);
                int r2 = WinAPIHelper.SendMessage(hTv, (int)WinAPIHelper.TV_Messages.TVM_EXPAND, (int)WinAPIHelper.TVM_EXPAND.TVE_EXPAND, new IntPtr(retval));
                if (r2 == 1)
                {
                    //Console.WriteLine("可以展开");
                    itemCount = WinAPIHelper.SendMessage(hTv, (int)WinAPIHelper.TV_Messages.TVM_GETCOUNT, 0, IntPtr.Zero);
                    item.OwnChild = true;
                }
                else
                {
                    //Console.WriteLine("不可以展开");
                }
                bool bSuccess = WinAPIHelper.WriteProcessMemory(hPro, remoteBuffer, ref tvItem, Marshal.SizeOf(tvItem), IntPtr.Zero);
                if (bSuccess == false)
                {
                    Console.WriteLine("写入远程地址失败");
                    continue;
                }
                int r3 = WinAPIHelper.SendMessage(hTv, (int)WinAPIHelper.TV_Messages.TVM_GETITEM, 0, remoteBuffer);
                if (r3 == 0)
                {
                    Console.WriteLine("选择项操作失败");
                    continue;
                }
                bSuccess = WinAPIHelper.ReadProcessMemory(hPro, remoteBuffer, localBuffer, Marshal.SizeOf(tvItem), IntPtr.Zero);
                if (bSuccess == false)
                {
                    Console.WriteLine("读取远程地址失败");
                    continue;
                }
                WinAPIHelper.TVITEM retItem = (WinAPIHelper.TVITEM)Marshal.PtrToStructure(localBuffer, (Type)typeof(WinAPIHelper.TVITEM));
                int readLen = 0;
                IntPtr pStrLocaAddress = Marshal.AllocHGlobal(MAX_TVMSTRING);
 
                bSuccess = WinAPIHelper.ReadProcessMemory(hPro, pStrBufferMemory, pStrLocaAddress, MAX_TVMSTRING, out readLen);
                string pszItemText = Marshal.PtrToStringUni(pStrLocaAddress);
                // Console.WriteLine(pszItemText);
                item.Handle = tvItem.hItem;
                item.Text = pszItemText;
                bool withOutAdd = false;
                bool containsAdd = false;
                if (item.OwnChild)
                {
                    goto NEXTITEM;
                }
                if (containsList != null)
                {
                    foreach (var conItem in containsList)
                    {
                        if (pszItemText.Trim().Contains(conItem.Trim()))
                        {
                            containsAdd = true; goto ADDITEM;
                        }
                    }
                }
                if (withOutList != null)
                {
                    bool isContains = false;
                    foreach (var outItem in withOutList)
                    {
                        if (pszItemText.Trim().Contains(outItem.Trim()))
                        {
                            isContains = true;
                            goto NEXTITEM;
                        }
                    }
                    if (!isContains)
                    {
                        withOutAdd = true;
                        goto ADDITEM;
                    }
                }
 
            ADDITEM:
                if ((withOutList == null && containsList == null) || (withOutAdd || containsAdd))
                {
                    if (withOutAdd)
                    {
                        if (withOutResultItems.Count < withOutPreferenceCount && takeCount - containsPreferenceCount > withOutResultItems.Count)
                        {
                            withOutResultItems.Add(item);
                            addCount++;
                        }
                    }
                    if (containsAdd)
                    {
                        if (containsResultItems.Count < containsPreferenceCount && takeCount - withOutPreferenceCount > containsResultItems.Count)
                        {
                            containsResultItems.Add(item);
                            addCount++;
                        }
                    }
                    containsAdd = false;
                    withOutAdd = false;
                    if (addCount == takeCount)
                        break;
                }
            NEXTITEM:
                retval = WinAPIHelper.SendMessage(hTv, (int)WinAPIHelper.TV_Messages.TVM_GETNEXTITEM, (int)WinAPIHelper.TVM_GETNEXTITEM.TVGN_NEXTVISIBLE, new IntPtr(retval));
                index++;
            }
            if (localBuffer != IntPtr.Zero)
            {
                try { Marshal.FreeHGlobal(localBuffer); }
                catch { }
            }
            if (remoteBuffer != IntPtr.Zero)
            {
                try { WinAPIHelper.VirtualFreeEx(hPro, remoteBuffer, 0, WinAPIHelper.MEM_RELEASE); }
                catch { }
            }
            if (pStrBufferMemory != IntPtr.Zero)
            {
                try { WinAPIHelper.VirtualFreeEx(hPro, pStrBufferMemory, 0, WinAPIHelper.MEM_RELEASE); }
                catch { }
            }
            if (hPro != IntPtr.Zero)
            {
                try { WinAPIHelper.CloseHandle(hPro); }
                catch { }
            }
            withOutResultItems.AddRange(containsResultItems);
            return withOutResultItems;
        }
 
        static public bool SelectItems(IntPtr hTv, List<WinTreeViewItem> items)
        {
            if (items.Count < 1) return false;
 
            int r1 = WinAPIHelper.SendMessage(hTv, (int)WinAPIHelper.TV_Messages.TVM_SELECTITEM, (int)WinAPIHelper.TVM_GETNEXTITEM.TVGN_CARET, items[0].Handle);
            if (items.Count == 1) return r1 == 1 ? true : false;
            IntPtr hPro = WinAPIHelper.OpenProcess(WinAPIHelper.PROCESS_ALL_ACCESS, false, WndHelper.GetProcessId(hTv));
            IntPtr remoteBuffer = WinAPIHelper.VirtualAllocEx(hPro, IntPtr.Zero, buff_size, WinAPIHelper.AllocationType.Commit, WinAPIHelper.MemoryProtection.ExecuteReadWrite);
            for (int i = 1; i < items.Count; i++)
            {
 
                WinAPIHelper.TVITEM tvItem = new WinAPIHelper.TVITEM();
                tvItem.hItem = items[i].Handle;
                tvItem.mask = WinAPIHelper.TVIF_STATE;
                tvItem.state = WinAPIHelper.TVIS_SELECTED;
                tvItem.stateMask = WinAPIHelper.TVIS_SELECTED;
                int size = Marshal.SizeOf(tvItem);
                bool result = WinAPIHelper.WriteProcessMemory(hPro, remoteBuffer, ref tvItem, size, IntPtr.Zero);
                int retval = WinAPIHelper.SendMessage(hTv, (int)WinAPIHelper.TV_Messages.TVM_SETITEM, 0, remoteBuffer);
 
                Thread.Sleep(100);
            }
            if (remoteBuffer != IntPtr.Zero)
            {
                try { WinAPIHelper.VirtualFreeEx(hPro, remoteBuffer, 0, WinAPIHelper.MEM_RELEASE); }
                catch { }
            }
            if (hPro != IntPtr.Zero)
            {
                try { WinAPIHelper.CloseHandle(hPro); }
                catch { }
            }
            return true;
        }
 
 
    }

QQ 讨论组广告群发工具(已开发完成)索引

你可能感兴趣的:(treeview)