vb利用SendMessage操作listview居然失败,太怪异了!!
我参考网上的代码,这段代码我测试了,对于“任务管理器”是有效的,但是对于我实际项目中的listview就无效了。。。代码如下:
...省略定义
Private Type LVITEM
mask As Long
iItem As Long
iSubItem As Long
State As Long
stateMask As Long
pszText As Long
cchTextMax As Long
iImage As Long
lParam As Long
iIndent As Long
End Type
Private Sub Command1_Click()
Dim hWnd As Long
hWnd = FindWindow(vbNullString, "GAINCNGTMGR - Sterling Trader(R) Manager")
'hWnd = FindWindowEx(hWnd, 0, "#32770", vbNullString)
hWnd = FindWindowEx(hWnd, 0, "SysListView32", vbNullString)
Call ListView_SetItemText(hWnd, 5, 0, "12345667") '这句执行了无效
Call ListView_DelItem(hWnd, 5)
'这句执行了看到目标sylistview32 已经删掉了第5行,但马上sylistview32又自动出现刚才被删掉的那行,出现在尾行,意思就是仍然无法删除
End Sub
'*************************************************************************
'**函 数 名:ListView_SetItemText
'**输 入:ByVal hWnd(Long) - ListView控件句柄
'** :ByVal iItem(Long) - 行
'** :ByVal iSubItem(Long) - 列
'** :ByVal ItemText(String) - 更改的内容
'**输 出:(Boolean) -
'**功能描述:更改其他程序ListView控件中某个Item的内容
'**全局变量:
'**调用模块:
'**作 者:bbb620
'**日 期:2008-07-27 13:26:03
'**修 改 人:
'**日 期:
'**版 本:V1.0.0
'*************************************************************************
Public Function ListView_SetItemText(ByVal hWnd As Long, ByVal iItem As Long, ByVal iSubItem As Long, ByVal ItemText As String) As Boolean
Dim PID As Long
Dim hProcess As Long
Dim nSize As Long
Dim plvItem As Long
Dim p_MyItemText As Long
Dim myItem As LVITEM
nSize = LenB(StrConv(ItemText, vbFromUnicode)) '获取字符串长度
GetWindowThreadProcessId hWnd, PID '获取与指定窗口关联在一起的一个进程和线程标识符
hProcess = OpenProcess(PROCESS_ALL_ACCESS, False, PID) '打开一个现有进程的句柄
If hProcess <> 0 Then
plvItem = VirtualAllocEx(hProcess, 0, Len(myItem), MEM_COMMIT, PAGE_READWRITE) '分配内存
p_MyItemText = VirtualAllocEx(hProcess, 0, nSize, MEM_COMMIT, PAGE_READWRITE) '分配内存
'初始化结构
myItem.iSubItem = iSubItem
myItem.pszText = p_MyItemText
If plvItem And p_MyItemText Then
Call WriteProcessMemory(hProcess, p_MyItemText, ByVal ItemText, nSize, 0) '写入内存
Call WriteProcessMemory(hProcess, plvItem, myItem, Len(myItem), 0) '写入内存
Call SendMessage(hWnd, LVM_SETITEMTEXT, iItem, ByVal plvItem)
ListView_SetItemText = True
'释放内存
CloseHandle (hWnd)
CloseHandle (hProcess)
Call VirtualFreeEx(hProcess, plvItem, 0, MEM_RELEASE)
Call VirtualFreeEx(hProcess, p_MyItemText, 0, MEM_RELEASE)
End If
End If
End Function
'*************************************************************************
'**函 数 名:ListView_DelItem
'**输 入:ByVal hwnd(Long) - ListView控件句柄
'** :ByVal i(Long) - 行
'**输 出:无
'**功能描述:删除其他程序ListView控件中的某行Item
'*************************************************************************
Public Function ListView_DelItem(ByVal hWnd As Long, ByVal i As Long)
SendMessage hWnd, LVM_DELETEITEM, i, 0
End Function
我本来是用c#的,在c#下也是无法取得listview的数据,后来参考vb代码以为可以解决,谁知道仍然是一样。
这段代码vb中尝试修改某个items的数据项,也是失败,运行中我发现listview那个目标闪了一下,但原来的值不变。删除的时候,明明看到已经删除了,但一会马上又出现了原来那一行。难道目标的listview是禁止了SendMessage 消息,不可修改也不可取值的?
c#版我的帖子发了几天了,无人解决,所以到这边求教,希望遇到高手指点一下!
谢谢!!!!!! --------------------编程问答-------------------- 你的目标 ListView 可能是 OwnerDraw 风格的。
数据由程序管理而不是存放在 ListView 中的。
所以操作 ListView 是没有效果的。 --------------------编程问答--------------------
那么请问有什么办法可以读到listview的数据吗?
我在c#下可以读到listview的行数以及列数,而文本数据也是读取不到。
谢谢! --------------------编程问答-------------------- 基本上是取不到的。
就像不带现金带信用卡一样,花钱时直接划账,不怕抢。 --------------------编程问答--------------------
我用spy查看了,
类名 SysListView32
窗口样式是WS_CHILD|WS_HSCROLL|WS_VISIBLE
扩展样式是WS_EX_CLIENTEDGE
这种也不行吗 --------------------编程问答--------------------
请问怎么才能确定 ListView 是 OwnerDraw 风格的,我在spy下没看到OwnerDraw ?
另外我在网上看到有人说,远程子类化,再截取LVN_GETDISPINFO消息可以取出文本数据?
另外还有个说法,“...注入目标进程,用经过改写的FindControl来找到目标窗口的对象,然后再根据对象->属性或方法的方式读取内容”
请问如上思路是否可行?
难道真的没有办法取出listview的文本数据了吗?
还请各位高手指点一二!
谢谢!!! --------------------编程问答-------------------- MSDN 的 VC 样例中有一个 VListVw,可以参考一下,如果是这样的风格应该可以用 LVN_GETDISPINFO 取得数据。
但是也可能是通过子类化实现的自绘,数据完全自己控制,就取不到了。 --------------------编程问答-------------------- 推荐使用spy4win软件。 --------------------编程问答--------------------
我正是使用spy4win的,但一点用处没有,他有个内容框,仍然取不到值,一片空白。
此外,我查看窗口风格,也没有出现OwnerDraw之类的字样。
我目前所知道的就是,目标程序的数据是来自对方服务器,且目标程序是vb写的
谢谢!! --------------------编程问答-------------------- 求教还有什么办法可以去到这个listview的数据,我那天看到一个人说可以子类化再拦截消息,现在在研究子类化,过2天试试,不知道这条路行不行的通。
如果实在行不通,我只能对那个listview实现截图,用学习图像分析处理,强行把上面的文字读取出来。。。。。 --------------------编程问答--------------------
仅供参考
void MyGetListViewItem(HWND hWindow) {--------------------编程问答--------------------
int nLVItemCount;
int nColumns;
DWORD dwProcessID;
HANDLE hProcess;
HWND hHeaderCtrl;
LVITEM lvItemLocal;
HDITEM hdItemLocal;
DWORD dwBytesRead, dwBytesWrite;
BOOL bSuccess,bWriteOK;
LPVOID lpTextRemote;
LPVOID lpListItemRemote;
LPVOID lpHeadItemRemote;
int i,j;
GetWindowThreadProcessId(hWindow,&dwProcessID);
hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessID);
if (!hProcess) goto ABORT0;
lpTextRemote=VirtualAllocEx(hProcess,NULL,nMaxLen+1,MEM_COMMIT,PAGE_READWRITE);
if (!lpTextRemote) goto ABORT1;
lpListItemRemote=VirtualAllocEx(hProcess,NULL,sizeof(LVITEM),MEM_COMMIT,PAGE_READWRITE);
if (!lpListItemRemote) goto ABORT2;
lpHeadItemRemote=VirtualAllocEx(hProcess,NULL,sizeof(HDITEM),MEM_COMMIT,PAGE_READWRITE);
if (!lpHeadItemRemote) goto ABORT3;
nLVItemCount=ListView_GetItemCount(hWindow);
hHeaderCtrl =ListView_GetHeader(hWindow);
nColumns =Header_GetItemCount(hHeaderCtrl);
init();
if (nColumns<=0) {
nColumns=1;
} else {
ln[0]=0;
for (j=0;j<nColumns;j++) {
ZeroMemory(szBuf,nMaxLen+1);
bWriteOK= WriteProcessMemory(hProcess,lpTextRemote,(LPVOID)szBuf,nMaxLen+1,(LPDWORD)&dwBytesWrite);
if (!bWriteOK) goto ABORT4;
hdItemLocal.mask=HDI_TEXT;
hdItemLocal.cchTextMax=nMaxLen;
hdItemLocal.pszText=(LPTSTR)lpTextRemote;
dwBytesWrite=0;
bWriteOK=WriteProcessMemory(hProcess,lpHeadItemRemote,(LPVOID)&hdItemLocal,sizeof(HDITEM),(LPDWORD)&dwBytesWrite);
if (!bWriteOK) goto ABORT4;
SendMessage(hHeaderCtrl,HDM_GETITEM,(WPARAM)j,(LPARAM)lpHeadItemRemote);
bSuccess=ReadProcessMemory(hProcess,lpTextRemote,szBuf,nMaxLen+1,&dwBytesRead);
if (!bSuccess) goto ABORT4;
if (j>0) strcat(ln,"|");
strstrip(szBuf1,szBuf);
strcat(ln,szBuf1);
}
add1(ln);
}
// printf("ListView的Columns数: %d\n",nColumns);
// printf("---------------------------\n");
for (i=0;i<nLVItemCount;i++) {
ln[0]=0;
for (j=0;j<nColumns;j++) {
ZeroMemory(szBuf,nMaxLen+1);
bWriteOK= WriteProcessMemory(hProcess,lpTextRemote,(LPVOID)szBuf,nMaxLen+1,(LPDWORD)&dwBytesWrite);
if (!bWriteOK) goto ABORT4;
lvItemLocal.iItem=i;
lvItemLocal.iSubItem=j;
lvItemLocal.mask=LVIF_TEXT;
lvItemLocal.cchTextMax=nMaxLen;
lvItemLocal.pszText=(LPTSTR)lpTextRemote;
dwBytesWrite=0;
bWriteOK=WriteProcessMemory(hProcess,lpListItemRemote,(LPVOID)&lvItemLocal,sizeof(LVITEM),(LPDWORD)&dwBytesWrite);
if (!bWriteOK) goto ABORT4;
SendMessage(hWindow,LVM_GETITEMTEXT,(WPARAM)i,(LPARAM)lpListItemRemote);
bSuccess=ReadProcessMemory(hProcess,lpTextRemote,szBuf,nMaxLen+1,&dwBytesRead);
//从指定进程存储空间读取文本
if (!bSuccess) goto ABORT4;
if (j>0) strcat(ln,"|");
strstrip(szBuf1,szBuf);
strcat(ln,szBuf1);
}
add1(ln);
}
ABORT4:
VirtualFreeEx(hProcess,lpHeadItemRemote,0,MEM_RELEASE);
ABORT3:
VirtualFreeEx(hProcess,lpListItemRemote,0,MEM_RELEASE);
ABORT2:
VirtualFreeEx(hProcess,lpTextRemote,0,MEM_RELEASE);
ABORT1:
CloseHandle(hProcess);
ABORT0:
over();
return;
}
//---------------------------------------------------------------------------
此外,我查看窗口风格,也没有出现OwnerDraw之类的字样。
ListView 提供的 OwnerDraw 风格只是一种可选方案,不是所有的自绘一定就是通过这种风格实现的。
就像购物付款,你可以刷卡,也可以付现金,后者明显不利于商家追踪你的购物习惯。 --------------------编程问答--------------------
推荐使用spy4win软件。
仅供参考void MyGetListViewItem(HWND hWindow) {
int nLVItemCount;
int nColumns;
DWORD dwProcessID;
HANDLE hProcess;
HWND hHeaderCtrl;
...
}
//---------------------------------------------------------------------------
谢谢!!
我对c++不是很熟悉,我这里有一段vb代码,应该是和你给出的代码是一样的功能。即通过获取目标进程,把进程数据写入内存。代码是这样的,您看下是不是一样的?
Public Function GetListviewItem(ByVal hWindow As Long, ByVal ProcessID As Long, ByVal pColumn As Long, ByVal pRow As Long) As String
Dim Result As Long
Dim myItem As LV_ITEMA
Dim pHandle As Long
Dim pStrBufferMemory As Long
Dim pMyItemMemory As Long
Dim strBuffer() As Byte
Dim Index As Long
Dim tmpString As String
Dim strLength As Long
'******************************
'为动态数组变量重新分配存储空间
'******************************
ReDim strBuffer(MAX_LVMSTRING)
'*****************************************************************************************************
'打开一个现有进程的句柄,返回值Long,如执行成功,返回进程句柄;零表示失败。会设置GetLastError
'Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, ByVal dwProcId As Long) As Long
'参数 类型及说明
'dwDesiredAccess Long,指定这个句柄要求的访问方法。指定API32.TXT文件中以PROCESS_???开头的一个或多个常数
'bInheritHandle Long,如句柄能够由子进程继承,则为TRUE
'dwProcessId Long,要打开那个进程的进程标识符
'*****************************************************************************************************
pHandle = OpenProcess(PROCESS_VM_OPERATION Or PROCESS_VM_READ Or PROCESS_VM_WRITE, False, ProcessID)
'*****************************************************************************************************
'VirtualAllocEx(目标进程的句柄,0,内存区域的大小,分配类型,新分配内存的存取保护类型)返回所分配页面的基址
'*****************************************************************************************************
If pStrBufferMemory = 0 Then
pStrBufferMemory = VirtualAllocEx(pHandle, 0, MAX_LVMSTRING, MEM_COMMIT, PAGE_READWRITE)
End If
'*************************************************
'初始化LV_ITEM 结构
'MyItem.iSubItem 列的索引号
'myItem.pszText 数据内容(此处是一个分配的内存地址)
'*************************************************
myItem.mask = LVIF_TEXT
myItem.iSubItem = pColumn
myItem.pszText = pStrBufferMemory
myItem.cchTextMax = MAX_LVMSTRING
'***********************************************************
'把这个结构写入远程进程process's 存储量
'WriteProcessMemory(目标进程的句柄,地址,写入的数据,字节数,0)
'***********************************************************
If pMyItemMemory = 0 Then
pMyItemMemory = VirtualAllocEx(pHandle, 0, Len(myItem), MEM_COMMIT, PAGE_READWRITE)
End If
Result = WriteProcessMemory(pHandle, pMyItemMemory, myItem, Len(myItem), 0)
'********************************
'发送消息,得到项目信息和写入内存
'********************************
Result = SendMessage(hWindow, LVM_GETITEMTEXT, pRow, ByVal pMyItemMemory)
Result = ReadProcessMemory(pHandle, pStrBufferMemory, strBuffer(0), MAX_LVMSTRING, 0)
Result = ReadProcessMemory(pHandle, pMyItemMemory, myItem, Len(myItem), 0)
'************************
'把字节列阵变成串和送回它
'************************
tmpString = StrConv(strBuffer, vbUnicode)
If InStr(tmpString, Chr$(0)) > 0 Then
tmpString = Left$(tmpString, InStr(tmpString, Chr$(0)) - 1)
End If
tmpString = Trim$(tmpString)
'****************************
'释放分配的内存和关闭进程句柄
'****************************
Result = VirtualFreeEx(pHandle, pStrBufferMemory, 0, MEM_RELEASE)
Result = VirtualFreeEx(pHandle, pMyItemMemory, 0, MEM_RELEASE)
Result = CloseHandle(pHandle)
If Len(tmpString) > 0 Then GetListviewItem = tmpString
End Function
这些代码在普通的listview都是可以取到值的,比如任务管理器,但我那个listview数据是动态实时更新的,取出都是空值。 --------------------编程问答-------------------- 少Lock
不行就需要用WinDbg或WinHex了。
推荐使用WinHex软件查看硬盘或文件或内存中的原始字节内容。
--------------------编程问答-------------------- 请将你的VB代码改成和我的C++代码一样检查每个函数的返回值再试试。
另外有99%的可能是你得到的hWnd不是你想抓取其内容的Listview的hWnd。
参考下面代码:
HWND MyFindListViewWindow()--------------------编程问答-------------------- 运行spy4win,将狗头图标拖动到主窗口,
{
const int MyMaxParentWinCount = 4;
// 父窗口类名数组
char *A_szClassName[MyMaxParentWinCount] = {
"ThunderRT6FormDC",
"ThunderRT6Frame",
"ThunderRT6Frame",
"ListView20WndClass"
};
// 父窗口标题数组
char *A_szWinName[MyMaxParentWinCount] = {
Title,
"",
"",
""
};
// 首先求得顶级父窗口
HWND hMainWin = FindWindow(A_szClassName[0], A_szWinName[0]);
if (NULL==hMainWin) {
return NULL;
}
// 逐次用FindWindowEx函数求出各级子窗口
HWND hNextChildWin=NULL;
HWND hLastWin=NULL;
for (int i=1; i<MyMaxParentWinCount; i++) {
if (1==i) {
hMainWin = FindWindowEx(hMainWin,NULL ,A_szClassName[i],A_szWinName[i]);
} else if (2==i) {
hNextChildWin = FindWindowEx(hMainWin,hNextChildWin,A_szClassName[i],A_szWinName[i]);
if (NULL==hNextChildWin) break;//
hNextChildWin = FindWindowEx(hMainWin,hNextChildWin,A_szClassName[i],A_szWinName[i]);
hLastWin=hNextChildWin;
} else {
hLastWin = FindWindowEx(hLastWin,NULL ,A_szClassName[i],A_szWinName[i]);
}
}
return hLastWin;
}
//---------------------------------------------------------------------------
选“附加工具、导出窗口信息”保存到文件WinInfo.txt,
看里面的“子窗口列表:……”
--------------------编程问答--------------------
运行spy4win,将狗头图标拖动到主窗口,
选“附加工具、导出窗口信息”保存到文件WinInfo.txt,
看里面的“子窗口列表:……”
谢谢。我按照你说的,查看信息如下
子窗口列表:
TreeView的项目数: 8
-------------------------
|-Afx:00400000:b:00010003:00000006:00010482
|---SysListView32
|-----SysHeader32
|---ToolbarWindow32
|---ToolbarWindow32
|---#32770
|-----Static
|---msctls_statusbar32
这里面就一个syslistview32, hWnd应该没错啊 --------------------编程问答--------------------
运行spy4win,将狗头图标拖动到主窗口,
选“附加工具、导出窗口信息”保存到文件WinInfo.txt,
看里面的“子窗口列表:……”
此外,我查看窗口风格,也没有出现OwnerDraw之类的字样。
ListView 提供的 OwnerDraw 风格只是一种可选方案,不是所有的自绘一定就是通过这种风格实现的。
就像购物付款,你可以刷卡,也可以付现金,后者明显不利于商家追踪你的购物习惯。
我最近一直在研究这个问题,我自己用c#在写一个别的程序,里面放了2个listview,定时向listview发送数据显示数据,用上述的代码以及用spy4win7仍然无法获取值,但可以成功获取到listview的行数列数。我也没有用own的风格。
c#下我都是用listview1.Items.AddRange()添加数据,外部仍然无法取得这些数据。 --------------------编程问答-------------------- 参考10楼代码,检查每次函数调用的返回值,如果不正常,输出错误码+错误信息。 --------------------编程问答-------------------- 当前登录用户的权限要够高。杀毒或防火墙软件暂时关掉。 --------------------编程问答--------------------
参考10楼代码,检查每次函数调用的返回值,如果不正常,输出错误码+错误信息。
当前登录用户的权限要够高。杀毒或防火墙软件暂时关掉。
我的c#程序里,有2个listview和一个listbox ,他们都是实时刷新数据,我用spy4win7 是可以获取到listbox的内容的,但是就是listview 取不到数据,只能取到行数列数。
谢谢,因为我对c++不熟悉,要把那段翻译成vb还要点时间。 --------------------编程问答--------------------
我最近一直在研究这个问题,我自己用c#在写一个别的程序,里面放了2个listview,定时向listview发送数据显示数据,用上述的代码以及用spy4win7仍然无法获取值,但可以成功获取到listview的行数列数。我也没有用own的风格。
c#下我都是用listview1.Items.AddRange()添加数据,外部仍然无法取得这些数据。
用 Reflector 看 .Net 的控件代码,你会发现在没有 hWnd 的情况下依旧可以操作控件。
.Net 控件的数据、状态都是存放在对象中的。
不要再纠结这个问题了,不支持就是不支持,除非你能让目标程序不用 .Net 框架。
再次强调,自绘控件不是一定有 OwnerDraw 风格标记的。
--------------------编程问答--------------------
我最近一直在研究这个问题,我自己用c#在写一个别的程序,里面放了2个listview,定时向listview发送数据显示数据,用上述的代码以及用spy4win7仍然无法获取值,但可以成功获取到listview的行数列数。我也没有用own的风格。
c#下我都是用listview1.Items.AddRange()添加数据,外部仍然无法取得这些数据。
用 Reflector 看 .Net 的控件代码,你会发现在没有 hWnd 的情况下依旧可以操作控件。
.Net 控件的数据、状态都是存放在对象中的。
不要再纠结这个问题了,不支持就是不支持,除非你能让目标程序不用 .Net 框架。
再次强调,自绘控件不是一定有 OwnerDraw 风格标记的。
谢谢,并不是我纠结这个问题,因为现在特别特别需要那个读取外部listview的功能,如果这个无法实现,换一个思路,会十分麻烦而且费事费力。
如你所说,意思就是c#的listview是无法从外部读取的?
但现在目标程序换成我自己的程序,listview 和listbox 都是我自己写的,数据也是我自己添加的。但用最前面的vb代码,可以获取到listbox的数据,却无法取得listview的数据。
另外你说的自绘控件意思我不大明白,例如我自己的listview,我并未对控件重绘,只是定时对控件用listview1.Items.AddRange()添加数据,这个应该是c#下listview的最基本的添加数据方式,不算重绘吧。 --------------------编程问答-------------------- 不纠缠术语了。
反正改造利用控件的方法众多,想用一种方法统一取数是不可能的。 --------------------编程问答--------------------
不纠缠术语了。
反正改造利用控件的方法众多,想用一种方法统一取数是不可能的。
谢谢,我1楼的代码是删去listview的某一项,其实我真正需要的功能是“获取listview的文本数据”。只是一直不成功,所以就试着增删listview。
我不明白的是,为什么listview的文本内容已经显示在电脑屏幕上了,应该这些内容至少在电脑内存上把?即便如你所说这些数据是在控件的对象中,那这些对象不也是在内存中?当然这个是我的直观猜测。我在10楼贴的vb代码已经是在获取目标进程读取内存数据,为什么还是空值。。。 --------------------编程问答-------------------- 存在≠可取
随便拉个人到你面前,你能知道他/她脑袋里在想什么? --------------------编程问答-------------------- 你用spy4win获取到的内容都为空的话 hWnd的获取方法就不是你那样获取的了 有遮照窗口
除非你找到他真正的窗口 用spy++看看能找到不
修改了就变了的话 当然是在时刻的读取数据了 所以改不到 --------------------编程问答--------------------
我那个listview数据是动态实时更新的,取出都是空值。
数据再怎么快速更新都得读写内存不是
所以任何数据都能在内存查到的
查不到只能说明查的地址不对
建议用CE之类的工具来查出数据基址 --------------------编程问答--------------------
你用spy4win获取到的内容都为空的话 hWnd的获取方法就不是你那样获取的了 有遮照窗口
除非你找到他真正的窗口 用spy++看看能找到不
修改了就变了的话 当然是在时刻的读取数据了 所以改不到
spy4win有个功能可以把所有子窗口列出来,我前面的回帖已经贴出来了,遮照窗口就不能找到hwnd么,spy++不行啊
--------------------编程问答--------------------
我那个listview数据是动态实时更新的,取出都是空值。
数据再怎么快速更新都得读写内存不是
所以任何数据都能在内存查到的
查不到只能说明查的地址不对
建议用CE之类的工具来查出数据基址
我之所以一直研究,正是有和你一样的想法,认为数据在内存里总应该读得出来。尤其行列数已经读出来了,其实列数不同,远远大于我看到的实际列数,不知道为什么。
这个问题确实太难 --------------------编程问答-------------------- 人人实测发现,相同的代码在WIN7中无法成功,但在win2003,XP 中OK,不知道是什么原因
补充:VB , API