SiBrit
SiBrit

Reputation: 1542

216 error thrown by Delphi 10.2 Tokyo program when connecting to SQL through a Delphi DLL

We've had this error being reported on and off for a few years, and now I've got to spend some time on it to sort it out for good.

There's been a few mentions of it from other sources:

https://forums.embarcadero.com/thread.jspa?threadID=112713

https://forums.devart.com/viewtopic.php?t=37398

https://forums.devart.com/viewtopic.php?f=10&t=16520

I had opened a ticket with DevArt, giving them a copy of my test program and dll, but they quite rightly replied that the problem occurs even without DevArt drivers, which I have confirmed using the standard MSSQL driver supplied with 10.2 Tokyo Enterprise and no DevArt drivers being installed, at all.

The DLL has a single function:

exports
  CheckConnection;

And here's the unit code in the DLL:

unit Unit7;

interface

uses
  System.SysUtils, Data.SqlExpr, Data.DBXMSSQL;

function CheckConnection(const ServerName, DatabaseName, UserName, Password: PAnsiChar): Boolean; stdCall export;

implementation

function CheckConnection(const ServerName, DatabaseName, UserName, Password: PAnsiChar): Boolean; stdCall export;
var
  SQLConnection: TSQLConnection;
begin
  SQLConnection := TSQLConnection.Create(nil);

  try
    SQLConnection.DriverName := 'MSSQL';
    SQLConnection.LibraryName := 'dbxmss.dll';
    SQLConnection.VendorLib := 'sqlncli10.dll';
    SQLConnection.GetDriverFunc := 'getSQLDriverMSSQL';

    SQLConnection.Params.Values['HostName'] := ServerName;
    SQLConnection.Params.Values['Database'] := DatabaseName;
    SQLConnection.Params.Values['User_Name'] := UserName;
    SQLConnection.Params.Values['Password'] := Password;

    SQLConnection.LoginPrompt := False;
    SQLConnection.Open;

    Result := SQLConnection.Connected;
  finally
    SQLConnection.Close;
    FreeAndNil(SQLConnection);
  end;
end;

end.

This implementation line enables the DLL function to be used from the main program:

function CheckConnection(const Server, Database, User, Password: PAnsiChar): Boolean; stdCall; external 'Project3.dll';

And here's the code for the button click event for calling the DLL:

procedure TForm8.Button1Click(Sender: TObject);
var
  Server, Database, User, Password: AnsiString;
begin
  Server := Edit1.Text;
  Database := Edit2.Text;
  User := Edit3.Text;
  Password := Edit4.Text;

  if CheckConnection(@Server[1], @Database[1], @User[1], @Password[1]) then
    Label1.Caption := 'DLL connected OK'
  else
    Label1.Caption := 'DLL did not connect';
end;

The problem stems from the loop within TDBXDriverRegistry.CloseAllDrivers, where it is calling TDBXDriverRegistry.DBXDriverRegistry.FreeDriver for each dbExpress driver installed/used.

When FreeDriver is called, the execution thread goes to this method:

destructor TDBXDynalinkDriver.Destroy;
begin
  if FMethodTable <> nil then
    FMethodTable.FDBXBase_Close(FDriverHandle);
  FDriverHandle := nil;
  FreeAndNil(FMethodTable);
  inherited Destroy;
end;

It is the FMethodTable.FDBXBase_Close(FDriverHandle); line that is throwing the Access Violation, and since it is untrapped, it causes the 216 error to occur in the calling program.

This call only fails on the last driver being freed, and only if we actually Open the TSQLConnection.

Given my experience with using DevExpress VCL components in DLL's, where you need to call the dxInitialize and dxFinalize in order to work with GDIPlus properly, I can only think that something needs to be done either in the DLL, or from the calling program, in order to resolve this error, but I just cannot figure out what that may be, hence this question.

Upvotes: 2

Views: 758

Answers (1)

SiBrit
SiBrit

Reputation: 1542

I received a response from Embarcadero that says we should add AutoUnloadDriver=True to the parameters to

"avoid finalization order issues."

The only place we can add this and make any difference (in that it resolved the 216 error) is in the [MSSQL] block of the dbxdrivers.ini file.

http://edn.embarcadero.com/article/39392/

I've been informed by DevArt that they have now added support for the AutoUnloadDriver parameter to their dbExpress driver for SQL Server. They sent me a nightly build that included this fix, since it is not yet in their public release version.

With a dbxdrivers.ini file in the application installation folder, and with the AutoUnloadDriver=True parameter added to the [MSSQL] and [DevArtSQLServer] sections, we no longer get any 216 errors when closing applications where a DLL was used to make a connection to SQL Server.

Upvotes: 1

Related Questions