Searching for JNI, I've always found something like:
Method in C/C++, call in Java
Method in Java, call in C/C++
But never method in C/C++, call in C/C++ using JNI.
I ask that, because I have a 3rd party Java library, which has some C/C++ libraries, and I've always used Java to call them. And I would like now, to create some program using C instead of Java. Unfortunately they don't provide the any API in C/C++, only in Java.
Is it possible, if yes how can I try to do it?
Thank you in advance!
The JNI methods are always publicly exposed with an extern C signature.
This means that you could just link to the JNI dll directly if you have the method signature. (which you can easily get if you look at the Java class native method signature).
But you will have to make sure you give the method the environment it expects.
This means pass the "this" object and environment object.
You'll have to make sure that the class loader object has the right packages loaded in it. In most cases JNI code assumes the environment that called the method has the right class loader in it already (because the java method was called...).
And there's also an issue with local references that JNI code might assume are released automatically from the stack (which doesn't happen in C++ native threads).
Anyway, it would look like this:
//MyClass.java
class MyClass
{
native void Blah();
}
//method signature, some header file:
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_MyClass_Blah
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
//Call the function, C++ code
Java_MyClass_Blah(myEnv, myClassInstanve);
Related
I am currently attempting to integrate the AzureStorageCPP into Unreal Engine 4 by using this documentation page. What I am doing is to create a custom C++ DLL wrapper around AzureStorageCPP, and then have Unreal Link this DLL in Unreal Code.
I installed the AzureStorageCPP as DLLs, via vcpkg. On completion, wastorage.dll is one of the important DLLs that needs to be linked.
The C++ DLL wrapper I wrote around AzureStorageCPP is below:
#include "was/storage_account.h"
#include "was/blob.h"
void UploadFileToAzure(char* FileName)
{
azure::storage::cloud_storage_account storage_account = azure::storage::cloud_storage_account::parse(U("DefaultEndpointsProtocol=https;AccountName=test;AccountKey=test"));
}
As far as I can tell, azure::storage::cloud_storage_account::parse lives in wastorage.dll
The issue is that when this parsing code is executed as a C++ Console, it runs just fine. The ConnectionString is passed correctly down the callstack to wastorage.dll, and the connection string is not garbage.
Here is the view one level down the callstack from the code above.
However, if I put the code into the C++ wrapper DLL I made called AzureStorage.DLL, link it into Unreal, and call from there, with the same call stack the string becomes garbage when it gets passed into wastorage.dll to parse.
I thought this might be related to this issue, but I still don't understand how to fix this, as I don't control the Parse method that is from AzureStorage.
Here is how I created the C++ DLL wrapper header, the source is already shown above:
AzureStorage.h
#pragma once
#define AZURESTORAGEDLL_API _declspec(dllexport)
#ifdef __cplusplus //if C++ is used convert it to C to prevent C++'s name mangling of method names
extern "C"
{
#endif
void AZURESTORAGEDLL_API UploadFileToAzure(char* FileName);
#ifdef __cplusplus
}
#endif
I have compiled a library that I have created with MinGW into an existing application using Borland 6 (I know its old but that's what it was made with). I have used implib to create the .lib file and imported it into my project. I have also added the paths to the dll and necessary header files.
When I try to compile I get a pile of Unnresolved external type errors. Have I missed out any steps of the importing process? Assuming I haven't and the issue is something like name-mangling how do I go about writing the interface in such a way that name mangling won't matter. I know it involves extern C but thats about the limit of my knowledge. There are only two classes that need to be accessed from outside the dll the remainder are all only used internally. I'm not sure how to use extern C with something that is entirely built with classes. I'm stil hopeful that it's my importing with borland 6.
extern "C" cannot be used for classes, only for free functions. So you have an option of writing a "C" interface to your class, where each function takes a pointer to your class and you would probably have create and destroy functions.
This is a way it is typically done, and your class could be forwardly declared as struct, which is the same as class, and then could even be used by applications written in C. You would normally put extern "C" only when __cplusplus is defined so there are normally #ifdef guards around it.
There is another option, if you only want your class to be used by C++ and you don't want to have to write a C interface for all your class methods.
Users of the DLL use an abstract interface and still use Create and Destroy methods (with extern "C") to create a pointer to the abstract interface, but then use the pointer in the normal C++ way. Of course ideally you will wrap this pointer in a smart pointer, e.g. a boost shared_ptr with a custom deleter that calls the Destroy method. (Users of the library should do that but you can provide a headers-only interface to do it).
There are a few other issues you would need to beware of if doing this, e.g. anything to do with run-time type information is likely to not work on the user-side, including exceptions. And once again your library could provide "open-source" C++ wrappers (compiled on the client side) to handle this in a more C++ way. A sort-of pImpl.
The name mangling is not standardized across compilers. Only expose extern C functions so that they are not name mangled. But this has a limitation that you cannot use object orient programming.
Another option is to implement COM objects as they are binary compatible.
I have a C++ library xyz. It has many classes like xyzA, xyzB etc. I want to use the method getAge() from the class xyzA which is in the xyz library.
The xyz.so file already exists.
Steps I have followed:
Created a Java class xyz.java
class xyz {
public native int getAge();
public static void main(String[] args) {
new xyz().getAge();
}
static {
System.loadLibrary("xyz");
}
}
Created the header for the Java class.
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class xyz */
#ifndef _Included_xyz
#define _Included_xyz
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: xyz
* Method: getAge
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_xyz_getAge
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
The cpp wrapper class looks like:
#include <stdio.h>
#include "xyz.h"
#include <jni.h>
JNIEXPORT jint JNICALL Java_xyz_getAge(JNIEnv *, jobject)
{
// some code
}
I successfully compile the class as follows:
gcc -fPIC -shared -l stdc++ -I/grid/0/gs/java/jdk64/current/include -I/grid/0/gs/java/jdk64/current/include/linux xyz.cpp
Then run the Java prog as:
java -Djava.library.path=/grid/0/tmp/direct/lib xyz
I get the following error:
Exception in thread "main" java.lang.UnsatisfiedLinkError: xyz.getAge()I
at xyz.getAge(Native Method)
at xyz.main(xyz.java:6)
It cannot find the method getAge() specific to the class xyzA. How can that method be accessed? Also, is the library getting linked through my wrapper class?
Any pointers would be appreciated.
Thanks.
If you are running on Unix, the shared library has to be named libxyz.so, not xyz.so.
This error normally means that the library has been successfully loaded,
but that there is an inconsistency in the signature of the function.
Globally, your code looks correct, but we do put an extern "C" in
front of JNIEXPORT in our code base. We don't use the generated
headers, so the definition of the function is the only place we could
specify extern "C". In your case, I think that the compiler is
supposed to recognize that the function declared in the header and the
one defined in your .cpp are the same, and define the function as
extern "C", but having never done it this way, I'm not 100% sure. You
can verify by doing something like:
nm -C libxyz.so | egrep getAge
The function should appear as a C function, with a T in the second
column; without the -C, it should appear unmangled. Note that the
slightest difference in the definition and the declaration will mean
that you're defining a different function; I don't see one in the code
you've posted, but it's worth double checking.
(I'd also wrap the call to LoadLibrary in a try block, just to be sure.)
EDITED to add some additional information:
I'm not sure when and how Linux links the additional libraries needed;
we've always loaded all of our libraries explicitly. You might try
adding a call to dlopen in either JNI_OnLoad or the constructor of a
static object. (I would recommend this anyway, in order to control the
arguments to dlopen. We found that when loading several different
.so, RTTI didn't work accross library boundaries if we didn't.) I
would expect not doing so to result in a loader error, but it all
depends on when Linux tries to load libxyz.so; if it only does so when
the JVM calls dlsym, then you'll get the above error. (I think this
depends on the arguments the JVM passes to dlopen when you call
java.lang.System.LoadLibrary: if it passes RTLD_LAZY or RTLD_NOW.
By forcing the load in JNI_OnLoad or the constructor of a static
object, you more or less guarantee that loader errors appear as loader
errors, and not link errors, later.)
Exported function names in C++ libraries are mangled: The plain function name is decorated with class and namespace names, parameter and return types. For instance, here is one of the methods from a boost library:
?estimate_max_state_count#?$perl_matcher#PB_WV?$alloca
tor#U?$sub_match#PB_W#boost###std##U?$regex_traits#_WV?$w32_regex_traits#_W#boos
t###boost###re_detail#boost##AAEXPAUrandom_access_iterator_tag#std###Z
The format of the name is not standardised and varies from compiler to compiler. The end result is that it is difficult to call an exported C++ member function unless you're writing another module in C++ and compiling it using the same compiler. Trying to call from Java is more trouble than it is worth.
Instead, if the C++ library is yours, export helper functions using the extern "C" calling convention:
Foo * foo;
extern "C"
{
void myfunc()
{
foo->bar(); // Call C++ function within a C-style function.
}
}
If the library is third-party, you should wrap it with your own library which exposes necessary functions using the C-style export, as per above.
I'd like to use Pure Data as a prototyping tool for my own library. I found out that Pure Data patches are written in C, but my library is written in C++. So how can I use this code in pure data? Since I haven't used plain C, I'd like to know how I could write a C wrapper for C++ classes and how do instantiate my classes then? Or do I have to rewrite everything in C?
You will need to write wrapper functions for every function which needs to be called. For example:
// The C++ implementation
class SomeObj { void func(int); };
extern "C" {
SomeObj* newSomeObj() {return new SomeObj();}
void freeSomeObj(SomeObj* obj) {delete obj;}
void SomeObj_func(SomeObj* obj, int param) {obj->func(param)}
}
// The C interface
typedef struct SomeObjHandle SomeObj;
SomeObj* newSomeObj();
void freeSomeObj(SomeObj* obj);
void SomeObj_func(SomeObj* obj, int param);
Note this must be C++ code. The extern "C" specifies that the function uses the C naming conventions.
You can also write objects for Pure Data using C++ using the flext framework.
Let me put it another way:
1) You can call C functions, data and libraries from C++ source, and you call C++ source from C.
2) Whenever C calls into C++ source, however, that source must be written in a C subset of C++.
3) Part of this is using "extern C".
4) Another part is using "#ifdef __cplusplus"
5) The links I cited above give plenty of details
6) I looked at the Pure Data site. You might have to make some "tweaks" to your library. You might wish to create a new header. But fundamentally, I think you can probably accomplish your goal of getting your library to integrate with Pure Data.
IMHO...
You can absolutely call C from C++ - no problemo!
Worst case, you might have to explicitly declare those functions you call from Pure Data as "extern C". But it's almost certain that Pure Data has already done that for you (you'll probably see "extern C" in the Pure Data header files.
Here's more info:
http://msdn.microsoft.com/en-us/library/0603949d%28v=vs.80%29.aspx
'Hope that helps!
I have a (big and complex) C++ program with its own classes and methods.(obviously)
I would like to load at runtime a dll. From the main program i want to call a function inside the dll passing a class instance , and this function will use the method of this class.
In pseudo code
Main program:
class MyClass{
myattr1();
myattr2();
mymethod1();
mymethod2();
}
void main(){
MyClass* object = &(new MyClass())
handle_of_the_dll = *some function that load the dll*
dllfunc = getfunc(handle_of_the_dll, "interesting_function")
dllfunc(object)
[...etc...]
and the dll
#ifdef __cplusplus
extern C {
#endif
#ifdef BUILD_DLL
#define EXPORT __declspec(dllexport)
#else
#define EXPORT __declspec(dllimport)
#endif
#include all the needed headers from main program, where the class is defined
EXPORT void interesting_functionn(MyClass object){
object.mymethod1();
}
#ifdef __cplusplus
}
#endif
Is this valid/doable/right?
EDIT:
i know that this would be a poor design for a general program, but this method is intended to give the users the ability to use their custom dll that can access the api of the main program. Something like Cpython modules
It is valid and doable. Generate a sample DLL in visual C++ and the code will have all the bases covered. Replace the static linking in the client with LoadLibrary and GetProcAdress.
Name mangling could be an issue, DependencyWalker will help you there.
Keep a low profile with emory management. For example, if you use a memory manager on one side but not on the other, your begging for trouble.
I'd say it's possible, but with all due respect, it looks like bad design to me. There is a circular dependency between the main program and the dll: the program calls functions from the dll, and viceversa. In an ideal design, the main program would consume some dll functions, and the dll wouldn't know anything about the main program, or in general, about its clients.
Perhaps you should isolate the functions from the main program that the dll needs to use in a common dll or static lib which both the program and dll use.
As long as the methods are declared virtual and you pass the object by reference or pointer there should be no problems. If however you want to access non-virtual methods of the class you will need to put it's implementation in a separate DLL.