Chinaunix首页 | 论坛 | 博客
  • 博客访问: 9548140
  • 博文数量: 1227
  • 博客积分: 10026
  • 博客等级: 上将
  • 技术积分: 20273
  • 用 户 组: 普通用户
  • 注册时间: 2008-01-16 12:40
文章分类

全部博文(1227)

文章存档

2010年(1)

2008年(1226)

我的朋友

分类: C/C++

2008-04-23 22:06:16

遍历和查找外部程序 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

	*	

	*	

	*	本程序能顺序遍历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

	*	

	*	

	*	本程序能按用户指定的项目名称顺序查找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);

	//顺序查找完毕

结束语

代码写得不够幽雅,大家见笑了。在此之前,类似的拙文我已经写了四篇,希望大家看完后能举一反三。谢谢。

阅读(359) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~