Beyond Compare and binary DFMs
Binary DFMs are a pain, because you can’t see what changed between two versions. Right?
That’s what we thought. We’ve been avoiding binary DFMs since time immemorial, especially back in the Dark Ages when we used VSS, which won’t even keep revision history for binary files. Even now that we’re on Subversion — which has no problem with binary files, thank you — we still avoid binary DFMs. Among other things, it’s hard to write a Ruby script to grep through a directory of binary DFMs. And, of course, they’re hard to diff. Or so I thought.
So imagine my surprise yesterday when I was writing a Ruby script to grep through our code and DFMs, and it dumped out some gibberish and beeped a couple of times. I looked, and sure enough, we had a binary DFM. It wasn’t even some fringe GUI that we had written once and never modified — we had, in fact, made several changes to the DFM in the past year.
This really puzzled me. Did this file get accidentally changed to binary format recently? But we should’ve noticed when we checked in. We shouldn’t ever check in without looking at the diffs first. And of course you can’t diff a binary DFM, right?
But I looked at the file history. That file had been in our repository for years, binary all the way. It had been modified several times. Surely people had tried to look at the diffs. So what happened?
I will, at this point, call your attention to two things:
- We’ve been happy users of the superb Beyond Compare diff tool for almost three years, and have made it our default diff viewer for TortoiseSVN.
Beyond Compare is written in Delphi.
Yep. Of course Beyond Compare, as a Delphi app, has some Delphi-isms built in. It’s so cool that it can diff binary DFMs, and make them look just like text in the diff viewer. Internally, I’m sure they call Classes.ObjectBinaryToText (handy thing about writing a tool in Delphi that works with Delphi code, you get the library functions for free), so as far as you can tell when you’re looking at diffs, the file looks like it’s been a text DFM all along.
My only gripe is that it does this utterly silently. There’s no indication that this is a binary DFM that it converted to text for your convenience. And since binary DFMs are inconvenient in other ways (e.g., Trac can’t show diffs on them), I’d rather not have any binary DFMs in our code base at all. So in some ways, I’d almost rather not have this feature — I’d notice the binary DFMs a lot sooner if they looked like garbage in the diff window.
Then again, when I re-saved the DFM as text, it was sure nice to have those diffs to show that nothing important had changed. Binary DFM on the left, text DFM on the right, and the handful of changes highlighted just as nice as you please.
May 31st, 2008 at 7:46 am
Convert between a DFM and a TXT file
A conversion from DFM (binary Delphi Form file) to a text file is done easily using the function ObjectResourceToText. For the opposite direction, use ObjectTextToResource.
The following code sample packs the whole task in a handy function.
Usage:
// convert a DFM file to TXT
ConvertFormOrText(’e:\temp\unit11.dfm’, ConvertToText);
// vice versa, extension is not necessary
ConvertFormOrText(’e:\temp\unit11′, ConvertToDFM);
type
TDFMorTXT = (ConvertToForm, ConvertToText);
{ Given a file name this routine will convert the file from either
1. A text file to a DFM file or
2. A DFM file to a text file
The output file name is built from the input file name }
function ConvertFormOrText(FileToConvertFrom : string;
ConversionType : TDFMorTXT) : boolean;
var
InputStream, OutputStream : TFileStream;
FileToConvertTo : string;
begin
Result := True;
FileToConvertTo := FileToConvertFrom;
{ change file extensions as appropriate }
case ConversionType of
ConvertToForm:
begin
FileToConvertFrom := ChangeFileext(FileToConvertFrom, ‘.TXT’);
FileToConvertTo := ChangeFileext(FileToConvertFrom, ‘.DFM’);
end;
ConvertToText:
begin
FileToConvertFrom := ChangeFileext(FileToConvertFrom, ‘.DFM’);
FileToConvertTo := ChangeFileext(FileToConvertFrom, ‘.TXT’);
end;
end;
try
try
{ Create a file stream for the specified file }
InputStream := TFileStream.Create(FileToConvertFrom, fmOpenRead);
OutputStream := TFileStream.Create(FileToConvertTo, fmCreate);
{ Now perform the selected conversion }
case ConversionType of
ConvertToForm : ObjectTextToResource(InputStream, OutputStream);
ConvertToText : ObjectResourceToText(InputStream, OutputStream);
end;
except
On EStreamError do Result := False;
end;
finally
InputStream.Free;
OutputStream.Free;
end;
end;
May 31st, 2008 at 8:02 am
my submission is not my creation; it’s something i saw on the internet a few years ago & used occasionally. i don’t know the author.
June 1st, 2008 at 6:21 pm
You should check out the latest beta of Beyond Compare that has been made public. It has a bunch of improvements over the current version.
June 2nd, 2008 at 12:34 am
Hi,
you say, this tool is written in Delphi…
How comes that the new version supports Linux ?!?
http://www.scootersoftware.com/beta3/download.php
June 2nd, 2008 at 4:16 pm
Yeah, I’m a huge fan of Beyond Compare too. Their latest offering (which I think is in public beta currently) adds the significant improvement of 3-way merging, which means that you can use BC as your merge tool in TortoiseSVN as well, including 3-way merging of binary DFMs.
BC rocks!
June 3rd, 2008 at 10:37 am
Joe: As Jeremy said, you should definitely check out our new version. Among other things, it now displays whether it’s a converted DFM or not, and it’s now possible to disable the support if you really want to.
Markus: We’re using Kylix for the Linux version. We’re using the Qt3 version of the old Unofficial VisualCLX patches along with numerous patches of our own. We have a wrapper layer that sits on top of the TNT controls in Windows and CLX in Linux, so we’re able to compile everything form a single set of .pas/.dfm files without maintaining .xfms.