delphidelphi-10-seattlebpldelphi-10.1-berlin

Dynamically loading BPL fails in LoadLibrary


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:

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.


Solution

  • The exception text indicates that the call to LoadLibrary failed and GetLastError returned ERROR_MOD_NOT_FOUND. There are two common explanations for this:

    1. The package file cannot be found by the DLL search. Perhaps the name was wrong, or the package is not in a location searched by the DLL search algorithm.
    2. The package file can be found, but one of its dependencies cannot be found. For instance, perhaps the rtl package cannot be found.

    Debug the issue using Dependency Walker. Use it in profile mode and it will tell you which module cannot be found.