ネットワークインターフェース情報の取得(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);

に起因してるんだとは思うんだけど・・・。
この辺りをこれから調査です。