0

一位同事今天来找我,代码在 Windows XP 上运行但在 Windows 7 上失败:

用户登录“SalesOrdersystem”失败

我的物理调试告诉我,他正在对已关闭或忘记打开的数据库连接运行查询。

从 ADO2.6 开始,在 Windows Vista 中,PersistSecurityInfo连接字符串中的默认值为False,而不是True

在 Windows Vista 之前的连接字符串,例如:

Data Source=deathstar;User ID=SalesOrderSystem;Password=password1

连接打开后将密码保留在连接字符串中,这相当于:

Data Source=deathstar;User ID=SalesOrderSystem;
      Password=password1;PersistSecurityInfo=true

从 Windows Vista 开始,默认情况下,密码会从连接的ConnectionString属性中删除:

Data Source=deathstar;User ID=SalesOrderSystem

这相当于

Data Source=deathstar;User ID=SalesOrderSystem;
      Password=password1;PersistSecurityInfo=false

我知道我的同事正在经历这种密码被删除的行为。然后,当连接关闭时,他试图打开一个查询(即 ADOQuery.Open),该查询试图打开连接。但是如果没有在连接字符串中保存密码,他会得到原来的错误

问题变成了,“你为什么要使用连接而不先打开它?”

我们将其追溯到(多线程代码)他使用的连接后来被释放:

伪代码:

customer := TCustomer.Create(ADOConnection)
ADOConnection.Free;
customer.RefreshFromDatabase;

而不是

customer := TCustomer.Create(DataModule.ADOConnection);
customer.RefreshFromDatabase;

开玩笑地说,我建议他可以通过将连接字符串更改为包含以下内容来掩盖错误并留下潜在的崩溃PersistSecurityInfo=True

connectionString := ...+
    ';PersistSecurityInfo=True';

他做到了。


我们有一些在内部使用ADOConnection对象的库代码。我希望能够从以下位置更改我的代码:

destructor TAsyncFill.Destroy; 
begin
   ...
   FreeAndNil(FADOConnection)
end;

destructor TAsyncFill.Destroy; 
begin
   ...
   FADOConnection.Close;
   FADOConnection.ConnectionString := 'This connection object has been freed. Why are you using it?';
   FreeAndNil(FADOConnection);
end;

但我确信它会引入错误,而这些错误曾经发生过。

我在想的是某种闭包,我可以在其中OnConnect向连接对象注入处理程序:

destructor Destroy; 
begin
   ...
   FADOConnection.Close;
   FADOConnection.BeforeConnect := { 
       OutputDebugString('You''re using a connection that''s been freed!'); 
       Windows.Beep(1000, 60000) };
   FreeAndNil(FADOConnection);
end;

但是 Delphi 没有匿名事件处理程序。

任何人都可以想出一种方法来提醒人们在释放对象后使用它吗?


注意:我知道我的要求不支持。考虑到现实的局限性,我正在寻求最佳黑客的想法。

4

3 回答 3

2

当您正在“FreeAndNil”您的 AdoConnection 时,我假设您也在自己实例化它。在这种情况下,您可以做的是派生自己的 TMyAdoConnection 并对其进行实例化。如果您采用“拦截器”方法,您甚至不必给它另一个类名:

type
  TAdoConnection = class(AdoDb.TAdoConnection)
  end;

然后,覆盖受保护的 DoConnect 方法。尽管它的名字是“只是”一种触发 OnConnect 事件的方法。它实际上确实打开了一个连接。还有一个类似的 DoDisconnect 方法,它实际上关闭了连接。

在这两个被覆盖的方法中,加上 Create 和 Destroy 的覆盖,您可以编写一个简单的检测机制来检测 Opens 和 Closes 与 Creates 和 Destroys 不匹配的情况。

如果您有一个 AdoConnection 实例,您可以只跟踪几个全局变量中的内容。否则,您可能必须编写一个小型注册表来跟踪每个实例的内容。如果实例已被释放并设置为 nil,则尝试在该注册表中找到“Self”会很困难。因此,您可能必须暂时放弃 FreeAndNil 并重新编码if Assigned(FAdoConnection)以检测仍需要创建实例,以及其他内容。

警告:这是基于 Delphi 6 中的 TAdoConnection。我目前没有在 Delphi 5 中安装 Ado 组件。因此,您必须检查 DoConnect 和 DoDiscoonect 在 D5 中是否存在并且是虚拟的。

于 2011-08-26T17:48:18.093 回答
1

当您的代码在对象被释放后访问它时,具有完整调试功能的 FastMM 会通知您。

在此处输入图像描述

显然,您不能启用该设置,但在运行测试套件时将其打开,这样的错误将被公开。

于 2011-08-26T16:40:08.037 回答
0

为什么编码器使用可能已经被释放的 TADOConnection 对象?为什么不让线程创建它自己的连接?这将允许线程控制连接的生命周期。

于 2011-08-26T16:46:36.403 回答