aardio官方社区

 找回密码
 注册会员

!connect_header_login!

只需一步,快速开始

搜索
查看: 7639|回复: 10

读取QQ消息

[复制链接]

188

主题

2544

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
14186
发表于 2017-10-15 15:11:58 | 显示全部楼层 |阅读模式
import com;

var oleaut = raw.loadDll("OleAut32.DLL");
var oleacc = raw.loadDll("OLEACC.DLL");
IID_IAccessible =
'\xE0\x36\x87\x61\x3D\x3C\xCF\x11\x81\x0C\x00\xAA\x00\x38\x9B\x71';

var findAccessibleChildren;
findAccessibleChildren =
function(accParent,name){

   
/*
    这里用结构体数组也可以,直接分配内存代码更简单,
    因为aardio分配内存时已经初始化为VT_EMPTY,所以不需要 VariantInit
    */

   
var output = raw.buffer( 16/*sizeof(Variant)*/ * accParent.accChildCount );

    obtained = {int size};
//用结构体给API提供int指针
    var hr,obtained = oleacc.AccessibleChildren(accParent, 0,accParent.accChildCount, output,obtained);
   
if( hr < 0 ) return;//COM中小于0的都是错误码

   
for(i=0;obtained.size-1;1){
        
var p = raw.toPointer(output,16*i);
        
var variant = com.Variant( p,true);//指针转换为Variant对象,方便操作
        oleaut.VariantClear(p);
        
        
if( variant.vt != 9/*_VT_DISPATCH*/ ) continue;
        
var accChild = com.QueryObject( variant.pdispVal ); //转换为COM对象(COM.IDispatch)
        if( accChild.accName(0/*CHILDID_SELF*/) == name ){  
            
var v = accChild.accValue(0/*CHILDID_SELF*/);
            
if(#v) return v;
        }

        
var v = findAccessibleChildren(accChild,name);
        
if(#v) return v;
    }
}

findAccessibleString =
function(hwnd,name){
   
var pvObject = {ptr p}
   
if( 0 == oleacc.AccessibleObjectFromWindow(hwnd,0,IID_IAccessible,pvObject) ){
        
var accParent = com.QueryObject( pvObject.p ); //转换为COM对象(COM.IDispatch)
        com.Release(pvObject.p);
        
        
return findAccessibleChildren( com.QueryObject( pvObject.p ),name );
    }   
}

import winex;
import console;

for hwnd in winex.each( "TXGuiFoundation") {
   
var qqMessage = findAccessibleString(hwnd,"消息");
   
if( #qqMessage ) win.msgbox( qqMessage )
}

console.pause();
回复

使用道具 举报

188

主题

2544

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
14186
 楼主| 发表于 2017-10-15 15:16:33 | 显示全部楼层

换一种写法,这次改用结构体数组

import com;

var oleaut = raw.loadDll("OleAut32.DLL");
var oleacc = raw.loadDll("OLEACC.DLL");
IID_IAccessible =
'\xE0\x36\x87\x61\x3D\x3C\xCF\x11\x81\x0C\x00\xAA\x00\x38\x9B\x71';

var findAccessibleChildren;
findAccessibleChildren =
function(accParent,name){
   
var variants = {
        struct item[] ={  
//这里没有声明数组长度
            length = accParent.accChildCount; //使用length在运行时指定数组长度
            {
                WORD vt;
                WORD r1;
                WORD r2;
                WORD r3;
                ptr pdispVal;
                ptr r4;
            };
//结构体数组至少要声明第一个结构体成员
        }
    }
   
   
var obtained = {int size};//用结构体给API提供int指针
    var hr = oleacc.AccessibleChildren(accParent, 0,accParent.accChildCount, variants,obtained);
   
if( hr < 0 ) return;//COM中小于0的都是错误码

   
for(i=1;obtained.size;1){
        
var variant = variants.item[ i ]
        
if( variant.vt != 9/*_VT_DISPATCH*/ ) {
            oleaut.VariantClear(variant);
            
continue;
        }
        
        
var accChild = com.QueryObject( variant.pdispVal ); //转换为COM对象(COM.IDispatch)
        oleaut.VariantClear(variant);;//释放Variant对象
        
        
if( accChild.accName(0/*CHILDID_SELF*/) == name ){  
            
var v = accChild.accValue(0/*CHILDID_SELF*/);
            
if(#v) return v;
        }
        
        
var v = findAccessibleChildren(accChild,name);
        
if(#v) return v;
    }
}

findAccessibleString =
function(hwnd,name){
   
var pvObject = {ptr p}
   
if( 0 == oleacc.AccessibleObjectFromWindow(hwnd,0,IID_IAccessible,pvObject) ){
        
var accParent = com.QueryObject( pvObject.p ); //转换为COM对象(COM.IDispatch)
        com.Release(pvObject.p);
        
        
return findAccessibleChildren( com.QueryObject( pvObject.p ),name );
    }   
}

import winex;
import console;

for hwnd in winex.each( "TXGuiFoundation") {
   
var qqMessage = findAccessibleString(hwnd,"消息");
   
if( #qqMessage ) win.msgbox( qqMessage )
}

回复

使用道具 举报

188

主题

2544

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
14186
 楼主| 发表于 2017-10-15 15:28:52 | 显示全部楼层
之前在公众号上发的代码,做了一些小的改进。
回复

使用道具 举报

3

主题

51

帖子

325

积分

二级会员

Rank: 3Rank: 3

积分
325
发表于 2017-10-15 22:24:54 | 显示全部楼层
版主,这个对QQ版本有什么要求吗?
回复

使用道具 举报

188

主题

2544

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
14186
 楼主| 发表于 2017-10-15 22:47:52 | 显示全部楼层
幻月 发表于 2017-10-15 22:24
版主,这个对QQ版本有什么要求吗?

我试了没问题,但并没有测试过QQ的所有版本。
回复

使用道具 举报

3

主题

51

帖子

325

积分

二级会员

Rank: 3Rank: 3

积分
325
发表于 2017-10-15 23:01:21 | 显示全部楼层
我用的是8.9.4 21603版本,电脑同时登陆3个QQ,并未收到消息
回复

使用道具 举报

188

主题

2544

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
14186
 楼主| 发表于 2017-10-15 23:03:12 | 显示全部楼层
幻月 发表于 2017-10-15 23:01
我用的是8.9.4 21603版本,电脑同时登陆3个QQ,并未收到消息
你要打开QQ聊天窗口,收发消息,然后从聊天窗口读聊天记录,没打开读不了的。
回复

使用道具 举报

3

主题

51

帖子

325

积分

二级会员

Rank: 3Rank: 3

积分
325
发表于 2017-10-15 23:07:38 | 显示全部楼层
嗷嗷,这样啊,了解了,我再试试
回复

使用道具 举报

2

主题

39

帖子

369

积分

二级会员

Rank: 3Rank: 3

积分
369
发表于 2017-10-16 09:01:28 | 显示全部楼层
窗口没过滤,遍历到的窗口可能不是聊天窗口而读不到消息,最后的窗口遍历写成这样可能好点:
for hwnd in winex.each( "TXGuiFoundation") {
        var title = winex.getText(hwnd);
        if title != "" ? title != "QQ" ? title != "TXMenuWindow" ? title != "腾讯网迷你版" {
            var qqMessage = findAccessibleString(hwnd,"消息");
            if( #qqMessage ) win.msgbox( qqMessage )
    }
}
其实我想问Jacenvariant结构体定义成这样该怎么理解,{
                WORD vt;
                WORD r1;
                WORD r2;
                WORD r3;
                ptr pdispVal;
                ptr r4;
            };
看msdn也是很迷糊的,虽然自已瞎折腾了下可以用下面代码取出群成员,但是还是只知其然不知其所以然
        if variant.vt == 3/*VT_I4*/ { //元素
                if accParent.accRole(tonumber(variant.pdispVal)) == 34 ? accParent.accState(tonumber(variant.pdispVal)) != 32768 {
                                console.debug(accParent.accName(tonumber(variant.pdispVal)));//指针转child ID   -- 取群成员
                        }
        }
回复

使用道具 举报

188

主题

2544

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
14186
 楼主| 发表于 2017-10-16 09:20:47 | 显示全部楼层
Danboy 发表于 2017-10-16 09:01
窗口没过滤,遍历到的窗口可能不是聊天窗口而读不到消息,最后的窗口遍历写成这样可能好点:
for hwnd in w ...

findAccessibleString(hwnd,"消息"); 的第二个参数就类似过滤了标题,
应当取不到消息会继续到其他窗口找。我只是简单的举个例子,你可以根据需要加代码。

Variant的介绍MSDN看了模糊,你可以看网上其他的文章,看你写的代码其实你已经理解了,如果要取多个不同的类型,可以参考前面发的使用 com.Variant 的代码。
var variant = com.Variant( p,true);//指针转换为Variant对象,方便操作
取到对象以后,你可以用 variant.value 直接获取值。
回复

使用道具 举报

188

主题

2544

帖子

1万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
14186
 楼主| 发表于 2020-11-22 15:40:23 | 显示全部楼层
上面的代码有几处BUG,
其中一个主要的问题:IDispatch指针需要用com.GetIUnknown()转换为IAccessible指针,才能传入AccessibleObjectFromWindow的第一个参数,不然有些程序里会导致内存错误。

现在可以 aardio 新版提供的 winex.accObject 读取QQ消息会更简单,几句代码就可以了:

//MSAA自动化接口
import winex;
import winex.accObject;
import console;

for hwnd in winex.each( "TXGuiFoundation" ) {
   
   
var accObject = winex.accObject.fromWindow(hwnd)
   
if(accObject){
        
var accMessage = accObject.find(role="list")
        
if(accMessage){
            
for accChild in accMessage.each(){
                console.log(accChild.roleText(),accChild.name(  ),accChild.value())
            }   
        }
    }
}
   
console.pause(
true);



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

手机版|未经许可严禁引用或转载本站文章|站长邮箱|aardio.com|aardio官方社区 ( 皖ICP备09012014号 )

GMT+8, 2021-6-19 09:08 , Processed in 0.050323 second(s), 22 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表