Differencing VFP "Things" (vcx, scx, frx, dbf, dbcx, etc)
VFP 8 and 9 came with a tool scctext.prg which can convert many binary files into text for comparison using your favorite tool. ( Some info at http://msdn.microsoft.com/en-us/library/eb43dww2(v=vs.80).aspx ) This tool can run automatically if you tell VFP to integrate with your source code control tool.
This is a VFP form that shows the difference between 2 VCX's at a fine resolution. Class definitions are somehow matched up, properties are matched (missing prop values are greyed out) and the methods are paired up. Currently the method code is just displayed in VFP edit boxes which is next to useless, but the code can automatically be written out to 2 files which can then be diffed using your favorite diff tool.
Beyond Compare is a workable works great for this, because it will let you write out new data to the same file it is displaying, so it makes it easy to flip between Vcx Diff and Beyond Compare. (best to just try it and see.)
This tool, available from january 2014, is for VFP 9, and support 2-way conversion of pjx, pjm, scx, vcx, mnx, frx, lbx, dbf and dbc binary files to text (prg-style for pjx/pjm/scx/vcx/mnx and xml-style for dbf/dbc/frx/lbx) for comparison using your favorite tool.
The capability of converting texts back to binary makes this tool perfect for Vfp Merge operations with most popular SCM tools.
The project is under VFPx Codeplex site, at https://vfpx.codeplex.com/wikipage?title=FoxBin2Prg
(see next section for VFE specific stuff)
Fill in the Left and Right with the names of 2 vcx, scx, dbf or dirs to compare. Hit Diff.
If the Right is just a dir, it will use the filename from the left.
Cmpares VFE metadata: Set Left and Right to 2 different dirs, like foo\project\metadata\ - if it finds coremeta.dbf in Left it runs the DiffDbcx and shoves the results in the prop grid. It 'works', just isn't exactly pretty.
Dirs can be compared: end Left and Right with \*.*, hit Diff. You will see the superset of filenames and the last mod datetime. Select a file and hit the "Diff Files..." button - it will run a diff on that file.
The "To Files" button will create c:\temp\Left.prg and c:\temp\Right.prg. the Checkbox will write those files every time new code is shown in the 2 method editboxes (it won't write out if there is no code.) Leave Beyond Compare open, and when you switch to it you will be prompted "Files changed, reload?"
(We assume you have VFP8 or 9 installed to the default location.)
0. Unzip anywhere.
1. DO FORM vfpdiff.scx
2. Hit "Diff" - this will find all the .vcx's in vfp\fcc that are different between the 2 versions of VFP.
3. In the right hand grid, select "registry.vcx" and hit "Diff One File" - this will find the differences in the 2 versions of registry.vcx
4. click on "init" in the lower grid. - this will show you the 2 init methods.
5. hit "Target" - this will open the 2 classes/methods in the class designer and put the cursor where it found the diff.
1. close all the class designers, get back to the form that has Init in the grid.
2. Below the grid is a textbox with "p:\htmp" - change that to your favoriate dir.
3. Hit "To Files."
4. Look in the dir - you should see left.prg and right.prg.
5. stuff those into Beyond Compare.
More Advanded demo
1. Leave the left/right.prg files open in BC (Beyond Compare.)
2. Find some other classs to diff and diff them
3. Find a method that has a difference,
4. Hit "To Files"
5. Switch to BC - it should see that left/right.prg changed and prompt you to reload them. say yes.
Quick instructions for what VFEers want do now
1. copy vfe\vfeframe\libs\*.* \temp\
2. Run VFE setup.exe, unzip the zip, get all the new classes "installed".
3. CD into the Vfp Diff.scx dir.
4. Put \temp\*.vcx in Left, vfe\vfeframe\libs\*.* in Right, hit "Diff" (just diff, not the all files button.) This will give you a list of files with different timestamps (like all of them.)
5. Now you can hit "Diff All Files" and it will hit each one and open up a new form if it finds differences in the VCX.
Or if you don't want 20 forms opened, pick a file and hit "Diff one file"
Now the fun part: use the 1st grid to pick a class, and the other 2 will show the different props and methods. Well, it will try to show the different methods, but an edit box just isn't big enough. That is what the “Target” button is for: it will open the 2 classes up in the editor and put the cursor on the line where the code changed. (where Real code changed, not comets)
Also, if you check the “To Files” box and set a dir, it will create “Left.prg and “Right.prg” out of the current method. Use your favorite diff program on those 2 files. If ou use beyond Compare, don't close it, just write out new left/riths and it will prompt you to reload the files. Pretty slick.
UI needs an overhaul.
Problems with current version: first grid is used for object names and dbcx file names, but the headers don't change. The 2nd grid is used to display class properties, dbcx properties and filenames/mod datetime, also with the same headers. The method editboxes are usless.
File names need to be VFP aware: For sets of files, like dbf/fpt/cdx. vcx/vct, etc. show only the 'main' file and the most recient datetime of the set.
Deal with added/removed classes. (i think the works now... needs testing.)
Option to match on Class Name instead of UniqueID. Or something so this can be used to compare a sub-class to it's parent.
Wholy cow. UniqueID has duplicate values in it. Had to switch to ObjName. In a way that is better. Not sure what the implications are, other than it works better.
Look at ALL the fields in the VCX, not just Properties and Methods.
Allow editing, either by MODIFY CLASS or editing the data on the Vcx Diff form and then writing it back to the vcx.
Compare all the files in 2 dirs, or the files contained in 2 projects.
dbf diff - some code there, never finished it.
Add a checkbox to only show differences between the two VCXs, etc.? (code is mostly there, but 'disabled' to make debuggin easier... don't know why I didn't think of making it an option... duh. That's the glory of the wiki! - Thanks.
Beta: http://dev.personnelware.com/carl/pub/VfpDiff/VfpDiff.zip (still pretty rough)
Or get the bleading edge: (you will need to have Subversion)
svn checkout svn://svn.personnelware.com/VfpDiff
Comments and sugestions appreciated.
Espically ideas for the method code Diff Programs and a being used with a project hook class that drives Sub Version.
VFP 6 Compatibilities
Hi Carl. Is there a VFP6 version available?
I think it will work with this one hack at the top of .Load:
#define varchar char
Otherwise, download the VFP9 public beta.
Thanks for the suggestion but there is also autoinc, which I can guess can be tackled with loops somewhere?
Hmm, I don't think I am actually using that field for anything yet. But by now you will shoud have 9 downloaded ;)
Here is what I have done 1) uncommented the
#define varchar char in Load() and just underneath added
#define autoinc 2) put in
REPLACE ALL kProp_pk WITH RECNO() in DiffDBCXProps() and DiffProps() 3) StrExtract() maybe a new VFP function as it is not available 4) ASCAN() has more parameters than in VFP6 in DiffMethods(), DiffDBCXProps() and DiffProps()
I do plan on making the form resizable using VFP9's control anchors, not sure how previous versions will react to it.
I don't know how many would like to use it in VFP6, but I guess there are #if ways to stop execution in a given version, maybe you can use them to keep it backward compatible maybe a couple of less facilities due to older versions?!?
It is currently turned intoa big ball of mud that needs to be re-coded. I have cut/pasted the same wad of code about 6 times now. Each time I improve on it a bit, and sometimes replace older version with the improved version. One reason for this is because the wad of code has to A) get arrays of whatever it is diffing, and B) know how to compare each item. I am sensing a ton of classes coming out of this. I am also stuffing things into the grids that don't quite make sense. Given the amount of mess this is, trying to be VFP6 compatible doesn't sound like much fun. What I could do is distribute the source plus e exe+runtimes.
Carls Vcx Utils and Browser Add Ins - vt
05-nov-2004 - Addin 'Sorting' can work with fields
'Properties' and 'Reserved3' also.
9-aug-2005 'Sorting' can make backup copies and
compiling now. -- vt
* FUNCTION VcxDiff()
* Shows the difference between 2 VCX's (SCX`s) by means Class Browser
* tcVcxScx1 - A name of .vcx, .scx number 1
* tcVcxScx2 - A name of .vcx, .scx number 2
* tcClass1 - A class in the .vcx N1 to compare
* tcClass2 - A class in the .vcx N2 to compare
* See also a Class Browser add-in VComp.prg
* Vladimir Tokarev, Russia.
#DEFINE cc_FIRST_PRG_FILE 'Vcx2Prg1.prg'
#DEFINE cc_SECOND_PRG_FILE 'Vcx2Prg2.prg'
#DEFINE cc_LOG_FILE 'VcxDiff.log'
* same name as in Sorting.h
* (Sorting - A Class Browser Add-In that can sort all procedures
* in .VCX or .SCX)
#DEFINE cc_ADDIN_SORTING_NAME 'Sort procedures'
LPARAMETERS tcVcxScx1, tcVcxScx2, tcClass1, tcClass2
IF VARTYPE(tcVcxScx1) <> 'C' OR EMPTY(tcVcxScx1) ;
OR NOT (FILE(tcVcxScx1) OR FILE(FORCEEXT(tcVcxScx1, 'vcx')) ;
OR FILE(FORCEEXT(tcVcxScx1, 'scx')))
lcVcxScx1 = GETFILE('vcx,scx','','',0,'Where is .VCX (.SCX) N1')
lcVcxScx1 = IIF(FILE(tcVcxScx1), tcVcxScx1, ;
IIF(FILE(FORCEEXT(tcVcxScx1, 'vcx')), FORCEEXT(tcVcxScx1, 'vcx'), ;
IF VARTYPE(tcVcxScx2) <> 'C' OR EMPTY(tcVcxScx2) ;
OR NOT (FILE(tcVcxScx2) OR FILE(FORCEEXT(tcVcxScx2, 'vcx')) ;
OR FILE(FORCEEXT(tcVcxScx2, 'scx')))
lcVcxScx2 = GETFILE('vcx,scx','','',0,'Where is .VCX (.SCX) N2')
lcVcxScx2 = IIF(FILE(tcVcxScx2), tcVcxScx2, ;
IIF(FILE(FORCEEXT(tcVcxScx2, 'vcx')), FORCEEXT(tcVcxScx2, 'vcx'), ;
lcClass1 = ''
lcClass2 = ''
IF VARTYPE(tcClass1) = 'C'
lcClass1 = tcClass1
IF VARTYPE(tcClass2) = 'C'
lcClass2 = tcClass2
IF NOT EMPTY(lcClass1)
lcClass2 = lcClass1
IF VARTYPE(_oBrowser) <> 'O'
DO (_browser) with ,,,,,,.t.
llBrowser = .t.
WAIT WINDOW 'Processing a file N1 ...' NOWAIT
lcFileName = .cFileName
IF NOT .lScxMode AND NOT EMPTY(lcClass1) AND NOT EMPTY(lcClass2)
WAIT WINDOW 'Processing a file N2 ...' NOWAIT
IF NOT lcFileName == .cFileName
IF NOT .lScxMode AND NOT EMPTY(lcClass1) AND NOT EMPTY(lcClass2)
lcCmd = 'RUN fc /N ' + ;
cc_FIRST_PRG_FILE + ' ' + ;
cc_SECOND_PRG_FILE + ' > ' + ;
* lcSaveSafety = SET('safety')
* SET SAFETY OFF
* STRTOFILE(CPCONVERT(866, 1251, FILETOSTR(cc_LOG_FILE)), cc_LOG_FILE)
* SET SAFETY &lcSaveSafety
* MODIFY FILE (cc_LOG_FILE) AS 866
MODIFY FILE (cc_LOG_FILE)
DELETE FILE (cc_FIRST_PRG_FILE)
DELETE FILE (cc_SECOND_PRG_FILE)
* IF FILE(cc_LOG_FILE)
* DELETE FILE (cc_LOG_FILE)
Category Code Samples Category Meta Data Category File Comparison Tools