I have a requirement to make a call from C++ to Java using JNI and invoke a void method in MainActivity.
I do not have the JavaVM argument to pass to the JNI layer in this case. So I am using env->NewGlobalRef like below and retriving it by (g_javaVM)->AttachCurrentThread(&env, NULL)
I am getting an error while trying to invoke CallVoidMethod as jobject service = env->NewObject(jobj, ctor) results in NULL service.
Can you please let me know what I am missing here?
JavaVM* g_javaVM = nullptr;
jclass cls;
jclass jobj;
jint JNI_OnLoad( JavaVM* vm, void* reserved ) {
JNIEnv* env;
if( vm->GetEnv( reinterpret_cast<void**>( &env ), JNI_VERSION_1_6 ) != JNI_OK ) {
return -1;
}
cls = env->FindClass("com/test/MainActivity");
jobj = (jclass)env->NewGlobalRef(cls);
g_javaVM = vm;
return JNI_VERSION_1_6;
}
void NativeLib::play() {
JNIEnv *env;
jint rs = (g_javaVM)->AttachCurrentThread(&env, NULL);
assert(rs == JNI_OK);
jmethodID ctor = env->GetMethodID(jobj, "<init>", "()V");
jmethodID jobj_play= env->GetMethodID(jobj,"play","()V");
jobject service = env->NewObject(jobj, ctor); //Getting error on this line. service is NULL
env->CallVoidMethod(service, jobj_play);
}
Related
I am loading JVM in a dll but it fails (indicated in the code where it fails.). I tried the same code in an exe and it works fine.
JavaVMInitArgs vm_args; /* JDK 1.1 VM initialization arguments */
JNIEnv *env;
JavaVMOption options;
options.optionString = "-Djava.class.path=C:\\Core\\bin\\otk-1.4.1-with-dependencies.jar";
vm_args.version = JNI_VERSION_1_6;
vm_args.nOptions = 1;
vm_args.options = &options;
vm_args.ignoreUnrecognized = 0;
jvm_dll = LoadLibrary("C:\\Program Files\\Java\\jdk1.6.0_23\\jre\\bin\\server\\jvm.dll");
if(jvm_dll == NULL)
{
getManager()->log( "InitialiseJava::Can't Load JVM DLL.", HIGH_IMPORTANCE );
return false;
}
JNI_CreateJavaVM_ptr = (JNI_CreateJavaVM_func)GetProcAddress(jvm_dll, "JNI_CreateJavaVM");
if(JNI_CreateJavaVM_ptr == NULL)
{
getManager()->log( "InitialiseJava::Can't create JVM.", HIGH_IMPORTANCE );
return false;
}
int ret = JNI_CreateJavaVM_ptr(jvm, (void**)&env, &vm_args); // fails here
if(ret < 0)
{
getManager()->log( "InitialiseJava::Unable to call JVM.", HIGH_IMPORTANCE );
return false;
}
Please help.
I m following the tutorial for wrapping c++ classes in a php extension.
I m using wamp server and php 5.4.16.
something going wrong in PHP_METHOD(Car, __construct).
It seems that after calling to
car_object *obj = ( car_object *) zend_object_store_get_object ( object TSRMLS_CC );
then obj ->car is not a valid address. when comment the line obj ->car = car, wamp is not shutting down. so i guess obj ->car is not valid or legal address.
bellow is my code:
vehicles.cc:
#include "php.h"
#include "car.h"
#define PHP_VEHICLES_EXTNAME "vehicles"
#define PHP_VEHICLES_EXTVER "0.1"
zend_object_handlers car_object_handlers;
struct car_object {
zend_object std;
Car *car;
};
zend_class_entry *car_ce;
void car_free_storage(void *object TSRMLS_DC)
{
car_object *obj = (car_object *)object;
delete obj->car;
zend_hash_destroy(obj->std.properties);
FREE_HASHTABLE(obj->std.properties);
delete obj->car;
efree(obj);
}
zend_object_value car_create_handler(zend_class_entry *type TSRMLS_DC)
{
zend_object_value retval;
car_object *obj = (car_object *)emalloc(sizeof(car_object));
memset(obj, 0, sizeof(car_object));
obj->std.ce = type;
ALLOC_HASHTABLE(obj->std.properties);
zend_hash_init(obj->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0);
#if PHP_VERSION_ID < 50399
zval *tmp;
zend_hash_copy(obj->std.properties, &type->default_properties, (copy_ctor_func_t) zval_add_ref, (void *)&tmp, sizeof(zval *));
#else
object_properties_init(&(obj->std), type);
#endif
retval.handle = zend_objects_store_put(obj, NULL, car_free_storage, NULL TSRMLS_CC);
retval.handlers = &car_object_handlers;
return retval;
}
PHP_METHOD(Car, __construct)
{
long maxGear;
Car *car = NULL;
zval *object = getThis();
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &maxGear) == FAILURE) {
RETURN_NULL();
}
car = new Car(maxGear);
car_object *obj = ( car_object *) zend_object_store_get_object(object TSRMLS_CC );
//obj->car = car;
}
...
...
zend_function_entry car_methods[] = {
PHP_ME(Car, __construct, NULL, ZEND_ACC_PUBLIC | ZEND_ACC_CTOR)
PHP_ME(Car, shift, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Car, accelerate, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Car, brake, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Car, getCurrentSpeed, NULL, ZEND_ACC_PUBLIC)
PHP_ME(Car, getCurrentGear, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};
PHP_MINIT_FUNCTION(vehicles)
{
zend_class_entry ce;
INIT_CLASS_ENTRY(ce, "Car", car_methods);
car_ce = zend_register_internal_class(&ce TSRMLS_CC);
car_ce->create_object = car_create_handler;
memcpy(&car_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
car_object_handlers.clone_obj = NULL;
return SUCCESS;
}
zend_module_entry vehicles_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
PHP_VEHICLES_EXTNAME,
NULL, /* Functions */
PHP_MINIT(vehicles),
NULL, /* MSHUTDOWN */
NULL, /* RINIT */
NULL, /* RSHUTDOWN */
NULL, /* MINFO */
#if ZEND_MODULE_API_NO >= 20010901
PHP_VEHICLES_EXTVER,
#endif
STANDARD_MODULE_PROPERTIES
};
ZEND_GET_MODULE ( vehicles );
car.h car.cc are the same as in the tutorial.
I guess that I'm missing something. maybe the call for "object_properties_init(&(obj->std), type);" in function car_create_handler is not correct..
thanks for your help/.
It seems that in your function car_free_storage, you are calling delete obj->car twice. This might not be a good idea.
Furthermore, when I was recently looking around another php module, I discovered that obj->std.properties is not always set to a valid address. So it might be worth checking if it isn't 0 before passing it to zend_hash_destroy. My code for this was:
if(obj->std.properties){
zend_hash_destroy(obj->std.properties);
FREE_HASHTABLE(obj->std.properties);
}
I have a C++ Custom Action Project. I have two functions, RegProductName and GetProductName.
I call RegProductName and it has three possible outcomes. I have these in an if statement that if it is outcome 1 or outcome 2 then i call my second function GetProductName but i can't seem to get it working. Can anyone give me an example of calling one function from another please?
extern "C" UINT __stdcall RegProductName(MSIHANDLE hInstall)
{
AssertSz(FALSE, "debug here");
DebugBreak();
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
char szProductName[MAX_PATH];
hr = WcaInitialize(hInstall, "RegProductName");
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
strcpy(szProductName, Orc_Get_Product_Name());
if(szProductName == "ORCHESTRATOR")
{
GetProductName();
}
else if (szProductName == "CORAL")
{
GetProductName();
}
else
{
MsiSetProperty(hInstall, "PRODUCTNAME", szProductName);
}
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
The error is "Too few arguments in function call when i hover over GetProductName();
extern "C" UINT __stdcall GetProductName(MSIHANDLE hInstall)
{
HRESULT hr = S_OK;
UINT er = ERROR_SUCCESS;
DWORD Ret;
CHAR *Section = "General";
CHAR szBuffer[MAX_PATH];
CHAR szProductIniFile[MAX_PATH];
char lpszString[MAX_PATH];
int lplValue;
hr = WcaInitialize(hInstall, "GetProductName");
ExitOnFailure(hr, "Failed to initialize");
WcaLog(LOGMSG_STANDARD, "Initialized.");
TCHAR* szValueBuf = NULL;
DWORD cchValueBuf = 0;
UINT uiStat = MsiGetProperty(hInstall, TEXT("TEMPFOLDER"), TEXT(""), &cchValueBuf);
if (ERROR_MORE_DATA == uiStat)
{
++cchValueBuf;
szValueBuf = new TCHAR[cchValueBuf];
if (szValueBuf)
{
uiStat = MsiGetProperty(hInstall, TEXT("TEMPFOLDER"), szValueBuf, &cchValueBuf);
}
}
if (ERROR_SUCCESS != uiStat)
{
if (szValueBuf != NULL)
delete[] szValueBuf;
return ERROR_INSTALL_FAILURE;
}
strcpy(szProductIniFile,szValueBuf);
Ret = strlen(szProductIniFile);
if(szProductIniFile[Ret-1] != '\\')
strcat(szProductIniFile,"\\");
strcat(szProductIniFile, "Product.ini");
Ret = GetPrivateProfileString(Section, // Section Title [General]
"PRODUCT_NAME", // Entry
"Orchestrator", // Default Value
szBuffer, // Address of buffer to read to
MAX_PATH, // Length of buffer
szProductIniFile); // .ini file name
if (strlen(szBuffer) == 0)
strcpy(szBuffer, "ORCHESTRATOR");
if (strlen(szBuffer) >= 3 && (stricmp(szBuffer+strlen(szBuffer)-3,"DEM") == 0))
lplValue = 1;
else
lplValue = 0;
MsiSetProperty(hInstall, "PRODUCTNAME", szBuffer);
LExit:
er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE;
return WcaFinalize(er);
}
Your GetProductName() function takes an argument MSIHANDLE hInstall. You'll need to provide that when calling it. For instance, if you just want to call it with the same handle as RegProductName() was called with:
GetProductName(hInstall);
This does not compare the string content:
if(szProductName == "ORCHESTRATOR")
either use strcmp() or use std::string and ==:
if(szProductName == std::string("ORCHESTRATOR"))
Your GetProductName looks like this:
extern "C" UINT __stdcall GetProductName(MSIHANDLE hInstall)
\________________/
The Argument
So it needs to take 1 argument, while you are calling it without argumanes at all:
getProductName( );
^
|
nothing is being passed here
hence the error you're getting. Based on your code you should probably pass your hInstall there:
getProductName( hInstall );
The GetProductName requires one argument of type MSIHANDLE whereas you are calling it without any parameter. Try instead
GetProductName(hInstall);
GetProductName is defined as GetProductName(MSIHANDLE hInstall), that means you MUST pass relevant MSIHANDLE as parameter. And that's exactly the error you're getting.
But you're doing szProductName == "ORCHESTRATOR" - this is not how you compare strings in C. You seem to lack basic knowledge about C. You should not be writing in C or C++.
I´m Trying to launch two threads witch calls "DispFrontEnd" function
First thread ended OK, second failed to start jvm.. ??
tks
#include "jni.h"
#include <process.h>
#include "Stdafx.h"
//DISPATCH Thread Check
bool DispatchThreadCreated = FALSE;
if (DispatchThreadCreated == FALSE)
{
HANDLE hDispThread;
hDispThread = (HANDLE)_beginthread(DispFrontEnd,0,(void *)dispatchInputs);
if ((long)hDispThread == -1)
{
log.LogError("Thread DispFrontEnd Returned********BG ", (long)hDispThread);
log.LogError("errno", errno);
log.LogError("_doserrno", _doserrno);
}
else
{
logloc->LogMethod("Dispatch Thread CREATED");
DispatchThreadCreated= TRUE;
//Espera que a thread termine
WaitForSingleObject( hDispThread, INFINITE );
DispatchThreadCreated= FALSE; // 01_02_2010
logloc->LogMethod("Dispatch Thread ENDED");
}
}
if (DispatchThreadCreated == FALSE)
{
HANDLE hDispThread3;
logloc->LogMethod("3 : Dispatch Thread CREATED");
hDispThread3 = (HANDLE)_beginthread(DispFrontEnd,0,(void *)dispatchInputs);
if ((long)hDispThread3 == -1)
{
log.LogError("3 : Thread DispFrontEnd Returned********BG ", (long)hDispThread3);
log.LogError("errno", errno);
log.LogError("_doserrno", _doserrno);
}
else
{
logloc->LogMethod("3 : Dispatch Thread CREATED");
DispatchThreadCreated= TRUE;
//Espera que a thread termine
WaitForSingleObject( hDispThread3, INFINITE );
DispatchThreadCreated= FALSE; // 01_02_2010
logloc->LogMethod("3 : Dispatch Thread ENDED");
}
}
void DispFrontEnd(void * indArr)
{
JNIEnv *env;
JavaVM *jvm;
env = create_vm(&jvm); // return null on second call ???
}
JNIEnv* create_vm(JavaVM ** jvm) {
CString str;
JNIEnv *env;
JavaVMInitArgs vm_args;
JavaVMOption options;
options.optionString = "-Djava.class.path=C:\\dispatch\\lib\\Run.jar;C:\\dispatch\\classes"; //Path to the java source code
vm_args.version = JNI_VERSION_1_6; //JDK version. This indicates version 1.6
vm_args.nOptions = 1;
vm_args.options = &options;
vm_args.ignoreUnrecognized = 0;
int ret = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args);
if(ret < 0)
{
env = NULL;
str.Format("ERROR! create JVM (%d)",ret); // show this on second call!! ?
logloc->LogMethod( str );
}
else
{
str.Format("JVM %x created Success!",env->GetVersion());
logloc->LogMethod( str );
}
return env;
}
Do you really have to start many JVM ? Could you use
jint AttachCurrentThread(JavaVM *vm, JNIEnv **p_env, void *thr_args);
instead ?
The only thing I know is a native thread cannot attach two different JVM at the same time.
I am having trouble starting my service on my pc. My code is based on this article http://www.gamedev.net/reference/articles/article1899.asp
When i call installService from my int main(int argc, char *argv[]), it is registered successfully (i can see it in msconfig and services.msc). However it has not started. I manually start the service via services.msv and i get the error "Error 2: system cannot find the file specified". Why is this? i registered the services no more then a min ago, my external HD is still on (where this is currently stored. i'll move a nondev version to c:/ when its ready) What am i doing wrong and is there another tutorial i can look at (i only found the one linked via google)
#define srvName "MyTestService_01312009"
void installService(char*path)
{
SC_HANDLE handle = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
SC_HANDLE service = ::CreateService(
handle,
srvName,
"MyTestService_01312009b",
GENERIC_READ | GENERIC_EXECUTE,
SERVICE_WIN32_OWN_PROCESS,
SERVICE_AUTO_START,
SERVICE_ERROR_IGNORE,
path,
NULL,
NULL,
NULL,
NULL,
NULL
);
}
void uninstallService()
{
SC_HANDLE handle = ::OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );//?
SC_HANDLE service = ::OpenService( handle, srvName, DELETE );
if( service != NULL )
{
// remove the service!
::DeleteService( service );
}
}
SERVICE_STATUS_HANDLE hStatus;
SERVICE_STATUS status;
/*
if( ::StartServiceCtrlDispatcher( dispatchTable ) == 0 )
{
// if this fails, it's probably because someone started us from
// the command line. Print a message telling them the "usage"
}
*/
void WINAPI ServiceCtrlHandler( DWORD control )
{
switch( control )
{
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
// do shutdown stuff here
status.dwCurrentState = SERVICE_STOPPED;
status.dwWin32ExitCode = 0;
status.dwCheckPoint = 0;
status.dwWaitHint = 0;
break;
case SERVICE_CONTROL_INTERROGATE:
// just set the current state to whatever it is...
break;
}
::SetServiceStatus( hStatus, &status );
}
void WINAPI ServiceDispatch( DWORD numArgs, char **args )
{
// we have to initialize the service-specific stuff
memset( &status, 0, sizeof(SERVICE_STATUS) );
status.dwServiceType = SERVICE_WIN32;
status.dwCurrentState = SERVICE_START_PENDING;
status.dwControlsAccepted = SERVICE_ACCEPT_STOP;
hStatus = ::RegisterServiceCtrlHandler( srvName, &ServiceCtrlHandler );
// more initialization stuff here
FILE *f = fopen("c:/testSrv.bin", "wb");
::SetServiceStatus( hStatus, &status );
}
SERVICE_TABLE_ENTRY dispatchTable[] =
{
{ srvName, &ServiceDispatch },
{ NULL, NULL }
};
Maybe you can use Process Monitor to find out what's wrong.
Start it, and look for NAME NOT FOUND results that occur in connection with the service start.