65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Netbeans 和 Visual Studio 调试 JNI 应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (12投票s)

2010年4月1日

CPOL

8分钟阅读

viewsIcon

106899

downloadIcon

1248

本文介绍如何使用 Netbeans 创建 Java Swing GUI 应用程序并将其与 JNI DLL 接口。

引言

本文解释了如何使用 Netbeans IDE 创建 Java swing 应用程序,以及如何使用 JNI 与原生 C++ 代码进行通信。它还展示了如何调试 Java 应用程序和原生 C++ DLL。

背景

很多时候,我们可能面临这样的情况:应用程序的主要部分,包括 GUI,需要在 Java 中开发,而一些用 C 或 C++ 编写的遗留库需要用于应用程序的某些功能。在这种情况下,Java Native Interface (JNI) 被用来围绕 C/C++ 库调用编写包装器,然后将这些包装器 DLL 与 Java 代码接口。

开始吧...

首先,让我们开始使用 Netbeans IDE 创建一个简单的桌面应用程序。

在 Netbeans 中启动一个新 Java 项目,并在“新建项目”向导中选择“Java Desktop application”。

DebugJavaJNIApplication/01_NewJavaProj.jpg

在下一页,将项目名称设置为 `NativeGUI`,您可以选择喜欢的名称。

DebugJavaJNIApplication/02_NewJavaProjName.jpg

向导将用基本桌面应用程序的代码填充项目,并打开交互式 GUI 编辑器。

DebugJavaJNIApplication/03_DesktopApp.jpg

删除菜单栏和状态栏。按照下图所示将组件添加到 GUI 中。

DebugJavaJNIApplication/04_DesktopAppMod.jpg

通过切换到“源代码”视图模式,删除 `NativeGUIView.java` 文件中的第 29 到 81 行。这是为需要删除的状态栏动画自动生成的代码。还请删除未使用的成员变量。Netbeans IDE 将在视觉上帮助您完成所有这些操作。或者,您可以选择保留菜单栏和状态栏,以避免正确删除代码的麻烦。

切换回“设计”视图模式,右键单击“Add”按钮,然后在上下文菜单中选择“Set Action”。您将看到“Set Action”对话框。在“Action”组合框中下拉并选择“Create New Action”。

DebugJavaJNIApplication/05_AddListener.jpg

将操作名称输入为 `onAddClicked`,然后按 OK。

DebugJavaJNIApplication/06_ActionName.jpg

向导将添加侦听器的代码,然后您将被带到代码编辑器,您将在其中输入以下代码——以粗体显示。

@Action
public void onAddClicked() {
    double d = 0.0;
    try
    {
        d = NativeAdd.add(Double.parseDouble(jSpinner1.getValue().toString()), 
        Double.parseDouble(jSpinner2.getValue().toString()));
    }
    catch(Exception e)
    {
        JOptionPane.showMessageDialog(mainPanel, e.getMessage());
    }
    jTextField1.setText(Double.toString(d));
} 

同样,为“Quit”按钮创建操作并进行设置。您可以单击编辑器上方面板中的“Design”按钮切换回 UI 编辑器。

DebugJavaJNIApplication/07_DesignView.jpg

使用相同的过程添加侦听器。

@Action
public void onQuitClicked() {
        System.exit(0);
}  

现在,在“Projects”视图中右键单击 `nativegui` 包,然后单击“Add”->“Java Class”。在“Class”对话框中,输入类名 `NativeAdd`,然后单击“Finish”。

DebugJavaJNIApplication/08_AddJavaClass.jpg

代码编辑器将打开新添加的类代码。将以下 `static` 方法行添加到类中(以粗体显示)。此类是用于加载和执行原生 DLL 并执行其中方法的包装器类。

public class NativeAdd {
    native public static double add(double n1, double n2);
    static
    {
        System.loadLibrary( "NativeAdd" );
    }
}

通过单击上面的工具栏中的绿色箭头运行一次项目。

DebugJavaJNIApplication/10_RunProj.jpg

这将为我们服务两个目的:

  1. 我们可以看到 GUI 的外观,以便进行布局上的必要更改。
  2. 它将为所有 java 文件生成类文件。我们需要 `NativeAdd.class` 文件才能进行 JNI 的进一步处理。

单击“Add”按钮,您将在 Netbeans 输出窗口中看到大量错误。这些与缺失的 DLL 有关,我们应该提供该 DLL,以便 `System.loadLibrary( "NativeAdd" );` 行能够找到要加载的 DLL。当我们创建带有 JNI 代码的 DLL 并将其放置在 JVM 可定位的文件夹中时,这将得到解决。

现在打开一个命令提示符窗口,导航到 `<Your Project Folder>\NativeGUI\build\classes\nativegui` 文件夹。所有已构建的类都可以在此处找到。**切换到上一级目录**并发出以下命令:

javah nativegui.NativeAdd 

`javah.exe` 是 JNI 头文件生成工具,可以在您的 Java SDK 安装的 `bin` 文件夹中找到。确保它在您的路径中。

您将在生成的文件夹中找到一个名为 `nativegui_NativeAdd.h` 的 C 头文件。文件名格式为 `packagename_classname.h`。打开它会发现一个名为 `Java_nativegui_NativeAdd_add` 的函数。名称格式为 `Java_packagename_classname_methodname`。此函数实际上将从名为 `NativeAdd.dll` 的 DLL 中加载。还记得我们为 `System.loadLibrary` 方法提供的参数吗?这就是 `nativegui_NativeAdd.h` 文件的内容。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class nativegui_NativeAdd */

#ifndef _Included_nativegui_NativeAdd
#define _Included_nativegui_NativeAdd
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     nativegui_NativeAdd
 * Method:    add
 * Signature: (DD)D
 */
JNIEXPORT jdouble JNICALL Java_nativegui_NativeAdd_add
  (JNIEnv *, jclass, jdouble, jdouble);

#ifdef __cplusplus
}
#endif
#endif 

我们将使用此代码来创建实现该函数的 JNI DLL。请记住,`javah.exe` 只生成了头文件。我们需要提供函数实现。请记住,我们在 `NativeAdd.dll` 中实现的仅是原生/遗留软件 API 的包装器,我们希望与 Java 一起使用。在这里,为演示起见,我们只编写代码来添加两个数字。在实际应用程序中,会调用原生 API 的代码。JNI 类和方法的设计应在仔细研究您希望调用的原生 API 函数或类之后进行。您也可以在 Windows 上调用 COM API。

现在让我们开始实现 DLL 的代码。

启动 Visual Studio,并创建一个名为 `NativeAdd` 的新 Win32 项目。

DebugJavaJNIApplication/11_NewWin32Proj.jpg

选择“DLL”作为应用程序类型,然后单击“Finish”。

DebugJavaJNIApplication/12_DllProj.jpg

项目将创建并填充用于创建 DLL 的代码。`NativeAdd.cpp` 文件也已创建,仅包含 `stdafx.h`。向项目中添加一个名为 `NativeAdd.h` 的新头文件。然后将 `nativegui_NativeAdd.h` 文件(在 `netbeans` 文件夹中生成)的内容复制并粘贴到 `Nativeadd.h` 文件中。在 `Nativeadd.cpp` 文件中实现函数,如下所示。

#include "NativeAdd.h"

jdouble JNICALL Java_nativegui_NativeAdd_add
  (JNIEnv *env, jclass cls, jdouble n1, jdouble n2)
{
    return n1 + n2;
}

通过以下步骤将 Java include 文件夹路径添加到 Visual Studio:

1) 在“Solution Explorer”中,右键单击项目 ->“Properties”。

2) 在“Property Pages”中,展开“C/C++”树并选择“General”节点。

3) 在页面中,将“<path_to_jdk>\include”和“<path_to_jdk>\include\win32”文件夹路径添加到“Additional Include Directories”。

或通过以下步骤:

1) 在菜单栏中单击“Tools”->“Options”。

2) 在“Options”对话框中,导航到“Projects and Solutions”->“VC++ Directories”节点。

3) 在页面中,使用标有“Show Directories For”的下拉框选择“Include Files”。

4) 为“<path_to_jdk>\include”和“<path_to_jdk>\include\win32”文件夹路径创建新条目。

第二种选择比第一种选择更可取,因为第一种选择只针对项目。

现在我们可以构建项目了。

构建项目。`NativeAdd.dll` 文件将在 DLL 项目路径的 `Debug` 文件夹中创建。

注意:如果您需要在 C++ 代码中访问 JVM,您还必须将库路径和 `jvm.lib` 库条目添加到链接器中。

现在我们有了 JNI 代码 DLL 和使用它的 Java GUI。但是 DLL 如何加载呢?为此,您必须将 DLL 复制到 `<Your Java SDK Folder>\bin`。但是,如果我们从 Netbeans 运行 GUI 项目,则不需要这样做。我们可以在“Project Properties”对话框的“Run”设置中设置工作目录。您可以右键单击 Netbeans “Project View”窗口中的项目,然后单击“Properties”来打开对话框。

DebugJavaJNIApplication/13_RunProperties.jpg

请确保选择您项目的路径……而不是我的!:)

现在运行 GUI 项目并测试两个数字的加法。万岁!您刚刚使用 JNI 将原生 C 代码接口到了 Java 应用程序。现在让我们进入应用程序的调试,并查看原生 C 代码是如何被调用的。通过单击工具栏上的“Debug”按钮开始调试 Java 程序。在 `NativeGUIView` 类的 `onAddClicked` 方法中设置一个断点。在应用程序的 spinboxes 中输入两个值,然后单击 Add。

调试器在断点处停止。

DebugJavaJNIApplication/14_NetbeansDebug.jpg

现在我们必须在原生 DLL 代码中设置断点。在 Visual Studio 中,单击“Debug”->“Attach to Process”。

DebugJavaJNIApplication/15_VSDebug.jpg

在“Attach to Process”对话框中,选择运行您应用程序的 `java.exe`。请记住,如果您正在运行其他 Java 应用程序,您的系统上可能会运行多个 `java.exe` 实例。请务必选择正在运行您应用程序的 `java.exe`。单击“Attach”。

DebugJavaJNIApplication/16_VSDebugProcess.jpg


然后,在 Visual Studio 中,在原生 JNI 函数实现中添加一个断点。

DebugJavaJNIApplication/17_VSBreakpoint.jpg

**注意**:断点将不会被命中,因为 DLL 尚未被 JVM 加载。断点符号会带有一个小的警告图标,并且工具提示会显示它不会被命中。

返回 Netbeans IDE,按 F8 逐步执行每块代码。在您逐步执行调用 `NativeADD.add` 方法的行之后,JVM 将动态加载 DLL,Visual Studio 将自动显示在顶部并命中断点。

DebugJavaJNIApplication/18_BreakpointHit.jpg

通过按 F10 在 Visual Studio 中逐步执行代码,最后在 Visual Studio 显示显示反汇编的警告消息框时按 F5。Java 应用程序将重新显示在顶部。然后,为了进一步调试应用程序,请返回 Netbeans IDE 并继续调试。

希望您喜欢这篇文章!

关注点

COM 服务器应用程序也可以通过将服务器 EXE 附加到调试器以类似的方式进行调试。

提示

这是 CodeProject 会员 **wc2009** 提供的一个附加技巧(请参阅评论部分)。如果您想使用 MinGW 编译器编译 DLL 项目,则必须在附加编译器选项中添加“-Wl,--add-stdcall-alias”。

历史

  • 2010 年 3 月 31 日:首次发布
  • 2010 年 4 月 12 日:添加了 MinGW 编译器的提示
© . All rights reserved.