Forum > Windows

Issue With DLL

(1/1)

BSaidus:
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  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} --- library ucNetProxy;{$mode delphi}{$H+}uses  Classes,  Interfaces,  { you can add units after this }  ucfNetProxy  ; exportsfQueryExportConnection,                                 // export a global variable gfP: : TSQLDBConnectionProperties ; .fQueryInitConnectionsToServer,                  // export a Init connection to Server .fQueryGetSQLQR                                                  // get String from Server .; begin  Dll_Process_Detach_Hook := @Dll_UC_ExitPoint;  Dll_Thread_Attach_Hook := @Dll_UC_EntryPoint;  Dll_Thread_Detach_Hook := @Dll_UC_ExitPoint;end.  
And the declaration of functions is :


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit ucfNetProxy; {$mode delphi}{$H+} interface uses  Classes, SysUtils, jsonConf, Forms,  //SynDBMidasVCLExtra,  mORMot,  SynDBMidasVCL,  SynDB,          //TQuery  SynTable,       //SynDBVCL, mORMotMidasVCL,  SynDBRemote,    // Remote Access  SynCommons  //SynDBDataset,   ; type  TUCCntConfig = record    host, port,  // 127.0.0.1:7443    db,          // medic    user, pass: String; // user/pass  end;  // DLL relatedprocedure Dll_UC_EntryPoint(dllparam : PtrInt);procedure Dll_UC_ExitPoint(dllparam : PtrInt); function fQueryInitConnectionsToServer ():Boolean; stdcall;function fQueryGetSQLQR ( var oQuery: TQuery; iSQLRQ: Integer): Boolean; stdcall;function fQueryExportConnection (): Pointer; stdcall;    {  -------------------------------------------------------------------------------------------------------  }  {  Global vars                                                                                              }  var    gfP       : TSQLDBConnectionProperties;    gSQLCache : TStringList;    gJConfig  : TUCCntConfig;    gId       : Integer ; // Id for patients global    gBUseCache: Boolean; implementation function fQueryGetConfig(): TUCCntConfig;var  fJson: TJSONConfig;  fApp: String;begin  fApp := ChangeFileExt(Application.ExeName, '.json');  fJson := TJSONConfig.Create( nil );  fJson.Filename := fApp;  try    Result.host := fJson.GetValue('/httpcnt/host'  ,'127.0.0.1');    Result.port := fJson.GetValue('/httpcnt/port'  ,'7443'     );    Result.db   := fJson.GetValue('/httpcnt/dbase' ,'medic1'   );    Result.user := fJson.GetValue('/httpcnt/user'  ,'user'     );    Result.pass := fJson.GetValue('/httpcnt/pass'  ,'pass'     );  finally    fJson.Free;  end;end; function fQueryHttpConnectionSQL ( ): TSQLDBConnectionProperties;var  sHostPort: String;begin  // Result is NULL  Result := nil;  // See if Global gfP is, and FreeIt  if gfP <> nil then    FreeAndNil(gfP);  // construct host:port  sHostPort := gJConfig.host + ':' + gJConfig.port;  // construct Connection String  Result := TSQLDBWinHTTPConnectionProperties.Create( sHostPort , gJConfig.db, gJConfig.user, gJConfig.pass);  // Add widestring support  Result.VariantStringAsWideString := true;  // see if object is Created.  if  Result = nil then    raise exception.Create( 'Error connecting to server... ' ); end; procedure Dll_UC_EntryPoint(dllparam: PtrInt);begin  fQueryInitConnectionsToServer();end; procedure Dll_UC_ExitPoint(dllparam: PtrInt);begin  if gfP <> nil then  begin    FreeAndNil(gfP);  end;end; function fQueryInitConnectionsToServer(): Boolean; stdcall;begin  Result := False;  gJConfig := fQueryGetConfig();  gfP := fQueryHttpConnectionSQL();  Result := True;end; function fQueryGetSQLQR          (var oQuery: TQuery; iSQLRQ: Integer): Boolean; stdcall;var  fQuery: TQuery ;  fSQL: string;  fIndex: Integer;  fNc: TSQLDBConnection;begin  Result := False;  //Screen.Cursor := crHourGlass;  try    //if oQuery = nil then    //  oQuery := TQuery.Create( gfP.NewConnection );    //    //oQuery.SQL.Clear;    //if ( gSQLCache.IndexOfObject(TObject(iSQLRQ)) <> -1 ) and ( gBUseCache ) then    //begin    //  fIndex := gSQLCache.IndexOfObject(TObject(iSQLRQ));    //  fSQL := gSQLCache.Strings[fIndex];    //  oQuery.SQL.Text := fSQL;    //  oQuery.Tag := iSQLRQ;    //end    //else    begin      fNc := gfP.NewConnection;      fQuery := TQuery.Create( fNc );      try        try          fQuery.SQL.Add( ' SELECT qr_sqltxt FROM uc_qrsql WHERE qr_id = ' + IntToStr(iSQLRQ) ) ;          fQuery.Open;          //if fQuery.RecordCount = 0 then          //begin          //  Lnc := oQuery.Connection;          //  FreeAndNil( Lnc );          //  FreeAndNil( oQuery );          //  raise Exception.Create('No SQLQr N: in the scope !!');          //end;          fSQL := fQuery.FieldByName('qr_sqltxt').AsString;          oQuery.SQL.Text := fSQL;          oQuery.Tag := iSQLRQ;          //gSQLCache.AddObject( fSQL, TObject(iSQLRQ) );  // Add SQL text to Cach        except          raise;        end;      finally        fQuery.Free;        fNc.Free;      end;    end;  finally    //Screen.Cursor := crDefault;  end;  Result := True;  // end; function fQueryExportConnection(): Pointer; stdcall;begin  Result := @gfP;end; end.  

What Expected:
   Export the global variable:
--- Code: --- gfP       : TSQLDBConnectionProperties; 
--- End code ---
from the DLL. (I know that fpc do not allow exporting variables).
   I used
--- Code: ---function fQueryExportConnection(): Pointer; stdcall;
--- End code ---
to export it as a Pointer ( sadly it do not work.)
   
this is a portion of test program:

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---// ...function fQueryInitConnectionsToServer():Boolean; stdcall; external 'ucNetProxy.dll';function fQueryGetSQLQR          (var oQuery: TQuery; iSQLRQ: Integer): Boolean; stdcall; external 'ucNetProxy.dll';function fQueryExportConnection(): Pointer; stdcall; external 'ucNetProxy.dll'; var  Form1: TForm1;  gfP       : TSQLDBConnectionProperties;  pfP       : Pointer; implementation {$R *.lfm} { TForm1 }// Button to initialise the system.procedure TForm1.btnInitClick(Sender: TObject);begin  fQueryInitConnectionsToServer();  pfP := fQueryExportConnection();  gfP := TSQLDBConnectionProperties(pfP^);end;    After clicking the Button
--- Code: ---procedure TForm1.btnInitClick(Sender: TObject);
--- End code ---
the program freeze.


So do you have any technic to solve this .

Thanks





PascalDragon:

--- Quote from: BSaidus on June 07, 2021, 01:25:16 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.
--- End quote ---

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:
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.

Navigation

[0] Message Index

Go to full version