Pull to refresh

JNI Получение и Подключение к JVM в Delphi

Reading time3 min
Views2.7K
Всем доброго времени суток! Сегодня разберем пример как получить загруженную JVM и подключиться к ней. Нужно нам это для того, чтобы выполнить внутри JVM некий код.Итак, приступим:

Создаем новый проект DLL. Добавим Process Attach:

procedure DllMain(dwReason: LongWord);
begin
  case dwReason of
  DLL_PROCESS_ATTACH:
    begin
       //**************************
    end;
  DLL_PROCESS_DETACH:
    begin
      //***************************
    end;
  end;
end;

begin
  DllProc := @DllMain;
  DllProc(DLL_PROCESS_ATTACH);
end.  

Отлично, добавили. Далее нас потребуется в Uses добавить компонент JNI:

uses
  System.SysUtils,
  System.Classes,
  windows,
  JNI;

А теперь давайте реализуем поиск и подключение к JVM. Для этого в DllMain добавим переменные:

var
  I: Integer;
  JVMArray: array of PJavaVM;
  NumberOfVMs: JSize;
  JNIEnv: PJNIEnv;
  GetCreatedJavaVMs: TJNI_GetCreatedJavaVMs;
const
  BufferSize = 128; 

Далее в DLL_PROCESS_ATTACH реализуем поиск и подключение загруженной JVM

begin
      try
        GetCreatedJavaVMs := GetProcAddress(GetModuleHandle('jvm.dll'), 'JNI_GetCreatedJavaVMs');
        SetLength(JVMArray, BufferSize);
        GetCreatedJavaVMs(@JVMArray[0], BufferSize, @NumberOfVMs);
      except
      Exit;
      end;
    if NumberOfVMs > 0 then
      begin
        for I := 0 to NumberOfVMs - 1 do
          begin
            JVMArray[I]^.GetEnv(JVMArray[I], @JNIEnv, JNI_VERSION_1_8);
            JVMArray[I]^.AttachCurrentThread(JVMArray[I], @JNIEnv, Nil);
          end;
      end
      else
      begin
        Exit;
      end;

Что же тут происходит. Для начала нам нужно получить адрес функции JNI_GetCreatedJavaVMs из jvm.dll. Затем установим длину буфера. Затем используем функцию GetCreatedJavaVMs для получения всех загруженных JVM. Ну а дальше просто отсев в буфере пока не останется именно та загруженная JVM и подключаемся к ней AttachCurrentThread.

Итак мы нашли и подключились к загруженной JVM. Теперь можно использовать любой код внутри JVM после строчки AttachCurrentThread. И в итоге мы получаем код DLL:

library Project1;

uses
  System.SysUtils,
  System.Classes,
  windows,
  JNI;

procedure DllMain(dwReason: LongWord);
var
  I: Integer;
  JVMArray: array of PJavaVM;
  NumberOfVMs: JSize;
  JNIEnv: PJNIEnv;
  GetCreatedJavaVMs: TJNI_GetCreatedJavaVMs;
const
  BufferSize = 256;
begin
  case dwReason of
  DLL_PROCESS_ATTACH:
    begin
      try
        GetCreatedJavaVMs := GetProcAddress(GetModuleHandle('jvm.dll'), 'JNI_GetCreatedJavaVMs');
        SetLength(JVMArray, BufferSize);
        GetCreatedJavaVMs(@JVMArray[0], BufferSize, @NumberOfVMs);
      except
      Exit;
      end;
    if NumberOfVMs > 0 then
      begin
        for I := 0 to NumberOfVMs - 1 do
          begin
            JVMArray[I]^.GetEnv(JVMArray[I], @JNIEnv, JNI_VERSION_1_8);
            JVMArray[I]^.AttachCurrentThread(JVMArray[I], @JNIEnv, Nil);
          end;
      end
      else
      begin
        Exit;
      end;
    end;
  DLL_PROCESS_DETACH:
    begin
      Exit;
    end;
  end;
end;

begin
  DllProc := @DllMain;
  DllProc(DLL_PROCESS_ATTACH);
end.

Давайте приведем пример как это использовать. Допустим, у вас есть некая функция, которую вы хотите использовать в Java.

procedure Com(JNIEnv: PJNIEnv);
var
JC: JClass;
JM: JMethodID;
Begin
JC:=jnienv^.FindClass(JNIEnv, 'ru/er_log/components/Frame');
JM:=jnienv^.GetMethodID(jnienv, jc, 'login', 'Ljavax/swing/JTextField;');
jnienv^.CallObjectMethod(jnienv, jc, jm);
End;

Это простое обращение к Методу в JNI и чтоб его использовать достаточно поместить эту процедуру после AttachCurrentThread.

 begin
            JVMArray[I]^.GetEnv(JVMArray[I], @JNIEnv, JNI_VERSION_1_8);
            JVMArray[I]^.AttachCurrentThread(JVMArray[I], @JNIEnv, Nil);
            Com(JNIEnv);
          end;

В итоге мы получаем подключение к JVM и выполнение в ней некой процедуры.
Tags:
Hubs:
Total votes 8: ↑5 and ↓3+2
Comments2

Articles