java对DWORD和TCHAR的处理

对端是WinCE,传输struct结构体内存数据的字节流(类似这样将struct结构数据保存到文件中)。这边是java,需要读取并用类似方式回传数据。

考虑了一下,有两种解决思路:

  • 使用JNI,java通过socket获取到流的字节数组,然后通过JNI用本地C编程转换成java可用的数据结构,比如整型数和char数组,这种办法肯定可行,但是需要开发人员会运用C编程,另外这里是Android,还要牵扯到NDK,部署起来也不灵活;
  • 使用纯java解决,java将字节数组通过转换和移位等操作转换为java可用的数据,比如long和String,这种办法较好,技术跨度小,部署和移植性好。

本文最终选择了后者实现了处理。

我们碰到的struct结构类似这样:

#define STAFF_ITEM_MAX_LEN     (63) 
typedef struct _STAFF_INFO_T 
{    
    DWORD dwStaffID; 
    TCHAR szTitle[STAFF_ITEM_MAX_LEN + 1]; 
    TCHAR szName[STAFF_ITEM_MAX_LEN + 1]; 
} STAFF_INFO, * PSTAFF_INFO; 
typedef const PSTAFF

对端的c处理代码:

STAFF_INFO stf1 = { 0 }; 
    STAFF_INFO stf2 = { 0 }; 
   stf1.dwStaffID = 0×12345678; 
   _tcscpy_s(stf1.szName, STAFF_ITEM_MAX_LEN, _T("Staff Name")); 
   _tcscpy_s(stf1.szTitle, STAFF_ITEM_MAX_LEN, _T("Staff Title")); 
   stf2.dwStaffID = 0xABCDEF00; 
   _tcscpy_s(stf2.szName, STAFF_ITEM_MAX_LEN, _T("Staff 名称")); 
   _tcscpy_s(stf2.szTitle, STAFF_ITEM_MAX_LEN, _T("Staff 头衔")); 
    FILE * pf = fopen("R:\\staff_sample.dat", "wb"); 
    if( NULL != pf ) 
   { 
       fwrite(&stf1, 1, sizeof(stf1), pf); 
       fwrite(&stf2, 1, sizeof(stf2), pf); 
       fclose(pf); 
    }

 

这里需要处理的数据类型是2种,DWORD和TCHAR。DWORD是无符号的4字节整数,TCHAR在这里确定是Unicode,即wchar(wchar_t)。

针对DWORD的转换

DWORD类型如果映射到java基本型的话,只有long类型可以对应,long是8字节的有符号表示整数,DWORD是4字节表示无符号整数。其中:

  • long的取值范围是263-1~-263,这里63是8位/字节*8个字节-1得来的
  • DWORD就简单了,是263

因此这种转换将无法表示DWORD的最大值。

转换的原理是通过左移位运算,将4字节合并到一个long型值中,如果四字节分别是:{0×12,0×34,0×56,0×78},那么先0×12转换为0×12000000,这样它需要左移3个字节长度即3*8=24位,以此类推,然后相加,即0×12000000+0×340000+0×5600+0×78,这样就得到了0×12345678。

这里需要注意,上面说的字节数组示例是为了方便理解,实际上得到的是倒排序的,即{0×78,0×56,0×34,0×12}。

从字节数组转long的方法:

public static long dwordBytesToLong(byte[] data) { 
    return (data[3] << 8 * 3) + (data[2] << 8 * 2) + (data[1] << 8 ) 
           + data[0]; 
}

从long到字节数组的方法:

public static byte[] longToDword(long value){ 
    byte[] data=new byte[4]; 
    
    for(int i=0;i>(8*i)); 
   } 
    
    return data; 
}

 

针对TCHAR的转换

VC里的TCHAR转换,如果无条件转换,无论是java还是*nux c都是比较麻烦的。因为TCHAR即可以是ASCII码,也可以是宽字符比如UNICODE,在*nix c中是区分的。还在这里对端肯定传的是unicode,使问题得到简化。

这里不能直接用String的参数是byte[]数组的构造方法,因为String会认为字节数组是字符集编码得到的字节,会自动用默认字符集解码,而struct的TCHAR是c从内存中直接将unicode字符的内存复制出来,就相当于把java的String字符在内存中复制下来一样。

unicode是内存中的字符表示,字符集比如GBK、UTF-8是unicode存储时的编码,二者有区别。

因为内存中unicode是两字节表示一个字符(char),因此将两个字节合并成java中的一个char,然后char数组再转换为字符串。这里要注意,java中的unicode的表示和c内存中位置颠倒,拿上面“头”字举例,字节的16进制表示是:0×5934。

转换TCHAR到字符串的代码:

public static String wcharUnicodeBytesToString(byte[] data) { 
   StringBuilder builder = new StringBuilder();

    for (int i = 0; i < data.length; i += 2) { 
       if (data[i] == 0xfffffffe) { 
           break; 
       } 
       byte[] temp = new byte[2]; 
       temp[0] = data[i]; 
       temp[1] = data[i + 1]; 
       builder.append(wcharUnicodeBytesToChar(temp)); 
    }

    return builder.toString(); 
}

 

private static char wcharUnicodeBytesToChar(byte[] data) { 
    char c = (char) (((data[1]) << 8 ) + data[0]); 
    return c; 
}

将java字符串转换为TCHAR的byte数组代码:

public static byte[] stringToWcharUnicodeBytes(String value,int arraySize){ 
    char[] valueChars=value.toCharArray(); 
    byte[] data=new byte[arraySize*2]; 
   Arrays.fill(data, (byte)0xFE); 
    
    for(int i=0;i>8); 
   } 
    
    return data; 
}

 

 

 

 

你可能感兴趣的:(JAVA,JNI)