【问题标题】:Using JNI to call functions defined within a given DLL file使用 JNI 调用给定 DLL 文件中定义的函数
【发布时间】:2017-07-05 12:55:13
【问题描述】:

我的目标是使用 JNI 从 kernel32.dll 访问函数。正如你在下面看到的,我做得很糟糕。我在答案中写下了整个过程。

Kernel32.java

package tn.kernel;

public final class Kernel32 {
    public static boolean loadKernel32(){
        System.loadLibrary("kernel32");
        return true;
    }

    public static native boolean K32EnumProcesses(int[] pProcessIds, int cb, int[] pBytesReturned);
}

MainClass.java

package tn.kernel;

public class MainClass {

    public static void main(String[] args) {
        System.out.println("Program started.");

        if(Kernel32.loadKernel32())
            System.out.println("Kernel32.dll loaded.");

        int n = 2000;
        int[] procs = new int[n];
        int ls = Integer.SIZE;
        int[] rs = new int[1];

        if(Kernel32.K32EnumProcesses(procs, ls * n, rs)){
            System.out.println("Success");
        }

        System.out.println("Done.");
    }

}

输出:

Program started.
Kernel32.dll loaded.
Exception in thread "main" java.lang.UnsatisfiedLinkError: tn.kernel.Kernel32.K32EnumProcesses([II[I)Z
    at tn.kernel.Kernel32.K32EnumProcesses(Native Method)
    at tn.kernel.MainClass.main(MainClass.java:15)

这是 EnumProcesses 的语法:

BOOL WINAPI EnumProcesses(
  _Out_ DWORD *pProcessIds,
  _In_  DWORD cb,
  _Out_ DWORD *pBytesReturned
);

如果 PSAPI_VERSION 为 2 或更大,则此函数在 Psapi.h 中定义为 K32EnumProcesses,并在 Kernel32.lib 和 Kernel32.dll 中导出。如果 PSAPI_VERSION 为 1,则此函数在 Psapi.h 中定义为 EnumProcesses,并在 Psapi.lib 和 Psapi.dll 中作为调用 K32EnumProcesses 的包装器导出。来源:msnd.microsoft.com

我尝试了 K32EnumProcesses 和 EnumProcesses。结果相同。

【问题讨论】:

  • 你有没有让 loadKernel32 返回 true?
  • @efekctive 是的,它确实返回了 true。你可以看到它写着“加载了Kernel32.dll”。在输出中
  • 是的。对不起。您是否厌倦了以详细模式运行 jvm 以查看是否有任何有意义的消息?
  • native 不是这样工作的;请参阅 javah 工具。使用 JNI,您可以定义应用程序需要的服务,然后编写一个 DLL 来提供它。您使用的样式更像JNA(在内部使用 JNI)。 Example

标签: java eclipse visual-studio dll java-native-interface


【解决方案1】:

为 Windows 创建 64 位动态链接库

先决条件:Visual Studio

1/ 创建一个 Visual Studio C++ 项目(例如:dllexample)

• 选择“Win32 控制台应用程序”

  • 选择“DLL”
  • 选择“空项目”

2/ 在“解决方案资源管理器”中右键单击“头文件”>“添加”>“新建项目...”> 选择一个名称(例如:dllexample.h)>“添加”

• 以这种方式在“dllexample.h”中定义函数的头文件:

__declspec(dllexport) <type> funcName(parameters…);
…

3/ 在“解决方案资源管理器”中右键单击“源文件”>“添加”>“新建项目...”>选择一个名称(例如:dllexample.cpp)>“添加”

• 使用:

#include “dllexample.h”

• 在“dllexample.cpp”源文件中定义函数体(来自“dllexample.h”头文件):

<type> funcName(parameters…){
    //body instructions
}

• 在上方工具栏中选择“x64”

• 选择“构建”>“构建解决方案”

4/ 完成

• 您可以在“projects/dllexample/x64/Debug”中找到“dllexample.dll”和“dllexample.lib”

• 您可以在“projects/dllexample/dllexample”中找到“dllexample.h”

从另一个 64 位 DLL 或 Windows 上的可执行文件调用 64 位 DLL 文件(例如:dllexample.dll)

先决条件:“dllexample.dll”、“dllexample.lib”和“dllexample.h”或精确的函数描述或指南和 Visual Studio

1/ 创建一个 Visual Studio C++ 项目(例如:dllcall)

• 选择“Win32 控制台应用程序”

  • 选择“DLL”创建DLL文件,选择“Consol Application”创建可执行文件
  • 选择“空项目”

2/ 将“dllexample.dll”、“dllexample.lib”和“dllexample.h”复制到“projects/dllcall/dllcall”

3/ 在“解决方案资源管理器”中右键单击“头文件”>“添加”>“现有项...”>选择“dllexample.h”>“添加”

• 如果您正在制作 DLL 文件,请创建一个新的头文件(例如:dllcall.h),您可以在其中以这种方式定义函数的头文件:

__declspec(dllexport) <type> funcName(parameters…);
…

4/ 在“解决方案资源管理器”中右键单击“源文件”>“添加”>“新建项目...”> 选择一个名称(例如:dllcall.cpp)>“添加”

• 使用:

#include “dllexample.h”

• 如果您要创建 DLL 文件,请使用:

#include “dllcall.h”

然后在“dllcall.cpp”源文件中定义函数体(来自“dllcall.h”头文件)。同时可以调用“dllexample.h”中的函数:

<type> funcName(parameters…){
    //body instructions
}

• 在上方工具栏中选择“x64”

• 在“解决方案资源管理器”中,右键单击“dllcall”>“属性”>“链接器”>“输入”>“附加依赖项”>“编辑”> 添加“dllexample.lib”(此选项将是仅为当前 Visual Studio 项目的 x64 调试器设置)

• 选择“构建”>“构建解决方案”生成 DLL 和导入库 (.lib) 文件,“运行”生成可执行文件并测试它

5/ 完成

• 您可以在“projects/dllcall/x64/Debug”中找到“dllcall.dll”和“dllcall.lib”或“dllcall.exe”

• 您可以在“projects/dllcall/dllcall”中找到“dllcall.h”

通过 JNI 从 64 位 Java 程序调用 64 位 DLL 文件(例如:dllexample.dll)

先决条件:“dllexample.dll”、“dllexample.lib”和“dllexample.h”或精确的函数描述或指南。 Visual Studio,带有 JNI 插件的 Eclipse

1/ 创建一个 Eclipse Java 项目(例如:dlltest)

2/ 创建一个类(例如:my.package.JNIClass)

• 使用关键字将方法头定义为与“dllexample.h”中的函数定义一样接近:

public static final native <type> funcName(parameters..);
…

• 运行项目生成“JNIClass.class”

3/ 在文件夹“workspace/dllcall/src”中打开命令行

• 运行命令生成“my_package_JNIClass.h”头文件:

javah my.package.JNIClass

4/ 创建一个 64 位 DLL 文件(例如:dllcall.dll),该文件调用“dllexample.dll”并包含“my_package_JNIClass.h”

• 在“dllcall.cpp”源文件中,定义“my_package_JNIClass.h”头文件中定义的函数体

• “my_package_JNIClass.h” 包括“jni.h”,要使其工作,您必须转到 Visual Studio 中的“解决方案资源管理器”并右键单击“属性”>“配置属性”>“C/C++” ” > “General” > “Additional Include Directories” > 添加64位“java/include”和“java/include/win32”路径(此选项将只为当前Visual Studio项目的x64调试器设置)

5/ 将“dllcall.dll”和“dllexample.dll”复制到“workspace/dllcall/src”

• 在“Package explorer”中,右键单击“dlltest”>“Properties”>“Java Build Path”>“Source”>展开“my/package/src”>选择“Native Library Location”>“Edit ” > 添加“my/package/src”作为位置路径

• 使用“JNIClass.java”导入 DLL 文件:

static {
    System.loadLibrary(“dllexample”);
    System.loadLibrary(“dllcall”);
}

6/如果没有选择64位JRE,则进入“运行”>“运行配置”>“JRE”>“Alternate JREs”>“已安装的JREs”>放入64位Java JDK目录(应该是类似:“C:\Program Files\Java \jdk”,而 32 位 Java JDK 可以在“Program Files (x86)”文件夹中找到

7/ 完成

• 现在您可以使用在步骤 2 中定义的方法

【讨论】:

    猜你喜欢
    • 2014-04-05
    • 2019-02-23
    • 1970-01-01
    • 2013-03-05
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    • 1970-01-01
    • 2011-09-12
    相关资源
    最近更新 更多