Search code examples
javac++java-native-interface

JNI Java Wrapper : How to pass byte[] argument


I need to call a Java API from C++, using JNI. I'm trying to pass a byte* as below:

Java

void OperateData(byte[] data, int dataLength)
{
   //Some Implementation
}

C++

void OperateData(byte* data, int dataLength)
{
    JavaMethod* methodObj = getMethod(_T("OperateData"));
    JNIEnv* jniEnv = JvmManager::GetInstance()->GetJNIEnv();
    jobject jBuffer = jniEnv->CallObjectMethod(m_javaObject->getJObject(), methodObj->getJMethodID(), (jobject)data, (jint)dataLength);
}

This is not working. It is throwing an exception. Can you please let me know what I am doing wrong?

Update: I modified the C++ code as below, based on suggestions. I'm still having the same issue. Is anything still wrong?

void OperateData(byte* data, int dataLength)
{
    JavaMethod* methodObj = getMethod(_T("OperateData"));
    JNIEnv* jniEnv = JvmManager::GetInstance()->GetJNIEnv();
    jbyteArray jBuff = jniEnv->NewByteArray(dataLength);
    jniEnv->SetByteArrayRegion(jBuff, 0, dataLength, (jbyte*)data);
    jobject jBuffer = jniEnv->CallObjectMethod(m_javaObject->getJObject(), methodObj->getJMethodID(), jBuff, (jint)dataLength);
    jniEnv->ReleaseByteArrayElements(jBuff,(jbyte*)data, 0);
}

Solution

  • You can't simply type-cast a raw C/C++ byte* pointer into a jobject representing a Java byte array. You need to use JNI functions to:

    For example:

    void OperateData(byte* data, int dataLength)
    {
        JavaMethod* methodObj = getMethod(_T("OperateData"));
        if (!methodObj) return;
        JNIEnv* jniEnv = JvmManager::GetInstance()->GetJNIEnv();
        if (!jniEnv) return;
        jbyteArray jData = jniEnv->NewByteArray((jsize)dataLength);
        if (!jData) return;
        jniEnv->SetByteArrayRegion(jData, 0, (jsize)dataLength, (jbyte*)data);
        jniEnv->CallVoidMethod(m_javaObject->getJObject(), methodObj->getJMethodID(), jData, (jint)dataLength);
        jniEnv->DeleteLocalRef(jData);
    }
    

    Update: In comments, you say you changed the Java code to return a byte[] instead of void. If so, you need to adjust the C++ code accordingly, eg:

    void OperateData(byte* data, int dataLength)
    {
        JavaMethod* methodObj = getMethod(_T("OperateData"));
        if (!methodObj) return;
        JNIEnv* jniEnv = JvmManager::GetInstance()->GetJNIEnv();
        if (!jniEnv) return;
        jbyteArray jData = jniEnv->NewByteArray((jsize)dataLength);
        if (!jData) return;
        jniEnv->SetByteArrayRegion(jData, 0, (jsize)dataLength, (jbyte*)data);
        jobject jBuffer = jniEnv->CallObjectMethod(m_javaObject->getJObject(), methodObj->getJMethodID(), jData, (jint)dataLength);
        if (jBuffer) jniEnv->DeleteLocalRef(jBuffer);
        jniEnv->DeleteLocalRef(jData);
    }