Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Closed
mp1609 opened this issue Apr 29, 2024 · 1 comment

Comments

@mp1609
Copy link

mp1609 commented Apr 29, 2024

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?!

@pyscripter
Copy link
Owner

pyscripter commented Apr 29, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants