检测到有人在释放我的 TADOConnection 后使用它吗?
今天,一位同事向我报告了一个错误,代码在 Windows XP 上可以运行,但在 Windows 7 上却失败了:
用户登录“SalesOrdersystem”失败
我的物理调试告诉我,他正在对已关闭的数据库连接运行查询,或者他忘记打开数据库连接。
从 ADO2.6 开始,在 Windows Vista 中,连接字符串中
PersistSecurityInfo
的默认值为False
,而不是True
。在 Windows Vista 之前,连接字符串例如:
数据源=deathstar;用户ID=SalesOrderSystem;密码=password1
在连接打开后将密码保留在连接字符串中,这使其相当于:
数据源=deathstar;用户ID=SalesOrderSystem; 密码=密码1;PersistSecurityInfo=true
从 Windows Vista 开始,默认情况下,密码将从连接的
ConnectionString
属性中删除:数据源=deathstar;用户ID=SalesOrderSystem
这相当于
数据源=deathstar;用户ID=SalesOrderSystem; 密码=密码1;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 没有匿名事件处理程序。
任何人都可以想出一种方法,能够在人们使用释放后的对象时发出警报吗?
注意:我知道我的要求没有得到支持。 我正在寻求想法考虑到现实的限制,最好的技巧。
A colleague came me to today with an error with code that worked on Windows XP but fails on Windows 7:
User failed for login 'SalesOrdersystem'
My phychic debugging told me he was running a query against a database connection that was closed, or that he forgot to open.
Starting with ADO2.6, in Windows Vista, the default value of
PersistSecurityInfo
in a connection string isFalse
, rather thanTrue
.Prior to Windows Vista a connection string such as:
Data Source=deathstar;User ID=SalesOrderSystem;Password=password1
would keep the password in the connection string after the connection is opened, which makes it the equivalent of:
Data Source=deathstar;User ID=SalesOrderSystem; Password=password1;PersistSecurityInfo=true
Starting with Windows Vista the password is, by default, removed from a connection's
ConnectionString
property:Data Source=deathstar;User ID=SalesOrderSystem
which is the equivalent of
Data Source=deathstar;User ID=SalesOrderSystem; Password=password1;PersistSecurityInfo=false
i knew my colleague was experiencing this behavior where the password is being removed. And then while the connection is closed he's trying to open a query (i.e. ADOQuery.Open) which imiplicitely tries to open the Connection. But without a password saved in the connection string he gets his original error
The question became, "Why are you using a connection without opening it first?"
We traced it back to (multi-threaded code) where he was using a connection that was later being freed:
pseudo-code:
customer := TCustomer.Create(ADOConnection)
ADOConnection.Free;
customer.RefreshFromDatabase;
rather than
customer := TCustomer.Create(DataModule.ADOConnection);
customer.RefreshFromDatabase;
In jest, i suggested he could mask the error, and leave the potential crash, by changing the connection string to include PersistSecurityInfo=True
:
connectionString := ...+
';PersistSecurityInfo=True';
Which he did.
We have some library code that internally makes use of an ADOConnection
object. i would love to be able to change my code from:
destructor TAsyncFill.Destroy;
begin
...
FreeAndNil(FADOConnection)
end;
to
destructor TAsyncFill.Destroy;
begin
...
FADOConnection.Close;
FADOConnection.ConnectionString := 'This connection object has been freed. Why are you using it?';
FreeAndNil(FADOConnection);
end;
But i am sure it will introduce errors, where things used to happen to work.
What i am thinking of is some sort of closure, where i can inject an OnConnect
handler to the connection object:
destructor Destroy;
begin
...
FADOConnection.Close;
FADOConnection.BeforeConnect := {
OutputDebugString('You''re using a connection that''s been freed!');
Windows.Beep(1000, 60000) };
FreeAndNil(FADOConnection);
end;
But Delphi doesn't have anonymous event handlers.
Can anyone think of a way to be able to alert people when they're using an object after it's been freed?
Note: i understand there is no support for what i'm asking. i'm asking for ideas for the best possible hacks - given the limits of reality.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
当您“FreeAndNil”您的 AdoConnection 时,我假设您也在自己实例化它。在这种情况下,您可以做的是派生您自己的 TMyAdoConnection 并实例化它。如果您采用“拦截器”方法,您甚至不必给它另一个类名:
然后,重写受保护的 DoConnect 方法。尽管它的名称如此,但它“不仅仅是”触发 OnConnect 事件的方法。它实际上确实打开了一个连接。还有一个类似的 DoDisconnect 方法,它实际上关闭连接。
在这两个重写方法中,加上 Create 和 Destroy 的重写,您可以编写一个简单的检测机制,用于检测 Opens 和 Closes 与 Creates 和 Destroys 不匹配的情况。
如果您有一个 AdoConnection 实例,您可以只跟踪几个全局变量中的内容。否则,您可能必须编写一个小型注册表来跟踪每个实例的内容。如果实例已被释放并设置为零,则尝试在该注册表中找到“Self”会很困难。因此,您可能必须暂时放弃 FreeAndNil 并重新编码任何
if Assigned(FAdoConnection)
以检测是否仍需要使用其他内容创建实例。警告:这是基于Delphi 6中的TAdoConnection。我目前没有在Delphi 5中安装Ado组件。因此,您必须检查 DoConnect 和 DoDiscoonect 在 D5 中是否存在且虚拟。
As you are "FreeAndNil"ing your AdoConnection, I am assuming that you are instantiating it yourself as well. In that case what you could do is derive your own TMyAdoConnection and instantiate that. You do not even have to give it another class name if you go for the "interceptor" approach:
Then, override the protected DoConnect method. In spite of its name it isn't "just" a method to trigger the OnConnect event. It actually does open a connection. There is a similar DoDisconnect method as well which actually closes the connection.
In these two overridden methods, plus overrides of Create and Destroy, you can write a simple detection mechanism for when the Opens and Closes do not match the Creates and Destroys.
If you have a single AdoConnection instance you could just keep track of things in a couple of global variables. Otherwise you may have to write a small registry where you keep track of stuff for each instance. Gonna be hard though trying to find "Self" in that registry if the instance has been freed and set to nil. So you may have to forgo the FreeAndNil for the time being and recode any
if Assigned(FAdoConnection)
to detect that the instance still needs to be created, with something else.Warning: This is based on TAdoConnection in Delphi 6. I don't have the Ado components installed in Delphi 5 at the moment. So you will have to check that the DoConnect and DoDiscoonect are present and virtual in D5 as well.
当您的代码在释放对象后访问该对象时,具有完全调试功能的 FastMM 会通知您。
显然,您无法在启用该设置的情况下发布,但在运行测试套件时将其打开,并且这些错误将被冲入公众视线。
FastMM with full debug will notify you when your code accesses an object after it has been freed.
Obviously you can't ship with that setting on, but switch it on when you run your test suite and such bugs will be flushed into open sight.
为什么编码器使用可能已被释放的 TADOConnection 对象?为什么不让线程创建它自己的连接?这将允许线程控制连接的生命周期。
Why the coder is using a TADOConnection object that may have already been freed? Why not have the thread create it's own connection? That would allow the thread to control the lifecycle of the connection.