You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过JNI调用无JNI导出函数的编译DLL?能否单独运行含导出函数的CPP文件?

嘿,针对你的三个问题,我来给你详细拆解一下:

一、用JNI调用无导出函数的C++ DLL

因为目标DLL没有提供JNI规范的导出函数,所以我们需要做一个中间包装DLL——它负责暴露符合JNI要求的函数,同时内部去调用原DLL的逻辑。具体步骤如下:

  1. 生成JNI头文件
    先写一个包含native方法的Java类,比如:

    public class CppDllCaller {
        static {
            System.loadLibrary("JniWrapper"); // 后续要加载的中间DLL
        }
        public native void invokeCppLogic();
        
        public static void main(String[] args) {
            new CppDllCaller().invokeCppLogic();
        }
    }
    

    编译这个类后,用javah工具生成JNI头文件:

    javah -jni CppDllCaller
    

    会得到CppDllCaller.h,里面定义了JNI函数的签名。

  2. 编写中间包装DLL的C++代码
    这个包装DLL需要加载原C++ DLL,并调用它的函数。如果原DLL的函数是C++类成员,还需要做对象实例化的适配。举个简单的动态加载示例:

    #include <jni.h>
    #include "CppDllCaller.h"
    #include <windows.h>
    
    // 定义原DLL中目标函数的指针类型
    typedef void (*TargetFunc)();
    
    JNIEXPORT void JNICALL Java_CppDllCaller_invokeCppLogic(JNIEnv*, jobject) {
        // 动态加载原DLL
        HINSTANCE hOriginalDll = LoadLibrary("YourOriginalCpp.dll");
        if (hOriginalDll != nullptr) {
            // 获取原函数的地址
            TargetFunc targetFunc = (TargetFunc)GetProcAddress(hOriginalDll, "targetFunctionName");
            if (targetFunc != nullptr) {
                targetFunc(); // 调用原DLL的函数
            }
            FreeLibrary(hOriginalDll);
        }
    }
    
  3. 编译中间包装DLL
    编译时要指定JNI的头文件路径($JAVA_HOME/include和$JAVA_HOME/include/win32),并链接必要的系统库(比如kernel32.lib)。以MSVC为例:

    cl /I"%JAVA_HOME%\include" /I"%JAVA_HOME%\include\win32" /LD JniWrapper.cpp kernel32.lib
    
  4. 运行Java程序
    确保中间DLL和原DLL都在Java的库搜索路径中,运行CppDllCaller即可。

二、JNA为什么更简便,以及JNI的差异

JNA的核心优势是无需编写C/C++中间层,它通过Java接口直接映射DLL的函数签名,底层自动完成类型转换和调用逻辑。比如用JNA调用原DLL的代码:

import com.sun.jna.Library;
import com.sun.jna.Native;

public interface OriginalDll extends Library {
    OriginalDll INSTANCE = Native.load("YourOriginalCpp", OriginalDll.class);
    
    void targetFunctionName(); // 直接声明原DLL的函数
}

// 调用示例
public class Main {
    public static void main(String[] args) {
        OriginalDll.INSTANCE.targetFunctionName();
    }
}

对比JNI,JNA省去了写包装层、编译DLL的繁琐步骤,对Java开发者更友好。但JNI也有优势:

  • 性能略高(少了JNA的动态映射开销)
  • 支持更复杂的内存管理、自定义类型转换
  • 适合对性能要求极高或需要深度操控原生代码的场景
三、能否单独运行带有导出函数的.cpp文件?

不行,.cpp是源代码文件,必须先编译成可执行文件(.exe)或DLL才能运行。不过你可以给带导出函数的.cpp加一个main入口,编译成.exe后就能单独执行,同时也能作为DLL被其他程序调用(虽然这种做法不常见,但技术上可行)。

举个例子:

#include <iostream>

// 导出函数
extern "C" __declspec(dllexport) void myExportedFunc() {
    std::cout << "This is the exported function!" << std::endl;
}

// 主入口,用于单独运行
int main() {
    std::cout << "Running as executable..." << std::endl;
    myExportedFunc();
    return 0;
}

编译成.exe后,双击就能直接运行;如果把它当成DLL加载,也能调用myExportedFunc。不过更清晰的做法是分开编译:一个版本做exe,一个版本做dll。

内容的提问来源于stack exchange,提问作者hugo411

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 04:28:34