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.