-
Notifications
You must be signed in to change notification settings - Fork 42
Description
Issue
In an attempt to wrap the Scichart Server Licensing shared object using java (JNA), I ended up with strange behavior when calling any function which returns char *. In Java, the returned pointer memory does not start at the correct offset which holds the string value and therefore invalid characters are returned.
Hypothesis
My guess, after evaluation, is that either the function is returning some std::string object, or that it is actually returning say str.c_str() where str is an std::string. The issue is that returning str.c_str() causes the reference to be dropped once it is out of scope, this means that a dangling pointer is returned. I ended up needing to wrap the function calls in another extern C layer to get the desired functionality in Java, see below.
scichart.h
#include<cstring>
#include <string>
namespace SciChart {
enum class SCRTLicenseType {
/// Invalid but informs the user a trial is being requested.
LICENSE_TYPE_NO_LICENSE = 0,
/// Trial - Valid but with trial notices
LICENSE_TYPE_TRIAL = 0x02,
/// Community - Watermark but no expiry
LICENSE_TYPE_COMMUNITY = 0x03,
/// Full - Valid
LICENSE_TYPE_FULL = 0x20,
/// Full expired - For Non-perpetual (web)
LICENSE_TYPE_FULL_EXPIRED = 0x04,
/// Trial expired - Invalid
LICENSE_TYPE_TRIAL_EXPIRED = 0x40,
/// Subscription expired - build is after expiry date
LICENSE_TYPE_SUBSCRIPTION_EXPIRED = 0x80,
/// Invalid developer license - Invalid machine specific license
LICENSE_TYPE_INVALID_DEVELOPER_LICENSE = 0x0F,
/// License that requires server validation
LICENSE_TYPE_REQUIRES_VALIDATION = 0x2F,
/// Invalid license - Invalid runtime license
LICENSE_TYPE_INVALID_LICENSE = 0xFF
};
namespace LicenseServer {
void ResetRuntimeLicense();
bool SetAssemblyName(const std::string &_assemblyName);
/// Sets the Runtime License ( narrow string version ).
/// Returns true passed license key is valid; otherwise false.
bool SetRuntimeLicenseKey(const std::string &_strKey);
/// Gets a type of the Runtime License.
SCRTLicenseType GetLicenseType();
/// Determines whether the Runtime License is valid.
bool CheckLicenseValid();
/// Gets the OrderId of the current license
std::string GetOrderId();
/// Gets the reason for the license failure
std::string GetLicenseErrors();
/// Decode and check challenge. Generate response with encrypted expiry
std::string ValidateChallenge(const std::string &_challenge);
/// Dumps the information of the Runtime License to returned string.
std::string Dump();
} // namespace LicenseServer
} // namespace SciChart
#ifdef __cplusplus
extern "C" {
#endif
bool SciChartSCS_SetRuntimeLicenseKey(char* _strKey);
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
extern "C" {
#endif
char *SciChartSCS_GetLicenseErrors();
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus
extern "C" {
#endif
char *SciChartSCS_ValidateChallenge(char* _challenge);
#ifdef __cplusplus
}
#endifscichart.cpp
#include <string>
#include <cstring>
#include "scichart.h"
extern "C" {
bool SciChartSCS_SetRuntimeLicenseKey(char *_strKey) {
using namespace std;
return SciChart::LicenseServer::SetRuntimeLicenseKey(_strKey);
}
}
extern "C" {
char *SciChartSCS_GetLicenseErrors() {
using namespace std;
std::string val = SciChart::LicenseServer::GetLicenseErrors();
char *ret = (char *)malloc(val.size() + 1);
strcpy(ret, val.c_str());
return ret;
}
}
extern "C" {
char *SciChartSCS_ValidateChallenge(char *_challenge) {
using namespace std;
std::string val = SciChart::LicenseServer::ValidateChallenge(_challenge);
char *ret = (char *)malloc(val.size() + 1);
strcpy(ret, val.c_str());
return ret;
}
}
int main() { return 0; }I then free the malloc on the Java side to get this to work. I believe that if the current shared object actually wrote it's c_str() to a native char* buffer then returned it, there would be no dangling pointer on the Java side and we would not need to to any additional wrapping.