環境需求
- JDK (64 位元建議) + Visual Studio 2017/2019/2022 (C++ 桌面開發工作負載)
- 目錄結構:
E:\JNI
├── com\yiluboke\zxy\WriteReadJ.java
└── WriteReadCpp\
├── writeReadDo.h
├── writeReadDo.cpp
└── com_yiluboke_zxy_WriteReadJ.cpp
1. Java 類別 (同 Linux 版)
package com.yiluboke.zxy;
public class WriteReadJ {
public native int writeFile(byte[] data, String filePath);
public native int readFilePre(String filePath);
public native byte[] readFile(String filePath);
static { System.loadLibrary("jniwritereadfile"); } // jniwritereadfile.dll
public static void main(String[] args) { /* 測試 */ }
}
2. 編譯與產生 Header
cd E:\JNI
javac com\yiluboke\zxy\WriteReadJ.java
javah -jni com.yiluboke.zxy.WriteReadJ
# 產生 com_yiluboke_zxy_WriteReadJ.h
3. C++ 核心實作 (Visual Studio 專案設定:x64、Release、DLL)
writeReadDo.h
#ifndef __WRITEREADDO_H__
#define __WRITEREADDO_H__
typedef unsigned char BYTE;
const int MAXLEN = 1024;
extern BYTE g_data[MAXLEN];
extern int g_len;
int writeFile(BYTE* data, int len, const char* path);
int readFile(const char* path);
#endif
writeReadDo.cpp
#include "writeReadDo.h"
#include
using namespace std;
BYTE g_data[MAXLEN] = {};
int g_len = 0;
int writeFile(BYTE* data, int len, const char* path) {
ofstream out(path, ios::out | ios::binary);
if (!out) return -1;
out.write((const char*)data, len);
out.close();
return 0;
}
int readFile(const char* path) {
ifstream in(path, ios::in | ios::binary);
if (!in) return -1;
in.seekg(0, ios::end);
int cnt = in.tellg();
in.seekg(0, ios::beg);
g_len = min(cnt, MAXLEN);
in.read((char*)g_data, g_len);
in.close();
return 0;
}
com_yiluboke_zxy_WriteReadJ.cpp (JNI 橋接層)
#include "com_yiluboke_zxy_WriteReadJ.h"
#include "writeReadDo.h"
#include
static const char* ENCODING = "GB2312";
char* Jstring2CStr(JNIEnv* env, jstring jstr, int* outLen) {
jclass cls = env->FindClass("java/lang/String");
jmethodID mid = env->GetMethodID(cls, "getBytes", "(Ljava/lang/String;)[B");
jstring enc = env->NewStringUTF(ENCODING);
jbyteArray arr = (jbyteArray)env->CallObjectMethod(jstr, mid, enc);
jsize len = env->GetArrayLength(arr);
*outLen = len + 1;
jbyte* elems = env->GetByteArrayElements(arr, JNI_FALSE);
char* result = new char[len + 1];
memcpy(result, elems, len);
result[len] = '\0';
env->ReleaseByteArrayElements(arr, elems, JNI_ABORT);
return result;
}
JNIEXPORT jint JNICALL Java_com_yiluboke_zxy_WriteReadJ_writeFile(
JNIEnv* env, jobject, jbyteArray jData, jstring jPath) {
int pathLen;
char* path = Jstring2CStr(env, jPath, &pathLen);
jbyte* data = env->GetByteArrayElements(jData, JNI_FALSE);
jsize len = env->GetArrayLength(jData);
int ret = writeFile((BYTE*)data, (int)len, path);
env->ReleaseByteArrayElements(jData, data, JNI_ABORT);
delete[] path;
return ret;
}
JNIEXPORT jint JNICALL Java_com_yiluboke_zxy_WriteReadJ_readFilePre(
JNIEnv* env, jobject, jstring jPath) {
int pathLen;
char* path = Jstring2CStr(env, jPath, &pathLen);
int ret = readFile(path);
delete[] path;
return ret;
}
JNIEXPORT jbyteArray JNICALL Java_com_yiluboke_zxy_WriteReadJ_readFile(
JNIEnv* env, jobject, jstring) {
jbyteArray arr = env->NewByteArray(g_len);
env->SetByteArrayRegion(arr, 0, g_len, (jbyte*)g_data);
return arr;
}
4. Visual Studio 專案關鍵設定
- **平台:x64** (對應 64 位元 JDK),不可為 Win32
- **組態類型:動態連結庫 (.dll)**
- **C/C++ → 一般 → 附加包含目錄**:
$(JAVA_HOME)\include;$(JAVA_HOME)\include\win32
- **連結器 → 一般 → 輸出檔案**:
jniwritereadfile.dll
- **偵錯 → 命令引數**:
com.yiluboke.zxy.WriteReadJ (可直接 F5 偵錯)
5. 執行驗證
cd E:\JNI
java com.yiluboke.zxy.WriteReadJ
常見錯誤排查
| 錯誤 | 原因 | 解決 |
Can't load IA 32-bit .dll on a AMD 64-bit platform | DLL 編譯為 32 位元,JDK 為 64 位元 | VS 專案平台設為 x64 |
jni.h not found | 包含目錄未設定 | 加入 $(JAVA_HOME)/include 及 win32 |
UnsatisfiedLinkError | DLL 未在 PATH / java.library.path | 將 DLL 放在專案根目錄或設定 -Djava.library.path |
完整 Demo 下載
百度網盤:JNI Windows Demo
總結
Windows JNI 核心在 **x64 編譯、GB2312/UTF-8 編碼轉換、byte 陣列 Get/Release 配對、DLL 命名與載入路徑** 。Visual Studio 提供完整偵錯體驗,建議新手優先在 Windows 熟悉 JNI 流程再移植 Linux。
- 上一篇文章: JNI Linux 平台開發指南:套件結構、中文路徑、陣列傳遞完整實作
- 下一篇文章: Java 程式設計偏執式技巧 10 選:防禦性編碼實戰精選
Copyright ©
程式碼之家 All Rights Reserved