没有从Java Access Bridge接收回调

2019年5月5日 90点热度 0条评论

我正在尝试使用Java Access Bridge从C++应用程序内部获取有关Swing组件的信息。但是,我注册的回调都没有被调用过。我尝试枚举Windows,然后在每个句柄上调用IsJavaWindow(),但它始终返回false。关于它为什么显然不起作用的任何想法?

我认为这是我的应用程序而不是网桥安装程序出现的问题,因为演示猴子和Ferret程序正常工作,initializeAccessBridge()返回true,调试器显示已加载WindowsAccessBridge dll。

我正在Windows Vista上使用Java 6,update 13,我认为访问桥的版本为2.0.1。

JavaAccess::JavaAccess(void)
{
   using namespace std;

   BOOL isInitialized = initializeAccessBridge();
   if(isInitialized)
   {
      cout << "Bridge Initialized!" << endl;
   }
   else
   {
      cout << "Initialization failed!" << endl;
      return;
   }

   EnumWindows((WNDENUMPROC)EnumWndProc, NULL);

   SetJavaShutdown(OnJavaShutdown);
   SetFocusGained(OnFocusGained);
   SetMouseClicked(OnMouseClicked);
}

JavaAccess::~JavaAccess(void)
{
   shutdownAccessBridge();
}

void JavaAccess::OnJavaShutdown( long vmID )
{
   using namespace std;
   cout << "Java shutdown!" << endl;
}

void JavaAccess::OnFocusGained( long vmID, FocusEvent event, AccessibleContext context )
{
   using namespace std;
   cout << "Focus Gained!" << endl;

   ReleaseJavaObject(vmID, event);
   ReleaseJavaObject(vmID, context);
}

void JavaAccess::OnMouseClicked( long vmID, jobject event, jobject source )
{
   std::cout << "Mouse clicked!" << std::endl;

   ReleaseJavaObject(vmID, event);
   ReleaseJavaObject(vmID, source);
}

BOOL CALLBACK JavaAccess::EnumWndProc( HWND hwnd, LPARAM lparam )
{
   if (IsJavaWindow(hwnd))
   {
      std::cout << "Found Java Window!" << std::endl;
      return FALSE;
   }
   else
   {
      std::cout << "Still looking" << std::endl;
      return TRUE;
   }
}

所有的回调都是静态函数。

解决方案如下:

我也一直在努力解决这个问题,并且刚刚找到了一种切实可行的解决方案。我最终不得不构建WindowsAccessBridge.dll的调试版本,并使用调试器进入其中以观察发生了什么。

  • 调用“initializeAccessBridge”要求您具有 Activity 的Windows消息泵。
  • 在“initializeAccessBridge”内部,它最终(使用CreateDialog)创建一个隐藏的对话框窗口。创建对话框后,它将执行带有已注册消息的PostMessage。访问桥的JavaVM端对此消息做出响应,并将另一条消息发回到创建的对话框中(它看起来像是应用程序与Java VM之间的“hello”类型的握手)。这样,如果您的应用程序没有 Activity 的消息泵,那么您的应用程序将永远不会收到JavaVM的返回消息。

    这很重要,因为在收到此消息之前,永远不会正确初始化桥接器,因此对“IsJavaWindow”的所有调用都会失败(在内部,一旦收到消息,桥接器就会初始化内部结构-因此,没有 Activity 的消息泵,无需初始化)。我猜这就是为什么您也永远不会收到回调消息的原因。

    不仅如此,还必须在消息泵可以处理消息的位置调用initializeAccessBridge,然后才能调用IsJavaWindow。

    这就是JavaFerret和JavaMonkey起作用的原因-它们在启动时进行初始化,然后在网桥通过消息泵接收到初始化消息之后对枚举菜单消息的响应进行枚举。

    我能够在MFC对话框应用程序(以及基于MFC的应用程序)中解决此问题的方法是,确保在某个时刻调用“initializeAccessBridge”,以使内置MFC消息泵可以推送“hello”消息返回到该隐藏对话框,然后再使用。在简单的MFC对话框的情况下,这意味着在OnInitDialog中调用initializeAccessBridge,并调用枚举过程以响应按钮调用(例如)。如果希望枚举在对话框出现后立即出现,则可以使用计时器在OnInitDialog完成后触发(例如10毫秒)以允许处理初始化消息。

    如果打算在控制台应用程序中使用此功能,则需要编写自己的自定义消息泵来处理初始化消息。

    无论如何,我希望这足够清楚!尽管没有办法知道这是否是“正确”的方式(除了付钱给Sun工程师告诉我们),但这肯定解决了我的问题。

    干杯-达伦。

    哦。顺便说一句,我发现一个晦涩的Sun页面提到了有关AccessBridge的内容仅适用于基于awt的java应用程序(但考虑到Sun自2004年以来未更新任何文档,这可能已更改)。我不是Java程序员-为了进行测试,我获取了许多免费的Java应用程序(以及jdk附带的免费Java应用程序)并试用了我的测试应用程序。它适用于我尝试过的所有产品-YMMV。祝好运!