1、JAB的安装
可参见官网的说明:
技术资源 | Oracle 中国
具体步骤大致如下:
(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的使用首先可以熟悉一下安装文件中的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()放在对话框的初始化之中。
注:
对“initializeAccessBridge”的调用要求您有一个活动的 Windows 消息泵。在“initializeAccessBridge”内部,它(最终)创建了一个隐藏的对话窗口(使用 CreateDialog)。创建对话框后,它会使用已注册的消息执行 PostMessage。访问桥的 JavaVM 端响应此消息,并向创建的对话框回发另一条消息(它似乎是您的应用程序和 Java VM 之间的“hello”类型握手)。因此,如果您的应用程序没有活动的消息泵,这很重要,因为在收到此消息之前,网桥永远不会正确初始化,因此所有对“IsJavaWindow”的调用都会失败(在内部,一旦收到消息,网桥就会初始化一个内部结构——因此,没有活动的消息泵,没有初始化)。我猜这就是你永远不会收到回调消息的原因。
不仅如此,您还必须在消息泵可以处理消息的位置调用 initializeAccessBridge,然后才能调用 IsJavaWindow。
这就是 JavaFerret 和 JavaMonkey 工作的原因——它们在启动时初始化,然后在桥接器通过消息泵接收到初始化消息之后枚举对菜单消息的响应。
我能够在我的 MFC 对话应用程序(和我们基于 MFC 的应用程序)中解决这个问题的方法是确保您在某个点调用“initializeAccessBridge”,这样内置的 MFC 消息泵可以推送“hello”在您使用它之前,将消息返回到这个隐藏的对话框。在简单的 MFC 对话框案例中,这意味着在 OnInitDialog 中调用 initializeAccessBridge,并调用枚举过程以响应按钮调用(例如)。如果您希望在对话框出现时立即发生枚举,您可以使用计时器在 OnInitDialog 完成后触发(例如 10 毫秒)以允许处理初始化消息。
如果您计划在控制台应用程序中使用它,您将需要编写自己的自定义消息泵来处理初始化消息。
(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
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?