文字化け・・・WMIも修正
文字コードの問題が理解できたので、始めに作ったwmiも作り直してみました。
wmi.cppのgetString(VARIANT *value)メソッドでVT_BSTRを別ロジックにしていたのを、VT_BOOL以外同じロジックに集約しました。
/* * VARIANTデータを文字列値に変換し取得する * param : データ(VARIANTポインター) * * return : String */ String WmiResult::getString(VARIANT *value) { String result; if (value != NULL) { if (isArray(value)) { string_vector list = getStringArray(value); if (!list.empty()) result = list.at(0); } else { if (getType(value) == VT_BOOL) { BSTR bool_str; /* * VT_BOOL型のデータをVT_BSTR型のデータに変換 * return : S_OK = 正常に変換できた(ここでは結果はチェックしない) * その他 = エラー * 参照:http://msdn.microsoft.com/en-us/library/ms221338.aspx */ VarBstrFromBool(((isByRef(value)) ? *value->pboolVal : value->boolVal), 0, LOCALE_NOUSEROVERRIDE, &bool_str); result.append(_bstr_t(bool_str)); } else { result.append((_TCHAR *)_bstr_t(*value)); } } } return result; }
これに会わせて、JNIのjniwmiのload_classを以下のように修正しました。
/* * WmiCallからの結果をJavaのクラスに展開する処理 */ jobject load_class(JNIEnv *env, WmiResult results) { /* * WmiResultオブジェクトの生成 */ jobject wmiObj = env->NewObject(wr_class, wr_ctor); /* * プロパティ値の設定 */ size_t prop_count = results.items(); jobjectArray properties = env->NewObjectArray((jsize)prop_count, str_class, NULL); for (size_t i = 0; i < prop_count; i++) { _bstr_t prop = _bstr_t((_TCHAR *)results.getProperty(i).c_str()); jobject prop_str = env->NewString((const jchar *)((wchar_t *)prop), (jsize)prop.length()); env->SetObjectArrayElement(properties, (jsize)i, prop_str); } env->SetObjectField(wmiObj, wr_propertiesID, properties); /* * 戻り値の設定 */ size_t row = results.size(); jobjectArray result = env->NewObjectArray((jsize)row, vector_class, NULL); for (size_t i = 0; i < row; i++) { jobject vector = env->NewObject(vector_class, vector_init); for (size_t j = 0; j < prop_count; j++) { String prop = results.getProperty(j); String entry_value = results.getString(prop, i); _bstr_t entry = _bstr_t((_TCHAR *)entry_value.c_str()); jobject entry_str = env->NewString((const jchar *)((wchar_t *)entry), (jsize)entry.length()); jboolean res = env->CallBooleanMethod(vector, vector_add, entry_str); } env->SetObjectArrayElement(result, (jsize)i, vector); } env->SetObjectField(wmiObj, wr_resultID, result); return wmiObj; }
これで少しは綺麗なロジックになったかな。
文字化け
色々と調べていくと、Javaでの表示とC++での表示で文字表示が一致していないのは、内部的なロケールの処理の問題でした。
IfInfo.cppの中のgetInterfaceList()メソッドとloadInterfaceInfo(IfInfo& if_table)を書き換えて
/* * システムで利用可能なインターフェースの一覧を取得する * param : 無し * * return : interface_list */ interface_list IfInfo::getInterfaceList() { interface_list if_list; long retCode = NO_ERROR; DWORD dwSize = 0; GetIfTable(NULL, &dwSize, true); PMIB_IFTABLE pIfTable = (MIB_IFTABLE *)malloc(dwSize); memset(pIfTable, 0, dwSize); retCode = GetIfTable(pIfTable, &dwSize, true); if (retCode == NO_ERROR) { for (u_int i = 0; i < pIfTable->dwNumEntries; i++) { IfInfo if_table = IfInfo(); MIB_IFROW pInterface = pIfTable->table[i]; if_table.ifIndex = pInterface.dwIndex; if_table.ifName = if_table.description = String((_TCHAR *)_bstr_t((char *)pInterface.bDescr)); if_table.ifType = pInterface.dwType; if_table.opStatus = pInterface.dwOperStatus; if_list.push_back(if_table); } } free(pIfTable); return if_list; } /* * インターフェースの詳細情報を設定する * param : インターフェーステーブル(IfInfoポインター) * * return : 無し */ void IfInfo::loadInterfaceInfo(IfInfo& if_table) { DWORD dwSize = 0; long retCode = NO_ERROR; if (GetAdaptersInfo(NULL, &dwSize) == ERROR_BUFFER_OVERFLOW) { PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)malloc(dwSize); memset(pAdapterInfo, 0, dwSize); retCode = GetAdaptersInfo(pAdapterInfo, &dwSize); if (retCode == NO_ERROR) { PIP_ADAPTER_INFO pAdapter = pAdapterInfo; while (pAdapter) { if (pAdapter->Index == if_table.ifIndex) { if_table.adapterName = String((_TCHAR *)_bstr_t(pAdapter->AdapterName)); if_table.ifAddress = getMacAddress(pAdapter->Address, pAdapter->AddressLength); if_table.ipAddressList = AddrInfo::setAddressList(if_table.ifIndex); if_table.dhcpEnabled = pAdapter->DhcpEnabled ? true : false; HKEY hkey = NULL; _bstr_t adapter = _bstr_t(_T("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\")); adapter += _bstr_t((char *)pAdapter->AdapterName); adapter += _bstr_t(_T("\\Connection")); int rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (_TCHAR *)adapter, 0, KEY_READ, &hkey); if (rc == ERROR_SUCCESS) { DWORD valueType = 0; DWORD nameLen = 0; rc = RegQueryValueEx(hkey, _T("Name"), 0, &valueType, NULL, &nameLen); _TCHAR *buff = (_TCHAR *)calloc(nameLen+1, sizeof(char)); rc = RegQueryValueEx(hkey, _T("Name"), 0, &valueType, (LPBYTE)buff, &nameLen); if_table.ifName = String(buff); RegCloseKey(hkey); free(buff); } } pAdapter = pAdapter->Next; } free(pAdapterInfo); } } }
テストプログラムのメインの部分にロケールを設定する行を追加しました。
int _tmain(int argc, _TCHAR* argv[]) { COUT.imbue(locale("japanese")); getAddress(); getInterface(); key_wait(); return 0; }
ネットワークインターフェース情報の取得(2)
Javaのクラスは、取りあえずこんな感じにしてみました。
アドレスを持つクラスとして、
AddrInfo.java
package net; public class AddrInfo { public static int IPADDR_PRIMARY = 0x0001; // Primary ipaddr public static int IPADDR_DYNAMIC = 0x0004; // Dynamic ipaddr public static int IPADDR_DISCONNECTED = 0x0008; // Address is on disconnected interface public static int IPADDR_DELETED = 0x0040; // Address being deleted public static int IPADDR_TRANSIENT = 0x0080; // Transient address public static int IPADDR_DNS_ELIGIBLE = 0X0100; // Address is published in DNS. private String ipAddress = null; private String netMask = null; private String bcastAddr = null; private int ipType = 0; public AddrInfo() { } public String getIpAddress() { return ipAddress; } public String getNetMask() { return netMask; } public String getBroadcastAddress() { return bcastAddr; } public int getIpType() { return ipType; } public boolean isPrimaryAddress() { if ((ipType & IPADDR_PRIMARY) > 0) return true; return false; } }
それから、インターフェース情報を保存する基本となるクラスとして
IfInfo.java
package net; public class IfInfo { public static int IP_ADAPTER_DHCP_ENABLED = 0x0004; public static int IF_OPER_STATUS_OPERATIONAL = 5; private int index = 0; private String name = null; private String adapter = null; private String description = null; private String mac = null; private int type = 0; private int status = 0; private boolean dhcp = false; private AddrInfo[] address = null; static { System.loadLibrary("jninet"); init(); } public IfInfo() { } public int getIndex() { return index; } public String getName() { return name; } public String getAdapterName() { return adapter; } public String getDescription() { return description; } public String getMacAddress() { return mac; } public int getType() { return type; } public AddrInfo[] getAddressList() { return address; } public int getOpStatus() { return status; } public boolean isRunning() { if (status == IF_OPER_STATUS_OPERATIONAL) return true; return false; } public boolean isDhcpEnable() { return dhcp; } private static native void init(); public static native IfInfo[] getInterfaces(); }
テストのクラスはこんな感じで
IfInfoTest.java
package net.test; import net.AddrInfo; import net.IfInfo; public class IfInfoTest { public IfInfoTest() { IfInfo[] nis = IfInfo.getInterfaces(); for (int i = 0; i < nis.length; i++) { print(nis[i]); } } private void print(IfInfo ni) { if (ni != null) { System.out.println("Nic : "+ni.getIndex()); System.out.println(" Name : "+ni.getName()); System.out.println(" AdapterName : "+ni.getAdapterName()); System.out.println(" Description : "+ni.getDescription()); System.out.println(" MacAddress : "+ni.getMacAddress()); System.out.println(" Type : "+ni.getType()); System.out.println(" Running : "+ni.isRunning()); System.out.println(" DHCP : "+ni.isDhcpEnable()); AddrInfo[] ais = ni.getAddressList(); if (ais != null) { for (int j = 0; j < ais.length; j++) { AddrInfo ai = ais[j]; if (ai != null) { System.out.println(" Address : "+ai.getIpAddress()+"/"+ai.getNetMask()+" --- "+ai.getBroadcastAddress()); } } } } } public static void main(String[] args) { new IfInfoTest(); } }
IfInfo.javaをコンパイルして、JNI用のヘッダファイルを生成してから、
C++でJNIのロジックを作成ました。
#include "jninet.h" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) { return TRUE; } jobject load_address(JNIEnv *env, AddrInfo addr) { jobject addrifObj = env->NewObject(ai_class, ai_ctor); String address_value = addr.getIpAddress(); _bstr_t address_str = _bstr_t((_TCHAR *)address_value.c_str()); jobject address = env->NewString((const jchar *)((wchar_t *)address_str), (jsize)address_str.length()); env->SetObjectField(addrifObj, ai_ipAddressID, address); String mask_value = addr.getNetMask(); _bstr_t mask_str = _bstr_t((_TCHAR *)mask_value.c_str()); jobject mask = env->NewString((const jchar *)((wchar_t *)mask_str), (jsize)mask_str.length()); env->SetObjectField(addrifObj, ai_netMaskID, mask); String bcast_value = addr.getBroadcastAddress(); _bstr_t bcast_str = _bstr_t((_TCHAR *)bcast_value.c_str()); jobject bcast = env->NewString((const jchar *)((wchar_t *)bcast_str), (jsize)bcast_str.length()); env->SetObjectField(addrifObj, ai_bcastAddrID, bcast); env->SetIntField(addrifObj, ai_ipTypeID, addr.getIpType()); return addrifObj; } jobject load_class(JNIEnv *env, IfInfo info) { int bLen = 0; jobject netifObj = env->NewObject(if_class, if_ctor); env->SetIntField(netifObj, if_indexID, info.getIndex()); String name_value = info.getName(); _bstr_t name_str = _bstr_t((_TCHAR *)name_value.c_str()); jobject name = env->NewString((const jchar *)((wchar_t *)name_str), (jsize)name_str.length()); env->SetObjectField(netifObj, if_nameID, name); _bstr_t adapter_str = _bstr_t((_TCHAR *)info.getAdapterName().c_str()); jobject adapter = env->NewString((const jchar *)((wchar_t *)adapter_str), (jsize)adapter_str.length()); env->SetObjectField(netifObj, if_adapterID, adapter); _bstr_t description_str = _bstr_t((_TCHAR *)info.getDescription().c_str()); jobject description = env->NewString((const jchar *)((wchar_t *)description_str), (jsize)description_str.length()); env->SetObjectField(netifObj, if_descriptionID, description); _bstr_t mac_str = _bstr_t((_TCHAR *)info.getMacAddress().c_str()); jobject mac = env->NewString((const jchar *)((wchar_t *)mac_str), (jsize)mac_str.length()); env->SetObjectField(netifObj, if_macID, mac); env->SetIntField(netifObj, if_typeID, info.getType()); address_list addrs = info.getIpAddressList(); if (!addrs.empty()) { jsize ad_count = (jsize)addrs.size(); jobjectArray addr_info = env->NewObjectArray(ad_count, ai_class, NULL); for (jsize j = 0; j < ad_count; j++) { AddrInfo addr = addrs.at(j); jobject one_address = load_address(env, addr); env->SetObjectArrayElement(addr_info, j, one_address); } env->SetObjectField(netifObj, if_addressID, addr_info); } else { env->SetObjectField(netifObj, if_addressID, NULL); } env->SetIntField(netifObj, if_statusID, info.getOpStatus()); env->SetBooleanField(netifObj, if_dhcpID, info.isDhcpEnabled()); return netifObj; } JNIEXPORT void JNICALL Java_net_IfInfo_init(JNIEnv *env, jclass cls) { if_class = (jclass)env->NewGlobalRef(cls); if_indexID = env->GetFieldID(if_class, "index", "I"); if_nameID = env->GetFieldID(if_class, "name", "Ljava/lang/String;"); if_adapterID = env->GetFieldID(if_class, "adapter", "Ljava/lang/String;"); if_descriptionID = env->GetFieldID(if_class, "description", "Ljava/lang/String;"); if_macID = env->GetFieldID(if_class, "mac", "Ljava/lang/String;"); if_typeID = env->GetFieldID(if_class, "type", "I"); if_statusID = env->GetFieldID(if_class, "status", "I"); if_dhcpID = env->GetFieldID(if_class, "dhcp", "Z"); if_addressID = env->GetFieldID(if_class, "address", "[Lnet/AddrInfo;"); if_ctor = env->GetMethodID(if_class, "<init>", "()V"); ai_class = env->FindClass("net/AddrInfo"); ai_class = (jclass)env->NewGlobalRef(ai_class); ai_ipAddressID = env->GetFieldID(ai_class, "ipAddress", "Ljava/lang/String;"); ai_netMaskID = env->GetFieldID(ai_class, "netMask", "Ljava/lang/String;"); ai_bcastAddrID = env->GetFieldID(ai_class, "bcastAddr", "Ljava/lang/String;"); ai_ipTypeID = env->GetFieldID(ai_class, "ipType", "I"); ai_ctor = env->GetMethodID(ai_class, "<init>", "()V"); jclass c = env->FindClass("java/lang/String"); str_class = (jclass)env->NewGlobalRef(c); env->DeleteLocalRef(c); } JNIEXPORT jobjectArray JNICALL Java_net_IfInfo_getInterfaces(JNIEnv *env, jclass cls) { jobjectArray if_list = NULL; interface_list list = IfInfo::getIfList(); if (!list.empty()) { jsize if_count = (jsize)list.size(); if (if_count > 0) { if_list = env->NewObjectArray(if_count, if_class, NULL); for (jsize i = 0; i < if_count; i++) { IfInfo info = list.at(i); jobject one_interface = load_class(env, info); env->SetObjectArrayElement(if_list, i, one_interface); } } list.clear(); } return if_list; }
ただテストプログラムを実行すると、AdapterNameとDescriptionで文字化けが発生しちゃいました。
この問題、実は以前のWMIの処理の時にも発生していて、WMIの時はJNIからgetString()メソッドを呼ぶんじゃなくって、getValue()メソッドを呼び出した結果を直接処理するように変更をしています。
どの部分が問題なのかは分かっているんですが、どうして・・・がまだ分かっていないんですよね。
問題なのは、基本となるifinfo.cppの中で文字列を処理する部分があって、C++のテストプログラムで漢字が入った文字列を表示させる時に正しくないんで組み込んだ、
basic_stringstream<_TCHAR> buf;
buf << _bstr_t((char *)pInterface.bDescr);
に起因してるんだとは思うんだけど・・・。
この辺りをこれから調査です。
ネットワークインターフェース情報の取得
JavaのNetworkInterfaceでgetName()メソッドを呼び出してもXP環境では文字化けして正しく表示できないばかりでなく、getByName()メソッドでは、通常Windowsで利用される"ローカル エリア接続"等では探すことが出来ません。
それで、自前でアドレス情報を取得したりインターフェースを探せるクラスを作ってみようと思います。
まず、C++でインターフェース情報を取得するクラスの作成から。
IPアドレスを保存するクラスとして
addrinfo.h
#include <windows.h> #include <tchar.h> #include <comdef.h> #include <iphlpapi.h> #define LOOPBACK_INTERFACE IF_TYPE_SOFTWARE_LOOPBACK #define IPADDR_PRIMARY MIB_IPADDR_PRIMARY #define IPADDR_DYNAMIC MIB_IPADDR_DYNAMIC #define DIRECT_ROUTE MIB_IPROUTE_TYPE_DIRECT #define INDIRECT_ROUTE MIB_IPROUTE_TYPE_INDIRECT #include <vector> #include <string> using namespace std; typedef basic_string<_TCHAR> String; class AddrInfo; typedef vector<AddrInfo> address_list; extern const String HOST_MASK; extern const String GATEWAY_NET; extern const String GATEWAY_MASK; class AddrInfo { private: static String getBcastAddr(DWORD ip, DWORD mask, DWORD type); public: AddrInfo(const AddrInfo& info); AddrInfo(String address, String mask = HOST_MASK, String bcast = String(_T("")), int type = 0); AddrInfo(); ~AddrInfo(); String getIpAddress(); String getNetMask(); String getBroadcastAddress(); int getIpType(); bool isPrimaryAddress(); static address_list setAddressList(int ifIndex); private: String ipAddress; String netMask; String bcastAddr; int ipType; };
addrinfo.cpp
#include "addrinfo.h" const String HOST_MASK = String(_T("255.255.255.255")); const String GATEWAY_NET = String(_T("0.0.0.0")); const String GATEWAY_MASK = String(_T("0.0.0.0")); // Copy Constructor AddrInfo::AddrInfo(const AddrInfo& info) { ipAddress = info.ipAddress; netMask = info.netMask; bcastAddr = info.bcastAddr; ipType = info.ipType; } // Constructor AddrInfo::AddrInfo(String address, String mask, String bcast, int type) { ipAddress = address; netMask = mask; if (!bcast.empty()) { bcastAddr = bcast; } else { bcastAddr = address; } ipType = type; } // Constructor AddrInfo::AddrInfo() { ipType = 0; } // Destructor AddrInfo::~AddrInfo() { } /* * IPアドレスを取得する * param : 無し * * return : String */ String AddrInfo::getIpAddress() { return ipAddress; } /* * ネットワークマスクを取得する * param : 無し * * return : String */ String AddrInfo::getNetMask() { return netMask; } /* * Broadcastアドレスを取得する * param : 無し * * return : String */ String AddrInfo::getBroadcastAddress() { return bcastAddr; } /* * IPアドレスのタイプを取得する * param : 無し * * return : int */ int AddrInfo::getIpType() { return ipType; } /* * IPアドレスがプライマリアドレスか確認する * param : 無し * * return : bool * true = プライマリアドレスである * false = プライマリアドレスでない */ bool AddrInfo::isPrimaryAddress() { return ((ipType & IPADDR_PRIMARY) > 0) ? true : false; } /* * IPアドレスとネットマスクからブロードキャストアドレスを取得する * param : IPアドレス(DWORD) * ネットマスク(DWORD) * IPタイプ(DWORD) * * return : String */ String AddrInfo::getBcastAddr(DWORD ip, DWORD mask, DWORD type) { DWORD bcast = (type == 1) ? 0xffffffff : 0x00000000; bcast ^= mask; DWORD network = ~bcast; network &= ip; bcast |= network; return String((_TCHAR*)_bstr_t(inet_ntoa(*(struct in_addr *)&bcast))); } /* * 指定したインターフェースに設定されたアドレスを取得する * param : インターフェースの番号(int) * * return : address_list */ address_list AddrInfo::setAddressList(int ifIndex) { address_list result; String ipaddress; String netmask; String bcastaddress; long retCode = NO_ERROR; DWORD dwSize = 0; GetIpAddrTable(NULL, &dwSize, 0); PMIB_IPADDRTABLE pIPAddrTable = (MIB_IPADDRTABLE *)malloc(dwSize); memset(pIPAddrTable, 0, dwSize); if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == NO_ERROR) { for (unsigned int num = 0; num < pIPAddrTable->dwNumEntries; num++) { if (ifIndex == pIPAddrTable->table[num].dwIndex) { ipaddress = String((_TCHAR *)_bstr_t(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwAddr))); netmask = String((_TCHAR *)_bstr_t(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwMask))); bcastaddress = getBcastAddr(pIPAddrTable->table[num].dwAddr, pIPAddrTable->table[num].dwMask, pIPAddrTable->table[num].dwBCastAddr); AddrInfo info = AddrInfo(ipaddress, netmask, bcastaddress, pIPAddrTable->table[num].wType); result.push_back(info); } } } free(pIPAddrTable); return result; }
インターフェース情報を保存するクラスとして
ifinfo.h
#include <vector> #include <string> #include <sstream> using namespace std; #include "addrinfo.h" typedef basic_string<_TCHAR> String; class IfInfo; typedef vector<IfInfo> interface_list; typedef interface_list::iterator if_iter; class IfInfo { private: static interface_list getInterfaceList(); static void loadInterfaceInfo(IfInfo *if_table); static String getMacAddress(unsigned char *value, unsigned long len); public: IfInfo(const IfInfo& info); IfInfo(); ~IfInfo(); unsigned long getIndex(); String getName(); String getAdapterName(); String getDescription(); String getMacAddress(); int getType(); int getOpStatus(); address_list getIpAddressList(); bool isDhcpEnabled(); bool isRunning(); static interface_list getIfList(); static IfInfo findInterfaceByIndex(unsigned long index); static IfInfo findInterfaceByName(String ifname); private: unsigned long ifIndex; String ifName; String adapterName; String description; String ifAddress; int ifType; int opStatus; address_list ipAddressList; bool dhcpEnabled; };
ifinfo.cpp
#include "ifinfo.h" /* * システムで利用可能なインターフェースの一覧を取得する * param : 無し * * return : interface_list */ interface_list IfInfo::getInterfaceList() { interface_list if_list; long retCode = NO_ERROR; DWORD dwSize = 0; GetIfTable(NULL, &dwSize, true); PMIB_IFTABLE pIfTable = (MIB_IFTABLE *)malloc(dwSize); memset(pIfTable, 0, dwSize); retCode = GetIfTable(pIfTable, &dwSize, true); if (retCode == NO_ERROR) { for (u_int i = 0; i < pIfTable->dwNumEntries; i++) { IfInfo if_table = IfInfo(); MIB_IFROW pInterface = pIfTable->table[i]; if_table.ifIndex = pInterface.dwIndex; basic_stringstream<_TCHAR> buf; buf << _bstr_t((char *)pInterface.bDescr); if_table.ifName = if_table.description = buf.str(); if_table.ifType = pInterface.dwType; if_table.opStatus = pInterface.dwOperStatus; if_list.push_back(if_table); } } free(pIfTable); return if_list; } String IfInfo::getMacAddress(unsigned char *value, unsigned long len) { _bstr_t mac; for (u_int i = 0; i < len; i++) { if (i > 0) mac += _T(":"); char code[3]; sprintf(code, "%02x", value[i]); mac += code; } return String((_TCHAR *)mac); } // interface_info // Copy Constructor IfInfo::IfInfo(const IfInfo& info) { ifIndex = info.ifIndex; ifName = info.ifName; adapterName = info.adapterName; description = info.description; ifAddress = info.ifAddress; ifType = info.ifType; opStatus = info.opStatus; dhcpEnabled = info.dhcpEnabled; if (info.ipAddressList.size() > 0) { for (size_t i = 0; i < info.ipAddressList.size(); i++) { ipAddressList.push_back(info.ipAddressList[i]); } } } // Constructor IfInfo::IfInfo() { } // Destructor IfInfo::~IfInfo() { } /* * インターフェースの詳細情報を設定する * param : インターフェーステーブル(IfInfoポインター) * * return : 無し */ void IfInfo::loadInterfaceInfo(IfInfo *if_table) { DWORD dwSize = 0; long retCode = NO_ERROR; if (GetAdaptersInfo(NULL, &dwSize) == ERROR_BUFFER_OVERFLOW) { PIP_ADAPTER_INFO pAdapterInfo = (IP_ADAPTER_INFO *)malloc(dwSize); memset(pAdapterInfo, 0, dwSize); retCode = GetAdaptersInfo(pAdapterInfo, &dwSize); if (retCode == NO_ERROR) { PIP_ADAPTER_INFO pAdapter = pAdapterInfo; while (pAdapter) { if (pAdapter->Index == if_table->ifIndex) { if_table->adapterName = String((_TCHAR *)_bstr_t(pAdapter->AdapterName)); if_table->ifAddress = getMacAddress(pAdapter->Address, pAdapter->AddressLength); if_table->ipAddressList = AddrInfo::setAddressList(if_table->ifIndex); if_table->dhcpEnabled = pAdapter->DhcpEnabled ? true : false; HKEY hkey = NULL; _bstr_t adapter = _bstr_t(_T("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\")); adapter += _bstr_t((char *)pAdapter->AdapterName); adapter += _bstr_t(_T("\\Connection")); int rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, (_TCHAR *)adapter, 0, KEY_READ, &hkey); if (rc == ERROR_SUCCESS) { DWORD valueType = 0; DWORD nameLen = 0; rc = RegQueryValueEx(hkey, _T("Name"), 0, &valueType, NULL, &nameLen); nameLen++; char *buff = (char *)calloc(nameLen, sizeof(char)); rc = RegQueryValueEx(hkey, _T("Name"), 0, &valueType, (LPBYTE)buff, &nameLen); RegCloseKey(hkey); basic_stringstream<_TCHAR> buf; buf << _bstr_t((_TCHAR *)buff); if_table->ifName = buf.str(); free(buff); } } pAdapter = pAdapter->Next; } free(pAdapterInfo); } } } /* * インターフェースの名前を取得する * param : 無し * * return : String */ String IfInfo::getName() { return ifName; } /* * インターフェースのアダプタ名を取得する * param : 無し * * return : String */ String IfInfo::getAdapterName() { return adapterName; } /* * インターフェースの説明を取得する * param : 無し * * return : String */ String IfInfo::getDescription() { return description; } /* * インターフェースのMACアドレスを取得する * param : 無し * * return : String */ String IfInfo::getMacAddress() { return ifAddress; } /* * インターフェースの番号を取得する * param : 無し * * return : u_long */ u_long IfInfo::getIndex() { return ifIndex; } /* * インターフェースのタイプを取得する * param : 無し * * return : int */ int IfInfo::getType() { return ifType; } /* * インターフェースのステータスを取得する * param : 無し * * return : int */ int IfInfo::getOpStatus() { return opStatus; } /* * インターフェースのアドレスリストを取得する * param : 無し * * return : address_list */ address_list IfInfo::getIpAddressList() { return ipAddressList; } /* * インターフェースがDHCPに設定されているか確認する * param : 無し * * return : bool * true = DHCPである * false = DHCPでない */ bool IfInfo::isDhcpEnabled() { return dhcpEnabled; } /* * インターフェースが動作中か確認する * param : 無し * * return : bool * true = 動作中である * false = 動作中でない */ bool IfInfo::isRunning() { return (opStatus == IF_OPER_STATUS_OPERATIONAL) ? true : false; } /* * システムで利用可能なインターフェースの一覧を取得する * param : 無し * * return : interface_list */ interface_list IfInfo::getIfList() { interface_list net_list = getInterfaceList(); for (if_iter if_ent = net_list.begin(); if_ent != net_list.end(); if_ent++) { IfInfo::loadInterfaceInfo(&(*if_ent)); } return net_list; } /* * インターフェースの一覧からインターフェース番号の情報を取得する * param : インターフェース番号(u_long) * * return : IfInfo */ IfInfo IfInfo::findInterfaceByIndex(unsigned long index) { IfInfo if_table; interface_list net_list = getInterfaceList(); for (if_iter if_ent = net_list.begin(); if_ent != net_list.end(); if_ent++) { if ((*if_ent).getIndex() == index) { IfInfo::loadInterfaceInfo(&(*if_ent)); return *if_ent; } } return if_table; } /* * インターフェースの一覧からインターフェース名の情報を取得する * param : インターフェース名(charポインター) * * return : IfInfo */ IfInfo IfInfo::findInterfaceByName(String ifname) { IfInfo if_table; interface_list net_list = getIfList(); for (if_iter if_ent = net_list.begin(); if_ent != net_list.end(); if_ent++) { if ((*if_ent).getName().compare(ifname) == 0) { IfInfo::loadInterfaceInfo(&(*if_ent)); return *if_ent; } } return if_table; }
テストの為に次のプログラムを実行して確認。
#include <windows.h> #include <string> #include <iostream> using namespace std; #include <addrinfo.h> #include <ifinfo.h> using namespace penguin; #ifdef _UNICODE #define COUT wcout #define CIN wcin #else #define COUT cout #define CIN cin #endif // _UNICODE void getAddress() { for (int i = 0; i < 256; i++) { address_list list = AddrInfo::setAddressList(i); for (size_t j = 0; j < list.size(); j++) { AddrInfo info = list.at(j); COUT << _T("Index : ") << i << _T("-") << info.getIpAddress() << endl; } } } void getInterface() { interface_list list = IfInfo::getIfList(); for (size_t j = 0; j < list.size(); j++) { IfInfo info = list.at(j); COUT << _T("Index : ") << info.getIndex() << _T("-") << info.getDescription() << _T("-") << info.getName() << endl; } IfInfo intInfo = IfInfo::findInterfaceByName(String(_T("ローカル エリア接続 3"))); COUT << _T("Index : ") << intInfo.getIndex() << _T("-") << intInfo.getDescription() << _T("-") << intInfo.getName() << endl; } int _tmain(int argc, _TCHAR* argv[]) { getAddress(); getInterface(); return 0; }
このプログラムをベースにJNIでJavaからのアクセスクラスを作成します。
SerialNumberの取得(3)
色々やっていると欲が出てくるもので、VistaとXPに関しては昨日のロジックでなんら問題は無かったのですが、今度はWindows2000で表示できないというのが発覚しました。
最近ではWindows2000なんて殆ど見ることもなかったのですが、仕事の関係でWindows2000のサポートは無視するわけにはいかず、とにかくネットで検索して
http://www.usefullcode.net/2007/02/if.html
なんかを参考にして必要最低限まで削った関数を作っちゃいました。
#include <winioctl.h> #include <ata.h> enum IO_CONTROL_CODE { DFP_GET_VERSION = 0x00074080, DFP_RECEIVE_DRIVE_DATA = 0x0007c088, }; enum ATAPI_COMMAND_CODE { ATAPI_IDENTIFY_DEVICE = 0xA1, ATA_IDENTIFY_DEVICE = 0xEC, }; typedef GETVERSIONINPARAMS GETVERSIONOUTPARAMS, *PGETVERSIONOUTPARAMS, *LPGETVERSIONOUTPARAMS; #pragma pack(push,1) typedef struct _IDENTIFY_DEVICE_OUTDATA { SENDCMDOUTPARAMS sSendCmdOutParam; BYTE pData[IDENTIFY_BUFFER_SIZE-1]; } IDENTIFY_DEVICE_OUTDATA, *PIDENTIFY_DEVICE_OUTDATA; #pragma pack(pop) String getDiskSerial(int drive, _TCHAR *device_id) { BOOL status = FALSE; DWORD ret_size = 0; String serialNumber = String(_T("N/A")); /* * 取得したい物理デバイスをオープンする * return : I/Oハンドル */ HANDLE hIoCtrl = CreateFile(device_id, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); if (hIoCtrl != INVALID_HANDLE_VALUE) { GETVERSIONOUTPARAMS ver_param; memset((void*)&ver_param, 0, sizeof(ver_param)); /* * デバイスからDFPのバージョンを取得する * return : 処理ステータス * 0以外 = 成功() * 0 = 失敗 * 参照:http://msdn.microsoft.com/en-us/library/aa363216.aspx */ status = DeviceIoControl(hIoCtrl, DFP_GET_VERSION, NULL, 0, &ver_param, sizeof(ver_param), &ret_size, NULL); if (status != 0) { SENDCMDINPARAMS send_cmd; IDENTIFY_DEVICE_OUTDATA device_data; memset(&send_cmd, 0, sizeof(SENDCMDINPARAMS)); send_cmd.cBufferSize = IDENTIFY_BUFFER_SIZE; send_cmd.irDriveRegs.bCommandReg = (ver_param.bIDEDeviceMap >> drive & 0x10) ? ATAPI_IDENTIFY_DEVICE : ATA_IDENTIFY_DEVICE; send_cmd.irDriveRegs.bDriveHeadReg = 0xA0 | ((drive & 1) << 4); send_cmd.bDriveNumber = drive; ret_size = 0; memset(&device_data, 0, sizeof(IDENTIFY_DEVICE_OUTDATA)); /* * デバイスからDFPのバージョンを取得する * return : 処理ステータス * 0以外 = 成功 * 0 = 失敗 */ status = DeviceIoControl(hIoCtrl, DFP_RECEIVE_DRIVE_DATA, &send_cmd, sizeof(SENDCMDINPARAMS), &device_data, sizeof(IDENTIFY_DEVICE_OUTDATA), &ret_size, NULL); if (status != 0) { PIDENTIFY_DEVICE_DATA id_device; id_device = (PIDENTIFY_DEVICE_DATA)device_data.sSendCmdOutParam.bBuffer; size_t len = sizeof(id_device->SerialNumber); unsigned char *serial = (unsigned char *)calloc(len+1, sizeof(char)); for(size_t i = 0; i < len; i += 2) { serial[i] = id_device->SerialNumber[i+1]; serial[i+1] = id_device->SerialNumber[i]; } _bstr_t buff = _bstr_t((char *)serial); serialNumber = String((_TCHAR*)buff); free(serial); } } else { STORAGE_PROPERTY_QUERY query; STORAGE_DESCRIPTOR_HEADER header; memset(&query, 0, sizeof(STORAGE_PROPERTY_QUERY)); query.PropertyId = StorageDeviceProperty; query.QueryType = PropertyStandardQuery; /* * デバイスのプロパティを取得する(但しここではプロパティを格納するエリアのサイズだけ取得する) * return : 処理ステータス * 0以外 = 成功 * 0 = 失敗 * 参照:http://msdn.microsoft.com/en-us/library/ms803642.aspx */ status = DeviceIoControl(hIoCtrl, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(STORAGE_PROPERTY_QUERY), &header, sizeof(STORAGE_DESCRIPTOR_HEADER), &ret_size, NULL); if (status != 0) { PSTORAGE_DEVICE_DESCRIPTOR device_desc; device_desc = (PSTORAGE_DEVICE_DESCRIPTOR)calloc(header.Size+1, sizeof(char)); ret_size = 0; /* * デバイスのプロパティを取得する * return : 処理ステータス * 0以外 = 成功 * 0 = 失敗 * 参照:http://msdn.microsoft.com/en-us/library/ms803642.aspx */ status = DeviceIoControl(hIoCtrl, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(STORAGE_PROPERTY_QUERY), device_desc, header.Size, &ret_size, NULL); if (status != 0) { if ((int)device_desc->SerialNumberOffset > 0) { char *serial = (char *)device_desc; serial[header.Size] = 0; _bstr_t buff = _bstr_t((char *)&serial[device_desc->SerialNumberOffset]); serialNumber = String((_TCHAR*)buff); } } free(device_desc); } } CloseHandle(hIoCtrl); } return serialNumber; }
DDKを使っているので、これは
http://www.microsoft.com/japan/whdc/DevTools/WDK/WDKpkg.mspx
からリンクを辿ってダウンロードしてきました。
それで、呼び出しプログラム部分は、
WmiResult disk_drive = wmi.access(_T("SELECT * FROM Win32_DiskDrive")); if (disk_drive.items() == 0) { String msg = wmi.getErrorMessage(); return; } COUT << disk_drive.print(); for (size_t i = 0; i < disk_drive.size(); i++) { String serial = getDiskSerial(disk_drive.getLong(_T("Index"), i), (_TCHAR *)disk_drive.getString(_T("DeviceID"), i).c_str()); COUT << _T("SerialNumber = ") << serial << endl; }
に変更して完了です。
SerialNumberの取得(2)
Vistaのレジストリ情報とXPでは違っていたみたいで、このロジックでは取得できなくなってしまった。
WmiToolsを眺めていると、
Win32_PhysicalMediaに"SerialNumber"情報が入っていて、欲しい情報が取得できるみたいです。
WmiResult disk_drive = wmi.access(_T("SELECT * FROM Win32_DiskDrive")); if (disk_drive.items() == 0) { String msg = wmi.getErrorMessage(); return; } COUT << disk_drive.print(); for (size_t i = 0; i < disk_drive.size(); i++) { String drive = WmiCall::escape(disk_drive.getString(String(_T("DeviceID")), i)); String sub_select = WmiCall::createSelect(_T("ASSOCIATORS OF {Win32_DiskDrive.DeviceID=\"%s\"} WHERE AssocClass = Win32_DiskDrivePhysicalMedia ResultClass = Win32_PhysicalMedia"), drive.c_str(), NULL); WmiResult media = wmi.access(sub_select); if (media.items() > 0) { COUT << _T("SerialNumber = ") << media.getString(_T("SerialNumber")) << endl; } }
こんな感じでVistaでも取れるようになりました。
ついでにWimCallクラスの中に、escapeと言う名前のメソッドを作成しました。
/* * 文字列中の'\'をエスケープするために'\\'になるように変換した文字列を返す * param : 処理の対象となる文字列(String) * * return : String */ String WmiCall::escape(String original) { size_t spos = 0, epos = String::npos; String token = String(_T("")); do { epos = original.find_first_of(_T("\\"), spos); if (epos != String::npos) { size_t count = epos - spos + 1; token.append(original.substr(spos, count)); token.append(_T("\\")); } else { token.append(original.substr(spos)); } spos = epos + 1; } while (epos != String::npos); return token; }
これは、disk_drive.getString(String(_T("DeviceID")), i)の処理の結果として
\\.\PHYSICALDRIVE1
と言う値が帰ってくるので、これを次のWQL文に変換する時には、
\\\\.\\PHYSICALDRIVE1
のように、"\"を"\\"にエスケープするために作成しました。
HDDのSerianNumberを取得する方法
レジストリキーの
HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\Scsi
以下のTreeに
\Scsi Port w\Scsi Bus x\Target Id y\Logical Unit Id z\SerialNumber
という値が存在するので、これを利用することにしました。
Wmi32_DiskDriveを使って、次のようなロジックで処理しました。
WmiResult disk_drive = wmi.access(_T("SELECT * FROM Win32_DiskDrive")); if (disk_drive.items() == 0) { String msg = wmi.getErrorMessage(); return; } COUT << disk_drive.print(); for (size_t i = 0; i < disk_drive.size(); i++) { String reg_key = WmiCall::createSelect(_T("HARDWARE\\DEVICEMAP\\Scsi\\Scsi Port %d\\Scsi Bus %d\\Target Id %d\\Logical Unit Id %d"), disk_drive.getLong(_T("SCSIPort"), i), disk_drive.getLong(_T("SCSIBus"), i), disk_drive.getLong(_T("SCSITargetId"), i), disk_drive.getLong(_T("SCSILogicalUnit"), i), NULL); HKEY hkey = NULL; int rc = ERROR_SUCCESS; if ((rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, reg_key.c_str(), 0, KEY_READ, &hkey)) == ERROR_SUCCESS) { DWORD buffSize = 0; DWORD valueType = 0; rc = RegQueryValueEx(hkey, _T("SerialNumber"), 0, &valueType, NULL, &buffSize); String result; if (rc == ERROR_SUCCESS) { buffSize++; char *buff = (char *)calloc(buffSize, sizeof(char)); if (buff != NULL) { if (RegQueryValueEx(hkey, _T("SerialNumber"), 0, &valueType, (unsigned char *)buff, &buffSize) == ERROR_SUCCESS) { _bstr_t value = _bstr_t(buff); result = String((_TCHAR *)buff); } free(buff); } } RegCloseKey(hkey); if (!result.empty()) { COUT << _T("SerialNumber = ") << result << endl; } } }