交叉编译总结笔记

  在项目的流程中,我们涉及到使用交叉编译的部分,关于这一块,我将研究后的结果总结如下。

 

  DLL是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。

 

整体环境:

64位 Win7系统

jdk1.8.0_45

Eclipse-Mars

Visual Studio 2013

 

  首先,JAVA是无法直接调用C#的dll的,需要通过经过桥接的方式,我才用的方法是通过管理性的c++桥接的方式,帮助JAVA调用C#的dll。整体流程如下。

  

  整体的大概流程是:

    Java -> JNI -> C++ dll  <== Managed C++ ==> C# dll

  依照这个顺序来执行的话,第一步需要在JAVA中写好一个类,我们将其命名为TestJNI,这个类里面声明的函数最终将会由C#来实现:

public class TestJNI {

  public native void denoiseWord(String path,int trd);
  public native void cutwords(int trd, String analyzer);
  public native void key(String Path,int trd);
  public native void translate(int trd);

static {  
        System.loadLibrary("c++dll"); 
    }  
    public static void main(String[] args) {  
        TestJNI t = new TestJNI(); 
        System.out.println(System.getProperty("java.library.path"));
    }  
}  

  在这里我声明了函数translate,这个函数在JAVA中不会被实现,他真正被实现的地方是在C#所写的dll中。

  在完成了JAVA部分的代码后,需要用javah命令产生.h文件,我是用的JAVA编译软件是eclipse,在工程目录下的bin文件夹内可以看到.class文件,打开命令行,输入

javah -classpath . -jni TestJNI

  正确执行指令之后,在当前文件夹内生成了这个类的.h文件。

  

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

#ifndef _Included_TestJNI
#define _Included_TestJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     TestJNI
 * Method:    denoiseWord
 * Signature: (Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_TestJNI_denoiseWord
  (JNIEnv *, jobject, jstring, jint);

/*
 * Class:     TestJNI
 * Method:    cutwords
 * Signature: (ILjava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_TestJNI_cutwords
  (JNIEnv *, jobject, jint, jstring);

/*
 * Class:     TestJNI
 * Method:    key
 * Signature: (Ljava/lang/String;I)V
 */
JNIEXPORT void JNICALL Java_TestJNI_key
  (JNIEnv *, jobject, jstring, jint);

/*
 * Class:     TestJNI
 * Method:    translate
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_TestJNI_translate
  (JNIEnv *, jobject, jint);

#ifdef __cplusplus
}
#endif
#endif

  在这个.h头文件中,可以清楚地看到,在JAVA中声明的函数在这个头文件中被声明了。暂时跳过c++的部分,因为c++的作用是起到中间的桥梁的作用,真正的两边是JAVA和C#,所以我们先完成C#的部分,最后再来搭建中间的桥梁。

  创建一个C#控制台程序,在开始编程之前,在菜单栏中选择【项目】-》【设置】-》【应用】中将输出类型改为类库。

  

  完成C#部分对于函数的实现,编译成动态链接库。

  下一步就是C++部分。首先用VS2013创建一个C++编写类库的工程,首先承建一个C++的win32工程,在设置页面注意勾选:

  

  在编译C++DLL之前,需要做以下配置,在项目属性对话框中选择"C/C++"|"Advanced",将Compile AS 选项的值改为"C++"。在运行目录下,需要引入刚才C#编译完成的动态链接库,还有三个头文件分别是jni.h、jni_md.h和刚才通过javah指令生成的头文件,前两个文件可以从网上下载,在C++程序中引入这几样头文件,使用c#动态链接库的命名空间。

#include "stdafx.h"
#include "c++dll.h"
#include "jni.h" 
#include "jni_md.h"  
#include "TestJNI.h"  
#include <string>
#include "string.h"

#include <malloc.h>
#include <stdlib.h>
#include <vcclr.h>

//引入c#的库和命名空间  
#using "ClassToDll.dll"
using namespace ClassToDll;

#using "mscorlib.dll"
#using "System.dll"
using namespace System;

  特别需要注意,选择《项目》-> 《属性页》->《配置属性》->《常规》->《公共语言运行库支持》,选择公共语言运行库支持(/clr)。

  由于可能会用到jstring和string之间的转换问题,所以需要自己实现以下几个函数,用来做到字符串格式之间的转换。

  

jstring stringTojstring(JNIEnv* env, const char* pat)
{
    jclass strClass = env->FindClass("Ljava/lang/String;");
    jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
    jbyteArray bytes = env->NewByteArray(strlen(pat));
    env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte*)pat);
    jstring encoding = env->NewStringUTF("utf-8");
    return (jstring)env->NewObject(strClass, ctorID, bytes, encoding);
}

char* jstringTostring(JNIEnv* env, jstring jstr)
{
    char* rtn = NULL;
    jclass clsstring = env->FindClass("java/lang/String");
    jstring strencode = env->NewStringUTF("utf-8");
    jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)env->CallObjectMethod(jstr, mid, strencode);
    jsize alen = env->GetArrayLength(barr);
    jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE);
    if (alen > 0)
    {
        rtn = (char*)malloc(alen + 1);
        memcpy(rtn, ba, alen);
        rtn[alen] = 0;
    }
    env->ReleaseByteArrayElements(barr, ba, 0);
    return rtn;
}

String^ jstringToStr(JNIEnv* env, jstring jstr)
{
    char* str = jstringTostring(env, jstr);
    String^ value = gcnew String(str);
    free(str);
    return value;
}

jstring strTojstring(JNIEnv* env, String^ rtn)
{
    pin_ptr<const wchar_t> wch = PtrToStringChars(rtn);
    size_t convertedChars = 0;
    size_t sizeInBytes = ((rtn->Length + 1) * 2);
    char *ch = (char *)malloc(sizeInBytes);
    errno_t err = wcstombs_s(&convertedChars,
        ch, sizeInBytes,
        wch, sizeInBytes);
    jstring js = stringTojstring(env, ch);
    free(ch);
    return js;
}

  之后,根据javah生成头文件中的函数声明,依次实现各个函数。

JNIEXPORT void JNICALL Java_TestJNI_denoiseWord
(JNIEnv *env, jobject obj, jstring path, jint trd)
{
    //c#中的对象 
    Denoise ^d = gcnew Denoise();
    d->denoiseWord(jstringToStr(env, path),(int)trd);
}

JNIEXPORT void JNICALL Java_TestJNI_cutwords
(JNIEnv *env, jobject obj, jint trd ,jstring)
{
    Denoise ^d = gcnew Denoise();
    d->cutwords((int)trd, "Lucene.China.ChineseAnalyzer");
}

JNIEXPORT void JNICALL Java_TestJNI_key
(JNIEnv *env, jobject,  jstring Path ,jint trd)
{
    Denoise ^d = gcnew Denoise();
    d->key( jstringToStr(env, Path),(int)trd);
}

JNIEXPORT void JNICALL Java_TestJNI_translate
(JNIEnv *env, jobject, jint trd)
{
    Denoise ^d = gcnew Denoise();
    d->translate((int)trd);
}

  将c++工程进行编译,得到c++的动态链接库。

  回到JAVA程序,我特意在最开始留了一句

System.out.println(System.getProperty("java.library.path"));

  用来输出library.path的地址,在这个地址下,将刚才编译好的C++,C#和其他用到的动态链接库放在这个文件夹下,运行JAVA程序,就可以使用JAVA调用c#编写的动态链接库了。

 

报错处理:

  如果出现如下报错

  一般是因为没有把库放在正确的路径下面,或者缺少依赖的库,补上后可以消除这个问题。

 

  如果出现如下报错

  这个是JAVA虚拟机内部错误,一般问题是出在了C#运行中出现的错误,由于C#和JAVA中string的编码格式不同,所以使用命令行输出会出现输出乱码的问题,推荐将错误信息输出到文件,这样可以看到C#里面出错的部分在哪里。

 

 

  

 

 

posted @ 2017-01-05 03:24  崔正龙  阅读(386)  评论(1编辑  收藏  举报