几年来我们一直在报告此错误,现在,我不得不花一些时间对此问题进行彻底解决。
还有其他来源提到过它:
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
我已经使用DevArt打开了一张票,给了他们我的测试程序和dll的副本,但是他们很正确地回答说,即使没有DevArt驱动程序,该问题也会出现,我已经确认使用10.2 Tokyo Enterprise随附的标准MSSQL驱动程序根本没有安装任何DevArt驱动程序。
DLL具有单个功能:
exports
CheckConnection;
这是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.
此实现行允许从主程序使用DLL函数:
function CheckConnection(const Server, Database, User, Password: PAnsiChar): Boolean; stdCall; external 'Project3.dll';
这是用于调用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;
问题源于 TDBXDriverRegistry.CloseAllDrivers 中的循环,在该循环中,它为安装/使用的每个dbExpress驱动程序调用 TDBXDriverRegistry.DBXDriverRegistry.FreeDriver 。
调用 FreeDriver 时,执行线程将转到此方法:
destructor TDBXDynalinkDriver.Destroy;
begin
if FMethodTable <> nil then
FMethodTable.FDBXBase_Close(FDriverHandle);
FDriverHandle := nil;
FreeAndNil(FMethodTable);
inherited Destroy;
end;
这是引发访问冲突的 FMethodTable.FDBXBase_Close(FDriverHandle); 行,由于未捕获该行,因此在调用程序中导致216错误。
仅当我们实际上打开 TSQLConnection 时,此调用才在释放最后一个驱动程序时失败。
基于我在DLL中使用DevExpress VCL组件的经验,您需要调用 dxInitialize 和 dxFinalize 才能正确使用GDIPlus,我只能认为为了解决此错误,需要在DLL中或从调用程序中进行某些操作,但我只是无法弄清楚可能是什么,因此出现了这个问题。
编辑9/4/2019
我收到了Embarcadero的回复,说我们应该将 AutoUnloadDriver = True 添加到
的参数中“避免敲定订单问题。”
我们唯一可以添加此文件并有所作为(因为它解决了216错误)的位置是dbxdrivers.ini文件的[MSSQL]
块中。如果通过以下两种方法之一将其添加到 TSQLConnection 的 Params 属性中,则无效:
SQLConnection.Params.Values['AutoUnloadDriver'] := 'True';
或
SQLConnection.Params.AddPair('AutoUnloadDriver', 'True');
这可能是一厢情愿的,但是在DLL中使用该驱动程序时,将该参数添加到dbxdrivers.ini文件的[DevArtSQLServer]
块中也没有任何影响。
我现在再次联系DevArt,看看他们是否还有其他内容要添加,同时还尝试让Embarcadero向我们提供有关在部署情况下使用此 AutoUnloadDriver = True 的更多信息,因为最终用户安装没有dbxdrivers.ini文件。
编辑2 9/4/2019
本文介绍了对此参数所做的修复,
http://edn.embarcadero.com/article/39392/
您应该可以将其添加到 TSQLConnection 对象的 Params 集合中。
进入Embarcadero代码后,事实证明,如果dbxdrivers.ini中存在驱动程序名称(在此示例中为MSSQL),则在那里定义的参数将覆盖运行时设置的参数,但某些参数除外主机名,数据库,用户名和密码。这意味着
SQLConnection.Params.AddPair('AutoUnloadDriver', 'True');
正在努力将其添加到集合中,但是当它从ini文件中定义的驱动程序默认值重新加载集合时被删除了。
这确实意味着,如果在运行时进行设置,则它应该可以在完全没有dbxdrivers.ini文件的系统上运行。
答案 0 :(得分:1)
我收到了Embarcadero的回复,说我们应该将 AutoUnloadDriver = True 添加到
的参数中“避免敲定订单问题。”
我们唯一可以添加此文件并有所作为(因为它解决了216错误)的位置是dbxdrivers.ini文件的<form onsubmit="return check_form()">
...
</form>
<script>
function check_form()
{
return document.getElementById('g-recaptcha-response').value != '';
}
</script>
块中。
http://edn.embarcadero.com/article/39392/
DevArt通知我,他们现在已在其用于SQL Server的dbExpress驱动程序中添加了对 AutoUnloadDriver 参数的支持。他们向我发送了包含此修复程序的每晚版本,因为该版本尚未发布。
在应用程序安装文件夹中包含一个 dbxdrivers.ini 文件,并将 AutoUnloadDriver = True 参数添加到check_form()
和[MSSQL]
部分,关闭使用DLL来建立与SQL Server的连接的应用程序时,再也不会出现216错误。