I would like to load BPL modules dynamically in Delphi 10 Seattle (Update 1) or Delphi 10.1 Berlin project (Enterprise version). But LoadPackage function fails with message (on both 32 and 64 bit target platforms):
Project LoadPackageTest.exe raised exception class EPackageError with message 'Can't load package "real path here"\TestBplPackage.bpl. The specified module could not be found'.
My development platform is Windows 10 Pro 64 bit.
I am sure that the filename passed is correct (it contains the full path). What I have done up to now:
The same project group works without problems (I had to re-create it from scratch) if compiled with Delphi 2007 Enterprise - on the same Win 10 PC
If I load standard .DLL - it is loaded properly and I can call functions in D2007, D10 and D10.1 (works for both 32 and 64 bit targets on D10 and D10.1).
Actually LoadPackage calls SafeLoadLibrary, which calls LoadLibrary (all these procedures are in System.SysUtils.
I compiled the testing executable with and without Run Time packages There is the code:
DLL project (TestDLL.dpr), works in all cases
library TestDLL;
uses SysUtils, Classes;
{$R *.res}
function GetMyTime: TDateTime; stdcall;
begin
Result:= Now;
end;
exports GetMyTime;
end.
BPL Project (TestBplPackage.dpr)
package TestBplPackage;
{ standard compiler directives - the project was created with New->Package}
requires
rtl,
vcl;
contains
TestBPLUnit in 'TestBPLUnit.pas';
end.
unit TestBPLUnit;
interface
function GetMyTime: TDateTime; stdcall;
implementation
uses classes, sysutils;
function GetMyTime: TDateTime;
begin
Result:= Now;
end;
exports GetMyTime;
end.
Testing application - LoadPackageTest.dpr
Form1: TForm contains dOpen: TOpenDialog and a Button1: TButton
type
TMyDateTimeFunction = function: TDateTime; stdcall;
procedure TForm1.Button1Click(Sender: TObject);
var
ext: string;
h: HModule;
func: TMyDateTimeFunction;
begin
if dOpen.Execute then begin
ext:= ExtractFileExt(dOpen.FileName);
if SameText(ext, '.bpl') then begin
h:= LoadPackage(PChar(dOpen.FileName));
if h > 0 then begin
func:= GetProcAddress(h, 'GetMyTime');
if Assigned(func) then
ShowMessage(FormatDatetime('yyyy-mm-dd hh:nn:ss', func));
UnloadPackage(h);
end;
end else if SameText(ext, '.dll') then begin
h:= LoadLibrary(PChar(dOpen.FileName));
if h > 0 then begin
func:= GetProcAddress(h, 'GetMyTime');
if Assigned(func) then
ShowMessage(FormatDatetime('yyyy-mm-dd hh:nn:ss', func));
FreeLibrary(h);
end;
end;
end; //dOpen.execute
end;
Has anybody tried something similar?
Avoid deleting unit from "Contains" node of the Project manager tree - both Delphi 10 and 10.1 crash...
EDIT 1: It works on some conditions
Thanks to the David's answer, I managed to achieve some progress:
it works properly when the contents of the relevant
C:\Program Files (x86)\Embarcadero\Studio\18.0\Redist\
Win32 or Win64 subfolders is either in the folder where the application and the test BPL are or in relevant System32 or SysWOW64 folder.
Without the above, I could not manage to make it working despite the fact that both
C:\Program Files (x86)\Embarcadero\Studio\18.0\bin and
C:\Program Files (x86)\Embarcadero\Studio\18.0\bin64
were in the %PATH% Environment variable. It was not finding the RTL package.
There is an easy to be explained side effect if an application relies on the %PATH% variable to find the necessary BPLs. Because I have C:\Windows\SysWOW64;C:\WINDOWS\system32;C:\WINDOWS
in the %PATH% variable, if I compile for Win32 platform with run time packages, I get the following error message:
The application was unable to start correctly (0xc000007b)
which is because a 32 bit application attempts to load 64 bit BPLs.
I can easily swap the places of System32 and SysWOW64, but this is the global, not the user path variable and requires restart for the changes to take effect.
I will continue to experiment, but up to now the only 100% working solution is to keep the used "standard" BPLs into the platform output folder.