I am familiar with Jeff Atwood's article about how errors are always the programmer's fault, but I believe I have really and truly found a bug in a Delphi .pas file.
Specifically, I'm using Delphi 2007, and the error is on line 955 of the DBCommon.pas file, which on my machine is located here:
C:\program files\codegear\rad studio\5.0\source\Win32\db\DBCommon.pas
And the code is this:
...
FieldIndex := StrToInt(Token);
if DataSet.FieldCount >= FieldIndex then
LastField := DataSet.Fields[FieldIndex-1].FieldName else
...
If "Token" has a value of zero, then we try to access index -1 of DataSet.Fields, resulting in a list index out of bounds error.
This error is not raised to the user, because it is handled before it gets that high up, but it is enormously irritating to have the debugger break in every time this happens.
I could "Ignore this exception type" but Index out of bounds errors are common enough that I don't want to universally ignore them.
The situation that causes FieldIndex to be zero is when you have a SELECT statement whose ORDER BY contains a function, as in:
ORDER BY
CASE WHEN FIELD1 = FIELD3 THEN 1 ELSE 2 END
,CASE WHEN FIELD2 = FIELD4 THEN 1 ELSE 2 END
I can fix the bug in DBCommon.pas, but Delphi will not recompile itself, and my change does not take effect. If I rename the .DCU file, then it just complains that "DBCommon.dcu" cannot be found.
So (finally) my question is: Can I recompile DBCommon.pas with my fix, and if so, how?
You can probably put dbcommon.pas in youre project directory. It will then be compiled along with the rest of the project.
See previous answers for how to create the situation where you can recompile modified VCL source. I would however add that you seriously consider managing your changes in your change control system, using the "Vendor Branch" SCM pattern.
In simple terms (using SVN as a reference):
Create a "vendor source" copy of the original vendor supplied files. This is your "pristine" reference copy.
Create a branch representing that pristine copy (e.g. "2009" for the Delphi 2009 version of the VCL)
create a further branch into a separate "vendor library" folder. THIS is the copy of the library that you should reference in your projects
any modifications to the vendor source are made in the "vendor library" branch.
when the vendor provides a new version of the library you check the new version in to the "vendor source" project and create a new branch for the new version.
you can then easily diff the vendor revisions. But more importantly (with SubVersion, and possibly othe SCM systems) you should also be able to simply merge (i.e. automatically) the new vendor source with your "vendor library" branch to easily incorporate vendor changes with your own modifications.
This is all described far better than I just did in the excellent O'Reilly book: "Version Control with SubVersion"
NOTE however that the "loaddirs" utility mentioned in that book is no longer supported due to copyright issues, so updating "vendor drops" is currently a manual exercise, but this occurs only infrequently and is not a major burden.
We are using this pattern ourselves, although in the case of the VCL we do not maintain a complete copy of the entire VCL source tree in our "vendor source" or "vendor library", but instead only track changed and dependent units. For other libraries managed under a vendor branch we typically do maintain complete copies but decided this wasn't necessary for the VCL.
We've only just implemented this pattern however, so we may yet decide that we need to take a more comprehensive approach with the VCL too.
ymmv
You can, but often you don't have to. Recompiling a VCL unit sometime means recompiling all the rest of the VCL units either because you've changed the interface of a unit or because the compiler gets confused and thinks you've changed the interface. Recompiling a VCL unit also rules out the possibility of using most run-time packages because you can't recompile Delphi's run-time packages.
Instead of recompiling, you can use run-time patching. I've used the method in the TNT Unicode controls, but Madshi also provides a way to replace a function with your own implementation. If you copy the implementation of DBCommon.GetIndexForOrderBy into your own unit and make your fixes, you can use this command to patch the VCL version with your own:
var
Old_GetIndexForOrderBy: Pointer;
HookCode(@DBCommon.GetIndexForOrderBy,
@Fixed_GetIndexForOrderBy,
Old_GetIndexForOrderBy,
0);
With the Tnt Unicode library, find the OverwriteProcedure
routine in the TntSystem unit. It's not public, so you'll need to either declare it in the unit interface or copy it into your own unit. Then you can call it much like the Madshi code above:
var
Old_GetIndexForOrderBy_Data: TOverwrittenData;
OverwriteProcedure(@DBCommon.GetIndexForOrderBy,
@Fixed_GetIndexForOrderBy,
@Old_GetIndexForOrderBy_Data);
We have a folder under our project source tree called VCL, into which we place copies of the VCL source that we wish to modify slightly. Your example is a good candidate for doing the same thing. You will need to modify the search path for your project so that "your" VCL folder is earlier on your path than the "Source" folders under your Delphi installation. You may also find that if you copy one VCL source unit out and modify it, you will have to also copy other VCL source units out into "your" folder which may be dependencies.
Our reason for doing this is that we want our builds to have zero compiler hints and warnings. There are some parts of the VCL source that are not hint/warning free.
"I am familiar with Jeff Atwood's article about how errors are always the programmer's fault, but I believe I have really and truly found a bug in a Delphi .pas file"
Are you joking me? With Delphi you always blame Borland first :) Something is weird, go Google it and see if it is a Delphi bug. Only if cannot find any similar reports you site down and check your code line by line.
After I reinstall Delphi I have to patch the original PAS files in 6 (six) places. There are tons of bugs that appear on a fresh Delphi installation and can be easily reproduced. Delphi (the one that we all love) is full of bugs. There is an entire history created around this. There are so many people releasing external patches (such as http://andy.jgknet.de/blog/?page_id=288) and Borland/Imprise/GoGear/Embarcadero keep ignoring them. It is a true wonder they included FastMM in their release.
Anyway, I have recompiled those PAS files and now I replace the original DCUs with the patched ones.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With