文档章节

Java 调用C# DLL

华山猛男
 华山猛男
发布于 2017/04/09 15:29
字数 2358
阅读 156
收藏 2
点赞 0
评论 0

先说明一下什么时候会使用到这个技术,最近公司要求在第三方地图引擎上做热力图,碰巧API中没有相关的接口,只能通过添加图片的方式进行显示,所以只能在后台代码中生成热力图,在往上面叠加,Java生成热力图并没有现成的代码可用(QAQ技术不够,求技术帝给份生成HeatMap的代码),C#中有,于是经理给搞出了热力图生成,所以需要到Java去调用C# dll来完成该功能,在其他方面,比如,使用dll来封装坐标计算公式,因为有些东西是要保密的所以要这样做处理,对dll文件使用网卡签名加密(安全,绑定网卡)等,这些都是非常有必要的,对于无法实现的功能采用跨平台的方式去实现它,也不失为完成任务目标的一种方法。

引用说明

C++和C#是不一样的。Java无法直接调用C# dll,需要经过桥接的方式,进行中继转发一下请求,通过管理性的C++桥接方式,成功完成了Java调用C# dll(这段话是在网上看到的,引用进行说明,具体引用流程是:Java --> C++ --> C#)。

DLL介绍

DLL(Dynamic Link Library)文件为动态链接库文件,又称“应用程序拓展”,是软件文件类型。在Windows中,许多应用程序并不是一个完整的可执行文件,它们被分割成一些相对独立的动态链接库,即DLL文件,放置于系统中。当我们执行某一个程序时,相应的DLL文件就会被调用。一个应用程序可使用多个DLL文件,一个DLL文件也可能被不同的应用程序使用,这样的DLL文件被称为共享DLL文件。

软件工具

Microsoft Visual Studio 2010、MyEclipse 2014

一、C# DLL文件生成

建立一个C#的类库:

文件信息如下:

代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestBuildCS
{
    public class General
    {
        private int result;
        public int Result
        {
            get { return result + 10; }
            set { this.result = value; }
        }
        public string outPutMess(string str1)
        {
            Console.WriteLine("20170420成功调用了dll");
            return "CS Information:" + str1;
        }
        public string submit(string str1, string str2)
        {
            Console.WriteLine("成功调用了dll");
            return "CS:" + str1 + ":" + str2;
        }
        public bool testBoolean(string str1, string str2)
        {
            if (str1.Equals("true"))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
    }
}

重要参数说明:

DLL文件名:TestBuildCS

工作空间(namespace):TestBuildCS

类名(class):General

方法名(method):outPutMess

传入值类型:string

返回值类型:string

保存后,点击顶排按钮栏,生成,即可在以下路径找到生成好的dll文件。

例:E:\VSWorkspace\Projects\TestBuildCS\TestBuildCS\bin\Debug\TestBuildCS.dll

二、Java 类文件生成

先下载JNative的Jar包文件,加入到Java工程中,并建立JniDllTest类文件。

代码如下:

package test;

public class JniDllTest {

	/**
	 * native声名
	 * C#对应说明:
	 * 1.方法名 outPutMess
	 * 2.传入参数类型 String
	 * 3.返回参数类型 String
	 * @param mess
	 * @return String
	 */
	public native String outPutMess(String mess);
	public native String submit(String a, String b);     
    public native int add(int a, int b);     
    public native boolean testBoolean(String a, String b);
	
	/**
	 * 此处引用C++ Dll
	 */
	static {
		//System.loadLibrary方法使用前提,必须将引用的JDK路径加入到环境变量Path中
		//System.loadLibrary方法默认引用当前工作空间引用JDK的bin目录下的dll文件,不需要传入后缀名
        System.loadLibrary("TestBuildC");
        //System.load方法参数必须为库文件的绝对路径,可以是任意路径
        //System.load("D:\\DevelopeSoft\\Java\\jdk1.7.0_79\\bin\\TestBuildC.dll");
    }
	
    public static void main(String[] args) {     
    	JniDllTest t = new JniDllTest();     
        System.out.println(t.outPutMess("OK!"));  
//      System.err.println(t.submit("user", "pass"));  
//      System.out.println(t.add(2, 20));     
//      System.err.println(t.testBoolean("1", "pass"));
    }  
	
}

重要参数说明:

类文件名:JniDllTest

包路径:test

注意:方法名,传入参数类型,返回值类型,必须与C#中的一致。

System.loadLibrary()装载的是c++的dll文件,不是C#的,做到这一步可以先假定一个文件名。

三、H头文件生成

生成JNI所需的h头文件,并存放在Java工程的src根目录下,这里使用了MyEclipse IDE工具,所以可以直接找到classes文件生成的目录,如果没有使用相关工具,请度娘如何生成java编译后的classes文件,因为这里会直接指向编译后的classes文件,而不是java文件。

使用了IDE的看这里,找到编译目录,这里以普通Java工程为例(手动调整了一下编译目录,原来默认是bin目录下,我调整为WEB的默认方式WebRoot\WEB-INF\classes)。

调整完后,打开红色部分的路径,可以看到工具已经自动帮我们编译好classes文件了。

使用命令:进入到classes编译目录下,并执行javah编译。

编译命令:javah -jni 包名.类名

注意:这个路径下对应的JniDllTest是classes文件,不是java文件的所在路径,编译后的h文件是要放在工程src根目录下的,所以要确定包名是否正确,编译的当前目录为clsses,具体视情况而定根目录。

E:
CD E:\Workspaces\MyEclipse Professional 2014\dllTest\WebRoot\WEB-INF\classes
JAVAH -JNI test.JniDllTest

CMD图:

h文件会在classes目录下生成,然后把它拷贝到工程的src目录下。

文件内容如图,可以看到定义的包名和方法,声名方式:

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

#ifndef _Included_test_JniDllTest
#define _Included_test_JniDllTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     test_JniDllTest
 * Method:    outPutMess
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_test_JniDllTest_outPutMess
  (JNIEnv *, jobject, jstring);

/*
 * Class:     test_JniDllTest
 * Method:    submit
 * Signature: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_test_JniDllTest_submit
  (JNIEnv *, jobject, jstring, jstring);

/*
 * Class:     test_JniDllTest
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_test_JniDllTest_add
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     test_JniDllTest
 * Method:    testBoolean
 * Signature: (Ljava/lang/String;Ljava/lang/String;)Z
 */
JNIEXPORT jboolean JNICALL Java_test_JniDllTest_testBoolean
  (JNIEnv *, jobject, jstring, jstring);

#ifdef __cplusplus
}
#endif
#endif

四、C++ DLL文件生成

建立一个C++ Win32项目:

接下来要设置一下项目属性,解决方案资源管理器 --> TestBuildC --> 属性;

配置:活动Debug --> Release;

配置管理器:活动解决方案平台 --> 新建x64(32位或64位文件在此设置);

配置属性:

常规 --> 公共语言运行时支持:公共语言运行时支持(/clr);

C/C++ --> 代码生成 --> 运行库:多线程DLL(/MD),启用增强指令集:是(/Gy);

添加支持的h头文件和dll文件到项目中,找到以下文件拷贝到C++工程目录里:

JDK所在目录/include/jni.h

JDK所在目录/include/win32/jni_md.h

生成的java项目类的h头文件 test_JniDllTest.h

生成的C# dll文件 TestBuildC.dll

接着再工程里,添加引用资源文件,右键资源文件添加现有项,将上面3个h头文件添加进来:

打开test_JniDllTest.h文件修改头部引用,将第二行的引用方式修改一下:

#include <jni.h>
改为
#include "jni.h"

编辑TestBuildC.cpp文件代码如下:

// CPP.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"      
#include "jni.h"   
#include "jni_md.h"
// 引用生成的h头文件
#include "test_JniDllTest.h"
#include "string.h"

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

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

// 其他引用
#using "System.dll"
#using "System.Web.dll"
#using "System.Web.Services.dll"

using namespace System;
using namespace System::Text;
using namespace System::Web;
using namespace System::Web::Services;
using namespace System::ComponentModel;

// 转换方法 start
// char* To jstring
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); 
}
// jstring To char*
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;
}
// jstring To String
String^ jstringToStr(JNIEnv* env, jstring jstr)
{
	char* str = jstringTostring(env, jstr);
	String^ value = gcnew String(str);
	free(str);
	return value;
}

// String To jstring
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;
}
// 转换方法 end

// 注意看这里是如何声名方法的,再进行修改(test包,JniDllTest类,outPutMess方法)
JNIEXPORT jstring JNICALL Java_test_JniDllTest_outPutMess
  (JNIEnv *env, jobject obj, jstring str1)  
{   
    //c#中的对象 General  
    General ^o = gcnew General();   
    return strTojstring(env, o->outPutMess(jstringToStr(env,str1)));   
}

JNIEXPORT jstring JNICALL Java_test_JniDllTest_submit
  (JNIEnv *env, jobject obj, jstring str1, jstring str2)  
{   
    //c#中的对象   
    General ^o = gcnew General();   
    return strTojstring(env, o->submit(jstringToStr(env,str1), jstringToStr(env,str2)));   
}

JNIEXPORT jint JNICALL Java_test_JniDllTest_add   
  (JNIEnv *env, jobject obj, jint a, jint b)   
{   
    //c#中的对象   
    General ^o = gcnew General();   
    o->Result = a + b;   
    return o->Result;   
}

JNIEXPORT jboolean JNICALL Java_test_JniDllTest_testBoolean
  (JNIEnv *env, jobject obj, jstring str1, jstring str2)  
{   
    //c#中的对象   
    General ^o = gcnew General();   
    return o->testBoolean(jstringToStr(env,str1), jstringToStr(env,str2));  
}

最后,点击菜单栏 --> 生成 --> 生成TestBuildC,在项目/x64/Debug文件夹中找到生成的64位Dll文件,第一次点击生成可能会出现报警,忽略掉再次生成一次,即可。

五、测试

终于到测试这一步了,将生成的C++和C# dll文件放入JDK/bin目录下,将生成的test_JniDllTest.h文件放入java工程的src根目录下,运行类文件进行测试。

常见问题:

  1. 确保JDK的环境变量配置正确,才可以正常运行javah -jni 生成头文件的命令。
  2. 在MyEclipse IDE工具中进行测试,确保所引用的JDK正确,才能正常调用bin目录下存放的dll文件。

六、参考案例

  • http://7wolfs.iteye.com/blog/2043835
  • http://blog.chinaunix.net/uid-22197900-id-3044019.html

© 著作权归作者所有

共有 人打赏支持
华山猛男
粉丝 6
博文 158
码字总数 78105
作品 0
海口
程序员
如何通过jni4net,在Java应用中调用C#接口

下载Dynamic .NET TWAIN 下载jni4net,学习里面的代码实例 在环境变量中设置好JAVAHOME和C:WindowsMicrosoft.NETFrameworkv3.5csc.exe 解压JavaTwain,在dll目录中运行工程,编译出JavaTwain...

yushulx
2014/07/02
0
0
【翻译】C#编程语言和JAVA编程语言的比较(上)

【翻译】C#编程语言和JAVA编程语言的比较(上) 原文地址:http://www.25hoursaday.com/CsharpVsJava.html 简介 C#语言是一门面向对象的语言,开发者可以使用C#和微软.NET平台快速构建各种应...

fbf
2013/08/13
0
0
结合Dynamic .NET TWAIN和Jetty,实现基于网页的TWAIN文件扫描

网页TWAIN扫描是通过多种技术结合实现的,看下流程图: 参考原文: Web-based Document Imaging Capture with .Net TWAIN and Jetty 准备工作 阅读: 如何通过jni4net,在Java应用中调用C#接...

yushulx
2014/08/18
0
0
在.NET平台上使用Scala语言(上):初尝

Scala是Java平台上的一门新兴起的语言,我也不止一次在博客上提到它。我非常希望它可以取代Java这种劣质语言,让Java平台的生产力上一个台阶。事实上,Scala从一开始——或者说“很早”就对生...

mj4738
2011/11/01
0
0
native关键字初识--java调用非java代码的接口

Java基础知识——JNI入门介绍(上) Java™ 本机接口(Java Native Interface,JNI)是一个标准的 Java API,它支持将 Java 代码与使用其他编程语言编写的代码相集成。如果您希望利用已有的代...

成长中的菜鸟
2015/02/10
0
0
[开源世界]从自动导出动态链接库接口看C++的缺点

自动导出动态链接库接口在C++编程中绝对是一件烦人的事情,因为你不得不大量的重复以下几个步骤: 1.加载动态链接库 2.定义导出函数指针定义 3.定义导出函数指针变量 4.从动态链接库中导出函...

梁欢
2013/10/21
0
2
jni c++ dll java

java通过jni 来调用.dll(在linux中称为.so文件) c++->.dll (windows) c++->.so (linux) 所以加载时,只要指定名就行了. 开发步骤; /* * 1.建立java工程编写带有native声明的方法的java类 ...

~小白
2012/03/28
0
0
Java中的native关键字

一. 什么是Native Method 简单地讲,一个Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。这个特征并非jav...

摆渡者
2014/03/04
0
1
java调用dll或so动态库文件(c++/c)

java调用dll或so动态库文件(c++/c)开发平台:Eclipse3.3.1.1+CDT(cdt-master-4.0.3)+MinGW(MinGW-5.1.4) 一:下面是java调用dll(C++) 1:下载并安装cdt :http://www.eclipse.org/cdt/downloa......

lee123lee
2013/08/30
0
0
Java 基础:认识&理解关键字 native 实战篇

Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket 泥瓦匠初次遇见 native是在 java.lang.Object 源码中的一个hashCode方法: public native int hashCode(); 为什么有个n...

泥沙砖瓦浆木匠
2015/04/25
0
8

没有更多内容

加载失败,请刷新页面

加载更多

下一页

about git flow

  昨天元芳做了git分支管理规范的分享,为了拓展大家关于git分支的认知,这里我特意再分享这两个关于git flow的链接,大家可以看一下。 Git 工作流程 Git分支管理策略   git flow本质上是...

qwfys
今天
1
0
Linux系统日志文件

/var/log/messages linux系统总日志 /etc/logrotate.conf 日志切割配置文件 参考https://my.oschina.net/u/2000675/blog/908189 dmesg命令 dmesg’命令显示linux内核的环形缓冲区信息,我们可...

chencheng-linux
今天
1
0
MacOS下给树莓派安装Raspbian系统

下载镜像 前往 树莓派官网 下载镜像。 点击 最新版Raspbian 下载最新版镜像。 下载后请,通过 访达 双击解压,或通过 unzip 命令解压。 检查下载的文件 ls -lh -rw-r--r-- 1 dingdayu s...

dingdayu
今天
0
0
spring boot使用通用mapper(tk.mapper) ,id自增和回显等问题

最近项目使用到tk.mapper设置id自增,数据库是mysql。在使用通用mapper主键生成过程中有一些问题,在总结一下。 1、UUID生成方式-字符串主键 在主键上增加注解 @Id @GeneratedValue...

北岩
今天
2
0
告警系统邮件引擎、运行告警系统

告警系统邮件引擎 cd mail vim mail.py #!/usr/bin/env python#-*- coding: UTF-8 -*-import os,sysreload(sys)sys.setdefaultencoding('utf8')import getoptimport smtplibfr......

Zhouliang6
今天
0
0
Java工具类—随机数

Java中常用的生成随机数有Math.random()方法及java.util.Random类.但他们生成的随机数都是伪随机的. Math.radom()方法 在jdk1.8的Math类中可以看到,Math.random()方法实际上就是调用Random类...

PrivateO2
今天
2
0
关于java内存模型、并发编程的好文

Java并发编程:volatile关键字解析    volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在...

DannyCoder
昨天
0
0
dubbo @Reference retries 重试次数 一个坑

在代码一中设置 成retries=0,也就是调用超时不用重试,结果DEBUG的时候总是重试,不是0吗,0就不用重试啊。为什么还是调用了多次呢? 结果在网上看到 这篇文章才明白 https://www.cnblogs....

奋斗的小牛
昨天
2
0
数据结构与算法3

要抓紧喽~~~~~~~放羊的孩纸回来喽 LowArray类和LowArrayApp类 程序将一个普通的Java数组封装在LowArray类中。类中的数组隐藏了起来,它是私有的,所以只有类自己的方法才能访问他。 LowArray...

沉迷于编程的小菜菜
昨天
0
0
spring boot应用测试框架介绍

一、spring boot应用测试存在的问题 官方提供的测试框架spring-boot-test-starter,虽然提供了很多功能(junit、spring test、assertj、hamcrest、mockito、jsonassert、jsonpath),但是在数...

yangjianzhou
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部