获取程序在任务栏中按钮的位置的方法
2010-01-06 00:34
260 查看
本人是上海同济大学软件学院的一个学生,在做一个期末项目的时候希望能够模拟“最小化”的一个动画效果,所以需要获得程序的任务栏按钮位置。但是从网上搜索的结果出乎意料的沮丧,好像真的没有一篇文章给出了完整的答案。所以今天努力了一下,给出一个正确的方法,代码直接就贴下面了用于交流。
由于按钮不是窗口,所以除非微软提供函数接口,否则我们无法得知任务栏是如何绘制的。
//头文件:
#include"Commctrl.h"
从网上看到,对一个TabButton(任务栏)可以发送以下消息(前题是有上述头文件)
TB_GETBUTTONTEXT
TB_GETBUTTONINFO
TB_BUTTONCOUNT
等等,但是http://topic.csdn.net/t/20030423/13/1697563.html这个文章只介绍了TB_BUTTONCOUNT,原因是什么呢?大家可以自己试一下,对着一个找到的任务栏窗口发送TB_GETBUTTONINFO有什么结果,我的结果是报了一个系统错误(从后面可以知道,是访问了一块没有被开辟的内存),然后任务栏重启(句柄被修改了,说明是重启过了)。
那么仔细看msdn
似乎没有任何问题。
我的调用方法如下(tool是窗口句柄):
DWORD si = SendMessage(tool, TB_GETRECT, (WPARAM)0, (LPARAM)prect);
一开始我以为是按钮的iID是一些特殊的整数,但是从0试到10几发现都有问题。每次都会出错重启一下任务栏(本人水平有限,不知道怎么获得报错信息,早点知道说不定早点就想到了),导致我想到一个问题,我传入的参数prect是我的进程中的地址,我之前直接通过new函数获得,但是我没有传入本进程的进程号或者已经打开了的进程句柄,任务栏对应的进程如何获得我的进程映射地址对应的内存地址?显然,TB_GETRECT 应该只对本地进程地址进行操作的。
既然这样,那我只需要为任务栏进程开辟对应的空间即可。
步骤简述:
1、找到任务栏的窗口句柄(我这里方便起见,直接用spy++抓获)
2、通过GetWindowThreadProcessId获得窗口对应的进程号
3、OpenProcess使用PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE这三个权限打开进程
4、VirtualAllocEx为此进程分配地址空间(空间必须开足够)
5、SendMessage发送消息,把分配得到的地址作为参数传入。
6、ReadProcessMemory将修改之后的内存复制一份到本地进程,用于读取。
7、(作为程序员素质的必须)VirtyalFreeEx释放内存。
代码:
for(int j = 0; j<50; j++)
{
SIZE_T len = 256;//sizeof(RECT);
TCHAR * ptb = new TCHAR[256];
SIZE_T size;
HWND tool = (HWND)0x002906C6;
DWORD proID;
GetWindowThreadProcessId(tool, &proID);
HANDLE hPro = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE , NULL, proID);
PVOID pBun = VirtualAllocEx(hPro, NULL, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
UINT buffer = len;
DWORD si = SendMessage(tool, TB_GETBUTTONTEXT , (WPARAM)j, (LPARAM)pBun);
ReadProcessMemory(hPro, pBun, (LPVOID)ptb, len, &size);
VirtualFreeEx(hPro, pBun, len, MEM_DECOMMIT);
delete ptb;
}
非常简短的几行就够了,理解起来应该不会让人头晕,其中tool这个窗口句柄的获得大家自由发挥^_^ 我说过我是直接用spy++抓取的。
最后扩展一下:
--使用此方法察看了之后发现,任务栏的“按钮”数量不只有现实的那么几个,一般程序都有两个(另外一个大小为0,不信自己看),我用VS2008编译生成的可执行程序运行出来之后也有两个,一个和进程同名,一个和窗口同名,这个大概就属于“编译器做的手脚”,因为有的程序只有一个button对应,所以应该可以修改编译设置,具体哪个有待高人解决。
--按钮ID从0开始,我上面的程序最后一个按钮返回的pBun一个指向NULL的指针,那么这个就是结束标记。
--有些消息,例如:TB_GETBUTTONINFO消息的发送,发送之前得先填充TBBUTTONINFO结构体的的cbSize属性,需要额外使用一个WriteProcessMemory把前四个字节填充掉。这些结构体都定义在Commctrl.h中,所以查一下就出来了。
--带有WS_EX_TOOLWINDOW属性的窗口没有按钮信息对应。
不好意思,似乎最重要的东西忘记了,就是怎么获得程序对应的按钮?我的方法是枚举按钮的TEXT信息(就是我上面写的),如果发现了一个和进程同名的按钮,并且紧跟着一个和窗口同名的按钮,那么这个和窗口同名的按钮的ID就是我需要的ID,然后再发送TB_GETRECT获得按钮位置。
还没完,因为这个按钮位置是相对窗口的而不是显示器,刚刚一开始不是都已经获得任务栏的句柄了吗,接下来只要通过GetWindowRect获得窗口位置,再加上按钮坐标就行了。
##完
由于按钮不是窗口,所以除非微软提供函数接口,否则我们无法得知任务栏是如何绘制的。
//头文件:
#include"Commctrl.h"
从网上看到,对一个TabButton(任务栏)可以发送以下消息(前题是有上述头文件)
TB_GETBUTTONTEXT
TB_GETBUTTONINFO
TB_BUTTONCOUNT
等等,但是http://topic.csdn.net/t/20030423/13/1697563.html这个文章只介绍了TB_BUTTONCOUNT,原因是什么呢?大家可以自己试一下,对着一个找到的任务栏窗口发送TB_GETBUTTONINFO有什么结果,我的结果是报了一个系统错误(从后面可以知道,是访问了一块没有被开辟的内存),然后任务栏重启(句柄被修改了,说明是重启过了)。
那么仔细看msdn
TB_GETRECT wParam = (WPARAM)(INT) iID; lParam = (LPARAM)(LPRECT) lprc;
Parameters
iID Button identifier. lprc Returns nonzero if successful, or zero otherwise. A RECT structure that receives the bounding rectangle information.似乎没有任何问题。
我的调用方法如下(tool是窗口句柄):
DWORD si = SendMessage(tool, TB_GETRECT, (WPARAM)0, (LPARAM)prect);
一开始我以为是按钮的iID是一些特殊的整数,但是从0试到10几发现都有问题。每次都会出错重启一下任务栏(本人水平有限,不知道怎么获得报错信息,早点知道说不定早点就想到了),导致我想到一个问题,我传入的参数prect是我的进程中的地址,我之前直接通过new函数获得,但是我没有传入本进程的进程号或者已经打开了的进程句柄,任务栏对应的进程如何获得我的进程映射地址对应的内存地址?显然,TB_GETRECT 应该只对本地进程地址进行操作的。
既然这样,那我只需要为任务栏进程开辟对应的空间即可。
步骤简述:
1、找到任务栏的窗口句柄(我这里方便起见,直接用spy++抓获)
2、通过GetWindowThreadProcessId获得窗口对应的进程号
3、OpenProcess使用PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE这三个权限打开进程
4、VirtualAllocEx为此进程分配地址空间(空间必须开足够)
5、SendMessage发送消息,把分配得到的地址作为参数传入。
6、ReadProcessMemory将修改之后的内存复制一份到本地进程,用于读取。
7、(作为程序员素质的必须)VirtyalFreeEx释放内存。
代码:
for(int j = 0; j<50; j++)
{
SIZE_T len = 256;//sizeof(RECT);
TCHAR * ptb = new TCHAR[256];
SIZE_T size;
HWND tool = (HWND)0x002906C6;
DWORD proID;
GetWindowThreadProcessId(tool, &proID);
HANDLE hPro = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE , NULL, proID);
PVOID pBun = VirtualAllocEx(hPro, NULL, len, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
UINT buffer = len;
DWORD si = SendMessage(tool, TB_GETBUTTONTEXT , (WPARAM)j, (LPARAM)pBun);
ReadProcessMemory(hPro, pBun, (LPVOID)ptb, len, &size);
VirtualFreeEx(hPro, pBun, len, MEM_DECOMMIT);
delete ptb;
}
非常简短的几行就够了,理解起来应该不会让人头晕,其中tool这个窗口句柄的获得大家自由发挥^_^ 我说过我是直接用spy++抓取的。
最后扩展一下:
--使用此方法察看了之后发现,任务栏的“按钮”数量不只有现实的那么几个,一般程序都有两个(另外一个大小为0,不信自己看),我用VS2008编译生成的可执行程序运行出来之后也有两个,一个和进程同名,一个和窗口同名,这个大概就属于“编译器做的手脚”,因为有的程序只有一个button对应,所以应该可以修改编译设置,具体哪个有待高人解决。
--按钮ID从0开始,我上面的程序最后一个按钮返回的pBun一个指向NULL的指针,那么这个就是结束标记。
--有些消息,例如:TB_GETBUTTONINFO消息的发送,发送之前得先填充TBBUTTONINFO结构体的的cbSize属性,需要额外使用一个WriteProcessMemory把前四个字节填充掉。这些结构体都定义在Commctrl.h中,所以查一下就出来了。
--带有WS_EX_TOOLWINDOW属性的窗口没有按钮信息对应。
不好意思,似乎最重要的东西忘记了,就是怎么获得程序对应的按钮?我的方法是枚举按钮的TEXT信息(就是我上面写的),如果发现了一个和进程同名的按钮,并且紧跟着一个和窗口同名的按钮,那么这个和窗口同名的按钮的ID就是我需要的ID,然后再发送TB_GETRECT获得按钮位置。
还没完,因为这个按钮位置是相对窗口的而不是显示器,刚刚一开始不是都已经获得任务栏的句柄了吗,接下来只要通过GetWindowRect获得窗口位置,再加上按钮坐标就行了。
##完
相关文章推荐
- Qt程序关于路径、用户目录路径、临时文件夹位置获取方法
- Android平台上捕获Back键的事件,Back键是手机上的后退键,一般的软件不捕获相关信息可能导致你的程序被切换到后台,而回到桌面的尴尬情况,在Android上有两种方法来获取该按钮的事件
- 获取系统任务栏位置宽高信息的方法
- 获取单选按钮方法不对导致程序闪退
- 获取其他程序焦点位置句柄的方法
- Android TextView获取ClickSpan点击位置的方法 3ff0
- CodedUI Test 测试WPF程序,无法获取控件属性值的解决方法
- windows7设置任务栏按钮【从不合并】并且【隐藏标签】的方法
- [转]C# 获取程序路径的方法集锦
- jQ获取目标div中鼠标的位置信息的俩种方法
- 获取光标位置方法研究
- 获取程序崩溃信息的方法
- C#获取项目程序路径的方法
- 基于JavaScript实现 获取鼠标点击位置坐标的方法
- 基于JavaScript实现 获取鼠标点击位置坐标的方法
- 获取滚动条位置的通用方法--兼容ie6,ie7,firefox2,firefox3,chrome等
- 用Qt5.9Creator获取鼠标位置和用键盘移动按钮例子
- Windows下获取当前程序文件名或完整路径方法
- .NET中常用获取程序根目录的方法
- JS实现点击按钮获取页面高度的方法