DLL Development (FireDAC)
Go Up to Working with Connections (FireDAC)
This topic describes how to use FireDAC in the dynamic-link libraries.
FireDAC may be used in a DLL as in a normal application. However, developers must be aware of two techniques, specific to DLL development.
Connection Sharing Between an Application and a DLL
When a connection must be transferred from an application to the DLL, the application should not transfer the TFDCustomConnection object, because this may lead to AV errors and other issues. This is not an issue of FireDAC. It is due to the Delphi RTL / RTTI limitations. Note that a connection cannot be shared with another process, because the sharing works only inside the same address space.
To transfer a connection between an application and a DLL, the application should transfer the TFDCustomConnection.CliHandle property value to the DLL. Here the handle must be assigned to the TFDCustomConnection.SharedCliHandle property.
After the TFDCustomConnection.SharedCliHandle is assigned, the DLL connection can be activated by setting Connected to True. Note that there is no need to set up a connection definition, including DriverID. Then, the connection can be used as a normal database connection. Finally, it can be closed by setting Connected to False. That does not close the physical connection, therefore the application connection remains active.
The application connection does not track state changes performed by the DLL. Therefore, the DLL should preserve the same transaction state as it had before the DLL call. It is indicated not to handle transactions in a DLL, change the transaction isolation level and other settings in a DLL.
Also, setting SharedCliHandle does not transfer any of the option values from an application to a DLL connection object. The DLL connection options can be set similar to the application connection options.
For example, the DLL code:
procedure SomeTask(ACliHandle: Pointer); stdcall; var oConn: TFDConnection; oQuery: TFDQuery; begin oConn := TFDConnection.Create(nil); oQuery := TFDQuery.Create(nil); try oConn.SharedCliHandle := ACliHandle; oConn.Connected := True; oQuery.Connection := oConn; oQuery.ExecSQL('delete from aaa'); finally oQuery.Free; oConn.Free; end; end; exports SomeTask;
And the application code:
procedure TForm1.Button1Click(Sender: TObject); type TSomeTaskProc = procedure (ACliHandle: Pointer); stdcall; var hDll: THandle; pSomeTask: TSomeTaskProc; begin hDll := LoadLibrary(PChar('Project2.dll')); try @pSomeTask := GetProcAddress(hDll, PChar('SomeTask')); FDConnection1.StartTransaction; try pSomeTask(FDConnection1.CliHandle); FDConnection1.Commit; except FDConnection1.Rollback; raise; end; finally FreeLibrary(hDll); end; end;
Note: This code does not:
- Handle DLL loading errors.
- Care about FireDAC DLL unloading - see next chapter.
- Keep DLL loaded for a long time and keep DLL objects for a long time.
FireDAC DLL Unloading
An application can hang up at unloading of a DLL containing FireDAC. The standard symptom for this is the application hanging up on the FreeLibrary call. This is a limitation of FireDAC, which has two workarounds:
- Enable the FireDAC silent mode in the DLL so that no wait cursors, no dialogs, and so on are shown by FireDAC. For this, put in your DLL DPR file:
uses FireDAC.UI.Intf; begin FFDGUIxSilentMode := True; end.
- Terminate FireDAC manager before the unloading of a DLL. For this, the DLL should export a procedure, which calls the ADTerminate procedure. For this, put in your DLL DPR file:
uses FireDAC.Stan.Factory; procedure Shutdown; begin FDTerminate; end; exports Shutdown;
Then, import and call the Shutdown procedure from the application before the FreeLibrary call:
var Shutdown: procedure; .... Shutdown := GetProcAddress(hLib, 'Shutdown'); if Assigned(Shutdown) then Shutdown(); FreeLibrary(hLib);
Note: The above technique must be used for the ActiveX servers with FireDAC inside.
- FireDAC DLL Sharing sample