pyscripter/python4delphi

using python4delphi via compiled DLL, avoids reading variables from python to delphi

mp1609 opened this issue · 1 comments

Hi there,

I got a strange issue when using python4delphi as a DLL.
I have a running demo (compiled as "exe" file) which is working:

FYI using the current latest code of this repo.

program ProjectWorking;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Classes,
  System.Variants,
  System.Diagnostics,
  WinApi.Windows,
  IOUtils,
  PythonEngine,
  VarPyth;

{$R *.res}


const
  coPythonFolder = 'python-3.8.3-embed-win32';

type
  TArrayOfDouble = TArray<double>;


var
  PythonEngine : TPythonEngine;
  cmd          : TStringList;

procedure CreatePythonEnvironment;
begin
end;

procedure CreatePyEngine;
begin
  PythonEngine := TPythonEngine.Create(nil);
  PythonEngine.Name := 'PythonEngine';

  with PythonEngine do
  begin
    APIVersion          := 1013;
    AutoLoad            := False;
    AutoUnload          := true;
    AutoFinalize        := true;
    UseLastKnownVersion := false;
    DllName := 'python38.dll';
    DllPath := GetCurrentDir + '\' + coPythonFolder;

    PyFlags := [];
  end;

  PythonEngine.LoadDll;
end;

procedure DestroyEngine;
begin
  PythonEngine.Free;
end;

procedure demo;  stdcall;
var
  foo   : Variant;
var
  taps,
  freq,
  gain : TArrayOfDouble;
begin
  freq := [0, 0.5, 1];
  gain := [0,   1, 0];

  try
    cmd.Clear;
    cmd.Add('N = [0,1,2,3,4,5,6,7,8,9,10]');

    PythonEngine.ExecStrings(cmd);
    foo             := MainModule.N;     // foo = OK, I see data of "N"

  finally

  end;
end;


exports
  demo;

var
  taps : Variant;
begin
  CreatePyEngine;
  cmd := TStringList.Create;
  demo;
end.

So, when running the code, my variable "foo" is readable:
grafik

Now I copy that same code and compile it as DLL:

library ProjectDll;


uses
  System.SysUtils,
  System.Classes,
  System.Zip,
  System.Variants,
  System.Diagnostics,
  WinApi.Windows,
  IOUtils,
  PythonEngine,
  VarPyth;

{$R *.res}


const
  coPythonFolder = 'python-3.8.3-embed-win32';

type
  TArrayOfDouble = TArray<double>;


var
  PythonEngine : TPythonEngine;
  cmd          : TStringList;

procedure CreatePythonEnvironment;
begin
end;

procedure CreatePyEngine;
begin
  PythonEngine := TPythonEngine.Create(nil);
  PythonEngine.Name := 'PythonEngine';

  with PythonEngine do
  begin
    APIVersion          := 1013;
    AutoLoad            := False;
    AutoUnload          := true;
    AutoFinalize        := true;
    UseLastKnownVersion := false;
    DllName := 'python38.dll';
    DllPath := GetCurrentDir + '\' + coPythonFolder;

    PyFlags := [];
  end;

  PythonEngine.LoadDll;
end;

procedure DestroyEngine;
begin
  PythonEngine.Free;
end;

procedure demo;  stdcall;
var
  foo   : Variant;
var
  taps,
  freq,
  gain : TArrayOfDouble;
begin
  freq := [0, 0.5, 1];
  gain := [0,   1, 0];

  try
    cmd.Clear;
    cmd.Add('N = [0,1,2,3,4,5,6,7,8,9,10]');

    PythonEngine.ExecStrings(cmd);
    foo             := MainModule.N;          // foo = is NOT OK - I can not see data of "N"

  finally

  end;
end;


exports
  demo;

var
  taps : Variant;
begin
  CreatePyEngine;
  cmd := TStringList.Create;
end.

To demonstrate how I call the DLL, here is my "DLL caller":

unit UnitDllCaller;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
    dll_demo : procedure;  stdcall;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;
  FDLLHandle : THandle;
implementation

{$R *.dfm}

procedure loadDLLFunction(var APointer : Pointer; AFnName : String; const ARequired : boolean = true);
var
  fnName : PAnsiChar;
begin
  fnName := PAnsiChar(AnsiString(AFnName));

  APointer := GetProcAddress(FDLLHandle, fnName);
end;


procedure TForm1.Button1Click(Sender: TObject);
begin

  FDLLHandle := LoadLibrary(PWchar('ProjectDll.dll'));

  if(FDLLHandle > 0) then
  begin
    loadDLLFunction(@dll_demo , 'demo');
    dll_demo();
  end;
end;

end.

So running DLL caller, is working, but my "foo" variable causes an exception:
grafik

Here is also the Delphi project to reproduce this:
demo.zip

Anyone got an idea?
The python code was always executed without errors.
I tried to debug a little bit more:

    cmd.Clear;
    cmd.Add('N = [0,1,2,3,4,5,6,7,8,9,10]');
    cmd.Add('f = open("demofile2.txt", "w")');
    cmd.Add('f.write(str(N))');
    cmd.Add('f.close()');

The written demofile2.txt looks as expected, so it seems that reading the variable back causes an error?!

Actually it does work fine despite inspection of foo not working. See below:
image