Recent

Author Topic: Problems with dynamic DLL binding.  (Read 12093 times)

CM630

  • Hero Member
  • *****
  • Posts: 1641
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Problems with dynamic DLL binding.
« on: February 10, 2015, 03:40:11 pm »
I have problems with a dynamically linked DLL.

Static bindings are:
Code: [Select]
function DAQmxGetTaskNumChans(taskHandle:Longint; data:PLongint):Longint; stdcall;
Dynamic bindings are:

Code: [Select]
type
  tDAQmxGetTaskNumChans=function (taskHandle:Longint; data:PLongint):Longint; stdcall;

var
  DAQmxSetDIDigFltrMinPulseWidth:tDAQmxSetDIDigFltrMinPulseWidth;

implementation

function LoadNIDAQMXLib: Boolean; //True if successful
begin
   if DllHandle = 0 then DllHandle := LoadLibrary('nicaiu.dll');
   if DLLHandle <> NilHandle then
   begin                   
      DAQmxGetTaskNumChans:=tDAQmxGetTaskNumChans(GetProcedureAddress (DLLHandle, 'DAQmxGetTaskNumChans'));...

end;
 


Here is the program (procedure), which runs okay with static bindings:

Code: [Select]
procedure TForm1.Button1Click(Sender: TObject);
var
  DAQmxError: integer;
  ChannelCount: integer;
  SampsPerChannel: integer=5000;
  RefreshRate: Integer= 5000;
  SamplesCount: integer;
  SampsPerChanRead: LongInt;
  i,j,k: integer;
  LineSeries: array of TLineSeries;
  Timeout: Double;
  TriggerTimeout:Double =30; //seconds
  scount: integer;
begin
   TimeOut:=(SampsPerChanRead/RefreshRate)+TriggerTimeout;
   DAQmxError:= DAQmxCreateTask ('',@TaskHandle);
   if DAQmxError <> 0 then begin Label1.Caption := 'Error №' + IntToStr (DAQmxError); exit; end else Label1.Caption:= 'No error';
   DAQmxError:= DAQmxCreateAIVoltageChan (TaskHandle, 'Dev1/ai4:7','',DAQmx_Val_InputTermCfg_NRSE, -10,10,DAQmx_Val_VoltageUnits1_Volts,'');
   if DAQmxError <> 0 then begin Label1.Caption := 'Error №' + IntToStr (DAQmxError); exit; end else Label1.Caption:= 'No error';
   DAQmxError:=DAQmxCfgSampClkTiming(taskHandle, 'OnboardClock', RefreshRate, DAQmx_Val_Rising, DAQmx_Val_AcquisitionType_FiniteSamps, SampsPerChannel);
   if DAQmxError <> 0 then begin Label1.Caption := 'Error №' + IntToStr (DAQmxError); exit; end else Label1.Caption:= 'No error';
   DAQmxError:= DAQmxGetTaskNumChans(taskHandle, @ChannelCount);
   if DAQmxError <> 0 then begin Label1.Caption := 'Error №' + IntToStr (DAQmxError); exit; end else Label1.Caption:= 'No error';
   SamplesCount:= SampsPerChannel * ChannelCount ;
   SetLength(DAQData,SamplesCount);

...
end;


Running the app with the dynamically bound libs causes a SIGSEGV exception.
If I comment SetLength(DAQData,SamplesCount); no exception occurs.
So, hopefully s.o. will show me the mistake?
I guess soemethings goes wrong with
« Last Edit: February 10, 2015, 03:42:45 pm by CM630 »
Лазар 4,4 32 bit (sometimes 64 bit); FPC3,2,2

Laksen

  • Hero Member
  • *****
  • Posts: 802
    • J-Software
Re: Problems with dynamic DLL binding.
« Reply #1 on: February 10, 2015, 04:07:47 pm »
It probably means that your problem is elsewhere. How are you using daqdata?

Fred vS

  • Hero Member
  • *****
  • Posts: 3804
    • StrumPract is the musicians best friend
Re: Problems with dynamic DLL binding.
« Reply #2 on: February 10, 2015, 04:16:33 pm »
Hello.

Quote
DllHandle := LoadLibrary('nicaiu.dll');
Have you try with the complete relative path of nicaiu.dll ?
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

CM630

  • Hero Member
  • *****
  • Posts: 1641
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Problems with dynamic DLL binding.
« Reply #3 on: February 10, 2015, 06:44:37 pm »
Hello.

Quote
DllHandle := LoadLibrary('nicaiu.dll');
Have you try with the complete relative path of nicaiu.dll ?
Daqdata is daqdata: array of double;
I have removed all code using it, and left only SetLength(DAQData,SamplesCount);

 What I should have mentioned is that the exception is generated after the end of the procedure.
I had some code handling the data from  daqdata, I will uncomment it and try to send output to a log file, to check if data is acquired and processed properly.
 If I make SetLength(DAQData,20000); no exception is generated (with the code handling the acquired data being commented).

Quote
DllHandle := LoadLibrary('nicaiu.dll');
Have you try with the complete relative path of nicaiu.dll ?

No, but should not I have problems while loading the DLL?  I will try, when possible.
« Last Edit: February 10, 2015, 06:46:10 pm by CM630 »
Лазар 4,4 32 bit (sometimes 64 bit); FPC3,2,2

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Problems with dynamic DLL binding.
« Reply #4 on: February 10, 2015, 07:41:34 pm »
What I should have mentioned is that the exception is generated after the end of the procedure.

This is a strong indication that the stack got corrupted. Most likely due to using the wrong calling convention for one or more of the imported functions. Check stdcall/cdecl of each used proc, or cancel them one by one and see which one to accuse.

Edit:
Are you using this binding? Strangely enough it uses cdecl calling convention for DAQmxLoadTask, while for DAQmxCreateAIVoltageChan it uses stdcall.
« Last Edit: February 10, 2015, 08:00:19 pm by engkin »

CM630

  • Hero Member
  • *****
  • Posts: 1641
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Problems with dynamic DLL binding.
« Reply #5 on: February 10, 2015, 08:00:46 pm »
What I should have mentioned is that the exception is generated after the end of the procedure.

Most likely due to using the wrong calling convention for one or more of the imported functions. Check stdcall/cdecl of each used proc, or cancel them one by one and see which one to accuse.
So I should start changing all stdcall-s to cdecl until the apps start working?
Лазар 4,4 32 bit (sometimes 64 bit); FPC3,2,2

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Problems with dynamic DLL binding.
« Reply #6 on: February 10, 2015, 08:13:18 pm »
Do one call at a time. If you did not receive an exception/error then the calling convention is most likely correct, other wise change it. Starting with DAQmxCreateTask:
Code: [Select]
function TForm1.TestOneProcButton(Sender: TObject);
var
  DAQmxError: integer;
  ChannelCount: integer;
  SampsPerChannel: integer=5000;
  RefreshRate: Integer= 5000;
  SamplesCount: integer;
  SampsPerChanRead: LongInt;
  i,j,k: integer;
  LineSeries: array of TLineSeries;
  Timeout: Double;
  TriggerTimeout:Double =30; //seconds
  scount: integer;
begin
   TimeOut:=(SampsPerChanRead/RefreshRate)+TriggerTimeout;
   DAQmxError:= DAQmxCreateTask ('',@TaskHandle);
   if DAQmxError <> 0 then begin Label1.Caption := 'Error №' + IntToStr (DAQmxError); exit; end else Label1.Caption:= 'No error';
end;

More accurate way is to use an assembly level debugger like OllyDbg and keep track of the stack after each call and see if it returns to its position before the call (and before parameter push[es])

Easier way is to post the c headers and pascal bindings and let other people check.

Fred vS

  • Hero Member
  • *****
  • Posts: 3804
    • StrumPract is the musicians best friend
Re: Problems with dynamic DLL binding.
« Reply #7 on: February 10, 2015, 08:17:30 pm »
Quote
Dynamic bindings are:

Code: [Select]
type
  tDAQmxGetTaskNumChans=function (taskHandle:Longint; data:PLongint):Longint; stdcall;

var
  DAQmxSetDIDigFltrMinPulseWidth:tDAQmxSetDIDigFltrMinPulseWidth;

implementation

function LoadNIDAQMXLib: Boolean; //True if successful
begin
   if DllHandle = 0 then DllHandle := LoadLibrary('nicaiu.dll');
   if DLLHandle <> NilHandle then
   begin                   
      DAQmxGetTaskNumChans:=tDAQmxGetTaskNumChans(GetProcedureAddress (DLLHandle, 'DAQmxGetTaskNumChans'));...

end;
 

Hum... all what engkin noted + why do you use type tDAQmxGetTaskNumChans ?
Why not simpler this ?  =>
Code: [Select]
uses
...,
 DynLibs.pas,...
...
var
DAQmxGetTaskNumChans: function (taskHandle:Longint; data:PLongint):Longint() ; stdcall ; // or try => cdecl;
...
implementation
...
function LoadNIDAQMXLib: Boolean; //True if successful
begin
   if DllHandle = 0 then DllHandle := LoadLibrary('/full_path_of/nicaiu.dll');
   if DLLHandle <> NilHandle then
   begin   
   Pointer(DAQmxGetTaskNumChans):= GetProcedureAddress(DLLHandle, 'DAQmxGetTaskNumChans');               
   end;
end;
...
« Last Edit: February 10, 2015, 08:24:00 pm by Fred vS »
I use Lazarus 2.2.0 32/64 and FPC 3.2.2 32/64 on Debian 11 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt.

https://github.com/fredvs
https://gitlab.com/fredvs
https://codeberg.org/fredvs

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Problems with dynamic DLL binding.
« Reply #8 on: February 10, 2015, 09:30:39 pm »
Just occurred to me that we can distinguish between cdecl or stdcall by comparing the stack pointer before and after the call.

Get two variables:
Code: [Select]
  var
    before, after: PtrUInt;

Save the stack pointer before you call a proc:
Code: [Select]
    asm
        MOV before, ESP
    end;

Do call one of the procedures:
Code: [Select]
  DAQmxError:= DAQmxCreateTask ('',@TaskHandle);

Save the stack pointer after you called the proc:
Code: [Select]
    asm
        MOV after, ESP
    end;

If after>before then you need to change it from cdecl to stdcall
If before>after then you need to change it from stdcall to cdecl:
Code: [Select]
  procedure CheckResults(vBefore, vAfter: PtrUInt);
  begin
    if vAfter > vBefore then
      writeLn('Change to stdcall')
    else if vBefore > vAfter then
      writeLn('Change to cdecl')
    else
      WriteLn('Correct');
  end;   

CM630

  • Hero Member
  • *****
  • Posts: 1641
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Problems with dynamic DLL binding.
« Reply #9 on: February 11, 2015, 05:47:12 pm »
Tomorrow I could try your ideas and post more sources, but shall I understand, that stdcall (or cdecl) might be fine with static binding, but not be okay with dynamic?
Лазар 4,4 32 bit (sometimes 64 bit); FPC3,2,2

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Problems with dynamic DLL binding.
« Reply #10 on: February 11, 2015, 06:20:48 pm »
... shall I understand, that stdcall (or cdecl) might be fine with static binding, but not be okay with dynamic?
No, static and dynamic binding should have the same calling convention.
« Last Edit: February 11, 2015, 06:34:36 pm by engkin »

rasberryrabbit

  • Full Member
  • ***
  • Posts: 150
Re: Problems with dynamic DLL binding.
« Reply #11 on: February 12, 2015, 12:17:07 am »
Is DLL 32bit or 64bit?
Code is long, Life is short, AI is not your enemy.

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Problems with dynamic DLL binding.
« Reply #12 on: February 12, 2015, 01:32:32 am »
Is DLL 32bit or 64bit?

32bit based on his signature:
Quote
Lazarus 1,2,6;XP;32bit;FPC2,6,4;rev 46529

CM630

  • Hero Member
  • *****
  • Posts: 1641
  • Не съм сигурен, че те разбирам.
    • http://sourceforge.net/u/cm630/profile/
Re: Problems with dynamic DLL binding.
« Reply #13 on: February 12, 2015, 08:57:36 am »
... shall I understand, that stdcall (or cdecl) might be fine with static binding, but not be okay with dynamic?
No, static and dynamic binding should have the same calling convention.
I use same calling convention with static binding and everything works okay. So the problem should be somewhere else?
The dll is 32 bit, I suppose.
Лазар 4,4 32 bit (sometimes 64 bit); FPC3,2,2

Laksen

  • Hero Member
  • *****
  • Posts: 802
    • J-Software
Re: Problems with dynamic DLL binding.
« Reply #14 on: February 12, 2015, 09:48:01 am »
Please try to compile the program where you do static binding with -gh and -Criot

The -gh might show where you have memory corruption. My bet is that you are overwriting some dynamic array, or trashing your heap some way, and your breaking setlength call is a little indicator of this

 

TinyPortal © 2005-2018