利用JNI方法,通过WMI获取本地硬件信息(主板型号,硬盘序列号,CPU参数等)
2014-10-31 16:38
801 查看
因为有几台服务器是windows 2000的,之前采用的WMIC方法不适用(windows 2003开始有),更不用说WMI .NET了(采用.NET Framework 3.5),只能采用JNI的方法,通过C++来调用本地WMI接口来查询数据。幸好,这次WMI从windows 2000就开始有,如果再之前的版本我就没办法了。
程序结构:
在java中声明本地方法,然后调用C++编写的DLL,在C++中实现WMI查询,返回结果到java,最后由java封装成XML文件。
具体过程
(1) 构建JNI
参考资料: http://blog.csdn.net/crayonyi/article/details/7413017
http://www.cnblogs.com/youxilua/archive/2011/09/16/2178554.html
1.首先在java中声明本地方法
2.然后用命令行cd到java文件所在目录,执行javac编译。如我的是javac GetHWInfo.java
3.编译好之后cd.. 到上级目录,执行javah 包名.类名。如我的是 javah WMI.GetHWInfo
这时候就在文件目录下得到一个.h的头文件了。(我的是WMI_GetHWInfo.h),其中包名和类名之间由下划线隔开。
Notice:
编译时在java文件中只需要声明好C++文件中需要的本地方法就足够了,其他要用到的方法可以先不用管或先注释掉。事实上,因为我们还可能引用到其他的jar包,而这些包又有可能不在我们的src下面,比如我引用到dom4j.jar,那么在编译时就需要通过classpath关键字加上其他jar的引用,如果包多起来还是挺麻烦的。所以可以先编译必要的native方法,反正编译出来的头文件少几个无关的方法也不影响。
4.将编译好的头文件添加到vs解决方案里面。我用的是vs2008, 操作就是项目->添加项->已有项->WMI_GetHWInfo.h. 然后在程序中#include WMI_GetHWInfo.h.
5.打开头文件,把里面的<jni.h>改成”jni.h”,因为<>的话只在本路径中找,而””会在本路径找不到的时候再去外围路径找,这样才能找到jni.h
6.在{JAVA_HOME}/include/中的jni.h和jni_md.h按照上面4的方式加进来。
7.复制WMI_GetHWInfo.h中的方法名,比如我的是
JNIEXPORTjstring JNICALL Java_WMI_GetHWInfo_GetInfoFromWMI
(JNIEnv * env,jobject, jstring head, jstring content)
后面就可以开始实现之了。
(2) COM编写
参考资料:http://blog.csdn.net/fdyang2008/article/details/7615708
http://msdn.microsoft.com/en-us/library/windows/desktop/aa390423(v=vs.85).aspx
http://blog.csdn.net/ly402609921/article/details/7446943
JNI构建好之后,可以进入WMI编程。根据微软的MSDN, WMI的C++编程是基于COM组件的,其实我也没学过COM,但是知道COM就是一种接口规范。既然知道是一种规范的话,那么就按着规范走就行了,没有什么难的。
这里的具体过程见代码。
Notice:
1.在windows2000的平台上,在进行CoInitializeSecurity时需要对 void* pAuthList赋一个具体的值而不能是NULL。这里我赋值为RPC_C_AUTHN_LEVEL_DEFAULT。其他的赋值参考MSDN
http://msdn.microsoft.com/en-us/library/aa393617.aspx
2.WMI有很多很多的字段可以供我们查询,那怎么知道我们需要查哪一个呢?这里提供两种方法
第一种 关键字+WMI->必应。 比如 os wmi->必应。这样就可以获得答案。事实上当google不行了之后我发现必应还是挺好用的。
第二种,神器WMI Code Creator,你可以轻松查到所有的可查询字段,以及可以自动生成VB和C#查询代码。可惜我用不到= =。下载地址是
http://msdn.microsoft.com/en-us/library/aa393617.aspx
(3) 返回Java
在结束前先把内存和对象释放掉。
然后在Java根据返回的字符进行处理即可。我在java中将获得的结果封装成XML文件。这里用了dom4j.jar包进行处理。Java的操作就简单多了啊,主要用到下面几个方法
就这么简单。
后面可以改进的,在C++中加入可抛出异常,然后在java端捕获。参考资料http://blog.csdn.net/a345017062/article/details/8068932
将整个字符串数组的处理交给C++完成从而减少COM组件初始化的耗时。
Notion:学习JNI和COM的过程,感觉最麻烦的就是各种数据类型的转换了,我主要用到的还只是字符串类型的转换。下面写一些小收获。
1.带_T的表示unicode编码,带w的表示宽字符格式。凡是有中文的都必须是带Unicode编码的宽字符存储。(如wchar_t,wstring)这个在JNI和COM中非常重要,因为java和C++中的编码格式是不一样的,所以在传输过程中都用了同意的UTF-16编码格式。因此在C++端要传输或者接受java传过来的数据(特别是带中文的时候),就需要在C端用wchar_t*这种类型来接收。
2.jchar和wchar_t是等价的,可以轻松进行强制类型转换。
3.字符串操作在C++中会容易很多,因为在C++中有string类型,可以有各种API对字符串进行拼接、复制等操作,而不用像C里面先声明变量,再初始化空间,再用strcpy和strcat来翻来覆去的操作。Const char*转成string很简单,直接转换;而string转成const char*用c_str()方法即可。
4.事实上在MFC里面也有CString类型,类似于string,只要#include “afx.h”即可。
5.字符串赋值的方法,除了strcpy,wsprintf也是很好用的。
附上字符串操作参考资料:
JNI中文传输解决方案
http://www.cnblogs.com/mingzi/archive/2009/09/14/1566573.html
JNI如何传递参数
http://blog.csdn.net/wangkr111/article/details/7883461
这个解决得更加彻底
http://www.cnblogs.com/lifesting/archive/2010/10/11/1846909.html
程序结构:
在java中声明本地方法,然后调用C++编写的DLL,在C++中实现WMI查询,返回结果到java,最后由java封装成XML文件。
具体过程
(1) 构建JNI
参考资料: http://blog.csdn.net/crayonyi/article/details/7413017
http://www.cnblogs.com/youxilua/archive/2011/09/16/2178554.html
1.首先在java中声明本地方法
</pre><pre name="code" class="cpp">public native String GetInfoFromWMI(String head, String content);
2.然后用命令行cd到java文件所在目录,执行javac编译。如我的是javac GetHWInfo.java
3.编译好之后cd.. 到上级目录,执行javah 包名.类名。如我的是 javah WMI.GetHWInfo
这时候就在文件目录下得到一个.h的头文件了。(我的是WMI_GetHWInfo.h),其中包名和类名之间由下划线隔开。
Notice:
编译时在java文件中只需要声明好C++文件中需要的本地方法就足够了,其他要用到的方法可以先不用管或先注释掉。事实上,因为我们还可能引用到其他的jar包,而这些包又有可能不在我们的src下面,比如我引用到dom4j.jar,那么在编译时就需要通过classpath关键字加上其他jar的引用,如果包多起来还是挺麻烦的。所以可以先编译必要的native方法,反正编译出来的头文件少几个无关的方法也不影响。
4.将编译好的头文件添加到vs解决方案里面。我用的是vs2008, 操作就是项目->添加项->已有项->WMI_GetHWInfo.h. 然后在程序中#include WMI_GetHWInfo.h.
5.打开头文件,把里面的<jni.h>改成”jni.h”,因为<>的话只在本路径中找,而””会在本路径找不到的时候再去外围路径找,这样才能找到jni.h
6.在{JAVA_HOME}/include/中的jni.h和jni_md.h按照上面4的方式加进来。
7.复制WMI_GetHWInfo.h中的方法名,比如我的是
JNIEXPORTjstring JNICALL Java_WMI_GetHWInfo_GetInfoFromWMI
(JNIEnv * env,jobject, jstring head, jstring content)
后面就可以开始实现之了。
(2) COM编写
参考资料:http://blog.csdn.net/fdyang2008/article/details/7615708
http://msdn.microsoft.com/en-us/library/windows/desktop/aa390423(v=vs.85).aspx
http://blog.csdn.net/ly402609921/article/details/7446943
JNI构建好之后,可以进入WMI编程。根据微软的MSDN, WMI的C++编程是基于COM组件的,其实我也没学过COM,但是知道COM就是一种接口规范。既然知道是一种规范的话,那么就按着规范走就行了,没有什么难的。
这里的具体过程见代码。
<span style="white-space:pre"> </span>setlocale(LC_ALL, "chs"); //设置本地化语言, 没有的话中文输出为乱码 //初始化COM组件 HRESULT hres; hres = CoInitializeEx(0, COINIT_MULTITHREADED); if (FAILED(hres)) { cout<< "fail to initialize COM library"<< hres<< endl; return refalse; } //初始化COM组件的安全等级 hres = CoInitializeSecurity( NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, RPC_C_AUTHN_LEVEL_DEFAULT, // If in Windows 2000: need to specify a the default authentication credentials // for a user by using a SOLE_AUTHENTICATION_LIST structure in the pAuthList // parameter of CoInitializeSecurity EOAC_NONE, NULL ); if (FAILED(hres)) { CoUninitialize(); cout << "fail to initial security level" << hres << endl; return refalse; } //通过调用CoCreateInstance初始化WMI的定位器(IWbemLocator类型的实例) IWbemLocator *pLoc = NULL; hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc); if (FAILED(hres)) { CoUninitialize(); cout << "fail to create IWemLocator object" << hres << endl; return refalse; } //调用IWbemLocator::ConnectServer方法,通过这个定位器连接到WMI的命名空间, //通过把一个IWbemServices的实例以参数形式传递给ConnectServer方法,就会创建这个服务。 //如我们需要一些BIOS信息,那么需要使用的WMI提供程序是Win32_BIOS,则需要连接到ROOT//CIMV2命名空间中。 IWbemServices *pSvc = NULL; hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, NULL, 0, 0, &pSvc ); if (FAILED(hres)) { pLoc->Release(); CoUninitialize(); cout << "cannot connect IWbmService" << hres<< endl; return refalse; } //设置代理的安全等级 hres = CoSetProxyBlanket( pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE ); if (FAILED(hres)) { pSvc->Release(); pLoc->Release(); CoUninitialize(); cout << "fail to set proxy blanket" << hres << endl; return refalse; } //通过IWbemServices *pSvc创建一个WQL查询,结果保存在pEnumerator中 IEnumWbemClassObject* pEnumerator = NULL; hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * FROM Win32_Processor"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator); if (FAILED(hres)) { cout << "Query for operating system name failed." << hres << endl; pSvc->Release(); pLoc->Release(); CoUninitialize(); return refalse; // Program has failed. } // 枚举pEnumerator,获得*pclsObj,通过Get方法获取特定字段值:vtProp.bstrVal IWbemClassObject *pclsObj; ULONG uReturn = 0; while (pEnumerator) { HRESULT h 4000 r = pEnumerator->Next(WBEM_INFINITE, 1, &pclsObj, &uReturn); if(0 == uReturn) { break; } VARIANT vtProp; //vtProp.bstrVal is what you need. hr = pclsObj->Get(stmtcont, 0, &vtProp, 0, 0); //VariantClear(&vtProp); //hr = pclsObj->Get(L"Capacity", 0, &vtProp, 0, 0);// example "sp3" //VariantClear(&vtProp); //hr = pclsObj->Get(L"Name", 0, &vtProp, 0, 0); // example x64 //VariantClear(&vtProp); /*加入你的操作<span style="font-family: Arial, Helvetica, sans-serif;">*/</span>
VariantClear(&vtProp); pclsObj->Release(); }
Notice:
1.在windows2000的平台上,在进行CoInitializeSecurity时需要对 void* pAuthList赋一个具体的值而不能是NULL。这里我赋值为RPC_C_AUTHN_LEVEL_DEFAULT。其他的赋值参考MSDN
http://msdn.microsoft.com/en-us/library/aa393617.aspx
2.WMI有很多很多的字段可以供我们查询,那怎么知道我们需要查哪一个呢?这里提供两种方法
第一种 关键字+WMI->必应。 比如 os wmi->必应。这样就可以获得答案。事实上当google不行了之后我发现必应还是挺好用的。
第二种,神器WMI Code Creator,你可以轻松查到所有的可查询字段,以及可以自动生成VB和C#查询代码。可惜我用不到= =。下载地址是
http://msdn.microsoft.com/en-us/library/aa393617.aspx
(3) 返回Java
在结束前先把内存和对象释放掉。
然后在Java根据返回的字符进行处理即可。我在java中将获得的结果封装成XML文件。这里用了dom4j.jar包进行处理。Java的操作就简单多了啊,主要用到下面几个方法
Document document =DocumentHelper.createDocument(); Element e =document.addElement("info"); //创建根节点 e.addCDATA(totalSpace+"");
就这么简单。
后面可以改进的,在C++中加入可抛出异常,然后在java端捕获。参考资料http://blog.csdn.net/a345017062/article/details/8068932
将整个字符串数组的处理交给C++完成从而减少COM组件初始化的耗时。
Notion:学习JNI和COM的过程,感觉最麻烦的就是各种数据类型的转换了,我主要用到的还只是字符串类型的转换。下面写一些小收获。
1.带_T的表示unicode编码,带w的表示宽字符格式。凡是有中文的都必须是带Unicode编码的宽字符存储。(如wchar_t,wstring)这个在JNI和COM中非常重要,因为java和C++中的编码格式是不一样的,所以在传输过程中都用了同意的UTF-16编码格式。因此在C++端要传输或者接受java传过来的数据(特别是带中文的时候),就需要在C端用wchar_t*这种类型来接收。
2.jchar和wchar_t是等价的,可以轻松进行强制类型转换。
3.字符串操作在C++中会容易很多,因为在C++中有string类型,可以有各种API对字符串进行拼接、复制等操作,而不用像C里面先声明变量,再初始化空间,再用strcpy和strcat来翻来覆去的操作。Const char*转成string很简单,直接转换;而string转成const char*用c_str()方法即可。
4.事实上在MFC里面也有CString类型,类似于string,只要#include “afx.h”即可。
5.字符串赋值的方法,除了strcpy,wsprintf也是很好用的。
附上字符串操作参考资料:
JNI中文传输解决方案
http://www.cnblogs.com/mingzi/archive/2009/09/14/1566573.html
JNI如何传递参数
http://blog.csdn.net/wangkr111/article/details/7883461
这个解决得更加彻底
http://www.cnblogs.com/lifesting/archive/2010/10/11/1846909.html
相关文章推荐
- WMIC查询获取本地硬件信息(主板型号,硬盘序列号,CPU参数等)
- WMI获取硬件信息封装函数方法(联想台式机出厂编号 CPUID BIOS序列号 硬盘信息 显卡信息 MAC地址)
- JSP利用JNI获取硬盘信息(型号,序列号,容量...)
- JSP利用JNI获取硬盘信息(型号,序列号,容量...) ,提供固定下载地址
- [转]获取机器的硬件信息(CPU ID序列号, 主板信息,硬盘序列号,系统信息)
- Atitit.获取主板与bios序列号获取硬件设备信息  Wmi wmic 的作用
- 通过WMI获取网卡MAC地址、硬盘序列号、主板序列号、CPU ID、BIOS序列号
- 通过WMI获取网卡MAC地址、硬盘序列号、主板序列号、CPU ID、BIOS序列号
- 获取机器的硬件信息(CPU ID序列号, 主板信息,硬盘序列号,系统信息)
- 通过mssql数据库来获取主机的硬件相关信息,网卡,硬盘,主板等
- 通过wmi获取本地硬件信息的一些疑问。
- 获取机器的硬件信息(CPU ID序列号, 主板信息,硬盘序列号,系统信息)
- 通过WMI获取网卡MAC地址、硬盘序列号、主板序列号、CPU ID、BIOS序列号
- 通过WMI获取网卡MAC地址、硬盘序列号、主板序列号、CPU ID、BIOS序列号
- [转]获取机器的硬件信息(CPU ID序列号, 主板信息,硬盘序列号,系统信息)
- MASM32编程通过WMI获取BIOS、主板、硬盘、CPU、网卡的信息
- C# 获取机器的硬件信息(CPU ID序列号, 主板信息,硬盘序列号,系统信息)
- 汇编语言通过WMI获取BIOS、主板、硬盘、CPU、网卡的信息
- (转)通过WMI获取网卡MAC地址、硬盘序列号、主板序列号、CPU ID、BIOS序列号
- 通过WMI获取网卡MAC地址、硬盘序列号、主板序列号、CPU ID、BIOS序列号