Recent

Author Topic: [Solved] getprocaddress  (Read 8505 times)

RednosePete

  • New Member
  • *
  • Posts: 17
[Solved] getprocaddress
« on: February 23, 2018, 11:42:56 am »
I try to use a procedure from dylib (coded in Fortran and compiled with ifort) in a 64-bit console app. I can make it work when a compile a test program with ppcx64; dylib is succesfully loaded and GetProcAddress find the procedure. However, when I embed the same code in Lazarus-project and build it with lazbuild, dylib is loaded but GetProcAddress returns nil.

var
 Handle1 : TLibHandle;
 MyProcedure : TMyProcedure;
 ptr1 : Pointer;
begin
  Handle1 := TLoadLibrary (PChar('libTest.dylib'));
  if Handle1 <> nil then
  begin
   ptr1 := GetProcAddress(Handle1,PChar('functioname'));
   if ptr1 <> nil then
    writeln('Function address found');
  else
    writeln('Failed ...');
  end;
end;

Any ideas? Are there some compiler options either on ppcx64 or Lazarus that should take note on? Should I drop lazbuild and build the project with ppcx64?
« Last Edit: February 25, 2018, 01:05:43 pm by RednosePete »

Phemtik

  • New Member
  • *
  • Posts: 19
Re: getprocaddress
« Reply #1 on: February 23, 2018, 12:00:46 pm »
Why you use a PChar cast  for LoadLibary and GetProcAdress?

Both functions want a normal String.
Intel i7-3610QM
Fedora 28

RednosePete

  • New Member
  • *
  • Posts: 17
Re: getprocaddress
« Reply #2 on: February 23, 2018, 12:20:23 pm »
Why you use a PChar cast  for LoadLibary and GetProcAdress?

Both functions want a normal String.

Ok,

thanks. I removed PChar and the same result; ppcx64  produces app that works, Lazarus (via lazbuild) not.

rvk

  • Hero Member
  • *****
  • Posts: 6110
Re: getprocaddress
« Reply #3 on: February 23, 2018, 12:27:10 pm »
I see much more problems with that code.
Where is the end for the begin/end block when Handle1 <> nil.
When I try to compile this... where (and what) is TLoadLibrary?
TLibHandle is a DWord (for me) so you can't check that against nil (which is a pointer). Check it against nilhandle.
Is "functioname" the real functioname? (note absence of n)

Is this better?
Code: Pascal  [Select][+][-]
  1. var
  2.   Handle1: TLibHandle;
  3.   ptr1: Pointer;
  4. begin
  5.   Handle1 := LoadLibrary('libTest.dylib');
  6.   if Handle1 <> nilhandle then
  7.   begin
  8.     ptr1 := GetProcAddress(Handle1, 'functioname');
  9.     if ptr1 <> nil then
  10.       writeln('Function address found');
  11.   end
  12.   else
  13.   begin
  14.     writeln('Failed ...');
  15.   end;
  16. end;


PLEASE DON'T POST JUST TYPED TEXT. Use real code you have in your project !!
One typo is easily made and we can't see your real code.
(and use the #-code button to mark code)
« Last Edit: February 23, 2018, 12:30:30 pm by rvk »

RednosePete

  • New Member
  • *
  • Posts: 17
Re: getprocaddress
« Reply #4 on: February 23, 2018, 02:14:01 pm »
PLEASE DON'T POST JUST TYPED TEXT.
Use real code you have in your project !!
(and use the #-code button to mark code)

Sorry about my newbie mistakes, and yes, your version of code looks better (lesson learned   :-[).
As you already concluded, I didn't copy-paste the actual code, hence the typos. The original code in my Lazarus-project is a bit longish.

The dylib my app loads is written in Fortran and compiled for 64-bit OSX. I can use the function successfully when compiling a simple test program with ppcx64 or when using early (static) binding in Lazarus. The problem is GetProcAddress when compiled via Lazarus target OS being "Default" and target CPU being x86_64. In my case, I use lazbuild with , but that shouldn't make any difference or does it?

So far, I've compared a large Lazarus project vs. a simple ppcx64 compiled console application. The former is built with:
lazbuild -B MApp.lpi --ws=cocoa --cpu=x86_64 --os=darwin --compiler=/usr/local/lib/fpc/3.0.4/ppcx64
and latter:
ppcx64 -MDelphi veri2.pas

My next step is testing GeProcAddress with a simple Lazarus-console application.

I suppose that calling convention shouldn't be an issue in 64-bit applications?
« Last Edit: February 23, 2018, 04:21:56 pm by RednosePete »

rvk

  • Hero Member
  • *****
  • Posts: 6110
Re: getprocaddress
« Reply #5 on: February 23, 2018, 02:25:23 pm »
As you already concluded, I didn't copy-paste the actual code, hence the typos. The original code in my Lazarus-project is a bit longish.
...
My next step is testing GeProcAddress with a simple Lazarus-console application.
Yes, when you run into something it would be best to create a small test-project where you try to accomplish the same. If that fails you can copy paste the correct compilable code so there are no possible typos (which we will otherwise find and think that's the issue).

ppcx64 -MDelphi veri2.pas
If you switch to Delphi mode you should also put Delphi mode in your lazbuild.
Better is to add the Delphi mode in your sources. So add something like this at the top
Code: Pascal  [Select][+][-]
  1. {$IFDEF FPC}  
  2.   {$MODE DELPHI}  
  3. {$ENDIF FPC}

RednosePete

  • New Member
  • *
  • Posts: 17
Re: getprocaddress
« Reply #6 on: February 23, 2018, 04:21:10 pm »
If you switch to Delphi mode you should also put Delphi mode in your lazbuild.
Better is to add the Delphi mode in your sources. So add something like this at the top
Code: Pascal  [Select][+][-]
  1. {$IFDEF FPC}  
  2.   {$MODE DELPHI}  
  3. {$ENDIF FPC}

Yup, that I had already in my Lazarus-project.
It is nice to have advice on those desperate moments when you have lost the path; thanks ... I'll be back after experimenting with a simple Lazarus app.

RednosePete

  • New Member
  • *
  • Posts: 17
Re: getprocaddress
« Reply #7 on: February 24, 2018, 05:58:22 pm »
This works when command-line compiling with ppcx64, but as a Lazarus app, it won't find the dylib.
Code: Pascal  [Select][+][-]
  1. procedure TMyApplication.DoRun;
  2. var
  3.   ErrorMsg: String;
  4.    prm1                    : single;
  5.    ptr1                    : Pointer;
  6.    Handle1                 : TLibHandle;
  7.    GetFigm                 : TGetFigm;
  8.    FIGM                    : string = 'libvol.dylib';
  9.    MYFUN                   : string = 'figm';
  10. begin
  11.      writeln('Trying to load');
  12.      Handle1 := LoadLibrary(FIGM);
  13.      if Handle1 <> nilhandle then
  14.      begin
  15.         writeln('handle 1 ok, looking after ',MYFUN);
  16.         ptr1 := GetProcAddress(Handle1, MYFUN);
  17.         if ptr1 <> nil then
  18.            writeln(MYFUN,' found')
  19.         else
  20.           writeln(MYFUN,' not found');
  21.      end
  22.      else
  23.          writeln(FIGM,' not loaded');
  24.   Terminate;
  25. end;

My newbie difficulties may arise not from the code itself but my incomplete understanding of search paths when loading libraries run-time. During my previous attempts, the Lazarus project loaded a different (and faulty) version of dylib while command-line compiled version used the current directory as default path when loading dylib with dynlibs.LoadLibrary. Now that I do have only one (and correct) version of the dylib, and it is located in the same folder with the executable, Lazarus app can't find the dylib at all;
Code: [Select]
Trying to load
libvol.dylib not loaded
And the same code compiled with ppcx64 results;
Code: [Select]
Trying to load
handle 1 ok, looking after figm
figm found

How do you define the location where Lazarus application is expecting to find a dylib when late-binding? In my case the test program is a simple console application that I run in a terminal. Are there some project options? What is the default location in Lazarus app? Sorry for beginner questions; my only excuse is that I am a beginner at OSX.

Phil

  • Hero Member
  • *****
  • Posts: 2737
Re: getprocaddress
« Reply #8 on: February 24, 2018, 06:06:47 pm »
How do you define the location where Lazarus application is expecting to find a dylib when late-binding? In my case the test program is a simple console application that I run in a terminal. Are there some project options? What is the default location in Lazarus app? Sorry for beginner questions; my only excuse is that I am a beginner at OSX.

Per FPC documentation, be sure to only pass a complete path to LoadLibrary. In your example code, you're only passing the file name of the library.

https://www.freepascal.org/docs-html/rtl/dynlibs/loadlibrary.html

In an app bundle, libraries are placed in the Frameworks folder. You can get this location with:

NSBundle.mainBundle.sharedFrameworksURL.UTF8String

With a console app, if the .dylib is in the same folder as the executable, use ParamStr(0) and extract the path.


Edit: oops, sharedFrameworksPath, not sharedFrameworksURL.

More here:

https://developer.apple.com/documentation/foundation/nsbundle?language=objc
« Last Edit: February 24, 2018, 06:11:52 pm by Phil »

RednosePete

  • New Member
  • *
  • Posts: 17
Re: getprocaddress
« Reply #9 on: February 24, 2018, 07:22:22 pm »
I tried both './libvol.dylib' and the full path given by pwd in terminal. Neither did help, Lazarus-built app won't find the dylib while compiling with ppcx64 (no command line options!) results as a working application. It must be some option in Lazarus project settings? I suppose that the compiler is in both cases (with or without Lazarus) actually the same.

Phil

  • Hero Member
  • *****
  • Posts: 2737
Re: getprocaddress
« Reply #10 on: February 24, 2018, 07:28:49 pm »
I tried both './libvol.dylib' and the full path given by pwd in terminal. Neither did help, Lazarus-built app won't find the dylib while compiling with ppcx64 (no command line options!) results as a working application. It must be some option in Lazarus project settings? I suppose that the compiler is in both cases (with or without Lazarus) actually the same.

This has nothing to do with Lazarus per se since Lazarus has no knowledge of or support for the macOS .app bundle structure, other than creating an incorrect .app bundle the first time you run the app (symlink pointing to executable outside of bundle - wrong, bad idea, design error, something not done by any other IDE on Earth).

You're doing something else wrong. Post a simple example (project source files, etc.).

RednosePete

  • New Member
  • *
  • Posts: 17
Re: getprocaddress
« Reply #11 on: February 24, 2018, 07:52:12 pm »
Project1.pas
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.    dateutils,dynlibs,
  7.   {$IFDEF UNIX}{$IFDEF UseCThreads}
  8.   cthreads,
  9.   {$ENDIF}{$ENDIF}
  10.   Classes, SysUtils, CustApp
  11.  
  12. type
  13.  
  14.   { TMyApplication }
  15.  
  16.   TMyApplication = class(TCustomApplication)
  17.   protected
  18.     procedure DoRun; override;
  19.   public
  20.     constructor Create(TheOwner: TComponent); override;
  21.     destructor Destroy; override;
  22.   end;
  23.  
  24. TGetFigm = function(var inval:single):single; stdcall;
  25.  
  26. { TMyApplication }
  27.  
  28. procedure TMyApplication.DoRun;
  29. var
  30.   ErrorMsg: String;
  31.    prm1                    : single;
  32.    ptr1                    : Pointer;
  33.    Handle1                 : TLibHandle;
  34.    GetFigm                 : TGetFigm;
  35.    FIGM                    : string = 'libvol.dylib';
  36.    MYFUN                   : string = 'figm';
  37. begin
  38.      writeln('Trying to load');
  39.      writeln(ExtractFilePath(ParamStr(0)));
  40.      Handle1 := LoadLibrary(FIGM);
  41.      if Handle1 <> nilhandle then
  42.      begin
  43.         writeln('handle 1 ok, looking after ',MYFUN);
  44.         ptr1 := GetProcAddress(Handle1, MYFUN);
  45.         if ptr1 <> nil then
  46.            writeln(MYFUN,' found')
  47.         else
  48.           writeln(MYFUN,' not found');
  49.      end
  50.      else
  51.          writeln(FIGM,' not loaded');
  52.   Terminate;
  53. end;
  54.  
  55. constructor TMyApplication.Create(TheOwner: TComponent);
  56. begin
  57.   inherited Create(TheOwner);
  58.   StopOnException:=True;
  59. end;
  60.  
  61. destructor TMyApplication.Destroy;
  62. begin
  63.   inherited Destroy;
  64. end;
  65.  
  66.  
  67. var
  68.   Application: TMyApplication;
  69. begin
  70.   Application:=TMyApplication.Create(nil);
  71.   Application.Title:='My Application';
  72.   Application.Run;
  73.   Application.Free;
  74. end.
  75.  

Project1.lpi
Code: XML  [Select][+][-]
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <CONFIG>
  3.   <ProjectOptions>
  4.     <Version Value="10"/>
  5.     <General>
  6.       <Flags>
  7.         <MainUnitHasCreateFormStatements Value="False"/>
  8.         <MainUnitHasScaledStatement Value="False"/>
  9.       </Flags>
  10.       <SessionStorage Value="InProjectDir"/>
  11.       <MainUnit Value="0"/>
  12.       <Title Value="My Application"/>
  13.       <UseAppBundle Value="False"/>
  14.       <ResourceType Value="res"/>
  15.     </General>
  16.     <BuildModes Count="1">
  17.       <Item1 Name="Default" Default="True"/>
  18.     </BuildModes>
  19.     <PublishOptions>
  20.       <Version Value="2"/>
  21.       <DestinationDirectory Value="/Users/myusername/Documents/TestCodes/LTest/Test"/>
  22.     </PublishOptions>
  23.     <RunParams>
  24.       <local>
  25.         <FormatVersion Value="1"/>
  26.       </local>
  27.     </RunParams>
  28.     <Units Count="1">
  29.       <Unit0>
  30.         <Filename Value="Project1.pas"/>
  31.         <IsPartOfProject Value="True"/>
  32.       </Unit0>
  33.     </Units>
  34.   </ProjectOptions>
  35.   <CompilerOptions>
  36.     <Version Value="11"/>
  37.     <Target>
  38.       <Filename Value="Project1"/>
  39.     </Target>
  40.     <SearchPaths>
  41.       <IncludeFiles Value="$(ProjOutDir)"/>
  42.       <Libraries Value="."/>
  43.       <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/>
  44.     </SearchPaths>
  45.     <CodeGeneration>
  46.       <TargetCPU Value="x86_64"/>
  47.     </CodeGeneration>
  48.   </CompilerOptions>
  49.   <Debugging>
  50.     <Exceptions Count="3">
  51.       <Item1>
  52.         <Name Value="EAbort"/>
  53.       </Item1>
  54.       <Item2>
  55.         <Name Value="ECodetoolError"/>
  56.       </Item2>
  57.       <Item3>
  58.         <Name Value="EFOpenError"/>
  59.       </Item3>
  60.     </Exceptions>
  61.   </Debugging>
  62. </CONFIG>
  63.  

Two binaries, the first (finds dylib) compiled by:
ppcx64 Project1.pas
the second (dylib not found):
lazbuild Project1.lpi
libvol.dylib is in the same folder where Project1.pas, Project1.lpi and the respective executable Project1 are.
Haven't tried Application bundle.
« Last Edit: February 24, 2018, 07:56:07 pm by RednosePete »

rvk

  • Hero Member
  • *****
  • Posts: 6110
Re: getprocaddress
« Reply #12 on: February 24, 2018, 07:59:43 pm »
Really strange.
Are both executables the same size?

(You did not give a complete path but I guess if it's not needed for one, it's also not need for the other)

Phil

  • Hero Member
  • *****
  • Posts: 2737
Re: getprocaddress
« Reply #13 on: February 24, 2018, 08:10:01 pm »
Haven't tried Application bundle.

Just tested with the libndfd.dylib library that can be built from this example:

https://macpgmr.github.io/MacXPlatform/PascalDynLibs_2.html

Created Frameworks folder in test app's bundle and copied libndfd.dylib to it.

Here's my complete code:

Code: Pascal  [Select][+][-]
  1. type TNdfdGetLibVersion = procedure(VerBuf    : PAnsiChar;
  2.                                     VerBufLen : cuint32); cdecl;
  3.  
  4. procedure TForm1.Button1Click(Sender: TObject);
  5. var
  6.   NdfdHandle : TLibHandle;
  7.   VersAddr   : TNdfdGetLibVersion;
  8.   Buffer     : array [0..100] of AnsiChar;
  9. begin
  10.   NdfdHandle := LoadLibrary(NSBundle.MainBundle.privateFrameworksPath.UTF8String +
  11.                             '/libndfd.dylib');
  12.   if NdfdHandle = 0 then
  13.     begin
  14.     WriteLn('Unable to load lib');
  15.     Exit;
  16.     end;
  17.   VersAddr := TNdfdGetLibVersion(GetProcAddress(NdfdHandle, 'NdfdGetLibVersion'));
  18.   VersAddr(Buffer, SizeOf(Buffer));
  19.   WriteLn(string(Buffer));
  20. end;
  21.  

When run in debugger (lldb) it prints "1.0.4" to console.


RednosePete

  • New Member
  • *
  • Posts: 17
Re: getprocaddress
« Reply #14 on: February 24, 2018, 08:13:06 pm »
Really strange.
Are both executables the same size?

(You did not give a complete path but I guess if it's not needed for one, it's also not need for the other)

The executables are of different size, ppcx64 produces a slightly bigger application:
the first one: 2021620 Feb 24 21:07 Project1
the second one: 1976020 Feb 24 21:08 Project1
Building from Lazarus IDE gives the same size of executable as lazbuild, so at least there is some logic ...

 

TinyPortal © 2005-2018