Recent

Author Topic: Getting Result from Database using DLL library  (Read 5717 times)

fiazhnd

  • New Member
  • *
  • Posts: 36
Getting Result from Database using DLL library
« on: March 28, 2018, 12:15:59 pm »
I am trying to access a value from sqlite3 database through dll library, but getting an error "raised exception class 'External: SIGSEGV'. "

Can anyone help me  %)


Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, Buttons, strutils, DynLibs;
  9.  
  10. type
  11.   { TForm1 }
  12.   TForm1 = class(TForm)
  13.     BtnCodeBySerial: TButton;
  14.     EditRCode: TEdit;
  15.     EditVSerial: TEdit;
  16.     procedure BtnCodeBySerialClick(Sender: TObject);
  17.   private
  18.     { private declarations }
  19.  
  20.   public
  21.  
  22.   end;
  23.  
  24. var
  25.   Form1: TForm1;
  26.  
  27. implementation
  28.  
  29. type
  30. TMyDLL = function (var RevFRCode: String): String; stdcall;
  31.  
  32. {$R *.lfm}
  33.  
  34. { TForm1 }
  35.  
  36. procedure TForm1.BtnCodeBySerialClick(Sender: TObject);
  37. var
  38. RadioCode: String;
  39. MyHandle: TLibHandle;
  40. MyDLLFUNC: TMyDLL;
  41. begin
  42.    RadioCode:= EditVSerial.Text;
  43.    MyHandle := SafeLoadLibrary('drofrc.dll');
  44.    if MyHandle <> 0 then
  45.      begin
  46.       MyDLLFUNC := TMyDLL(GetProcedureAddress(MyHandle, 'FRCBySerial'));
  47.       if Assigned (MyDLLFUNC) then
  48.       begin
  49.       MyDLLFUNC(RadioCode);
  50.       end;
  51.       end
  52.       else
  53.       Begin
  54.       ShowMessage('Function is not Found');
  55.       end;
  56.       FreeLibrary(MyHandle);
  57.       EditRCode.Text:= Radiocode;
  58. end;
  59. end.
  60.  

Library

Code: Pascal  [Select][+][-]
  1. library drofrc;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   LCLType, StdCtrls, Classes, SysUtils, sqlite3conn, sqldb, db, FileUtil, Forms, Controls, Interfaces, Windows;
  7.  
  8. var
  9.     SQLite3Connection1: TSQLite3Connection;
  10.     SQLQuery1: TSQLQuery;
  11.     SQLTransaction1: TSQLTransaction;
  12.  
  13. function FRCBySerial(FRCode: String): String; stdcall;
  14. var
  15.     LResult: String;
  16. begin
  17.   Result:= '';
  18.   //Creating Control
  19.   SQLite3Connection1:= TSQLite3Connection.Create(Nil);
  20.   SQLTransaction1:= TSQLTransaction.Create(Nil);
  21.   SQLQuery1:= TSQLQuery.Create(Nil);
  22.  
  23.   SQLite3Connection1.DatabaseName:= 'newWE.db';
  24.   SQLite3Connection1.Transaction:= SQLTransaction1;
  25.   SQLTransaction1.Database:= SQLite3Connection1;
  26.  
  27.   SQLQuery1.Database:= SQLite3Connection1;
  28.   SQLQuery1.Transaction:= SQLTransaction1;
  29.   SQLQuery1.Close;
  30.   SQLQuery1.SQL.Text:= ('Select RCode From FordRCBSerial Where VSerial = ' + FRCode);
  31.   SQLite3Connection1.Connected:= True;
  32.   SQLTransaction1.Active:= True;
  33.   SQLQuery1.Open;
  34.  
  35.   LResult := SQLQuery1.Fields[0].AsString;
  36.   SQLite3Connection1.Open;
  37.   Result:= LResult;
  38. end;
  39.  
  40. exports FRCBySerial;
  41.  
  42. begin
  43. end.
  44.  

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: Getting Result from Database using DLL library
« Reply #1 on: March 28, 2018, 01:01:50 pm »
Don't use String type across dynamic libraries, they are managed and can cause issues. Use PChar instead.

Leledumbo

  • Hero Member
  • *****
  • Posts: 8757
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Getting Result from Database using DLL library
« Reply #2 on: March 28, 2018, 02:57:48 pm »
Don't use String type across dynamic libraries, they are managed and can cause issues. Use PChar instead.
Adding to it: don't just cast the volatile string instance to pchar if it's meant to be still accessible in the (DLL) caller after the callee (DLL function) ends, it will point to invalid memory address and eventually the same SIGSEGV will be raised. Make a copy in the heap or make the interface provides fillable caller allocated space as parameter.

fiazhnd

  • New Member
  • *
  • Posts: 36
Re: Getting Result from Database using DLL library
« Reply #3 on: March 28, 2018, 04:38:26 pm »
I am trying to understand your instruction but it beyond my understanding can you please give me an example of you advice so next time when i write dll will keep in mind that things.  %)

Don't use String type across dynamic libraries, they are managed and can cause issues. Use PChar instead.
Adding to it: don't just cast the volatile string instance to pchar if it's meant to be still accessible in the (DLL) caller after the callee (DLL function) ends, it will point to invalid memory address and eventually the same SIGSEGV will be raised. Make a copy in the heap or make the interface provides fillable caller allocated space as parameter.

Phil

  • Hero Member
  • *****
  • Posts: 2737
Re: Getting Result from Database using DLL library
« Reply #4 on: March 28, 2018, 04:42:48 pm »
I am trying to understand your instruction but it beyond my understanding can you please give me an example of you advice

Maybe look at the NDFD library example code here for how to use a buffer to return string results from a dynamic library:

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


fiazhnd

  • New Member
  • *
  • Posts: 36
Re: Getting Result from Database using DLL library
« Reply #5 on: March 31, 2018, 09:56:13 am »
I amend the code as per instruction, not facing any error now but i am not receiving any result after calling the function. any suggestion.  ::)

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, Buttons, strutils, DynLibs;
  9.  
  10. type
  11.   { TForm1 }
  12.   TForm1 = class(TForm)
  13.     BtnCodeBySerial: TButton;
  14.     EditRCode: TEdit;
  15.     EditVSerial: TEdit;
  16.     procedure BtnCodeBySerialClick(Sender: TObject);
  17.   private
  18.     { private declarations }
  19.  
  20.   public
  21.  
  22.   end;
  23.  
  24. var
  25.   Form1: TForm1;
  26.  
  27. implementation
  28.  
  29. type
  30. TMyDLL = function (FRadioCode: PChar): PChar; stdcall;   //Importing Dynmic Function
  31.  
  32. {$R *.lfm}
  33.  
  34. { TForm1 }
  35.  
  36. procedure TForm1.BtnCodeBySerialClick(Sender: TObject);
  37. var
  38. RadioCode: PChar;
  39. MyHandle: TLibHandle;
  40. MyDLLFUNC: TMyDLL;
  41. begin
  42.    RadioCode:= PChar(EditVSerial.Text);
  43.    MyHandle := LoadLibrary('drofrc.dll');
  44.    if MyHandle <> 0 then
  45.      begin
  46.       MyDLLFUNC := TMyDLL(GetProcAddress(MyHandle, 'FRCBySerial'));
  47.       if Assigned (MyDLLFUNC) then
  48.       begin
  49.       MyDLLFUNC(RadioCode); //call the function
  50.       EditRCode.Text:= Radiocode;
  51.       end
  52.       else
  53.       ShowMessage('"FRCBySerial" Function is not Found');
  54.       FreeLibrary(MyHandle);
  55.       end else begin
  56.       ShowMessage('drofrc.dll not found / not loaded') ;
  57.       end;
  58. end;          
  59.  

Library

Code: Pascal  [Select][+][-]
  1. library drofrc;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   LCLType, StdCtrls, Classes, SysUtils, sqlite3conn, sqldb, db, FileUtil, Forms,
  7.   Controls, Interfaces, Windows;
  8.  
  9. var
  10.     SQLite3Connection1: TSQLite3Connection;
  11.     SQLQuery1: TSQLQuery;
  12.     SQLTransaction1: TSQLTransaction;
  13.  
  14. function FRCBySerial(RadioCode: PChar): PChar; stdcall;
  15. begin
  16.   Result:= '';
  17.   //Creating Control
  18.   SQLite3Connection1:= TSQLite3Connection.Create(Nil);
  19.   SQLTransaction1:= TSQLTransaction.Create(Nil);
  20.   SQLQuery1:= TSQLQuery.Create(Nil);
  21.  
  22.   SQLite3Connection1.DatabaseName:= 'newdbWE.db';
  23.   SQLite3Connection1.Transaction:= SQLTransaction1;
  24.   SQLTransaction1.Database:= SQLite3Connection1;
  25.  
  26.   SQLQuery1.Database:= SQLite3Connection1;
  27.   SQLQuery1.Transaction:= SQLTransaction1;
  28.   SQLQuery1.Clear;
  29.   SQLQuery1.Close;
  30.   SQLQuery1.SQL.Text:= ('Select RCode From RCBSerial Where VSerial = ' + RadioCode);
  31.   SQLite3Connection1.Connected:= True;
  32.   SQLTransaction1.Active:= True;
  33.   SQLQuery1.Open;
  34.  
  35.   Result:= PChar(SQLQuery1.Fields[0].AsString);
  36.  
  37.   SQLite3Connection1.Open;
  38. end;
  39.  
  40. exports FRCBySerial;
  41.  
  42. begin
  43. end.
  44.  

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: Getting Result from Database using DLL library
« Reply #6 on: March 31, 2018, 04:06:53 pm »
debugging DLL's is a messy thing, what I do is insert "Assertions" in the code for testing ..

In your case, you must test If the function is getting called! If the function is making it at the end, and is the
results set propertly before exiting the function ?

 It could also mean you may need to set a standard STRING to the result value and not pass a pointer back
from the AsString function, cause it could become invalid...

 Set the results of your DB actions in a string from the DLL and then pass that string back using PCHAR(MyresultString)..

make sure that string is not inside the function so it'll still be valid when the function exits.
The only true wisdom is knowing you know nothing

fiazhnd

  • New Member
  • *
  • Posts: 36
Re: Getting Result from Database using DLL library
« Reply #7 on: March 31, 2018, 06:09:41 pm »
Seriously your paragraph 2 and 3 confuse me a lot. btw i check no Value came back (Error raise on call the function)

Can give me a favor to amend my code above, it will help me to understand your point??? please

debugging DLL's is a messy thing, what I do is insert "Assertions" in the code for testing ..

In your case, you must test If the function is getting called! If the function is making it at the end, and is the
results set propertly before exiting the function ?

 It could also mean you may need to set a standard STRING to the result value and not pass a pointer back
from the AsString function, cause it could become invalid...

 Set the results of your DB actions in a string from the DLL and then pass that string back using PCHAR(MyresultString)..

make sure that string is not inside the function so it'll still be valid when the function exits.
« Last Edit: March 31, 2018, 06:35:16 pm by fiazhnd »

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: Getting Result from Database using DLL library
« Reply #8 on: March 31, 2018, 08:30:20 pm »
what I was conveying is using a Pointer (Pchar) to return the AsString may not work...

AsString is a function and yes it returns a string, if it supported but may be cleaned up when your function exits.

Create a String in your Variable section of the DLL, not inside the function of the DLL..
The code in the DLL function
 MyGlobalString := MySQL,,,,AsString;

Result := Pchar(MyGlobalString);

Make sure the string you return via the Pchar is not inside the function..


This way the meaning of the string will remain..
The only true wisdom is knowing you know nothing

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Getting Result from Database using DLL library
« Reply #9 on: March 31, 2018, 08:31:17 pm »
1-Put your function FRCBySerial in your application and call it there before you move it back into a dll.

2-Make sure your have sqlite dll - sqlite3.dll or whatever its name - in your application folder.

3-You have no way to find out if there is an error. I suggest you change your function into something like:
Code: Pascal  [Select][+][-]
  1. library drofrc;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   sqlite3conn, sqldb, sysutils;
  7.  
  8. var
  9.     SQLite3Connection1: TSQLite3Connection;
  10.     SQLQuery1: TSQLQuery;
  11.     SQLTransaction1: TSQLTransaction;
  12.     globalStr: string;
  13.  
  14. function FRCBySerial(RadioCode: PChar; out Res: PChar): boolean; stdcall;
  15. begin
  16.   Result:= False; { Assume an error }
  17.   //Creating Control
  18.   try
  19.   SQLite3Connection1:= TSQLite3Connection.Create(Nil);
  20.     try
  21.       SQLTransaction1:= TSQLTransaction.Create(SQLite3Connection1);
  22.       SQLQuery1:= TSQLQuery.Create(SQLite3Connection1);
  23.  
  24.       SQLite3Connection1.DatabaseName:= 'newdbWE.db';
  25.       SQLTransaction1.Database:= SQLite3Connection1;
  26.       SQLQuery1.Transaction:= SQLTransaction1;
  27.  
  28.       SQLQuery1.SQL.Text:= ('Select RCode From RCBSerial Where VSerial = ' + RadioCode);
  29.       SQLQuery1.Open;
  30.  
  31.       globalStr := SQLQuery1.Fields[0].AsString;
  32.     finally
  33.       SQLite3Connection1.Free;
  34.     end;
  35.     Res := pchar(globalStr);
  36.     Result := True;
  37.   except on e : Exception do
  38.     ShowException(e, ExceptAddr); { or whatever }
  39.   end;
  40. end;
  41.  
  42. exports FRCBySerial;
  43.  
  44. begin
  45. end.

if it does not return true then you know there is an error.

Test project:
Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. function FRCBySerial(RadioCode: PChar): PChar; stdcall; external 'drofrc';
  6.  
  7. begin
  8.   WriteLn(FRCBySerial(pchar('''test''')));
  9.   ReadLn;
  10. end.

Phil

  • Hero Member
  • *****
  • Posts: 2737
Re: Getting Result from Database using DLL library
« Reply #10 on: March 31, 2018, 08:35:47 pm »
Create a String in your Variable section of the DLL, not inside the function of the DLL..

No, set up a buffer in the _calling_ code, then pass the buffer and the _size_ of the buffer to the library function. The function then fills the buffer with the return string value, using the buffer size to guard against buffer overflows. This is the only safe way of doing it. See many examples in the Win API. Or take a look at the NDFD library, its wrapper class and examples that I referred to above.

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Getting Result from Database using DLL library
« Reply #11 on: March 31, 2018, 08:58:19 pm »
Create a String in your Variable section of the DLL, not inside the function of the DLL..

No, set up a buffer in the _calling_ code, then pass the buffer and the _size_ of the buffer to the library function. The function then fills the buffer with the return string value, using the buffer size to guard against buffer overflows.
I lost you here. How would a buffer overflow happen if the string is a global variable in the dll?

Phil

  • Hero Member
  • *****
  • Posts: 2737
Re: Getting Result from Database using DLL library
« Reply #12 on: March 31, 2018, 09:10:02 pm »
I lost you here. How would a buffer overflow happen if the string is a global variable in the dll?

The buffer overflow could occur with the buffer. You're confusing two different approaches.

In general, you never use global variables in dynamic libraries. Think of a threaded app using the library. The library could be called simultaneously by multiple threads or processes.

Follow the NDFD example like a cookbook and you'll be okay. Otherwise, you're just wandering around in the dark.


engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Getting Result from Database using DLL library
« Reply #13 on: March 31, 2018, 09:41:33 pm »
I lost you here. How would a buffer overflow happen if the string is a global variable in the dll?

The buffer overflow could occur with the buffer. You're confusing two different approaches.
I see. you mean the programmer needs to be careful and consider not to write buffer overflow vulnerable code using size and buffer method.

And if the buffer is not big enough, get the needed buffer size and try again. Finally, copy the buffer to a string.

So the function should include two arguments: buffer and size  and should return the actual size. Right?

In general, you never use global variables in dynamic libraries. Think of a threaded app using the library. The library could be called simultaneously by multiple threads or processes.
The question is not about thread-safe code, but to answer your concern:
For multiple threads, it is possible to change var to threadvar.
For different processes their is no problem when using global variables.

Follow the NDFD example like a cookbook and you'll be okay. Otherwise, you're just wandering around in the dark.
Thanks for the advice.

balazsszekely

  • Guest
Re: Getting Result from Database using DLL library
« Reply #14 on: April 01, 2018, 10:52:39 am »
@fiazhnd
Use callback functions. I attach a simple project, you can adapt it to your needs. The idea is to pass to your library a function, which will report back the error(if any). With this method you can send even managed types to your library.

 

TinyPortal © 2005-2018