15. JNI
JNI (Java Native Interface) - это стандарт по взаимодействию с C/C++-ным кодом.
Как и следовало ожидать, на этом водоразделе тоже происходит столкновение байтов и символов.
Большинство C/C++-ных программ пишется без учёта Unicode, многие программисты даже не знают о нём.
Я сам, за 7 лет писательства на C/C++, пока не начал писать на Java, про Unicode знал только по наслышке.
Большинство строковых операций в C/C++ сделаны для 8-битового сишного типа char.
В принципе, есть некоторые подвижки в этом направлении, в частности для Windows NT можно откомпилировать код, который будет взаимодействовать с Unicode-вариантами Win32 API, но, к сожалению, этого часто недостаточно.
Таким образом главная задача - получить тип char* из типа jstring (JNI-шное отображение String) и наоборот.
Практически во всех описаниях и примерах JNI для этого используется пара функций GetStringUTFChars()/ReleaseStringUTFChars().
Коварные буржуины и здесь приготовили засаду - эти функции формируют массив байтов по стандарту UTF, который соответствует ожидаемому только для ASCII-символов (первых 128 значений).
Русские буквы опять в пролёте.
Сишные строки char* очень хорошо ложатся на Java-овский тип byte[], но при этом возникает загвоздка в виде ноль-символа.
Его нужно добавлять при преобразовании byte[]->char* и учитывать при обратном преобразовании.
Пример:
private native int nAction(String msg);
private native String nGetErrorString(int error);
public void action(String msg)throws java.io.IOException
{
int res=nAction(msg);
if(res!=0) throw new java.io.IOException(nGetErrorString(res));
}
...
jbyteArray getStringBytes(JNIEnv *env, jstring str)
{
if(!str) return NULL;
jmethodID getBytes=
env->GetMethodID(env->GetObjectClass(str),"getBytes","()[B");
jbyteArray buf=(jbyteArray)env->CallObjectMethod(str,getBytes);
if(!buf) return NULL;
// Добавляем ноль-символ
jsize len=env->GetArrayLength(buf);
jbyteArray nbuf = env->NewByteArray(len+1);
if(len!=0)
{
jbyte *cbuf=env->GetByteArrayElements(buf,NULL);
env->SetByteArrayRegion(nbuf,0,len,cbuf);
env->ReleaseByteArrayElements(buf,cbuf,JNI_ABORT);
}
env->DeleteLocalRef(buf);
return nbuf;
}
JNIEXPORT jint JNICALL Java_Test_nAction
(JNIEnv *env, jobject obj, jstring msg)
{
jbyteArray bmsg=getStringBytes(env,msg);
if(!bmsg) return -1;
jbyte *cmsg=env->GetByteArrayElements(bmsg,NULL);
printf(cmsg);
jint res=do_something(cmsg);
env->ReleaseByteArrayElements(bmsg,cmsg,JNI_ABORT);
return res;
}
jstring newString(JNIEnv *env, jbyteArray jbuf, int len)
{
jclass stringClass=env->FindClass("java/lang/String");
if(!stringClass) return NULL;
jmethodID init=env->GetMethodID(stringClass,"","([BII)V");
if(!init) return NULL;
return (jstring)env->NewObject(stringClass,init,jbuf,0,len);
}
jstring newString(JNIEnv *env, const char *buf)
{
if(!buf) return NULL;
int bufLen=strlen(buf);
if(bufLen==0)
{
return env->NewString((const jchar *)L"",0);
}
jbyteArray jbuf=env->NewByteArray(bufLen);
if(!jbuf) return NULL;
env->SetByteArrayRegion(jbuf,0,bufLen,(jbyte*)buf);
jstring jstr=newString(env,jbuf,bufLen);
env->DeleteLocalRef(jbuf);
return jstr;
}
JNIEXPORT jstring JNICALL Java_Test_nGetErrorString
(JNIEnv *env, jobject obj, jint error)
{
char cmsg[256];
memset(cmsg,0,sizeof(cmsg));
get_error_string(error,cmsg,sizeof(cmsg));
return newString(env,cmsg);
}
Тут используется преобразование символов по умолчанию, что вполне естественно при взаимодействиях с системным API.
Если же Вам необходима определённая кодовая страница, соответственно нужно добавить её название.