Recent

Author Topic: Issue With DLL  (Read 2800 times)

BSaidus

  • Hero Member
  • *****
  • Posts: 541
  • lazarus 1.8.4 Win8.1 / cross FreeBSD
Issue With DLL
« on: June 07, 2021, 01:25:16 pm »
Hello;
I want to write a DLL and export some function from it, the countent of DLL functions encapsulate mORMot
functions and classes to help.

this is a source code.


Code: Pascal  [Select][+][-]
  1.  
  2. library ucNetProxy;
  3. {$mode delphi}{$H+}
  4. uses
  5.   Classes,
  6.   Interfaces,
  7.   { you can add units after this }
  8.   ucfNetProxy
  9.   ;
  10.  
  11. exports
  12. fQueryExportConnection,                                 // export a global variable gfP: : TSQLDBConnectionProperties ; .
  13. fQueryInitConnectionsToServer,                  // export a Init connection to Server .
  14. fQueryGetSQLQR                                                  // get String from Server .
  15. ;
  16.  
  17. begin
  18.   Dll_Process_Detach_Hook := @Dll_UC_ExitPoint;
  19.   Dll_Thread_Attach_Hook := @Dll_UC_EntryPoint;
  20.   Dll_Thread_Detach_Hook := @Dll_UC_ExitPoint;
  21. end.
  22.  
  23.  

And the declaration of functions is :

Code: Pascal  [Select][+][-]
  1. unit ucfNetProxy;
  2.  
  3. {$mode delphi}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, jsonConf, Forms,
  9.   //SynDBMidasVCLExtra,
  10.   mORMot,
  11.   SynDBMidasVCL,
  12.   SynDB,          //TQuery
  13.   SynTable,       //SynDBVCL, mORMotMidasVCL,
  14.   SynDBRemote,    // Remote Access
  15.   SynCommons
  16.   //SynDBDataset,
  17.  
  18.   ;
  19.  
  20. type
  21.   TUCCntConfig = record
  22.     host, port,  // 127.0.0.1:7443
  23.     db,          // medic
  24.     user, pass: String; // user/pass
  25.   end;
  26.  
  27.  
  28. // DLL related
  29. procedure Dll_UC_EntryPoint(dllparam : PtrInt);
  30. procedure Dll_UC_ExitPoint(dllparam : PtrInt);
  31.  
  32. function fQueryInitConnectionsToServer ():Boolean; stdcall;
  33. function fQueryGetSQLQR ( var oQuery: TQuery; iSQLRQ: Integer): Boolean; stdcall;
  34. function fQueryExportConnection (): Pointer; stdcall;
  35.  
  36.  
  37.   {  -------------------------------------------------------------------------------------------------------  }
  38.   {  Global vars                                                                                              }
  39.   var
  40.     gfP       : TSQLDBConnectionProperties;
  41.     gSQLCache : TStringList;
  42.     gJConfig  : TUCCntConfig;
  43.     gId       : Integer ; // Id for patients global
  44.     gBUseCache: Boolean;
  45.  
  46. implementation
  47.  
  48. function fQueryGetConfig(): TUCCntConfig;
  49. var
  50.   fJson: TJSONConfig;
  51.   fApp: String;
  52. begin
  53.   fApp := ChangeFileExt(Application.ExeName, '.json');
  54.   fJson := TJSONConfig.Create( nil );
  55.   fJson.Filename := fApp;
  56.   try
  57.     Result.host := fJson.GetValue('/httpcnt/host'  ,'127.0.0.1');
  58.     Result.port := fJson.GetValue('/httpcnt/port'  ,'7443'     );
  59.     Result.db   := fJson.GetValue('/httpcnt/dbase' ,'medic1'   );
  60.     Result.user := fJson.GetValue('/httpcnt/user'  ,'user'     );
  61.     Result.pass := fJson.GetValue('/httpcnt/pass'  ,'pass'     );
  62.   finally
  63.     fJson.Free;
  64.   end;
  65. end;
  66.  
  67. function fQueryHttpConnectionSQL ( ): TSQLDBConnectionProperties;
  68. var
  69.   sHostPort: String;
  70. begin
  71.   // Result is NULL
  72.   Result := nil;
  73.   // See if Global gfP is, and FreeIt
  74.   if gfP <> nil then
  75.     FreeAndNil(gfP);
  76.   // construct host:port
  77.   sHostPort := gJConfig.host + ':' + gJConfig.port;
  78.   // construct Connection String
  79.   Result := TSQLDBWinHTTPConnectionProperties.Create( sHostPort , gJConfig.db, gJConfig.user, gJConfig.pass);
  80.   // Add widestring support
  81.   Result.VariantStringAsWideString := true;
  82.   // see if object is Created.
  83.   if  Result = nil then
  84.     raise exception.Create( 'Error connecting to server... ' );
  85.  
  86. end;
  87.  
  88. procedure Dll_UC_EntryPoint(dllparam: PtrInt);
  89. begin
  90.   fQueryInitConnectionsToServer();
  91. end;
  92.  
  93. procedure Dll_UC_ExitPoint(dllparam: PtrInt);
  94. begin
  95.   if gfP <> nil then
  96.   begin
  97.     FreeAndNil(gfP);
  98.   end;
  99. end;
  100.  
  101. function fQueryInitConnectionsToServer(): Boolean; stdcall;
  102. begin
  103.   Result := False;
  104.   gJConfig := fQueryGetConfig();
  105.   gfP := fQueryHttpConnectionSQL();
  106.   Result := True;
  107. end;
  108.  
  109. function fQueryGetSQLQR          (var oQuery: TQuery; iSQLRQ: Integer): Boolean; stdcall;
  110. var
  111.   fQuery: TQuery ;
  112.   fSQL: string;
  113.   fIndex: Integer;
  114.   fNc: TSQLDBConnection;
  115. begin
  116.   Result := False;
  117.   //Screen.Cursor := crHourGlass;
  118.   try
  119.     //if oQuery = nil then
  120.     //  oQuery := TQuery.Create( gfP.NewConnection );
  121.     //
  122.     //oQuery.SQL.Clear;
  123.     //if ( gSQLCache.IndexOfObject(TObject(iSQLRQ)) <> -1 ) and ( gBUseCache ) then
  124.     //begin
  125.     //  fIndex := gSQLCache.IndexOfObject(TObject(iSQLRQ));
  126.     //  fSQL := gSQLCache.Strings[fIndex];
  127.     //  oQuery.SQL.Text := fSQL;
  128.     //  oQuery.Tag := iSQLRQ;
  129.     //end
  130.     //else
  131.     begin
  132.       fNc := gfP.NewConnection;
  133.       fQuery := TQuery.Create( fNc );
  134.       try
  135.         try
  136.           fQuery.SQL.Add( ' SELECT qr_sqltxt FROM uc_qrsql WHERE qr_id = ' + IntToStr(iSQLRQ) ) ;
  137.           fQuery.Open;
  138.           //if fQuery.RecordCount = 0 then
  139.           //begin
  140.           //  Lnc := oQuery.Connection;
  141.           //  FreeAndNil( Lnc );
  142.           //  FreeAndNil( oQuery );
  143.           //  raise Exception.Create('No SQLQr N: in the scope !!');
  144.           //end;
  145.           fSQL := fQuery.FieldByName('qr_sqltxt').AsString;
  146.           oQuery.SQL.Text := fSQL;
  147.           oQuery.Tag := iSQLRQ;
  148.           //gSQLCache.AddObject( fSQL, TObject(iSQLRQ) );  // Add SQL text to Cach
  149.         except
  150.           raise;
  151.         end;
  152.       finally
  153.         fQuery.Free;
  154.         fNc.Free;
  155.       end;
  156.     end;
  157.   finally
  158.     //Screen.Cursor := crDefault;
  159.   end;
  160.   Result := True;
  161.   //
  162.  
  163. end;
  164.  
  165. function fQueryExportConnection(): Pointer; stdcall;
  166. begin
  167.   Result := @gfP;
  168. end;
  169.  
  170. end.
  171.  
  172.  


What Expected:
   Export the global variable:
Code: [Select]
gfP       : TSQLDBConnectionProperties;  from the DLL. (I know that fpc do not allow exporting variables).
   I used
Code: [Select]
function fQueryExportConnection(): Pointer; stdcall; to export it as a Pointer ( sadly it do not work.)
   
this is a portion of test program:
Code: Pascal  [Select][+][-]
  1. // ...
  2. function fQueryInitConnectionsToServer():Boolean; stdcall; external 'ucNetProxy.dll';
  3. function fQueryGetSQLQR          (var oQuery: TQuery; iSQLRQ: Integer): Boolean; stdcall; external 'ucNetProxy.dll';
  4. function fQueryExportConnection(): Pointer; stdcall; external 'ucNetProxy.dll';
  5.  
  6. var
  7.   Form1: TForm1;
  8.   gfP       : TSQLDBConnectionProperties;
  9.   pfP       : Pointer;
  10.  
  11. implementation
  12.  
  13. {$R *.lfm}
  14.  
  15. { TForm1 }
  16. // Button to initialise the system.
  17. procedure TForm1.btnInitClick(Sender: TObject);
  18. begin
  19.   fQueryInitConnectionsToServer();
  20.   pfP := fQueryExportConnection();
  21.   gfP := TSQLDBConnectionProperties(pfP^);
  22. end;  
  23.  
After clicking the Button
Code: [Select]
procedure TForm1.btnInitClick(Sender: TObject); the program freeze.


So do you have any technic to solve this .

Thanks





« Last Edit: June 07, 2021, 01:28:11 pm by BSaidus »
lazarus 1.8.4 Win8.1 / cross FreeBSD
dhukmucmur vernadh!

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Issue With DLL
« Reply #1 on: June 07, 2021, 01:44:52 pm »
I want to write a DLL and export some function from it, the countent of DLL functions encapsulate mORMot
functions and classes to help.

You need to write a clean, flat API. Do not pass class instances across the DLL boundary and expect them to be called correctly. This might work in most situations, but in will just as well blow up upon the smallest differences. Also you must not raise Exceptions across the DLL boundary, because you can't filter them using a on-clause.

Thus you must use opaque pointers (which can be the class instances to simplify the implementation) and essentially provide import functions for all methods you'd like to call on these (these functions then take that opaque pointer as "Self" parameter). Exceptions must be caught in those functions and you need to provide them as results or some GetLastError-like funktionality.

abouchez

  • Full Member
  • ***
  • Posts: 110
    • Synopse
Re: Issue With DLL
« Reply #2 on: August 16, 2021, 09:21:16 am »
Sorry for responding so late.

The natural way of communicating with a mORMot DLL is to use its TRestServer.ExportServerGlobalLibraryRequest in mORMot 2 for instance or TSQLRestServer.ExportServer for mORMot 1.

In fact, you expose a REST server hosted by the DLL using an in-process JSON stream, and it will be used for efficient marshaling and memory allocation between the library and the mORMot kernel. It is easier to use than a full API, and it will work as a regular mORMot client on the consumer/executable side: then you could use interface based services in your main executable, and the mORMot ExportServer* wrapper will do all the marshalling and memory management for secure and efficient process.

One big advantage of this architecture, is that it would be easy to switch from a DLL hosted process to an external REST/HTTPS or WebSockets link in the future, if needed.

 

TinyPortal © 2005-2018