Recent

Author Topic: SIGSEGV after calling a DLL  (Read 503 times)

Mike_HDF

  • New Member
  • *
  • Posts: 15
SIGSEGV after calling a DLL
« on: January 27, 2021, 03:51:46 pm »
Hi, all,

I have a DLL written in C++ with a function defined this way:

void WINAPI GetBaseDirectory(LPSTR AppDir)

I'm trying to use it in a form application but it crash with the SIGSEGV error.

This is the full code of the main unit:

Code: Pascal  [Select][+][-]
  1. unit frmMain;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$calling stdcall}
  5.  
  6. interface
  7.  
  8. uses
  9.   Windows, Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  10.  
  11. type
  12.  
  13.   Tfrm = class(TForm)
  14.     lblDir: TLabel;
  15.     procedure lblDirDblClick(Sender: TObject);
  16.   private
  17.  
  18.   public
  19.  
  20.   end;
  21.  
  22. var
  23.   frm: Tfrm;
  24.  
  25. implementation
  26.  
  27. function GetTheAppDir(var AppDir : LPSTR) : LONG; stdcall; external 'ChkEnv' name 'GetBaseDirectory';
  28.  
  29. {$R *.lfm}
  30.  
  31. { Tfrm }
  32.  
  33. procedure Tfrm.lblDirDblClick(Sender: TObject);
  34. var
  35.   i : LONG;
  36.   s : LPSTR;
  37. begin
  38.   s := PChar('      ');
  39.   i := GetTheAppDir(s);
  40.  
  41.   if i <> 0 then begin
  42.     lblDir.Caption := Trim(String(s));
  43.   end else begin
  44.     lblDir.Caption := 'Program not installed';
  45.   end;
  46. end;
  47.  
  48. end.
  49.  

The error happens in this line:

lblDir.Caption := 'Program not installed';

Wich is really weird, because that means that the DLL call was ok and the problem should be elsewhere but can't see where.

I'm using Lazarus v 2.0.0 with FPC 3.0.4.

Any ideas?

Thank you.

-----------------------------
Edit: I did some tests. If I have this:

Code: Pascal  [Select][+][-]
  1. procedure Tfrm.lblDirDblClick(Sender: TObject);
  2. begin
  3.   lblDir.Caption := 'Program not installed';
  4. end;
  5.  

And comment the uses Windows and function definition, leaving the directive:

{$calling stdcall}

It crash too. If I comment the directive and comment out the function, it crashes again; if I delete stdcall from the function definition it works but still gets an access violation (on closing the form when debugging, after the double click event with the compiled exe).

I thought the function needed the stdcall...

My head explodes!
« Last Edit: January 27, 2021, 05:46:55 pm by Mike_HDF »

jamie

  • Hero Member
  • *****
  • Posts: 4189
Re: SIGSEGV after calling a DLL
« Reply #1 on: January 27, 2021, 07:03:29 pm »
U can't use constant , use a uniquestring for return
The only true wisdom is knowing you know nothing

MarkMLl

  • Hero Member
  • *****
  • Posts: 2073
Re: SIGSEGV after calling a DLL
« Reply #2 on: January 27, 2021, 07:14:48 pm »
Is this really safe? I thought that in the general case FPC couldn't call C++, only C.

MarkMLl
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.

ASerge

  • Hero Member
  • *****
  • Posts: 1737
Re: SIGSEGV after calling a DLL
« Reply #3 on: January 27, 2021, 07:15:48 pm »

void WINAPI GetBaseDirectory(LPSTR AppDir)

Code: Pascal  [Select][+][-]
  1. function GetTheAppDir(var AppDir : LPSTR) : LONG; stdcall; external 'ChkEnv' name 'GetBaseDirectory';
  2.  
The definitions are different. Need
Code: Pascal  [Select][+][-]
  1. procedure GetTheAppDir(AppDir: LPSTR); stdcall; external 'ChkEnv' name 'GetBaseDirectory';

Mike_HDF

  • New Member
  • *
  • Posts: 15
Re: SIGSEGV after calling a DLL
« Reply #4 on: January 27, 2021, 07:32:13 pm »
U can't use constant , use a uniquestring for return

Thanks for the reply.

Not sure about what you are saying (I use Lazarus from time to time for little projects), tried to use UniqueString but got the crash again after the event.

Mike_HDF

  • New Member
  • *
  • Posts: 15
Re: SIGSEGV after calling a DLL
« Reply #5 on: January 27, 2021, 07:42:35 pm »
Is this really safe? I thought that in the general case FPC couldn't call C++, only C.

MarkMLl

Hello, MarkMLI,

Oh, I said it's C++ and problaby I made a mistake since all sources are .c and not .cpp.

Mike_HDF

  • New Member
  • *
  • Posts: 15
Re: SIGSEGV after calling a DLL
« Reply #6 on: January 27, 2021, 08:08:48 pm »

void WINAPI GetBaseDirectory(LPSTR AppDir)

Code: Pascal  [Select][+][-]
  1. function GetTheAppDir(var AppDir : LPSTR) : LONG; stdcall; external 'ChkEnv' name 'GetBaseDirectory';
  2.  
The definitions are different. Need
Code: Pascal  [Select][+][-]
  1. procedure GetTheAppDir(AppDir: LPSTR); stdcall; external 'ChkEnv' name 'GetBaseDirectory';

Oh, thanks, I always make mistakes like that, but doesn't solve my problem: the moment I put stdcall it crashes before firing the double click event.

I tried another function of the DLL and commented the previous one:

Code: Pascal  [Select][+][-]
  1. function SPFileOpen(FileName : LPSTR) : HANDLE; stdcall; external 'ChkEnv' name 'SPFileOpen';
  2.  

I tested using a wrong file path and a correct one and it returns the values expected. But it crashes later and seems related to Strings (for example: ShowMessage('Any string')).

Don't know what to do, I'll try the code in another computer.


440bx

  • Hero Member
  • *****
  • Posts: 2170
Re: SIGSEGV after calling a DLL
« Reply #7 on: January 27, 2021, 08:42:29 pm »
It's not surprising your code doesn't work.  What's surprising is that it doesn't fail much sooner.

First, what @Serge mentioned is really the first step.  Get the definition right.  Any C function that returns "void" is a Pascal procedure not a function.  (though in this particular case, it wouldn't cause an access violation.)

The statement in your code:
Code: Pascal  [Select][+][-]
  1. i := GetTheAppDir(s);
doesn't make any sense because a function that returns void isn't returning a value (function result.)  The variable "i" in your code is getting whatever was left in eax/rax (depending on bitness.)

Second, the statement in your code:
Code: Pascal  [Select][+][-]
  1.  s := PChar('      ');
the variable "s" ends up being a pointer to a constant in a read-only segment (because of $H+)  The reason it doesn't crash there is because the function parameter is declared as "var" this cause a pointer to "s" (which is in a read-write segment) to be passed to the dll function.

Your declaration of "s" should be something like :
Code: Pascal  [Select][+][-]
  1. var s : packed array[0..MAXPATH] of char
and declare the parameter to GetTheAppDir as a pchar (instead of a "var" to an "LPSTR") basically the definition @Serge posted but with "pchar" instead of "LPSTR" (LPSTR is most likely equated to pchar.  "Unhiding" the parameter type is always nice.)

FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

Mike_HDF

  • New Member
  • *
  • Posts: 15
Re: SIGSEGV after calling a DLL
« Reply #8 on: January 27, 2021, 10:30:10 pm »
It's not surprising your code doesn't work.  What's surprising is that it doesn't fail much sooner.

First, what @Serge mentioned is really the first step.  Get the definition right.  Any C function that returns "void" is a Pascal procedure not a function.  (though in this particular case, it wouldn't cause an access violation.)

The statement in your code:
Code: Pascal  [Select][+][-]
  1. i := GetTheAppDir(s);
doesn't make any sense because a function that returns void isn't returning a value (function result.)  The variable "i" in your code is getting whatever was left in eax/rax (depending on bitness.)

Second, the statement in your code:
Code: Pascal  [Select][+][-]
  1.  s := PChar('      ');
the variable "s" ends up being a pointer to a constant in a read-only segment (because of $H+)  The reason it doesn't crash there is because the function parameter is declared as "var" this cause a pointer to "s" (which is in a read-write segment) to be passed to the dll function.

Your declaration of "s" should be something like :
Code: Pascal  [Select][+][-]
  1. var s : packed array[0..MAXPATH] of char
and declare the parameter to GetTheAppDir as a pchar (instead of a "var" to an "LPSTR") basically the definition @Serge posted but with "pchar" instead of "LPSTR" (LPSTR is most likely equated to pchar.  "Unhiding" the parameter type is always nice.)

Thanks, 440bx,

You're right, The packed array declaration and the other things you mention fix the problem. I still have a lot to learn.

jamie

  • Hero Member
  • *****
  • Posts: 4189
Re: SIGSEGV after calling a DLL
« Reply #9 on: January 27, 2021, 10:47:17 pm »
That functions/Procedure obviously isn't structured very well, it should have a length parameter and return the actual length...

but in any case.....

For such brain dead functions..

Var
  S:String;
Begin
 SetLength(S, MaxPath);
 GetBaseFileNameORwhatEver(Pchar(S));
 SetLength(S, Pos(#0, S));

End;

 Now S contains the data/././
The only true wisdom is knowing you know nothing

440bx

  • Hero Member
  • *****
  • Posts: 2170
Re: SIGSEGV after calling a DLL
« Reply #10 on: January 28, 2021, 04:45:29 am »
Thanks, 440bx,

You're right, The packed array declaration and the other things you mention fix the problem. I still have a lot to learn.
You're welcome.  As far as learning, that's often accomplished by making mistakes and asking questions, IOW, you're on the right track. :)
FPC v3.0.4 and Lazarus 1.8.2 on Windows 7 64bit.

PascalDragon

  • Hero Member
  • *****
  • Posts: 2751
  • Compiler Developer
Re: SIGSEGV after calling a DLL
« Reply #11 on: January 28, 2021, 09:09:53 am »
Is this really safe? I thought that in the general case FPC couldn't call C++, only C.

As long as the mangled name as well as the calling convention matches, nothing speaks against calling C++ code. Considering that linking worked (and it would indeed be a C++ library, not a C one) I assume the function is exported as extern "C" which uses a C mangled name. And as long as no class instances are involved C++ simply uses the standard C calling convention by default. In the past I have already successfully worked with static C++ functions by specifying their mangled name in the name-clause.

 

TinyPortal © 2005-2018