java程序中调用c语言库

1.写一个调用的C程序的类

例如一个简单的字符串输入输出类:

package com.lxy;
public class TestC {
	
	static{
		System.loadLibrary("c");
	}
	
	public native int get();
	
	public native void set(String a);
}



其中

System.loadLibrary("c");

表示在目录中找"c.dll"的动态链接库文件。
类中用native声明各接口方法。


2.编译类

命令行在文件目录中运行命令javac 类.java

本例中因为class文件在com.lxy包下,所以执行命令:

"C:\Users\TestC\src>javac com\lxy\TestC.java"




3.用上一步生成的class文件生成C的头文件

执行命令: 

javah -classPath (class路径) -jni com.lxy.TestC

本例中执行:

C:\Users\TestC\src>javah com.lxy.TestC


此时会在当前目录下生成 "com_lxy_TestC.h" 头文件:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_lxy_TestC */


#ifndef _Included_com_lxy_TestC
#define _Included_com_lxy_TestC
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_lxy_TestC
 * Method:    get
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_lxy_TestC_get
  (JNIEnv *, jobject);


/*
 * Class:     com_lxy_TestC
 * Method:    set
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_lxy_TestC_set
  (JNIEnv *, jobject, jstring);


#ifdef __cplusplus
}
#endif
#endif



4.实现C程序的功能

头文件中,get方法

JNIEXPORT jint JNICALL Java_com_lxy_TestC_get  (JNIEnv *, jobject);

和set方法
JNIEXPORT void JNICALL Java_com_lxy_TestC_set  (JNIEnv *, jobject, jstring);

声明了C程序功能暴露的公共接口。我们需要实现这些方法:


新建c文件"TestC.c"

#include "com_lxy_TestC.h"
#include 
#include 
#include 
#include "stdlib.h"
#include "string.h"


char* jstringToWindows( JNIEnv *env, jstring jstr );
jstring WindowsTojstring( JNIEnv* env, const char* str );

const char *s;

//实现get方法
JNIEXPORT jstring JNICALL Java_com_lxy_TestC_get
(JNIEnv *env, jobject o)
{
	jstring str = WindowsTojstring(env,s);
	return str;
}

//实现set方法
JNIEXPORT void JNICALL Java_com_lxy_TestC_set
  (JNIEnv *env, jobject obj, jstring param)
{
        const char *buf;
		//将jstring转换为C语言的char,编码格式为UTF-8
        //buf = (*env)->GetStringUTFChars(env,param, 0);//这样使用会造成中文乱码
		buf=jstringToWindows( env, param );//调用这个函数可以避免中文乱码
		s = buf;
		printf("printf:%s", s);
		//使用完成后清除占用的内存。注意在这里还不能清除,因为清除后s也没了。
        //(*env)->ReleaseStringUTFChars(env, param, buf);  
}	

//将JNI的jstring转换成c语言的char数组
char* jstringToWindows( JNIEnv *env, jstring jstr )
{
	int length = (*env)->GetStringLength(env,jstr );
	const jchar* jcstr = (*env)->GetStringChars(env,jstr, 0 );
	char* rtn = (char*)malloc( length*2+1 );
	int size = 0;
	size = WideCharToMultiByte( CP_ACP, 0, (LPCWSTR)jcstr, length, rtn,(length*2+1), NULL, NULL );
	if( size <= 0 )
		return NULL;
	(*env)->ReleaseStringChars(env,jstr, jcstr );
	rtn[size] = 0;
	return rtn;
}
//将c语言的char数组转换成JNI的jstring
jstring WindowsTojstring( JNIEnv *env, const char* str )
{
	jstring rtn = 0;
	int slen = strlen(str);
	unsigned short* buffer = 0;
	if( slen == 0 )
		rtn = (*env)->NewStringUTF(env,str ); 
	else
	{
		int length = MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, NULL, 0 );
		buffer = malloc( length*2 + 1 );
		if( MultiByteToWideChar( CP_ACP, 0, (LPCSTR)str, slen, (LPWSTR)buffer, length ) >0 )
		rtn = (*env)->NewString( env, (jchar*)buffer, length );
	}
	if( buffer )
		free( buffer );
	return rtn;
}


关于jstring和char的转换问题,参考自文章:

http://blog.csdn.net/fei0724/article/details/11145031


5.将C程序编译并链接为dll共享库:
这里使用tdm64-gcc-5.1.0-2来编译C程序。
因为include了,需要让编译器知道该文件及其引用的在哪。可以从JAVA_HOME\include及JAVA_HOME\include\win32下找到他们,复制到c文件目录下。然后在c文件目录下执行命令:

gcc -fpic -shared TestC.c -oc.dll -I(h文件路径)

其中-fpic是输出的对象模块式按照可重定位地址方式生成。
-shared表示生成动态链接库文件
TestC.c是要用哪个文件生成
-o指定输出的文件名为c.dll(与java中System.loadLibrary("c")一致)
-Ipath 告诉编译器从哪里找.h文件
如果没有错误,此时dll文件已经生成在当前目录了。
将dll文件放到项目根目录下,即可运行调用。注:要放在根目录或java.library.path中。



6.写一个测试类调用方法

package com.lxy;

public class Test {

	public static void main(String[] args) {
		TestC t = new TestC();
		t.set("abc");
		System.out.println(t.get());
	}

}

关于java调用C的生成,参考:

http://enijmax.2y.idv.tw/linux/CLib_Jni.html



顺便将C语言编译器gcc的命令简单摘要下:

1.gcc main.c -o main //可以将main.c文件编译为main.exe可执行文件 -o是命名为


2.如果main中引用了其他C文件的方法则:
gcc main.c a.c -o main //将所有引用的c文件一起编译,中间用空格区分
如果工程中有多个文件,可以使用make来编译。


3.要编译为dll文件则:
gcc -shared -fpic main.c a.c -o main.dll

4.如果有include其他.h文件,要告诉编译器引用的h文件的路径:
gcc main.c a.c -o main -I path

5.如果要引用lib库文件,比如引用libmylib.a,其路径在libPath:
gcc main.c -o main -l mylib -L libPath


如果项目中关联的文件较多,用gcc则显得不便。应该使用Makefile。

关于Makefile,可以参考:

http://www.chinaunix.net/old_jh/23/408225.html




你可能感兴趣的:(java程序中调用c语言库)