I'm using C++/CLI to connect a Java client to my C# ServiceHost. So far I have used this to access my service, where Client defines my ServiceHost client:
JNIEXPORT jstring JNICALL Java_Client_GetData(JNIEnv *env, jobject, jstring xml)
{
try
{
Client ^client = gcnew Client();
return env->NewStringUTF(marshal_as<string>(
client->GetData(marshal_as<String^>(env->GetStringUTFChars(xml, 0))
)).c_str());
}
catch(Exception^ ex)
{
Console::WriteLine(ex->ToString());
}
return NULL;
}
That works fine, but I want to be able to store my Client object on the Java side in order to make calls with the same object instead of opening and closing the connection for each call.
It has been difficult to find anything definitive about this. Is it possible?
Here is the solution I came up with per the marked answer:
JNIEXPORT jlong JNICALL Java_Client_Create(JNIEnv* env, jobject obj)
{
try
{
Client^ client = gcnew Client();
client->Connect();
long result = reinterpret_cast<long>(GCHandle::ToIntPtr(GCHandle::Alloc(client)).ToPointer());
return result;
}
catch(Exception^ ex)
{
Console::WriteLine(ex->ToString());
}
return NULL;
}
By storing that long in Java, I can pass it as a jlong parameter to my GetData method:
JNIEXPORT jstring JNICALL Java_Client_GetData(JNIEnv *env, jobject, jlong ptr, jstring xml)
{
try
{
GCHandle h = GCHandle::FromIntPtr(IntPtr(reinterpret_cast<void*>(ptr)));
Client^ client = safe_cast<Client^>(h.Target);
const char* xmlChars = (const char*)env->GetStringChars(xml, 0);
string xmlString(xmlChars);
env->ReleaseStringChars(xml, (const jchar*)xmlChars);
const char* data = marshal_as<string>(client->GetData(
marshal_as<String^>(xmlString)
)).c_str();
int length = strlen(data);
return env->NewString((const jchar*)data, length);
}
catch(EndpointNotFoundException^)
{
return NULL;
}
catch(Exception^ ex)
{
Console::WriteLine(ex->ToString());
}
return NULL;
}
All I have left to do is create another JNI method to close the Client connection and dispose the object.
GetStringCharsandNewString, andstd::wstringif needed as an intermediary (C++/CLI doesn't supportstd::u16string, which in VC++ is effectively the same thing).GetStringLengthto get the number of codeunits.GetStringCharsgives you a pointer to the codeunits. Then callMarshal::PtrToStringUniwith the pointer cast to an IntPtr and the number of codeunits to get a .NET string.Marshal::PtrToStringUniover casting between char/jchar? That does seem cleaner, I'm just trying to get a better grasp of this. Thanks