在windows x64系统下读取sata硬盘序列号

上一篇文章中我们提到如何使用jni在windows环境中读取硬盘序列号。然而,原有的代码,并不能有效地读取sata硬盘信息,并且在x64环境下也不能工作。所以,我们需要一种新的读取硬盘序列号的方法。

最终,经过笔者寻找,发现终于有一个有效地同时能够读取sata硬盘的c++代码,该代码的源地址为:

http://www.winsim.com/diskid32/diskid32.cpp

此代码能够在x86以及x64位平台下很好地工作。当然,仅是一段读取硬盘信息的代码,此处有详细的使用代码。可以下载以使用。

那么,在java中, 我们可以使用这段代码,分别编译出两个版本的dll文件,一个定义为acx86.dll,另一个定义为acx64.dll。这样在项目启动时,我们就可以根据当前jvm的版本(为什么不是windows版本,而是jvm版本,自己想),来选择加载不同的dll。那么,如何判断当前jvm版本呢,以下代码可以达到:

	public static boolean isJvm32() {
		return System.getProperty("sun.arch.data.model").contains("32");
	}

属性值sun.arch.data.model,当在32位jvm时,将返回32; 而在64位jvm时,返回64。这样就可以分别加载不同的dll了。

以上代码(包括c++和java)均已在本机测试通过(windows xp x64版本,jdk32位和64位)。

在linux下使用java(jni)读取硬盘序列号

    在linux下读取硬盘序列号相比在windows下简单一些,主要还是打开系统文件并获取相对应的序列号即可。
    以下代码为linux读取硬盘序列号的c++代码:

int get_hd_sn(const char* szDevName, char* szSN, int limit) {
	struct hd_driveid id;
	int fd = open(szDevName, O_RDONLY);
	if (fd < 0) {
		perror("打开失败:");
		return -1;
	}
	//这句话是关键
	if (!ioctl(fd, HDIO_GET_IDENTITY, &id)) {
		strncpy(szSN, (const char*) id.serial_no, limit);
		return 0;
	}
	return -1;
}

    有了以上代码,那么我们的jni方法就很好写了,只要定义一下相对应的方法,并返回代码值即可。如下所示的定义:

	public native int readSn(String s, char[] cs);

    其中 s表示传递的硬盘标识,一般为/dev/sda,cs为填充的硬盘序列号,在传递时只需要new一个传递过去即可。而返回值,表示了读取是否成功,当为0时,读取成功。否则失败。

继续阅读“在linux下使用java(jni)读取硬盘序列号”

使用jni在windows下读取硬盘序列号

    通常做java程序的开发人员都想过想要通过某种手段来保护自己的程序。一般的方式是通过授权,采取软件授权的方式。这个时间就需要读取客户机器上的一些关键信息,比如硬盘序列号,网卡,cpu信息或者bios信息等。其中,java本身可以读取网卡信息,对于其它的机器信息读取出来则有点困难了。然而,通过其它语言,比如c++,这些信息都可以通过一定的函数将其读取出来。
    本文描述了通过网上的一个读取硬盘序列号的小程序(使用c++实现),采用jni来再次引用实现,以达到首要的目的。

    首先是需要下载相应的通过c++读取硬盘信息的相应代码,相应的源代码可以到此地址下载。
    http://download.csdn.net/download/Fly_m/3581179
    待下载完全之后,通过以下简单的方式即可以读取硬盘序列号,并返回相应的java 字符串信息。如定义的读取硬盘序列号的java本地方法为:

static native String r();

    则相对应的jni的实现即为:

JNIEXPORT jstring JNICALL XXX_r
(JNIEnv *env, jclass cls) {
	DiskInfo d = DiskInfo();
	int i = d.GetDiskInfo();
	if(i != 0) {
		return env->NewStringUTF("");
	}
	return env->NewStringUTF(d.szSerialNumber);
}

    以上即为完整的通过jni读取硬盘序列号的例子,完全可以在生产环境中使用。

对java和c++之间有感,业务和技术,上层逻辑和底层交互

    最近做了几个项目,都用到了与java之外的东西,主要是与硬件交互。java做的主要是与业务相关的逻辑,但说到与硬件交互方面,java直接就什么忙也帮不上了。有个项目虽然未与硬件直接交互,但也间接操作硬件,对于我来说,都是java上的短板。

    现在来排列一下几个项目用到的非java技术吧。一是与led交互,需要与led进行通信以发布信息,这需要与相应dll交互;二是与m1卡类操作,也需要与dll交互;三则是一个与网络和串口相关的,需要读写串口数据。这三个项目,最终的解决方案都不是java版,作为使用的硬件厂商,由于种种原因,厂商也不能提供一些的帮助。最后只能靠公司自己来解决这个问题。
    最后的解决方法,第一个由兄弟公司请一个vb外援解决了;第二个准备实现一个activex版的界面程序;第三个请了一个作vc的外援解决此问题。都不是java实现,而且……最终的成品,也得不到最终的保证,仅仅是能用罢了。

    单就这几个项目而言,最主要的问题,不是说自己的开发人员不懂c++那么高的技术程序,而是根本不需要单独就一个硬件交互问题就推到c++版来处理。最终的问题仅仅是不知道如何使用c++来与这些硬件交互,或者说仅仅是不会调用c++的相关函数,不会使用c++而已。交互逻辑,业务逻辑,完全没有问题,仅仅是不能实现罢了。

继续阅读“对java和c++之间有感,业务和技术,上层逻辑和底层交互”

在jni中使用字符串和数组(摘录)

    本文摘自 http://java6ean.iteye.com/blog/518228 中的jni中文翻译,以处理在常规的jni应用中(通常是与硬件api)进行交互时,处理如何通过jni调用简单的c或c++底层api。
    通常来说,使用部分硬件api时,调用的api接口都相对简单,只需要访问由硬件方提供的几个api dll即可,但由于通常api都是通过c或c++来写的,只提供了能够调用底层的api实现,并没有提供高级语言的实现接口,如java,c#。这时候,在java项目中就需要自己编写jni了。现在虽然有一些jni技术,如sun(oracle)的jna,com4j以及jacob等,但如果想要正确的访问,还是需要自己来编写相应的实现,以直接操纵自己写的api,以直接访问内存,避免由于第三方的转译等,发生不必要的错误。

    使用对应的 JNI 函数把 jstring 转成 C/C++字串。JNI 支持 Unicode/UTF-8 字符编码互转。Unicode 以 16-bits 值编码;UTF-8 是一种以字节为单位变长格式的字符编码,并与 7-bits。ASCII 码兼容。UTF-8 字串与 C 字串一样,以 NULL('\0')做结束符, 当 UTF-8 包含非 ASCII码字符时,以'\0'做结束符的规则不变。7-bit ASCII 字符的取值范围在 1-127 之间,这些字符的值域与 UTF-8 中相同。当最高位被设置时,表示多字节编码。如下,调用 GetStringUTFChars,把一个 Unicode 字串转成 UTF-8 格式字串,如果你确定字串只包含 7-bit ASCII 字符。这个字串可以使用 C 库中的相关函数,如 printf.
    如下一个简单的接受由控制台输入并转换为java string的例子

Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt){
char buf[128];
const jbyte *str;
str = (*env)->GetStringUTFChars(env, prompt, NULL);
if (str == NULL) {
return NULL; /* OutOfMemoryError already thrown */
}
printf("%s", str);
(*env)->ReleaseStringUTFChars(env, prompt, str);
/* We assume here that the user does not type more than
* 127 characters */
scanf("%127s", buf);
return (*env)->NewStringUTF(env, buf);
}

继续阅读“在jni中使用字符串和数组(摘录)”

(转)在JNI编程中避免内存泄漏

    本文转自:http://www.ibm.com/developerworks/cn/java/j-lo-jnileak/index.html
    简介: 本文详细论述如何在 JNI 编程中避免内存泄漏。论述了 JNI 编程中可能引发的明显的内存泄漏。本文的重点是阐述 JNI 编程中潜在的内存泄漏,希望读者通过本文对 Local reference 有更深刻的理解,了解 Local reference 表的存在,区分 Local reference 和局部变量,从而认识到 Local reference 可能引发的 native memory 内存泄漏。

    JNI 编程简介
    JNI,Java Native Interface,是 native code 的编程接口。JNI 使 Java 代码程序可以与 native code 交互——在 Java 程序中调用 native code;在 native code 中嵌入 Java 虚拟机调用 Java 的代码。
    JNI 编程在软件开发中运用广泛,其优势可以归结为以下几点:

  1. 利用 native code 的平台相关性,在平台相关的编程中彰显优势。
  2. 对 native code 的代码重用。
  3. native code 底层操作,更加高效。
     

    然而任何事物都具有两面性,JNI 编程也同样如此。程序员在使用 JNI 时应当认识到 JNI 编程中如下的几点弊端,扬长避短,才可以写出更加完善、高性能的代码:

  1. 从 Java 环境到 native code 的上下文切换耗时、低效。
  2. JNI 编程,如果操作不当,可能引起 Java 虚拟机的崩溃。
  3. JNI 编程,如果操作不当,可能引起内存泄漏。

    JAVA 中的内存泄漏

    JAVA 编程中的内存泄漏,从泄漏的内存位置角度可以分为两种:JVM 中 Java Heap 的内存泄漏;JVM 内存中 native memory 的内存泄漏。

    Java Heap 的内存泄漏

    Java 对象存储在 JVM 进程空间中的 Java Heap 中,Java Heap 可以在 JVM 运行过程中动态变化。如果 Java 对象越来越多,占据 Java Heap 的空间也越来越大,JVM 会在运行时扩充 Java Heap 的容量。如果 Java Heap 容量扩充到上限,并且在 GC 后仍然没有足够空间分配新的 Java 对象,便会抛出 out of memory 异常,导致 JVM 进程崩溃。
    Java Heap 中 out of memory 异常的出现有两种原因——①程序过于庞大,致使过多 Java 对象的同时存在;②程序编写的错误导致 Java Heap 内存泄漏。
    多种原因可能导致 Java Heap 内存泄漏。JNI 编程错误也可能导致 Java Heap 的内存泄漏。

    JVM 中 native memory 的内存泄漏

    从操作系统角度看,JVM 在运行时和其它进程没有本质区别。在系统级别上,它们具有同样的调度机制,同样的内存分配方式,同样的内存格局。
    JVM 进程空间中,Java Heap 以外的内存空间称为 JVM 的 native memory。进程的很多资源都是存储在 JVM 的 native memory 中,例如载入的代码映像,线程的堆栈,线程的管理控制块,JVM 的静态数据、全局数据等等。也包括 JNI 程序中 native code 分配到的资源。
    在 JVM 运行中,多数进程资源从 native memory 中动态分配。当越来越多的资源在 native memory 中分配,占据越来越多 native memory 空间并且达到 native memory 上限时,JVM 会抛出异常,使 JVM 进程异常退出。而此时 Java Heap 往往还没有达到上限。
    多种原因可能导致 JVM 的 native memory 内存泄漏。例如 JVM 在运行中过多的线程被创建,并且在同时运行。JVM 为线程分配的资源就可能耗尽 native memory 的容量。
    JNI 编程错误也可能导致 native memory 的内存泄漏。对这个话题的讨论是本文的重点。

继续阅读“(转)在JNI编程中避免内存泄漏”