您的位置:首页 > 编程语言 > Java开发

JAB(java access bridge)学习和使用

2016-01-15 16:44 1656 查看

1、JAB的安装

可参见官网的说明:

http://www.oracle.com/technetwork/cn/java/javase/tech/index-jsp-136191-zhs.html

具体步骤大致如下:

(1)先安装java的运行环境,可以运行一个含有java程序的html文件,网页会提示需要安装插件,点击安装插件会自动跳转到java的官网上,下载并安装java的环境。(由于最新的java8安全环境设置不会让浏览器运行本地的java代码,只能去访问一个含有java窗口的网页)。

下载accessbridge2_0_2并安装。由于从java7之后就已经集成了JAB,如果安装的目录中含有对应需要安装的文件,可以不用覆盖,因为JAB版本比较老,最后的更新时间是2014年。具体文件存放的路径如下图:



(1)安装完成之后,打开含有java窗口的网页,在运行安装文件中的测试程序

JavaMonkey.exe 或者 JavaFerret.exe,如打开JavaMonkey-64.exe(64位的程序)之后,点击File 然后选择Refresh Tree,会出现当前窗口所有控件的属性结构图,如:



为了Ferret.exe(跟踪各种事件如鼠标等,并显示控件信息),需要使用下载的旧版本文件,替换最新的文件,但是在程序运行的时候却需要使用最新的版本。

2、JAB的使用

大致步骤可以参考如下文档(该文档在豆瓣上需要10块钱才能下载,暂时使用截图):

首先可以熟悉一下安装文件中的src目录include目录下的cpp和.h文件:

AccessBridgeCalls.h 文件中包含的函数是封装WindowsAccessBridge.dll中函数。

AccessBridgeCalls.c 文件中是函数的定义。

AccessBridgeCallbacks.h 文件中是回调函数的类型申明。

AccessBridgePackages.h 文件中是各种结构和类型的定义,以及函数参数的说明。

 

由于大部分文件中都使用了#include "jni.h",具体使用时还需要找到该文件。









上述文档中提到的代码示例,使用的是C#,本次尝试使用的是C++ MFC工程。

大致步骤如下:

(1)加载DLL中的函数。

加载的过程可以参照AccessBridgeCalls.c文件中的宏LOAD_FP,但是该文件中引用了#include "AccessBridgeDebug.h",而这个文件找不到;如果仅仅是简单注释掉这条语句,那么会提示如下错误:

错误4error LNK2019: 无法解析的外部符号 _PrintDebugString,该符号在函数 _initializeAccessBridge 中被引用

因此并不能直接使用AccessBridgeCalls.h中的函数。(尝试对AccessBridgeCalls.c文件中的代码进行修改,却找不到_PrintDebugString字符串)。

所以只能自己提取DLL中的函数,然后进行使用。

加载dll的代码大致如下:

 

HINSTANCE g_hDllInst;

typedef DWORD (*MYFUNC)();

 

g_hDllInst = LoadLibrary("WindowsAccessBridge-32.dll");

if(g_hDllInst)

{

MYFUNC Windows_run;

LoadOneFunction(g_hDllInst, "Windows_run", Windows_run);

Windows_run();

}

此处加载需要使用到32位的dll,其他两个dll都尝试失败。

为了使用其他函数,需要写多个提取过程或者函数,为了提高效率,参考AccessBridgeCalls.h中的宏:

#define LOAD_FP(result, type, name) \

    PrintDebugString("LOAD_FP loading: %s ...", name); \

    if ((theAccessBridge.result = \

        (type) GetProcAddress(theAccessBridgeInstance, name)) == (type) 0) { \

        PrintDebugString("LOAD_FP failed: %s", name); \

return FALSE; \

    }

 

如自己重新定义宏:

#define LOAD_FP(g_hDllInst, RetType, name, retFunc)   if (  (retFunc = (RetType) GetProcAddress(g_hDllInst, name)) == NULL) { MessageBox(name);}

 

宏的第一个参数是dll加载得到的 HINSTANCE ,第二个参数是函数的类型,第三个是函数的名称,第四个是接收函数地址的函数指针。

将需要使用到的函数都通过该方法进行加载,具体函数类型需要参照AccessBridgeCalls.h。

 

按照文档中的说明,函数的加载过程和Windows_run()放在对话框的初始化之中。

 

(2)获取java窗口的句柄。

直接使用FindWindow获取到的hwnd并不是java窗口(通过IsJavaWindow函数判断)。首先要通过工具AutoIt(使用64位的exe)获取窗口的标题和类名。



代码如下:

CWnd* windowHandle = FindWindow("SunAwtFrame","TARGET Corporation Advance Ship Notice");

HWND javaWindow = windowHandle->GetSafeHwnd();

IsJavaWindowFP IsJavaWindow = NULL;

LOAD_FP(g_hDllInst, IsJavaWindowFP, "isJavaWindow", IsJavaWindow);

if (!IsJavaWindow(javaWindow))

{

MessageBox("没有获取到TARGET Corporation Advance Ship Notice java 窗口!请先加载表单后再尝试!");

return;

}

(1)根据句柄获取虚拟机编号vmID 和根节点ac。使用到的AccessibleContext等类型,定义都在AccessBridgePackages.h文件中,因此需要包含该文件。

代码如下:

long vmID = 0;;

AccessibleContext ac = 0;;

if (GetAccessibleContextFromHWND(javaWindow, &vmID, &ac) == FALSE)

{

MessageBox("没有获取到vmID");

return;

}

 

上述两个步骤中,如果结果返回的是FALSE,说明获取过程失败,需要找到原因再继续。

 

(2)根据vmID和ac获取窗口信息。使用的函数和代码如下:

AccessibleContextInfo info;

BOOL bRet = FALSE;

bRet = GetAccessibleContextInfo(vmID,ac,&info);

if (bRet == FALSE)

{

MessageBox("获取info错误");

return;

}

AccessibleContextInfo结构的定义也在AccessBridgePackages.h头文件中。通过该结构可以看到窗口的具体信息。

 

(3)获取子节点。

首先通过JavaMonkey去找到目标节点的层次结构,将该层次通过一个数组来保存,如:

//order表单

int g_levelOrder[LEVEL_SIZE] = {0,1,0,1,0,0,0,1}; (0表示第一个子节点)

int g_nDepthOrder = 8;

利用已经获取的vmID 和ac 以及层次数组 获取子节点的ac函数如下:

//根据深度数组去找到目标节点

AccessibleContext GetAcessContextByArray( long vmId, AccessibleContext rootAc, int levelArray[], int length )

{

AccessibleContext childAc = rootAc;

for (int i = 0; i < length && childAc != 0; i++)

{

childAc = GetAccessibleChildFromContext(vmId, childAc, levelArray[i]);

}

if (childAc == 0)

{

MessageBox("没有获取到控件ac!");

}

return childAc;

}

 

如果控件的info信息中含有name,也可以通过name遍历查找得到该节点。递归查找的函数如下(在C#示例中参考修改得到,C#版本含有错误,只能垂直遍历):

AccessibleContext  FindAccessContext(long vmID, AccessibleContext parentAC, const wchar_t * name)

{

AccessibleContext act = 0;

AccessibleContextInfo aaci ;

GetAccessibleContextInfo(vmID, parentAC, &aaci);

for (int i = 0; i < aaci.childrenCount; i++)

{

AccessibleContext childAC = GetAccessibleChildFromContext(vmID, parentAC, i);

AccessibleContextInfo aacif;

GetAccessibleContextInfo(vmID, childAC, &aacif);

const wchar_t* Aname = aacif.name;

int childCount = aacif.childrenCount;

if (wcscmp(name,Aname) == 0)

{

act = childAC;

return act;

}

else

{

if (childCount > 0)

{

//如果有子节点 递归

AccessibleContext acTemp =  FindAccessContext(vmID, childAC, name);

if (acTemp != 0)

{

//如果找到则返回,否则继续找其他兄弟路径

return acTemp;

}

}

}

}

return act;

}

 

(5)在text类型的控件中写入文本:

 

在获取到text控件的ac之后,直接使用setTextContents函数就可以修改界面编辑框中的内容:

setTextContents(vmID,childAc, L"hello");

 

(6)模仿点击按钮:

首先需要通过getAccessibleActions函数得到该控件可以执行的操作,然后使用doAccessibleActions函数来执行你需要模拟的动作,具体代码如下:

//模拟按钮动作

Void ClickOneButton(long vmID, AccessibleContext acButton)

{

//获取控件的可执行动作

AccessibleActions actionList;

getAccessibleActions(vmID, acButton, &actionList);

jint nReturn = 0;

//将要执行的动作赋值给TODO

AccessibleActionsToDo actionTodoList;

actionTodoList.actions[0] = actionList.actionInfo[0];

actionTodoList.actionsCount = 1;

//执行该动作

doAccessibleActions(vmID, acButton, &actionTodoList, &nReturn);

if (nReturn < 0)

{

//成功与不成功都是返回的-1

//MessageBox("当前动作没有执行成功!");

}

}

在点击动作时,如果弹出一个新的java窗口,程序只能在窗口消失后继续运行,笔者尝试想找一个发送消息给java窗口的函数

(7)如果还需要使用按钮回调函数,可以使用SetMouseClicked函数来设定。回调函数需要自己按照说明进行定义。

//MouseClickedDelegate(vmID, cMouseClickedEvent, acClose);

SetMouseClicked(MouseClickedDelegate);

 

(8)为了控制下拉列表,可以使用Selection函数族。例如设置选中某个选项,

AddAccessibleSelectionFromContext函数。其类型是

typedef void (*AddAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as, int i);

其中AccessibleSelection 这个类型其实和AccessibleContext 是相同的东西,都可以理解为控件的句柄或者指针。所以首先获取到list或者comb控件,然后设置选中i选项(从0开始)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: