This my second time coding Java and never referencing any external library before. I follow the JNI examples online and I get UnsatisfiedLinkError when trying to load the dll. I thought that I have to create DLL first before trying to load, but all the examples I've looked they don't mention about creating DLL. Most of them stating that I should create Java code first then native code.
public class ClassSample1
{
public native void displayHelloWorld();
static
{
System.loadLibrary("MyLittleJNI");
}
public static void main(String[] args)
{
// TODO Auto-generated method stub
ClassSample1 classSample1;
classSample1 = new ClassSample1();
classSample1.displayHelloWorld();
System.out.println("Hello");
}
}
How can I get ride of the error?
The sample code you provide assumes that there is a DLL in the search path called MyLittleJNI.dll, containing a method displayHelloWorld. The actual C function name in the DLL is decorated using a well defined syntax.
If you get an UnsatisfiedLinkError in loadLibrary(), it is because the JVM cannot find the DLL. You can duck the issue temporarily by specifying the full pathname to the DLL using the System.load(filename) method instead.
Once load or loadLibrary succeeds, you need to make sure that the native function is named correctly. To aid in this, you can use javah to generate a header file containing prototypes for all the native functions in a class.
More information about how to use JNI can be found in here and here.
EDIT: Also, the "Related" column to the right of this questions seems to contain several useful related question.
I try to create new project again.
So here is JNISample2.java file
public class JNISample2
{
static
{
System.loadLibrary("JNISample2Dll");
}
public native void displayHelloWorld();
public static void main(String[] args)
{
System.out.println("from java Hello");
JNISample2 JNIsample2;
JNIsample2 = new JNISample2();
JNIsample2.displayHelloWorld();
}
}
And here .h file that's generated by the javah -classpath . JNISample2
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNISample2 */
#ifndef _Included_JNISample2
#define _Included_JNISample2
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: JNISample2
* Method: displayHelloWorld
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_JNISample2_displayHelloWorld
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Here is my .h file dll that I create VS2005 with MFC app.
// JNISample2Dll.h : main header file for the JNISample2Dll DLL
//
#pragma once
#ifndef __AFXWIN_H__
#error "include 'stdafx.h' before including this file for PCH"
#endif
#include "resource.h" // main symbols
#include "JNISample2.h"
// CJNISample2DllApp
// See JNISample2Dll.cpp for the implementation of this class
//
class CJNISample2DllApp : public CWinApp
{
public:
CJNISample2DllApp();
// Overrides
public:
virtual BOOL InitInstance();
DECLARE_MESSAGE_MAP()
};
JNIEXPORT void JNICALL Java_JNISample2_displayHelloWorld(JNIEnv *, jobject);
And here is my .cpp file
// JNISample2Dll.cpp : Defines the initialization routines for the DLL.
//
#include "stdafx.h"
#include "JNISample2Dll.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
//
//TODO: If this DLL is dynamically linked against the MFC DLLs,
// any functions exported from this DLL which call into
// MFC must have the AFX_MANAGE_STATE macro added at the
// very beginning of the function.
//
// For example:
//
// extern "C" BOOL PASCAL EXPORT ExportedFunction()
// {
// AFX_MANAGE_STATE(AfxGetStaticModuleState());
// // normal function body here
// }
//
// It is very important that this macro appear in each
// function, prior to any calls into MFC. This means that
// it must appear as the first statement within the
// function, even before any object variable declarations
// as their constructors may generate calls into the MFC
// DLL.
//
// Please see MFC Technical Notes 33 and 58 for additional
// details.
//
// CJNISample2DllApp
BEGIN_MESSAGE_MAP(CJNISample2DllApp, CWinApp)
END_MESSAGE_MAP()
// CJNISample2DllApp construction
CJNISample2DllApp::CJNISample2DllApp()
{
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}
// The one and only CJNISample2DllApp object
CJNISample2DllApp theApp;
// CJNISample2DllApp initialization
BOOL CJNISample2DllApp::InitInstance()
{
CWinApp::InitInstance();
return TRUE;
}
JNIEXPORT void JNICALL Java_JNISample2_displayHelloWorld(JNIEnv *, jobject)
{
MessageBox(NULL, TEXT("In JNISample2Dll"), TEXT("DLL"), 1);
}
After I use command prompt to run: java JNISample2, it display the string "from java Hello", but how come it does not display messagebox that I put inside the .cpp DLL file?
Related
So my situation is as follows:
I need to have a Code Analyzer that can detect if I'm using a Member (Method/Field/Property/Class) that is marked with the Attribute [EditorOnly] outside of a scope surrounded by a preprocessor that looks like this: #if UNITY_EDITOR ... #endif
To make a clear example:
I have the class with the editor-only method:
public class SampleClass {
#if UNITY_EDITOR
[EditorOnly]
public static void SampleMethod() {...}
#endif
}
and another class that is trying to use it at runtime:
public class BuildClass {
public void Start() {
SampleClass.SampleMethod();
}
}
A situation like this would fail to build as the call in the second script will not be able to find the method as it is excluded from the build.
So given what I said, the question is:
How do I create and use a Roslyn Analyzer that gives me a "Design-Time" error inside the IDE (JetBrains Rider) saying that I cannot use the method outside of the UNITY_EDITOR scope?
P.S. I don't want to use the [Conditional()] Attribute
MyActor.h
UCLASS()
class FPS_API AMyActor: public AActor
{
GENERATED_BODY()
...
public:
UFUNCTION(NetMulticast, Reliable)
void MulticastRPCMyFunction();
...
}
MyActor.cpp
void AMyActor::MulticastRPCMyFunction()
{
UE_LOG(LogTemp, Log, TEXT("Message"));
}
When i compile my project, i can check the error message below.
Compile error
*.gen.cpp.obj : error LNK2005: "public: void __cdecl *::MulticastRPCMyFunction(void)" (?MulticastRPCMyFunction#*##QEAAXXZ) already defined in *.cpp.obj
With networked functions (in your case the NetMulticast metadata) you do not name the function the same thing in the Cpp file as the header file because it gets generated by UHT (hence the linker error about it already being defined).
In your case your Cpp file would need to look like this:
void AMyActor::MulticastRPCMyFunction_Implementation()
{
UE_LOG(LogTemp, Log, TEXT("Message"));
}
Notice the _Implementation addition to the function name.
If you ever add the WithValidation metadata, then you would need another function with _Validate added to the end of the function name.
If I recall correctly, static linkage means that a variable or function is local to its compilation unit. Meaning that there could be variables or functions with the same name and parameters in other compilation units.
I want this for a class.
Let's say I have multiple compilation units that need to ensure proper deletion at exit. So I use atexit handlers. But every compilation unit should put its own atexit-handler into place.
I did this by making a class like this:
class Init {
private:
static Init* self;
Init() {
std::atexit(Init::cleanup);
}
static void cleanup() {
// Do cleanup
}
};
Init* Init::self = new Init;
But if I have multiple classes called Init in various CUs the linker gets confused. And the compiler won't let me do static class Init {...}.
How do I archive my cleanup (if possible with classes that are called Init)?
You can put your class into an unnamed namespace.
Then, although types don't have linkage, the same effect is borne.
// Everything inside here is unique to this TU
namespace {
class Init { /** whatever **/ };
Init* Init::self = new Init;
}
int main()
{
// "Init" in here will refer to that which you created above
}
My goal is to call methods, which are implemented in the Unity Code, from my UWP DLL. (So I can use them in my HoloLens Project)
I tried this with a bigger project but failed. Therefore I wrote a simple example to make it easier to find the mistake and exclude other influences.
But still, I get the same error.
My Working Environment:
64-bit Computer with OS Windows 10
Micsrosoft Visual Studio Community
2015 Version 14.0.25431.01 Update 3
HoloLens Emulator 10.0.14393.0
Unity 5.5.0f3 Personal (64 bit)
Creating the UWP DLL:
To approach this I created a C++ DLL(Windows Universal) in Visual Studio 2015 as followed:
New Project > Visual C++ > Windows > Universal > DLL(Universal Windows)
After the project was auto generated I added my code.
So the code looks like this:
Native Library Code:
SimpleProjectDLL.cpp:
#include "pch.h"
#define DLL_EXPORT __declspec(dllexport)
typedef void(*CB_V)();
typedef void(*CB_V_VI)(const char * a, int b);
CB_V_VI cb_native_log;
CB_V cb_call;
void log()
{
// this method makes problems !
cb_native_log("Call for callback", 1);
}
extern "C" {
DLL_EXPORT void initInterfaceCallbacks(
CB_V_VI native_log,
CB_V call
) {
cb_native_log = native_log;
cb_call = call;
}
DLL_EXPORT void callSmth()
{
cb_call();
}
DLL_EXPORT int getSomeInt()
{
return 42;
}
DLL_EXPORT void initCallback()
{
log();
}
}
SimpleProjectDLL.h is prepearing the delegates:
SimpleProjectDLL.h:
#pragma once
#include <cstdint>
#define DLL_EXPORT __declspec(dllexport)
extern "C"
{
typedef void(*CB_V)();
typedef void(*CB_V_VI)(const char * a, int b);
}
I did not make any changes to the auto generated files dllmain.cpp, pch.cpp, pch.h or targetver.h.
Finally I build the project for "Release" mode and architecture "x86" to generate the DLL-file.
Location of the DLL-file is now: project-root-folder/Release/SimpleProject/SimpleProjectDLL.dll.
---------------------
Next step I created a new Unity Project added the HoloLens-Toolkit and made sure that the new project is running fine on the emulator.
Unity Project Code:
After that I added the SimpleProjectDLL.dll in the Asset-Folder and implemented the following code:
First of all we need to create the connection between the delegates.
Cpp.cs prepears the Delegates:
Cpp.cs
using UnityEngine;
using System;
using System.Runtime.InteropServices;
namespace Cpp
{
delegate void DelegateV();
delegate void DelegateVVi(IntPtr a, int b);
}
SimpleInterfaceCpp.cs initializes the connection:
SimpleInterfaceCpp.cs
using Cpp;
using System.Runtime.InteropServices;
using UnityEngine;
public static class SimpleInterfaceCpp
{
public static void Init()
{
initInterfaceCallbacks(
SimpleInterface.NativeLog,
SimpleInterface.Call
);
}
[DllImport(SimpleInterface.DLL)]
private static extern void initInterfaceCallbacks(
DelegateVVi native_log,
DelegateV call
);
}
Main:
MainController.cs
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class MainController : MonoBehaviour
{
void Start ()
{
SimpleInterfaceCpp.Init();
SimpleInterface.TestCalls();
}
}
SimpleInterface.cs is calling the methodes:
SimpleInterface.cs
using System;
using UnityEngine;
using System.Runtime.InteropServices;
using AOT;
using IntPtr = System.IntPtr;
using Cpp;
using StringReturn = System.IntPtr;
public class SimpleInterface
{
public const string DLL = "SimpleProjectDLL";
public static void TestCalls()
{
// This works fine
int number = getSomeInt();
Debug.Log("getSomeInt: " + number);
// This also works fine and outputs "--- A callback ---"
callSmth();
// This call gives the output "call_log: native log" but crashes afterwards !
initCallback();
}
[MonoPInvokeCallback(typeof(DelegateVVi))]
public static void NativeLog(IntPtr logMessage,
int logLevel)
{
string result = StringFromCReturn(logMessage);
UnityEngine.Debug.Log(result); // outputs "call_log: native log"
}
[MonoPInvokeCallback(typeof(DelegateV))]
public static void Call()
{
UnityEngine.Debug.Log("--- A callback---");
}
[DllImport(DLL)]
private static extern void initCallback();
[DllImport(DLL)]
private static extern void callSmth();
[DllImport(DLL)]
private static extern int getSomeInt();
public static string StringFromCReturn(StringReturn someReturnVal)
{
return Marshal.PtrToStringAnsi(someReturnVal);
}
}
Now if I create a SLN, open the project in Visual Studio and start it with the "HoloLens Emulator" I get the following Output:
getSomeInt: 42
(Filename: C:/buildslave/unity/build/artifacts/generated/Metro/runtime/DebugBindings.gen.cpp Line: 51)
--- A callback---
(Filename: C:/buildslave/unity/build/artifacts/generated/Metro/runtime/DebugBindings.gen.cpp Line: 51)
call_log: native log
(Filename: C:/buildslave/unity/build/artifacts/generated/Metro/runtime/DebugBindings.gen.cpp Line: 51)
The program '[1932] SimpleProject.exe' has exited with code -1073740791 (0xc0000409).
After that the App just closes.
So my Question is, does anyone know what the problem could be?
Is this the right way to use callbacks in a HoloLens Project?
Or does someone know how to find an error description for the code "-1073740791 (0xc0000409)" ?
Additional Information:
I also tried it on a real HoloLens device, same issue, so the problem does not lays at the emulator.
The error is STATUS_STACK_BUFFER_OVERRUN. The call destroyed callstack.
You have different declarations of callbacks in SimpleProjectDLL.cpp and SimpleProjectDLL.h. Cpp file uses "CPP" call conversation, header uses "C" call conversation.
You should change SimpleProjectDLL.cpp by removing
typedef void(*CB_V)();
typedef void(*CB_V_VI)(const char * a, int b);
and adding
#include "SimpleProjectDLL.h"
I am currently writing a program in C++/CLI. I have a Windows Form which acts as a user interface. Now what I want to do is to declare a separate c++ class and give it access to a textbox on the form.
The form is declared in the file MyForm.h
The class is defined by a header file and cpp file, lets call them myClass.cpp and myClass.h
The functionality of the program should be as follows : The program should go through all the serial ports that are currently available, then try to open them and poll them for data. If there is a good answer from the serial port then then it should end searching and keep connected to this port. So in this case myClass should check for serial ports and return the name of the desired port. I also want to be able to track the progress of the port searching thread in a textbox on the windows form. I could probably just include the port search into the initialization code of the form, but that would be really messy and would result in a large chunk of code in a single header file. This does not really seem reasonable.
I know that I cannot directly access the Windows form textbox and have to use an Invoke method, this is currently not the problem. The problem is that I really do not know how to get the two classes to communicate with each other. I have tried declaring a pointer to a MyForm object, but this does not seem to be working. I cannot seem to get header and cpp files to connect.
I'm sorry if this sounds confusing, I'll try to explain by example and also code.
Just for concept testing I made a really simple program that consists of a Windows Form and a class called simpleAdder.
//simpleAdder.h
#pragma once
#include "MyForm.h"
using namespace System;
using namespace simpleClassTest;
public ref class simpleAdder
{
private:
Int16 a;
Int16 b;
MyForm^ m_form;
public:
simpleAdder(Int16 x,Int16 y, MyForm^ form);
Int16 add (void);
};
Here is the cpp file for simpleAdder :
//simpleAdder.cpp
#include "MyForm.h"
#include "simpleAdder.h"
using namespace System;
using namespace simpleClassTest;
simpleAdder::simpleAdder(Int16 x,Int16 y, MyForm^ form){
a = x;
b = y;
m_form = form;
}
Int16 simpleAdder::add (void){
return a+b;
//Try to invoke the textbox method. This is not implemented yet.
}
So the idea is that I would have a managed pointer to an existing form and through this, I could access the form itself. The adder class should basically just try to write the result of the x+y onto the form textbox.
Here is some code from the MyForm.h file
#pragma once
#include "simpleAdder.h"
namespace simpleClassTest {
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Drawing;
//ref class simpleAdder;
/// <summary>
/// Summary for MyForm
/// </summary>
public ref class MyForm : public System::Windows::Forms::Form
{
public:
MyForm(void)
{
InitializeComponent();
this->m_add = (gcnew simpleAdder (16,10));
//
//TODO: Add the constructor code here
//
}
protected:
/// <summary>
/// Clean up any resources being used.
/// </summary>
~MyForm()
{
if (components)
{
delete components;
}
}
private:
/// <summary>
/// Required designer variable.
/// </summary>
simpleAdder^ m_add;
private: System::Windows::Forms::Button^ button1;
private: System::Windows::Forms::TextBox^ textBox1;
System::ComponentModel::Container ^components;
//Here follows the automatically generated code.
The problem is that no matter what I do, I cannot get this to compile. The compiler does not recognize the MyForm type variable. Also having two headers that include each other does not seem right, but I really don't know what else to do. I tried declaring both classes in the same namespace, but that did not help either. Then I tried forward declaring the simpleAdder class in the MyForm header file, but that did not work.
I am obviously not experienced in c++/cli and it seems that I am doing something fundamentally wrong. I think there must be a better way to implement something like this. I mean in the end I would have to add other classes to the windows form to display information etc. There must be an easy way of doing this. Any help on this would be much appreciated.
Lets look at order of includes:
simpleAdder.cpp include MyForm.h
MyForm.h include simpleAdder.h
Inside simpleAdder.h include to MyFrom.h is skipped becose #pragma once
Then there is code:
public ref class simpleAdder
{
private:
Int16 a;
Int16 b;
MyForm^ m_form;
public:
simpleAdder(Int16 x,Int16 y, MyForm^ form);
Int16 add (void);
};
but there wasn't MyForm declared anywhere.
Read about forward declaration. You can declare MyForm before simpleAdder:
public ref class simpleAdder; // or without public