I'm exporting UE4 Project (version 4.23) to HTML5 that has WebSockets module enabled in .Build.cs:
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "WebSockets" });
It works fine when I play in Editor, but when I call FWebSocketsModule::Get().CreateWebSocket(url, protocol) on exported project, this method is not found.
Did check FWebSocketsModule.cpp and function is declared:
#if WITH_WEBSOCKETS
TSharedRef<IWebSocket> FWebSocketsModule::CreateWebSocket(....
....
#endif
So, WITH_WEBSOCKETS is set to 0 when exporting/running to html
I did declare this macro on my class and set to 1, but it got stuck at Loading engine... in browser
Did I miss any configuration?
Managed to call websockets from HTML by including emscripten websockets
in <Project>.Build.cs constructor:
if (Target.Platform == UnrealTargetPlatform.HTML5)
{
string libwebs = "<Engine instal dir>/UE_4.23/Engine/Extras/ThirdPartyNotUE/emsdk/emscripten/1.38.31/src/library_websocket.js";
PublicAdditionalLibraries.Add(libwebs);
}
In my GameInstance.h class (or whenever you want to register websockets):
#ifdef __EMSCRIPTEN__
#include <emscripten/html5.h>
#include <emscripten/emscripten.h>
#include <emscripten/websocket.h>
#endif
In .cpp class:
#ifdef __EMSCRIPTEN__
static EMSCRIPTEN_WEBSOCKET_T socket;
static EM_BOOL onopen(int eventType, const EmscriptenWebSocketOpenEvent* websocketEvent, void* userData) {
//code logic ...
return EM_TRUE;
}
static EM_BOOL onerror(int eventType, const EmscriptenWebSocketErrorEvent* websocketEvent, void* userData) {
//code logic ...
return EM_TRUE;
}
static EM_BOOL onclose(int eventType, const EmscriptenWebSocketCloseEvent* websocketEvent, void* userData) {
//code logic ...
return EM_TRUE;
}
static EM_BOOL onmessage(int eventType, const EmscriptenWebSocketMessageEvent* websocketEvent, void* userData) {
Yourclass* instance = reinterpret_cast<YourClass*>(userData);
//code logic ...
instance->YourNonStaticFunc(..);
return EM_TRUE;
}
#endif
In your .cpp Init/Method:
#ifdef __EMSCRIPTEN__
if (!emscripten_websocket_is_supported()) {
UE_LOG(LogTemp, Log, TEXT("\nemscripten_websocket not supoported :/\n"));
return;
}
EmscriptenWebSocketCreateAttributes ws_attrs = { TCHAR_TO_ANSI("ws://ip_address:port", NULL, EM_TRUE };
socket = emscripten_websocket_new(&ws_attrs);
emscripten_websocket_set_onopen_callback(socket, this, onopen);
emscripten_websocket_set_onerror_callback(socket, this, onerror);
emscripten_websocket_set_onclose_callback(socket, this, onclose);
emscripten_websocket_set_onmessage_callback(socket, this, onmessage);
#endif
For more about emscripten functions, you can check at: <Your engine install dir>/UE_4.23/Engine/Extras/ThirdPartyNotUE/emsdk/emscripten/1.38.31/system/include/emscripten/[websocket.h, emscripten.h, html5.h]
Related
I am working with Unity and I need to pass callback delegate from C# unity script to .dll
There is a api (MyApi.h) that I have on .dll side
typedef void(__stdcall * FuncPtr) (const char * str);
static FuncPtr DebugLog = nullptr;
static void debug_in_unity(std::string message)
{
if (DebugLog)
{
DebugLog(message.c_str());
}
}
extern "C"
{
DllExport void register_debug_callback(FuncPtr callback)
{
if (callback)
{
DebugLog = callback;
}
}
...
}
so, when I call register_debug_callback function from C# side I see that everything is ok and DebugLog assigned as expected.
Then in order to send my log message from .dll side to C# I need to call this function debug_in_unity()
So, I have another myfile.cpp file where I need to use this log function
#include "MyApi.h"
void MyClass::foo()
{
std::string log = "HERE!!!";
debug_in_unity(log);
}
So, everything looks fine, I have a global static method debug_in_unity and global func DebugLog that I assigned previously here register_debug_callback
But what actually is happen is - when I call this method register_debug_callback I see that I assigned DebugLog variable, but then when I call this method debug_in_unity I see that DebugLog is null. Looks like static variable is kind of not global like MyApi.h has instance of DebugLog and myfile.cpp has his own instance. I assume that it is a reason why I see assignment and then I see that the same value is null...
But how to use it properly? How to fix it?
You are breaking the one definition rule. You will need to have one translation unit (.c or .cpp file) defining DebugLog, and the header declaring it extern. Similarly your functions are also defined in multiple translation units.
MyApi.h
typedef void(__stdcall * FuncPtr) (const char * str);
extern FuncPtr DebugLog;
void debug_in_unity(std::string message);
extern "C"
{
DllExport void register_debug_callback(FuncPtr callback);
// ...
}
MyApi.cpp
#include "MyApi.h"
FuncPtr DebugLog = nullptr;
void debug_in_unity(std::string message)
{
if (DebugLog)
{
DebugLog(message.c_str());
}
}
extern "C"
{
DllExport void register_debug_callback(FuncPtr callback)
{
if (callback)
{
DebugLog = callback;
}
}
// ...
}
I am trying to create a program that uses plugins in C++ to get some experience with importing and exporting functions from .dll and .so libraries. For simplicity's sake let's only use .dll libraries here.
What I'm trying to do is to make the communication between the plugin and the main program that loaded it "two-way", meaning the main program can call functions from the plugin (this is solved) and the plugin should be able to call functions from the main program (this I'm having trouble with).
I currently am able to create a .dll where I exported the functions with extern "C" {} and __declspec(export).
TestPlugin.h
#pragma once
extern "C" {
__declspec(dllexport) const char* pluginName();
__declspec(dllexport) void onLoad();
__declspec(dllexport) void onShutdown();
}
TestPlugin.cpp
#include "TestPlugin.h"
#include <iostream>
const char * pluginName()
{
return "Test Plugin";
}
void onLoad()
{
std::cout << "onLoad() called!" << std::endl;
}
void onShutdown()
{
std::cout << "onShutdown() called!" << std::endl;
}
I am then loading this test plugin with the following (shortened) code. I removed the error checking and console output.
Plugin.h
#pragma once
#include <filesystem>
#include <iostream>
#include <windows.h>
class Plugin
{
private:
typedef const char*(*pluginNameType)();
typedef void(*onLoadType)();
typedef void(*onShutdownType)();
HINSTANCE m_lib;
public:
Plugin(std::filesystem::path filename);
~Plugin();
pluginNameType pluginName;
onLoadType onLoad;
onShutdownType onShutdown;
};
Plugin.cpp
#include "Plugin.h"
Plugin::Plugin(std::filesystem::path filename)
{
m_lib = LoadLibrary(filename.wstring().c_str());
pluginName = (pluginNameType)GetProcAddress(m_lib, "pluginName");
onLoad = (onLoadType)GetProcAddress(m_lib, "onLoad");
onShutdown = (onShutdownType)GetProcAddress(m_lib, "onShutdown");
}
Plugin::~Plugin()
{
FreeLibrary(m_lib);
}
What I can do now is to call the functions in the plugin (TestPlugin.cpp) from my main program.
main.cpp
Plugin *plugin = new Plugin("pathToDLLGoesHere");
plugin->onLoad();
plugin->onShutdown();
What I would like to do now is to also enable the test plugin I just loaded to have access to functions that are defined in the main program. So let's say in my main.cpp I have something like this ...
main.cpp
int testCall(int val) {
return val + 1;
}
int main()
{
...
return 0;
}
... how would I be able to call the testCall() from the test plugin?
Would it be as simple as to send the function pointer to the plugin and use it? Or do I need to take a different approach here? Thank you for your help!
I have figured out how this works. You can also use extern "C" {} and __declspec(dllexport) to export functions from your main program so the DLLs can see them and when you get the handle of your main program in the DLL, the functions can be called.
In one of your headers in your main program you export the function.
main.h
extern "C" {
__declspec(dllexport) int testCall(int val);
}
main.cpp
int testCall(int val) {
return val + 1;
}
In my test plugin header I created a handle for the main program and a definition for the function I am trying to call from main.
TestPlugin.h
#pragma once
#include <windows.h>
HINSTANCE app;
int(*testCall)(int val);
...
In the body I then assign the handle (calling GetModuleHandle with a nullptr will give you the handle of your program) and then get the exported function from my main program.
TestPlugin.cpp
app = GetModuleHandle(nullptr);
testCall = (int(*)(int val))GetProcAddress(app, "testCall");
After that, I can just call the function.
std::cout << testCall(5) << std::endl;
Error Message : Pending exception java.lang.NoSuchMethodError thrown by 'void com.exampleTest.NDKT.I(android.app.Activity):-2'
java.lang.NoSuchMethodError: no static method "Lcom/exampleTest/NDKT;.PN()Ljava/lang/String;"
I should try ndk c++ call java method because i want you java method thought android package name know
i tried below
java call method String -> void change
=> c++ : jmethodID jMethod = pE->GetStaticMethodID(jClass, "GO", "()V");
java code "this" -> "MainActivity.this"
- Java code
CallNDK class{
public static void initialize(final Activity a,final SIMECB cb) {
System.loadLibrary("simpleinit");
_CB = cb;
_INSTANCE = new CallNDK();
new Thread(new Runnable() {
public void run() {
_INSTANCE.I(a);
}
}).start();
}
}
==> initialize () -> Activity = MainActivity.this
- NDK
CallNDK.cpp
static string mPN; //Packege Name
#ifdef __ANDROID__
void CallNDK::I(JNIEnv* pE, jobject pA){
// Package Name Method call
CHECK::PN(pE, mPN);
}
CHECK.h
#ifndef CHECK_H_
#define CHECK_H_
#ifdef __ANDROID__
#include <jni.h>
#endif
using namespace std;
class CHECK {
public:
#ifdef __ANDROID__
static void PN(
JNIEnv* pE,
string& pO);
#endif
};
#endif
CHECK.cpp
#include "CHECK.h"
#ifdef __ANDROID__
#include <jni.h>
#endif
using namespace std;
#ifdef __ANDROID__
void CHECK::PN(JNIEnv* pE, string& pO){
jclass classes = pE->FindClass("com/exampleTest/NDKT");
jmethodID jMethod = pE->GetMethodID(classes, "SimpleMethod", ()Ljava/lang/String;");
if(!jMethod) {
CALLUtil::LOG("class loader method access");
return;
}
jstring jsResult = (jstring)pE->CallStaticObjectMethod(jClass, jMethod);
const char* cResult = pE->GetStringUTFChars(jsResult, NULL);
std::string sResult(cResult);
pO = sResult;
}
- NDK of call java class code
enter code here
NDKT Class {
private static int _MAX_TRY_INVOKE_COUNT = 5;
public static String SimpleMethod(Activity a){
for(int i = 0; i < _MAX_TRY_INVOKE_COUNT; i++) {
try {
return a.getApplicationContext().getPackageName();
} catch (Exception e) {
try { Thread.sleep(50); } catch (Exception e2) { }
}
}
return ""
}
}
Error
JNI DETECTED ERROR IN APPLICATION:
JNI FindClass called with pending exception 'java.lang.NoSuchMethodError' thrown in void com.exampleTest.CallNDK.I(android.app.Activity):-2 in call to FindClass from void com.exampleText.CallNDK.I(android.app.Activity)
Pending exception java.lang.NoSuchMethodError thrown by 'void com.exampleTest.NDKT.I(android.app.Activity):-2'
java.lang.NoSuchMethodError: no static method "Lcom/exampleTest/NDKT;.PN()Ljava/lang/String;"
I'm trying to make Wrapper class for FreeRTOS on ARM GNU compiler with C++. I did it on Keil MDK-ARM v5.14 without any problem but I cant accomplish on eclipse platform. I think it is about my wrong Source Location configuration on eclipse.
I'm using Eclipse Luna 4.4.2, ARM GNU GCC 4.9.2 and GNU ARM Eclipse Plug-ins
Here is my Wrapper class interface:
#ifndef TASKOOP_H_
#define TASKOOP_H_
#include "FreeRTOS.h"
#include "task.h"
class TaskOOP {
private:
TaskHandle_t _handle;
public:
TaskOOP();
virtual ~TaskOOP();//xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
void create(const char * const pcName, const uint16_t usStackDepth, UBaseType_t uxPriority); //xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask )
void deleteT(); //vTaskDelete( TaskHandle_t xTaskToDelete )
void suspend(); //vTaskSuspend( TaskHandle_t xTaskToSuspend )
void resume(); //vTaskResume( TaskHandle_t xTaskToResume )
UBaseType_t getTaskPriority(); //uxTaskPriorityGet( TaskHandle_t xTask )
void setTaskPriority(UBaseType_t uxNewPriority); //vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
static void delay(const TickType_t xTicksToDelay); //vTaskDelay( const TickType_t xTicksToDelay )
static void vTaskStartScheduler( void );
static void vTaskEndScheduler( void );
static void vTaskSuspendAll( void );
static BaseType_t xTaskResumeAll( void );
private:
static void bridge(void* pThis);
protected:
virtual void run(void) = 0;
};
#endif /* TASKOOP_H_ */
Here is my implementation:
#include "TaskOOP.h"
TaskOOP::TaskOOP():_handle(0) {
// TODO Auto-generated constructor stub
}
TaskOOP::~TaskOOP() {
// TODO Auto-generated destructor stub
}
void TaskOOP::create(const char * const pcName, const uint16_t usStackDepth, UBaseType_t uxPriority){
xTaskCreate(bridge,pcName,usStackDepth,this,uxPriority,&_handle);
}
void TaskOOP::deleteT(){
vTaskDelete(_handle);
}
void TaskOOP::suspend(){
vTaskSuspend(_handle);
}
void TaskOOP::resume(){
vTaskResume(_handle);
}
UBaseType_t TaskOOP::getTaskPriority(){
return uxTaskPriorityGet(_handle);
}
void TaskOOP::setTaskPriority(UBaseType_t uxNewPriority){
vTaskPrioritySet( _handle, uxNewPriority );
}
void TaskOOP::delay(const TickType_t xTicksToDelay){
vTaskDelay(xTicksToDelay);
}
void TaskOOP::vTaskStartScheduler(void){
vTaskStartScheduler();
}
void TaskOOP::vTaskEndScheduler(void){
vTaskEndScheduler();
}
void TaskOOP::vTaskSuspendAll(void){
vTaskSuspendAll();
}
BaseType_t TaskOOP::xTaskResumeAll( void ){
return xTaskResumeAll();
}
void TaskOOP::bridge(void* pThis){
TaskOOP* task = (TaskOOP *)pThis;
task->run();
}
Here are some print-screens from my eclipse settings:
I cant upload them as image because I dont have enough reputation.
So the question is how should I configure my Path and Source Location?
I have this pieces of code:
class DLL_API MyClassWrapper
{
private:
MyClass * m_myClass;
public:
MyClassWrapper(SIZE inputSize);
~MyClassWrapper();
inline int OutputSize();
}
typedef std::shared_ptr<MyClassWrapper> MyClassWrapperPtr;
extern "C"
{
DLL_API MyClassWrapperPtr CreatreMyClassWrapper(SIZE inputSize)
{
return std::make_shared<MyClassWrapper>(inputSize);
}
}
But it doesn't work, with error:
Error 1 error C2526: CreatreMyClassWrapper: C linkage function cannot return C++ class 'std::shared_ptr<_Ty>'
I understand the problem, but how can I fix it?
The options that I can see are:
1- Don't pass a shared pointer. which means that DLL user should delete the pointer after they used it.
2- Don't use extern "C" : which means that I must use mangled names.
Is there any other solution?
Straight to the point, to return C++ object from C function - just returns it via output arguments:
extern "C"
{
DLL_API void CreatreMyClassWrapper(SIZE inputSize, SomeClass* outputPtr)
{
*outputPtr = SomeClass(....);
}
}
In your example SomeClass == MyClassWrapperPtr, so:
extern "C"
{
DLL_API void CreatreMyClassWrapper(SIZE inputSize, MyClassWrapperPtr* outputPtr)
{
*outputPtr = make_shared<MyClassWrapper>(inputSize);
}
}
Consider however to change your interface a little, because in current shape you need to be sure that your applications and DLLs shall use the same compiler, linker, settings, libraries...*
You might want to export Create and Delete from your DLL to be sure memory management will occur in your DLL (this is based on this answer:
DLL
extern "C"
{
DLL_API MyClassWrapper* CreateMyClassWrapper(SIZE inputSize)
{
return new MyClassWrapper(inputSize);
}
DLL_API void DeleteMyClassWrapper(MyClassWrapper* wrapper)
{
delete wrapper;
}
}
Application
shared_ptr<MyClassWrapper> myClassWrapper(CreateMyClassWrapper(inputSize),
DeleteMyClassWrapper);