ttt

首先,我们要创建一个基本对话框的MFC工程MFC_TreeCRTL(名字随便给一个)。然后在资源视图中插入两个Dialog,ID分别为IDD_DIALOG11和IDD_DIALOG211,都更改Style属性为Child,Border属性为None,为它们建立两个类,分别命名为Cdialog11和Cdialog211,并在MFC_TreeCRTLDlg.CPP文件中包含dialog11.h和dialog211.h两个头文件。再导入几个资源图标作为树形控件节点的图标及装饰面板。最后在主面板上添加一个CTreeCtrl控件,ID为默认,并在ClassWizard中添加它的一个变量,命名为m_mytree。

接着,我们进行具体代码编写。
我们必须在CMFC_TreeCRTLDlg类中加入这些变量和函数

	CDialog * m_treePages[2];
	CString node_name;
	BOOL InitMytree();

我们还要在CMFC_TreeCRTLDlg类的构造函数中为m_treePages[2]分配空间,

	m_treePages[0]=new Cdialog11;
	m_treePages[1]=new Cdialog211;

InitMytree()函数为m_mytree的初始化过程

BOOL CMFC_TreeCRTLDlg::InitMytree()
{
	//节点的图标	 
	int i=0;
	int i_count=2;
	//载入图标
	HICON icon[4];
	icon[0]=AfxGetApp()->LoadIcon (IDI_ICON6); 
	icon[1]=AfxGetApp()->LoadIcon (IDI_ICON7);

	//创建图像列表控件
	CImageList *m_imagelist=new CImageList; 
	m_imagelist->Create(16,16,0,7,7); 
	m_imagelist->SetBkColor (RGB(255,255,255));
	for(int n=0;n<i_count;n++)
	{
	m_imagelist->Add(icon[n]);  //把图标载入图像列表控件
	}
	m_mytree.SetImageList(m_imagelist,TVSIL_NORMAL);  //为m_mytree设置一个图像列表,使CtreeCtrl的节点显示不同的图标 
	m_mytree.SetBkColor(RGB(0,250,255));//设置m_mytree的背景色

	//创建节点
	//父节点
	HTREEITEM root0=m_mytree.InsertItem("Dialog1",0,1,TVI_ROOT,TVI_LAST);
	HTREEITEM root1=m_mytree.InsertItem("Dialog2",0,1,TVI_ROOT,TVI_LAST);
	//一层子节点
	HTREEITEM sub_son0=m_mytree.InsertItem("Dialog 1-1",0,1,root0,TVI_LAST);
	HTREEITEM sub_son1=m_mytree.InsertItem("Dialog 2-1",0,1,root1,TVI_LAST);
	//二层孙子节点
	HTREEITEM sub_m_son0=m_mytree.InsertItem("Dialog 2-1-1",0,1,sub_son1,TVI_LAST);

	//建立节点对应的Dialog
	m_treePages[0]->Create(IDD_DIALOG11,this);
	m_treePages[1]->Create(IDD_DIALOG211,this);
	m_treePages[0]->ShowWindow(SW_SHOW);
	m_treePages[1]->ShowWindow(SW_HIDE);

	//把Dialog移到合适位置
	CRect m_rect;
	GetClientRect(m_rect);
	m_rect.left=200;
	m_treePages[0]->MoveWindow(m_rect);
	m_treePages[1]->MoveWindow(m_rect);
	
	return true;
}

  始初化完成后,我们要添加CTreeCtrl的消息响应事件,这样才能让它按我们的要求起作用。我们打开Class Wizard点选IDC_TREE1添加TVN_SELCHANGED消息,并在消息响应函数中写入代码。

void CMFC_TreeCRTLDlg::OnSelchangedTree1(NMHDR* pNMHDR, LRESULT* pResult)
{
	NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
	// TODO: Add your control notification handler code here
	
	UpdateData(true);
	node_name=m_mytree.GetItemText(pNMTreeView->itemNew.hItem);
	//在标题栏显示节点信息
	SetWindowText(node_name);
	//切换面板
	if(node_name=="Dialog 1-1"){
		m_treePages[0]->ShowWindow(SW_SHOW);
		m_treePages[1]->ShowWindow(SW_HIDE);
	}
	else if(node_name=="Dialog 2-1-1"){
		m_treePages[0]->ShowWindow(SW_HIDE);
		m_treePages[1]->ShowWindow(SW_SHOW);
	}
	UpdateData(false);
	*pResult = 0;
}

最后,我们在 CMFC_TreeCRTLDlg::OnInitDialog()初始化函数里调用InitMytree()函数。程序运行效果

到这里为止,我们就把一个Dialog粘贴到了主Dialog上了,通过CTreeCtrl控件的节点的变化,让不同的Dialog交替地粘贴在主Dialog上,从而方便于我们只用少数的窗口,调用更多的功能模块,不必再为每个模块都作为弹出窗口,而显得繁杂。

 

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

遍历和查找外部程序 Tree-View 中的项目

天津 赵春生

下载源代码

  《金山词霸2002》中的附录收集了很多古诗,有时为了寻找一篇古诗,得找很久很久(俺文科很差)。观察其附录的结构,发现是个Tree-View控件,如果能查找里面的项目该有多好,可这个功能软件本身却并没有提供(不知道现在最新的版本是否已经提供了这个功能,如果没有,赶快加上吧,顺便奖励俺一套该产品的最新版,哈哈)……问题出来了:我们要编写一个程序,让她在外部程序中的Tree-View控件里,按用户指定的项目名称顺序查找其中的项目。
  要查找首先得遍历,连范围都确定不好何谈查找?所以本篇分两部分进行讲解:第一部分解决遍历的问题;第二部分解决查找指定项目的问题。

第一部分:遍历外部程序Tree-View中的项目

一:程序说明:

如图一所示Tree-View控件的典型结构图,我们将按照图示的顺序来遍历其中的项目。


图一

翻阅SDK手册中关于Tree-View控件的相关章节,发现了几个有用的消息:

  • TVM_GETNEXTITEM:得到项目的句柄(参数:TVGN_ROOT得到根句柄,TVGN_NEXTVISIBLE得到下一个可见项目的句柄);
  • TVM_EXPAND:展开或折叠指定项目(参数:TVE_EXPAND展开指定项目);
  • TVM_SELECTITEM:选中指定项目。

利用这些消息和SendMessage()函数,我们可以很容易写出遍历代码。

二:具体实践

  在本文所提供的DEMO中,有一段将十六进制字符串转换成十进制无符号长整型的代码,作用是将用户输入的十六进制TV句柄值转换成十进制并存放在变量dec_sum中。此代码不列入本文讨论的范畴,大家不闲弱智的话就将就着用吧。下面是实现遍历功能的关键代码:

	/*	Tree-View Control_Demo_SeqShow 1.0 版
	*	版权所有 (C) 2006 天津 赵春生
	*	2006.08.28
	*	http://timw.yeah.net
	*	http://timw.126.com
	*	本程序能顺序遍历TV控件中的所有项目。
	*	代码在Win2000P+SP4 + VC6+SP6测试通过。
	*/

	if(error==0)//如果在数据验证转换的过程中未出现错误(error==0时无错误)
	{
		
		//下面为核心部分:顺序显示(选中)指定Tree-View控件中的所有Item.
		
		hwnd=HWND(dec_sum);//得到转换后的数据
		
		//得到根句柄
		tvitem.hItem=(HTREEITEM)::SendMessage(hwnd, TVM_GETNEXTITEM,TVGN_ROOT, 0x0);
		::SendMessage(hwnd, TVM_SELECTITEM,TVGN_CARET, (long)tvitem.hItem);//选中状态
		
		while((long)tvitem.hItem)
		{
			//当此项目能展开时
			while(::SendMessage(hwnd, TVM_EXPAND,TVE_EXPAND, (long)tvitem.hItem))
			{
				//选择下一个可见项目
				tvitem.hItem=(HTREEITEM)::SendMessage(hwnd, 
						TVM_GETNEXTITEM,TVGN_NEXTVISIBLE,
						(long)tvitem.hItem);

				//选中状态
				::SendMessage(hwnd, TVM_SELECTITEM,TVGN_CARET, (long)tvitem.hItem);
				continue;
			}
			

			//当不能再展开的时候,选择下一个可见项目
			tvitem.hItem=(HTREEITEM)::SendMessage(hwnd, 
					TVM_GETNEXTITEM,TVGN_NEXTVISIBLE, 
					(long)tvitem.hItem);

			//选中状态
			::SendMessage(hwnd, TVM_SELECTITEM,TVGN_CARET, 
						(long)tvitem.hItem);
			
		}
	}
	
	//释放内存
	CloseHandle(hwnd);
	//顺序显示(选中)完毕

三:TV_Demo_SeqShow的使用方法(图2):


图二

  1. 用SPY++的[Find Window]功能获得目标TV的句柄;
  2. 将句柄值输入到TV_Demo_SeqShow中的[Tree-View Control''s Handle:];
  3. 点击[GO!];

  如果你把[Windows 资源管理器]中的[文件夹]作为目标,那你可要作好心理准备了……如果实在忍受不了这种刺激,干脆把管理器关掉就可以了。

第二部分:查找外部程序Tree-View中的项目

一:程序说明:

  我们已经成功得对外部程序Tree-View中的项目进行了遍历,如果能在遍历的过程中读取每一个项目的名称,结合我们给定的项目名进行比较,那么查找某个项目的问题将变得易如反掌。由此可见:关键的问题是如何读取项目的名称。
  读取项目的名称要发送TVM_GETITEM消息,由于该消息需要为LPARAM参数提供一个TV_ITEM结构的地址,在跨进程发送消息的前提下,为了使外部程序正常使用该内存地址,所以我们必须将TV_ITEM结构插入到目标进程的地址空间中去,代码如下:

ptvitem=(TVITEM*)VirtualAllocEx(hProcess,NULL,sizeof(TVITEM),MEM_COMMIT,PAGE_READWRITE);//分配内存
WriteProcessMemory(hProcess,ptvitem,&tvitem,sizeof(TVITEM),NULL);//写入内存

在写入内存之前,要将TV_ITEM结构配置好:

    tvitem.mask=TVIF_TEXT;
    tvitem.cchTextMax=512;
    tvitem.pszText=pItem;

mask要设置成TVIF_TEXT,因为我们需要的是pszText的值;cchTextMax可以设置得稍微大一些,cchTextMax=512即可;hItem的值用来指定究竟哪个项目来接收TVM_GETITEM消息,该值在遍历的过程中动态获得;重要的是用来存放项目名称的缓冲区地址,即pszText参数的设置:和TV_ITEM结构一样,也要把她插入到目标进程的地址空间中去:

pItem=(char*)VirtualAllocEx(hProcess,NULL,16,MEM_COMMIT,PAGE_READWRITE);

二:具体实践:

  作为演示,下面的这段程序将在我们指定的Tree-View控件中查找我们需要的项目,在发现第一个部分匹配的项目后,程序将停止运行,不再进行查找操作。作为演示程序,程序并没有做速度上的优化,大家在具体应用的过程中可自行修改。程序找到目标后的效果图(图 三):

	/*	Tree-View Control_Demo_SeqSearch 1.0 版
	*	版权所有 (C) 2006 天津 赵春生
	*	2006.08.28
	*	http://timw.yeah.net
	*	http://timw.126.com
	*	本程序能按用户指定的项目名称顺序查找TV控件中的项目。
	*	代码在Win2000P+SP4 + VC6+SP6测试通过。
	*/

	if(error==0)//如果在数据验证转换的过程中未出现错误(error==0时无错误)
	{
		
		//下面为核心部分:按用户指定的项目名称顺序查找Tree-View控件中的Item.
		
		hwnd=HWND(dec_sum);//得到转换后的数据
		
		GetWindowThreadProcessId(hwnd, &PID);
		
		hProcess=OpenProcess(PROCESS_ALL_ACCESS,false,PID);
		if (!hProcess)
			MessageBox("获取进程句柄操作失败!","错误!");
		else
		{
			ptvitem=(TVITEM*)VirtualAllocEx(hProcess, 
						NULL, 
						sizeof(TVITEM), 
						MEM_COMMIT, 
						PAGE_READWRITE);
			pItem=(char*)VirtualAllocEx(hProcess, 
						NULL, 
						16, 
						MEM_COMMIT, 
						PAGE_READWRITE);
			
			if (!ptvitem)
				MessageBox("无法分配内存!","错误!");
			else
			{
				MessageBox("本演示程序将按用户指定的项目名称顺序查找。","提示");
				
				tvitem.mask=TVIF_TEXT;
				tvitem.cchTextMax=512;
				tvitem.pszText=pItem;
				
				//得到根句柄
				tvitem.hItem=(HTREEITEM)::SendMessage(hwnd, 
								TVM_GETNEXTITEM,
								TVGN_ROOT, 
								0x0);

				//选中状态
				::SendMessage(hwnd, 
							TVM_SELECTITEM,
							TVGN_CARET, 
							(long)tvitem.hItem);
				
				//将设置好的结构插入目标进程
				WriteProcessMemory(hProcess, 
								ptvitem, 
								&tvitem, 	
								sizeof(TVITEM), NULL);

				//发送TVM_GETITEM消息
				::SendMessage(hwnd, 
							TVM_GETITEM,
							0, 
							(LPARAM)ptvitem);

				//获取pszText
				ReadProcessMemory(hProcess, pItem, ItemBuf, 512, NULL);

				//MessageBox(ItemBuf,"ITEM TEXT");
				if( strnicmp( ItemBuf,
							str_item_text,
							strlen(str_item_text) ) == 0)
				{
					MessageBox("已经找到!","恭喜");
					Bingo=1;
					//如果根就是我们要找的目标,那么程序执行到这里就可以结束了。
					tvitem.hItem=(HTREEITEM)0x0;
				}
				
				while((long)tvitem.hItem)
				{
					//当此项目能展开时
					while(::SendMessage(hwnd, 
									TVM_EXPAND,
									TVE_EXPAND, 
									(long)tvitem.hItem))
					{

						//选择下一个可见项目
						tvitem.hItem=(HTREEITEM)::SendMessage(hwnd, 
								TVM_GETNEXTITEM,TVGN_NEXTVISIBLE, 
								(long)tvitem.hItem);
						//选中状态
						::SendMessage(hwnd, 
							TVM_SELECTITEM,TVGN_CARET, 
							(long)tvitem.hItem);

						//将设置好的结构插入目标进程
						WriteProcessMemory(hProcess, 
								ptvitem, 
								&tvitem, 
								sizeof(TVITEM), 
								NULL);
						//发送TVM_GETITEM消息
						::SendMessage(hwnd, 
							TVM_GETITEM,
							0, 
							(LPARAM)ptvitem);

						//获取pszText
						ReadProcessMemory(hProcess, 
								pItem, 
								ItemBuf, 
								512, 
								NULL);

						//MessageBox(ItemBuf,"ITEM TEXT");
						if( strnicmp( ItemBuf,
									str_item_text,
									strlen(str_item_text) ) == 0)
						{
							MessageBox("已经找到!","恭喜");
							Bingo=1;

							//如果发现我们要找的目标,那么程序执行到这里就可以结束了。
							tvitem.hItem=(HTREEITEM)0x0;
							break;
						}
						continue;
					}
					
					if(Bingo!=1)
					{
						//当不能再展开的时候,选择下一个可见项目
						tvitem.hItem=(HTREEITEM)::SendMessage(hwnd, 
									TVM_GETNEXTITEM,TVGN_NEXTVISIBLE, 
									(long)tvitem.hItem);

						//选中状态
						::SendMessage(hwnd, 
									TVM_SELECTITEM,
									TVGN_CARET, 
									(long)tvitem.hItem);

						//将设置好的结构插入目标进程
						WriteProcessMemory(hProcess, 
											ptvitem, 
											&tvitem, 
											sizeof(TVITEM), 
											NULL);

						//发送TVM_GETITEM消息
						::SendMessage(hwnd, 
									TVM_GETITEM,
									0, 
									(LPARAM)ptvitem);
									
						ReadProcessMemory(hProcess, 
										pItem, 
										ItemBuf, 
										512, 
										NULL);//获取pszText
										
						//MessageBox(ItemBuf,"ITEM TEXT");
						if( strnicmp( ItemBuf,
										str_item_text,
										strlen(str_item_text) ) == 0)
						{
							MessageBox("已经找到!","恭喜");
							Bingo=1;

							//如果发现我们要找的目标,那么程序执行到这里就可以结束了。
							tvitem.hItem=(HTREEITEM)0x0;
							break;
						}
					}
					
				}
			}
		}
	}
	
	//释放内存
	CloseHandle(hwnd);
	CloseHandle(hProcess);
	VirtualFreeEx(hProcess, ptvitem, 0, MEM_RELEASE);
	//顺序查找完毕
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章