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);
	//順序查找完畢
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章