Watch, Follow, &
Connect with Us

For forums, blogs and more please visit our
Developer Tools Community.


Welcome, Guest
Guest Settings
Help

Thread: JNI.pas verC gives access violation on new Delphi XE7



Permlink Replies: 10 - Last Post: Oct 29, 2014 2:26 PM Last Post By: Remy Lebeau (Te...
Alessio Pollero

Posts: 11
Registered: 1/27/13
JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 10:26 AM
Hi,
I'm working with the last version of the JNI.pas that I've found on the web : JNI_verC, and though it works correctly in Delphi7, it raises an access violation when :
Env^.CallVoidMethodA(Env, Obj, MethodID, ArgsToJValues(Args));
is called though I even tried to change the TJNIEnv.ArgsToJValues accordingly to this suggestion : http://delphi.cz/post/Jeste-jednou-Delphi-a-Java-tentokrat-v-XE2-64bit.aspx .

Is there a way to Fix this access violation ? There can be a possible wrong argument conversion problem ?

Here some snippets of involved code :

procedure TJNIEnv.CallVoidMethod(Obj: JObject; MethodID: JMethodID; const Args: array of const);
begin
  Env^.CallVoidMethodA(Env, Obj, MethodID, ArgsToJValues(Args));
end;


ArgsToJValues method :
function TJNIEnv.ArgsToJValues(const Args: array of const): PJValue;
var
  I: Integer;
begin
  if Length(Args) <> Length(FConvertedArgs) then
    SetLength(FConvertedArgs, Length(Args));
  for I := 0 to High(Args) do
    case Args[I].VType of
      vtInteger:
        FConvertedArgs[I].i := JInt(Args[I].VInteger);
      vtBoolean:
        FConvertedArgs[I].z := JBoolean(Args[I].VBoolean);
      vtWideChar:
        FConvertedArgs[I].c := JChar(Args[I].VWideChar);
      vtInt64:
        FConvertedArgs[I].j := JLong(Args[I].VInt64^);
      vtPointer, vtObject:
        FConvertedArgs[I].l := JObject(Args[I].VObject);
      vtAnsiString:
        FConvertedArgs[I].l := StringToJString(UTF8String(PAnsiString(Args[I].VAnsiString)));
      vtWideString:
        FConvertedArgs[I].l := WideStringToJString(PChar(PWideString(Args[I].VWideString)));
     {$IFDEF HAS_UNICODESTRING}
      vtUnicodeString:
        FConvertedArgs[I].l := WideStringToJString(PChar(PUnicodeString(Args[I].VUnicodeString)));
     {$ENDIF}
      vtExtended:
        FConvertedArgs[I].d := Args[I].VExtended^; // Extended to Double (we lose Floats here)
    else
      raise EJNIError.Create('Unsupported variant argument');
    end;
  Result := PJValue(FConvertedArgs);
end;


The complete file(JNI.pas) I'm referring to can be found here : http://pastebin.com/wrx0f2hx .
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 10:56 AM   in response to: Alessio Pollero in response to: Alessio Pollero
Alessio wrote:

I'm working with the last version of the JNI.pas that I've found on
the web : JNI_verC, and though it works correctly in Delphi7, it
raises an access violation when :
Env^.CallVoidMethodA(Env,
Obj, MethodID, ArgsToJValues(Args));
is called

What values are you passing to the Args parameter? Is the exception being
raised inside of ArgsToJValues(), or is it occuring on the actual CallVoiceMethodA()
call after ArgsToJValues() has exited?

vtPointer, vtObject:
FConvertedArgs[I].l := JObject(Args[I].VObject);

That is questionable for vtPointer.

vtWideString:
FConvertedArgs[I].l := WideStringToJString(PChar(PWideString(Args[I].VWideString)));

That should be using PWideChar instead of PChar, but more importantly it
should be dereferencing the PWideString pointer before then casting:

FConvertedArgs[I].l := WideStringToJString(PWideChar(PWideString(Args[I].VWideString)^));


vtUnicodeString:
FConvertedArgs[I].l := WideStringToJString(PChar(PUnicodeString(Args[I].VUnicodeString)));

Same thing about the dereferencing, but it also does not need to use a PChar
cast at all since WideStringToJString() has an overload that accepts a UnicodeString:

FConvertedArgs[I].l := WideStringToJString(PUnicodeString(Args[I].VUnicodeString)^);


--
Remy Lebeau (TeamB)
Alessio Pollero

Posts: 11
Registered: 1/27/13
Re: JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 11:27 AM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Hi, and thanks for responding .

I've tried changing in the way you suggest but there's no way to make it work .

The call that throw the exception is inside the JVM (specifically raised by jvm.dll ), and it occurs when this call is performed :

Env^.CallVoidMethodA(Env, Obj, MethodID, ArgsToJValues(Args));


In addition the problem with the code you suggest to use in the TJNIEnv.ArgsToJValues method is that if I use the code you suggest after the function call the value FConvertedArgs[I].l is nil, while with the implementation found on Delphi.cz I'm getting a valid memory reference ...

The value that I'm trying to pass as arguments its a single string argument ...
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 11:50 AM   in response to: Alessio Pollero in response to: Alessio Pollero
Alessio wrote:

The call that throw the exception is inside the JVM (specifically
raised by jvm.dll ), and it occurs when this call is performed :

That still does not answer my question - is the exception occuring inside
of ArgsToJValues() (which makes its own JNI calls), or inside of CallVoidMethodA()
after ArgsToJValues() has exited? It makes a difference.

procedure TJNIEnv.CallVoidMethod(Obj: JObject; MethodID: JMethodID; const 
Args: array of const);
var
  Values: PJValue;
begin
  Values := ArgsToJValues(Args); // <-- is the exception here?
  Env^.CallVoidMethodA(Env, Obj, MethodID, Values); // <-- or here?
end;


If the exception is occuring inside of ArgsToJValues(), then either it is
not accessing the TVarRec data correctly, or it is not making its internal
JNI calls correctly.

If the exception is occuring inside of CallVoiceMethodA(), then either the
JValue array is not being populated correctly, or the object method being
called is crashing, such as if it is not accessing the array values correctly.

In addition the problem with the code you suggest to use in the
TJNIEnv.ArgsToJValues method is that if I use the code you suggest
after the function call the value FConvertedArgs[I].l is nil, while
with the implementation found on Delphi.cz I'm getting a valid memory
reference ...

The only way the changes I gave should be producing nil values is if the
source strings are empty to begin with.

The value that I'm trying to pass as arguments its a single
string argument ...

Of what data type? AnsiString, UnicodeString, or WideString? Can you please
show a complete example of what you are attempting to do?

--
Remy Lebeau (TeamB)
Alessio Pollero

Posts: 11
Registered: 1/27/13
Re: JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 12:51 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Alessio wrote:

The call that throw the exception is inside the JVM (specifically
raised by jvm.dll ), and it occurs when this call is performed :

That still does not answer my question - is the exception occuring inside
of ArgsToJValues() (which makes its own JNI calls), or inside of CallVoidMethodA()
after ArgsToJValues() has exited? It makes a difference.

procedure TJNIEnv.CallVoidMethod(Obj: JObject; MethodID: JMethodID; const 
Args: array of const);
var
  Values: PJValue;
begin
  Values := ArgsToJValues(Args); // <-- is the exception here?
  Env^.CallVoidMethodA(Env, Obj, MethodID, Values); // <-- or here?
end;


If the exception is occuring inside of ArgsToJValues(), then either it is
not accessing the TVarRec data correctly, or it is not making its internal
JNI calls correctly.

If the exception is occuring inside of CallVoiceMethodA(), then either the
JValue array is not being populated correctly, or the object method being
called is crashing, such as if it is not accessing the array values correctly.

In addition the problem with the code you suggest to use in the
TJNIEnv.ArgsToJValues method is that if I use the code you suggest
after the function call the value FConvertedArgs[I].l is nil, while
with the implementation found on Delphi.cz I'm getting a valid memory
reference ...

The only way the changes I gave should be producing nil values is if the
source strings are empty to begin with.

The value that I'm trying to pass as arguments its a single
string argument ...

Of what data type? AnsiString, UnicodeString, or WideString? Can you please
show a complete example of what you are attempting to do?

--
Remy Lebeau (TeamB)


The call the raise the exception is definitely Env^.CallVoidMethodA, the parameter that I'm passing is a System.String, and the operation I'm doing can be summarised as follows :

var 
  fsLogNameLD       : string;
begin 
     fsLogNameLD := FIni.ReadString('GRP','KEY','defaultLD')    JNIUtils.CallMethod(JNiEnv,jLineDisplay,'open','void (String)' ,[fsLogNameLD],false);end
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 1:09 PM   in response to: Alessio Pollero in response to: Alessio Pollero
Alessio wrote:

The call the raise the exception is definitely Env^.CallVoidMethodA,
the parameter that I'm passing is a System.String, and the operation
I'm doing can be summarised as follows :

Are you sure the JniEnv pointer is valid to begin with?

var
  fsLogNameLD       : string;
begin
  fsLogNameLD := FIni.ReadString('GRP','KEY','defaultLD')
  JNIUtils.CallMethod(JNiEnv, jLineDisplay, 'open', 'void (String)', [fsLogNameLD], 
false);
end


What does the JNIUtils.CallMethod() code look like that is calling TJNIEnv.CallVoidMethod()?

--
Remy Lebeau (TeamB)
Alessio Pollero

Posts: 11
Registered: 1/27/13
Re: JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 1:34 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Alessio wrote:

The call the raise the exception is definitely Env^.CallVoidMethodA,
the parameter that I'm passing is a System.String, and the operation
I'm doing can be summarised as follows :

Are you sure the JniEnv pointer is valid to begin with?

var
  fsLogNameLD       : string;
begin
  fsLogNameLD := FIni.ReadString('GRP','KEY','defaultLD')
  JNIUtils.CallMethod(JNiEnv, jLineDisplay, 'open', 'void (String)', [fsLogNameLD], 
false);
end


What does the JNIUtils.CallMethod() code look like that is calling TJNIEnv.CallVoidMethod()?

--
Remy Lebeau (TeamB)

The JNIEnv seems valid, since it correctly initialize in this way :

     VM_args.version := JNI_VERSION_1_6;
     VM_args.options := @Options;
     VM_args.nOptions := 1;
     vm_args.ignoreUnrecognized := True;
     // .... Load the Virtual Machine ....
     Errcode := JavaVM.LoadVM(VM_args);
     if Errcode < 0 then fsMsgErrore := ECR_JavaVM_Error;
 
     // .... Caricamento Java Environment ....
     JNIEnv := TJNIEnv.Create(JavaVM.Env);


and that it can create objects correctly, the code of the JNIUtils.CallMethod() it is this one :

{ Call a method of an object/class that returns a primitive/string/void.
  If the method is not found, or its return type is an object, an exception is raised.
  JVM is a reference to the Java environment.
  ClsOrObj is a reference to the class (for static) or object in question.
  MethodName is the name of the method to call.
  MethodSig is the full text of the method's signature.
  Args is an array of items to pass to the method upon invocation.
  Static is True is the method is a static attribute of the class,
         or False (the default) is an ordinary attribute of an object. }
function CallMethod(const JVM: TJNIEnv; const ClsOrObj: JObject;
  const MethodName, MethodSig: UTF8String; const Args: array of const;
  const Static: Boolean): Variant;
var
  Cls: JClass;
  JSig, ReturnType: UTF8String;
  MID: JMethodId;
begin
  if Static then
    // The object is the class
    Cls := ClsOrObj
  else
    // Get the class associated with this object
    Cls := JVM.GetObjectClass(ClsOrObj);
  if Cls = nil then
    raise EJNIError.Create(ClassNotFound);
  // Get the method
  JSig := GetJavaMethodSig(MethodSig);
  if Static then
    MID := JVM.GetStaticMethodID(Cls, MethodName, JSig)
  else
    MID := JVM.GetMethodID(Cls, MethodName, JSig);
  if MID = nil then
    raise EJNIError.Create(Format(MethodNotFound, [MethodName, MethodSig]));
  // Get the value
  ReturnType := Copy(JSig, Pos(')', JSig) + 1, Length(JSig));
  Result     := Null;
  if Static then
    case ReturnType[1] of
      'Z': Result := JVM.CallStaticBooleanMethod(ClsOrObj, MID, Args);  // boolean
      'B': Result := JVM.CallStaticByteMethod(ClsOrObj, MID, Args);     // byte
      'C': Result := JVM.CallStaticCharMethod(ClsOrObj, MID, Args);     // char
      'S': Result := JVM.CallStaticShortMethod(ClsOrObj, MID, Args);    // short
      'I': Result := JVM.CallStaticIntMethod(ClsOrObj, MID, Args);      // int
      'J': Result := JVM.CallStaticLongMethod(ClsOrObj, MID, Args);     // long
      'F': Result := JVM.CallStaticFloatMethod(ClsOrObj, MID, Args);    // float
      'D': Result := JVM.CallStaticDoubleMethod(ClsOrObj, MID, Args);   // double
      'V': JVM.CallStaticVoidMethod(ClsOrObj, MID, Args);
      'L':  // object
        if ReturnType = 'Ljava/lang/String;' then
          Result := JVM.JStringToString(
            JVM.CallStaticObjectMethod(ClsOrObj, MID, Args))
        else
          raise EJNIError.Create(
            Format(NoObjectReturn, [Copy(MethodSig, 1, Pos('(', MethodSig) - 1)]));
      else raise EJNIError.Create(Format(UnknownFieldType, [MethodSig]));
    end
  else
    case ReturnType[1] of
      'Z': Result := JVM.CallBooleanMethod(ClsOrObj, MID, Args); // boolean
      'B': Result := JVM.CallByteMethod(ClsOrObj, MID, Args);    // byte
      'C': Result := JVM.CallCharMethod(ClsOrObj, MID, Args);    // char
      'S': Result := JVM.CallShortMethod(ClsOrObj, MID, Args);   // short
      'I': Result := JVM.CallIntMethod(ClsOrObj, MID, Args);     // int
      'J': Result := JVM.CallLongMethod(ClsOrObj, MID, Args);    // long
      'F': Result := JVM.CallFloatMethod(ClsOrObj, MID, Args);   // float
      'D': Result := JVM.CallDoubleMethod(ClsOrObj, MID, Args);  // double
      'V': JVM.CallVoidMethod(ClsOrObj, MID, Args);
      'L':  // object
        if ReturnType = 'Ljava/lang/String;' then
          Result := JVM.JStringToString(JVM.CallObjectMethod(ClsOrObj, MID, Args))
        else
          raise EJNIError.Create(
            Format(NoObjectReturn, [Copy(MethodSig, 1, Pos('(', MethodSig) - 1)]));
      else raise EJNIError.Create(Format(UnknownFieldType, [MethodSig]));
    end;
end;


Is anything wrong with it ? Why in delphi 7 the same code compiles and works correctly ?
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 2:06 PM   in response to: Alessio Pollero in response to: Alessio Pollero
Alessio wrote:

Is anything wrong with it ? Why in delphi 7 the same code
compiles and works correctly ?

You are going to have to debug the code and find out. Obviously there is
a mismatch somewhere. Double-check memory addresses being accessed, memory
allocations being made, alignments used, etc. Look for any differences.

--
Remy Lebeau (TeamB)
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 2:09 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy wrote:

You are going to have to debug the code and find out.

Also, which platform are you using JNI.pas on? Make sure you are not conflicting
with XE7's own JNI units for its Android support. For instance, Delphi has
its own StringToJString() function that is different than the TJNIEnv.StringToJString()
method.

--
Remy Lebeau (TeamB)
Alessio Pollero

Posts: 11
Registered: 1/27/13
Re: JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 2:13 PM   in response to: Remy Lebeau (Te... in response to: Remy Lebeau (Te...
Remy Lebeau (TeamB) wrote:
Remy wrote:

You are going to have to debug the code and find out.

Also, which platform are you using JNI.pas on? Make sure you are not conflicting
with XE7's own JNI units for its Android support. For instance, Delphi has
its own StringToJString() function that is different than the TJNIEnv.StringToJString()
method.

--
Remy Lebeau (TeamB)

I'm on Windows x86 VCL application, is it possible to have a conflicting function ?

Can you reproduce my problem, or am I the only one with the problem ?

Thanks.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: JNI.pas verC gives access violation on new Delphi XE7
Click to report abuse...   Click to reply to this thread Reply
  Posted: Oct 29, 2014 2:26 PM   in response to: Alessio Pollero in response to: Alessio Pollero
Alessio wrote:

Can you reproduce my problem, or am I the only one with the problem ?

I have not attempted to reproduce it yet.

--
Remy Lebeau (TeamB)
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02