ParallelFox 2.0 Released

ParallelFox 2.0 was featured in a session at Southwest Fox 2023 and provided to attendees in October 2023. It is now released to all FoxPro developers, and it contains the following new features:

  • In-process Workers
  • Single-command event handler
  • Reg-Free COM
  • Named Instances
  • Temporary Tables
  • Process Cancellation
  • Limited support for VFP Advanced

See the New in 2 and VFP Advanced Support pages for more details.

Installation

The easiest way to install ParallelFox 2.0 is to use Doug Hennig’s FoxGet Package Manager. FoxGet downloads only the files necessary to use ParallelFox, runs install.prg, and adds files to your project.

If you want to download all ParallelFox files, including the Help file and examples, follow the Manual Installation instructions on GitHub.

The Southwest Fox 2023 session video, white paper, and slides will be made available to all FoxPro developers at a later date.

Reg-Free COM with Out-of-Process Servers

Generally speaking, a COM object must be registered in the Windows registry to be used by applications. In the past, I have not had an issue with this, as it was just another step in the installation package. In recent years, we have been offering cloud services to our clients. On a single server, we now have multiple clients and dozens of users all relying on the same COM objects. This makes it more difficult to deploy updates to a COM object. It also prohibits having multiple versions of a COM object, because only one version can be registered at a time (AKA “DLL Hell”). These days, registration-free COM is looking a lot more attractive, as it enables multiple “side-by-side” versions of a COM object that can be managed independently. It also allows deployment without the admin elevation that is normally required to register a component.

For a while now, we have had the ability to use custom manifests that allow applications to make use of COM objects without registering them. Some Visual FoxPro devs might make use of tools by Craig Boyd to assist with this. Visual Studio provides tools for automating manifests as well. There’s one catch… this technique only works with in-process DLL COM servers. I often use out-of-process EXE COM servers for Visual FoxPro because they have fewer limitations than DLLs. Is there a way to use an EXE COM server without registering it first?

A cursory Google search might lead you to answer “No”, but Larry Osterman of Microsoft fame tells us otherwise. In his blog post from 2005, he points out a “clever trick” to accomplish this: “When COM tries to activate a COM object, before it looks in the registry, it looks to see if there’s a server already registered for that class.” In more detail… if the COM server EXE has already been launched and CoRegisterClassObject() was called BEFORE instantiating the COM object in a client application, the COM object can be used even though it is not in the Windows registry. Clear as mud?

First of all, we are talking about two different types of registration here. There’s the standard registration of a COM object into the Windows registry that we are all familiar with. Then there’s CoRegisterClassObject(), which registers out-of-process COM objects in memory with the Windows COM/OLE system (not the Windows registry). This is done from inside the COM server, and it is what allows the COM objects to be found by client applications. Okay… so how do we call CoRegisterClassObject()?

Good news! No Win32 API calls are necessary. Your out-of-process COM server already knows how to do this, you just have to tell it to do so. There is not a mandatory standard for all COM servers, but Microsoft convention is to include the following parameters when launching the EXE: “/automation -Embedding”. This convention is used for VFP COM EXEs, MS Office, and probably many others. Here’s some VFP code for launching the COM EXE:

Local lcRun
lcRun = [Run /n "] + FullPath("MyCOMServer.exe") + [" /automation -Embedding]
&lcRun

I have used FoxPro’s RUN command to launch the executable. Win32 API ShellExecute() could be used as well, although I should mention I did run into some timing issues that I didn’t bother to troubleshoot. CreateProcess() is another option.

If your COM server doesn’t use this convention, register and instantiate it normally on your dev machine. Then open Windows Task Manager, go to the Details tab, and enable the Command Line column. That will show you what parameters were included when the COM EXE was launched.

At this point, the COM EXE server is running and ready to be used by a client application. You should be able to use VFP’s CreateObject() to instantiate the object, right? Unfortunately, no. CoRegisterClassObject() has registered the object in memory using a “Class ID” (CLSID), which is a GUID. CreateObject() expects the more friendly “Program ID” (ProgID) that we are familiar with. Behind the scenes, CreateObject() calls CLSIDFromProgID() to lookup the ProgID in the Windows registry and return the corresponding CLSID, then it calls CoCreateInstance() using that CLSID. We want to avoid using the Windows registry for deployment, so job one is to find the CLSID for our COM object. To do that, you can look in the Windows registry on your development machine and find your ProgID under HKEY_CLASSES_ROOT. For VFP, there is also a VBR file in the project folder that you can open in a text editor and find an entry like this:

HKEY_CLASSES_ROOT\MyCOMServer.MyObject\CLSID = {12345678-90AB-CDEF-1234-567890ABCDEF}

Now that we have the CLSID, we are ready to instantiate the object. Again, VFP’s CreateObject() will not accept a CLSID, but CreateObjectEx() will!

#DEFINE MYCOMOBJECT_CLSID = "{12345678-90AB-CDEF-1234-567890ABCDEF}"
loMyComObject = CreateObjectEx(MYCOMOBJECT_CLSID, GetEnv("COMPUTERNAME"))

I should point out that this calls CoCreateInstanceEx() rather than CoCreateInstance() behind the scenes. This is typically used to instantiate a remote DCOM object on a different machine, but I have designated that it is on the current computer. I have not encountered any repercussions from doing this (performance or otherwise), but your mileage may vary. You could call CoCreateInstance() directly to instantiate the object as a “Local” out-of-process server instead.

We now have everything we need to instantiate an out-of-process COM object without registering it, but one caveat remains. FoxPro’s RUN command and ShellExecute() cause the client application to lose focus when the COM server EXE is launched. This doesn’t happen when instantiating a COM object that has been registered the normal way. You could probably prevent this by using CreateProcess() to launch the server, but it has a complex API. Using LockSetForegroundWindow() seems to avoid the unwanted behavior. Here’s the complete code:

#DEFINE MYCOMOBJECT_CLSID = "{12345678-90AB-CDEF-1234-567890ABCDEF}"
Local lcRun, loMyComObject 
DECLARE INTEGER LockSetForegroundWindow IN user32 INTEGER uLockCode
LockSetForegroundWindow(1) && lock	
lcRun = [Run /n "] + FullPath("MyCOMServer.exe") + [" /automation -Embedding]
&lcRun
loMyComObject = CreateObjectEx(MYCOMOBJECT_CLSID, GetEnv("COMPUTERNAME"))
LockSetForegroundWindow(2) && unlock

Double Trouble with COM Interop

I have been calling FoxPro code from C# via COM Interop (using DotNet2Fox) for several years, but I ran across an interesting issue last week. I have some FoxPro code that performs calculations and returns the results to .NET. I expected one of those values to be zero, but according to C#, it was -3.5527136788005e-15. Huh? After repeated tests, I got the same result.

I was assigning the FoxPro results to C# variables of type “decimal”. The C# compiler had already informed me that FoxPro was returning values with type “double” over COM. I have heard about the tension between binary and floating point math in computers before, but in practice, I have never run into an issue. I initially thought that the double-to-decimal conversion in C# was exposing this tension. As a test, I changed my C# variables to type double, but the problem persisted. I stepped through my code in FoxPro, and according to the debugger, the value was indeed zero. What the heck was going on?

I came up with a simpler calculation to reproduce the issue: 1.23 + 4.56. The result should be 5.79, right? Enter ? 1.23 + 4.56 in the FoxPro command window, and that’s what you’ll get. However, run the code below in C# and the result will be 5.790000000000001.

        dynamic vfp = Activator.CreateInstance(Type.GetTypeFromProgID("VisualFoxPro.Application", true));
        var result = vfp.Eval("1.23 + 4.56");
        vfp.Quit();
        return result;

Had I run across some glitch with COM interop? I needed to be certain that calculations done in FoxPro would come over to C# accurately. This was disconcerting.

Upon further thought… someone would have surely caught an issue like this with general COM interop, right? Maybe this was more on the FoxPro side. Then, I remembered a blog entry Christof wrote years ago on Significant digits in Visual FoxPro that mentions some oddities with 15-digit floating point calculations in VFP. Hmmmm… 15 digits. That’s the same precision of double types in C#. And look how many digits are after the decimal place in 5.790000000000001. So, I tried another test in the FoxPro command window:

SET DECIMALS TO 15
? 1.25 + 4.56

There it is! Now, FoxPro shows 5.790000000000001! Clearly, this is either some kind of binary-to-floating-point issue or something to do with that 15th digit, but SET DECIMALS masks that most of the time. SET DECIMALS is a mysterious command. It specifies the number of decimal places that will be displayed from calculations. It apparently also affects comparisons: ? 1.25 + 4.56 = 5.79 returns .T. when SET DECIMALS TO 2. But SET DECIMALS TO 15 and the same expression returns .F. Weird! SET DECIMALS appears to have no effect on calculated values returned over COM interop and always returns the full 15-digit precision of a double.

So, that explains why I had the issue when using COM interop. How do we fix it? Simple… round the calculated result to the desired number of decimals in FoxPro before returning the result, or in C# after the result is returned from FoxPro. In fact, I was able to resolve by rounding down to 14 digits (eliminating the troublesome 15th digit), but I can’t guarantee that would always work. By the way…if you want C# rounding to have the same behavior as FoxPro rounding, specify MidpointRounding.AwayFromZero:

    public static double FixDouble(double number, int roundDecimals = 14)
    {
        return Math.Round(number, roundDecimals, MidpointRounding.AwayFromZero);
    }

BindWinEvent and FoxTabs 1.3

Years ago, Greg Green reported that FoxTabs was conflicting with his custom VFP editors, due to VFP’s limitations binding to Win Msg events. Greg wrote this framework to deal with the limitation, and I made some modifications. I wrote about this on my blog, and BindWinEvent has been included with FoxTabs ever since.

More recently, I found that FoxTabs was conflicting with Doug Hennig’s ProjectExplorer for the same reason, so I implemented BindWinEvent in ProjectExplorer. Doug reported back with some fixes to issues he found. I then realized that BindWinEvent needed a separate repository of its own, so it is now a separate project and part of VFPX. If you have an IDE utility that binds to Win Msg events, I recommend using BindWinEvents to avoid conflicts with other utilities.

FoxTabs hasn’t had any love since 2014, so while I was in there, I figured I would investigate a long-standing issue where a label would disappear on some of the tabs. This turned out to be a compatibility issue with PEM Editor, because it sometimes changes the window title to include more information. Once I accounted for that, no more disappearing labels!

While working on that issue, I ran across some old code from Jim Nelson that used _EdGetEnv() from FoxTools.fll to determine the type of each window. I had never learned about that function before. I used it along with some other tricks to improve window type detection and the icons displayed for each type. Instead of a bunch of Fox heads, the tab icons are now a lot more informative and colorful.

You can get all these improvements in FoxTabs 1.3, available on GitHub or Thor Updater (coming soon if it’s not there already).

DotNet2Fox is now part of VFPX!

I’m happy to announce that DotNet2Fox has been accepted as part of the VFPX project.  “What is DotNet2Fox?”, you say?  Good question!  DotNet2Fox was first presented and released at Virtual Fox Fest 2021 (October), and I somehow failed to mention that here on my blog!  That is being rectified right now…

DotNet2Fox is an open-source library that simplifies calling Visual FoxPro code from .NET desktop and web applications. Have you tried your hand at COM interop only to be met with limitation after limitation? Did you find that exposing your existing FoxPro code over COM would require a major refactoring effort? DotNet2Fox provides a simplified interface for calling into existing real-world FoxPro code, and without all those limitations.

Over the past several years, I have been working to surface features from my desktop applications and make them available on the web via ASP.NET.  I found that the classic approach to COM interop recommended by Microsoft a couple of decades ago did not fit my needs. Over time, a library emerged to meet those needs: DotNet2Fox.

Geek Gatherings has graciously made recordings of Virtual Fox Fest sessions available to the entire FoxPro community on YouTube.

You can also get the whitepaper (documentation) and examples at the GitHub Wiki. Yes, full source code is on GitHub as well.  If you need help with DotNet2Fox, please start a Discussion or report an Issue. We’ll even accept Pull Requests.  I look forward to working with more folks  on this project.

ParallelFox 1.3 Released

The last release of ParallelFox was over 8 years ago in January 2012.  Also, 3 years have somehow elapsed since CodePlex was shut down and VFPX moved to GitHub. Time most definitely flies!  I have been using Doug Hennig’s Project Explorer on some projects. I decided to implement it with my VFPX projects, as well as do some housekeeping on GitHub. In the process, I noticed there were several tweaks/fixes (things checked in years ago on CodePlex) that never made their way into an official release.  So, I packaged those changes up into a new release: ParallelFox 1.3.

POTENTIAL BREAKING CHANGE: This release includes and requires ParallelFox.exe version 2.0 dated March 5, 2012. If you are using a previous version, this version needs to be installed and registered. This release is otherwise backwards compatible with previous versions, and it should not require changes in your code.

NOTE: If you previously cloned/downloaded the GitHub repository, you already have the changes in this release, including the latest ParallelFox.exe.

Welcome to JoelLeach.net!

Nearly a decade ago, I started this blog on the Foxite.com Community Weblog, a free service made available to the FoxPro community by Eric den Doop.  I wanted more control on the admin side — especially for dealing with spam – so I have moved the blog to my own site: joelleach.net.

All posts/comments have been retained, and links to the old site should automatically redirect to the new site.  Please update your RSS readers to point to the new feed.

Special thanks to Eric for making this service available to the community and for hosting my blog all these years.

Setting Subclass Properties in TypeScript

TypeScript brings full class-based inheritance to JavaScript projects.  This works very well, but you may run into some unexpected behavior when setting properties in a subclass and using those properties in the parent constructor. 

Here is a simple example that demonstrates the problem:

  • MyBaseClass is defined as an abstract class. Like other OOP languages, this means that the class cannot be instantiated directly.
  • MySubClass is a concrete subclass of MyBaseClass that can be instantiated. 
  • New in TypeScript 2.0 is the ability to define properties as abstract, which I have done with string1 and string2.  These properties must be set in the subclass, or the transpiler will generate an error. 
  • The parent class constructor sets the string3 property based on the values of string1 and string2 set in the subclass.  Imagine that string3 is a property that will be used by other methods in the base class (not shown in the example code), so it is a valid design choice to set that property in the constructor.
  • Finally, the last two lines of code instantiate the class and display string3.
// Abstract base class
abstract class MyBaseClass {

    // Abstract properties to be set in subclass
    protected abstract string1: string;
    protected abstract string2: string;

    public string3: string;

    constructor() {
        // Set string3 so it is available for rest of class
        this.string3 = this.buildString3();
    }

    protected buildString3(): string {
        return this.string1 + " " + this.string2;
    }
}

// Concrete subclass
class MySubClass extends MyBaseClass {

    // Abstract properties must be set or 
    //  transpiler error will occur
    protected string1 = "Hello";
    protected string2 = "World!";
}

// Create object and display string3
var myObject = new MySubClass();
alert(myObject.string3);

 

Of course, I expected this code to display “Hello World!”, but in fact it displays “undefined undefined”.  Why is that?  A look at the transpiled Javascript of the subclass constructor will give us a clue.

    function MySubClass() {
        _super.apply(this, arguments);
        // Abstract properties must be set or 
        //  transpiler error will occur
        this.string1 = "Hello";
        this.string2 = "World!";
    }

 

As you can see, the subclass properties aren’t set until AFTER the base constructor is called. Ryan Cavanaugh from Microsoft explains:

This is the intended behavior.

The order of initialization is:

1. The base class initialized properties are initialized

2. The base class constructor runs

3. The derived class initialized properties are initialized

4. The derived class constructor runs

Follow the link for more details on the reasons, but it comes down to the fact that property initialization is inextricably intertwined with the constructor. Alternative approaches have been suggested, but besides breaking existing code, the order above is likely to become part of the EcmaScript (JavaScript) standard. 

As an OOP veteran of other languages, I find this behavior unfortunate. By defining a class as abstract, you are in effect saying it is “incomplete“, and it will be completed by its concrete subclasses. These technical restrictions on property initialization and constructors get in the way, but there are things we can do to work around the problem.

Constructor Parameters

Rather than setting properties in the subclass, you can pass values to the base class constructor. 

// Abstract base class
abstract class MyBaseClass {

    // Changed abstract properties to private properties
    //  available in base class only 
    private string1: string;
    private string2: string;

    public string3: string;

    constructor(string1: string, string2: string) {
        // Set private properties in constructor
        this.string1 = string1;
        this.string2 = string2;
        // Set string3 so it is available for rest of class
        this.string3 = this.buildString3();
    }

    protected buildString3(): string {
        return this.string1 + " " + this.string2;
    }
}

// Concrete subclass
class MySubClass extends MyBaseClass {

    constructor() {
        // Pass values to base constructor
        var string1 = "Hello";
        var string2 = "World!";
        super(string1, string2);
    }
}

// Create object and display string3
var myObject = new MySubClass();
alert(myObject.string3);

 

This works, but stylistically, I don’t like it for an inheritance-based approach.  I’d rather have the ability to simply set properties in the subclass, but call it personal preference.  There is nothing wrong with this solution.

Constructor Hook Method

Here, I’ve added an initialize() hook method to the constructor that runs before the buildString3() method. This gives the subclass an opportunity to set properties the base class needs at the appropriate time.  I’ve declared the initalize() method as abstract, so that it must be implemented in the subclass.

// Abstract base class
abstract class MyBaseClass {

    // Properties to be set in subclass
    protected string1: string;
    protected string2: string;

    public string3: string;

    constructor() {
        // Call subclass initialize() before string3 is set
        this.initialize();
        // Set string3 so it is available for rest of class
        this.string3 = this.buildString3();
    }

    abstract initialize(): void;

    protected buildString3(): string {
        return this.string1 + " " + this.string2;
    }
}

// Concrete subclass
class MySubClass extends MyBaseClass {

    // Set properties
    initialize(): void {
        this.string1 = "Hello";
        this.string2 = "World!";
    }
}

// Create object and display string3
var myObject = new MySubClass();
alert(myObject.string3);

 

This also works, but it leaves much to be desired.  Even though I have declared the initialize() method as abstract, nothing forces the string1 and string2 properties to be set.  Notice that I had to remove the abstract keyword from those properties for this to transpile without error. In general, I like the idea of adding hook methods for subclasses to use, but they should be optional.  The base class should not depend on them, nor should it be ambiguous about which properties need to be set.

Getters/Setters

As you may have gathered from the above, methods do not suffer from the same constructor timing issues as properties.  The base class constructor called into the subclass initialize() method, and it functioned as expected.  Likewise, using getter/setter syntax for properties is an option:

// Abstract base class
abstract class MyBaseClass {

    // Abstract properties to be set in subclass
    // Use getter syntax
    protected abstract get string1(): string;
    protected abstract get string2(): string;

    public string3: string;

    constructor() {
        // Set string3 so it is available for rest of class
        this.string3 = this.buildString3();
    }

    protected buildString3(): string {
        return this.string1 + " " + this.string2;
    }
}

// Concrete subclass
class MySubClass extends MyBaseClass {

    // Abstract properties must be set or 
    //  transpiler error will occur
    // Getter syntax must be used in subclass as well
    protected get string1() { return "Hello"; }
    protected get string2() { return "World!"; }
}

// Create object and display string3
var myObject = new MySubClass();
alert(myObject.string3);

 

This is closer to the original vision.  Having to use getter syntax is a little wordy for my taste, when all you want to do is return a simple value.  You may not mind if you are used to this from other languages.

Move the Code

Finally, my favorite solution is to move the code out of the constructor, which is where the timing issue is.  I moved the code into the string3 property with getter syntax.  It won’t run until the property is accessed after the object has been constructed, so the timing issue is avoided.  I also added a private _string3 property for improved performance, but of course, that is optional.

// Abstract base class
abstract class MyBaseClass {

    // Abstract properties to be set in subclass
    protected abstract string1: string;
    protected abstract string2: string;

    // Private backing property (optional)
    private _string3: string;
    // Public property using getter syntax
    public get string3(): string {
        // Set private backing property if not already set
        if (!this._string3) {
            this._string3 = this.buildString3();
        }
        return this._string3;
    }

    protected buildString3(): string {
        return this.string1 + " " + this.string2;
    }
}

// Concrete subclass
class MySubClass extends MyBaseClass {

    // Abstract properties must be set or 
    //  transpiler error will occur
    protected string1 = "Hello";
    protected string2 = "World!";
}

// Create object and display string3
var myObject = new MySubClass();
alert(myObject.string3);

 

This solution is the closest the original code.  I also like the idea of doing more in the base class, so you can do less in subclasses.

Your mileage may vary depending on your specific scenario, so choose the workaround that works best for you.

References

Report Metadata Error

A few of our clients were getting an obscure error when running reports.  I couldn’t find any information on the error online, so I thought I would post a potential workaround here to help the next person that runs into it.

While the report is running and being prepared for preview, a dialog pops up prompting the user to find a table/DBF. When the user presses Cancel, the dialog may or may not pop up again multiple times. Eventually, the user receives this error:

File ‘m_4nl0nroh6.dbf’ does not exist.
The metadata for some report definition rows could not be loaded.
Some dynamic report features may be missing, or a report could not conclude successfully.

The error occurs in frxcursor.UnpackFrxMemberData() while attempting to ALTER TABLE on a cursor that was created only a few lines earlier. For some reason, the underlying DBF is missing, but I can’t explain why.

What I did find is that some of our reports contained generic MemberData in the STYLE field of the FRX that was not being used. Once removed, the code producing the error will not need to run. I suspect the data inadvertently got in there in the first place using the Report Designer. If you go to Field Properties->Other and click on the Run-time extensions->Edit Settings button, it will automatically add code to the STYLE field, which will be saved if you press OK.

NOTE: There are legitimate uses of MemberData in the STYLE field, such as rotated labels. In those cases, the STYLE field needs to remain populated, so be sure before deleting the data.

SCCTextX for Data

Over the years, there have been debates about whether it is best to use source control integrated with the VFP Project Manager or to keep it separate.  I’ve always preferred to have the integrated experience.  Regardless of which side you fall on, it is very useful to have textual representations of VFP’s binary source files (SCX, VCX, etc.).  These text files enable diffs, so a developer can compare different versions of a source file and see what changes were made.  VFP includes SCCText.prg in the box, which has improved over time, but leaves a lot to be desired.  The SCCTextX project on VFPX is a major improvement and makes the resulting text files much more usable.

However, one thing I’ve always wanted was the ability to generate text files for DBCs and DBFs that we include in our project and source control.  The DBCs contain valuable information about the data structures, as well as local/remote view definitions.  The DBFs are primarily metadata for things like Stonefield Database Toolkit and the framework we use, Visual ProMatrix.  Before I checked in my latest changes to these files, I decided to crack open SCCTextX.prg and take a look at what could be done.  Lo and behold! There is already code to deal with DBCs and the beginnings of code for DBFs, which by default had been disabled.  I thought to myself, “I could have something working within a couple of hours”, so I dug in. Three days later… I finally had a solution, but with caveats.

There was a reason the code for DBCs was disabled.  The text file it produced was useless for diffs.  After some trial, error, and experimentation, I ended up with modified versions of SCCTextX.prg and FoxPro’s GenDBC.prg .  SCCTextX_Data.prg now calls GenDBC_SCCTextX.prg to generate a text file for DBCs.  It expects GenDBC_SCCTextX.prg to be in the same directory as SCCTextX_Data.prg.  I made two modifications to the GenDBC program.  The first was to sort the entries, so they are created in a consistent order.  The second was to parse CREATE SQL VIEW commands into multiple lines, which otherwise appear in GenDBC on one line, making it very difficult to see what has changed.  I’ll tell you up front that the parsing is not very good, and definitely not as good as I have seen in other VFP products/projects, but I needed something simple and lightweight, and I find it good enough for diff purposes.  Also, GenDBC is a little slow compared to other text file generation, but it wasn’t a showstopper for me.  NOTE: GenDBC_SCCTextX.prg is only intended for source control diff purposes, and I do not recommend it as a replacement of the standard GenDBC.prg for creating databases.

Aside: If you look in SCCTextX.prg, you may notice the developers tried to change the extension for DBC text files from “DBA” to “DCA”.  I agree with this change.  Unfortunately, the VFP Project Manager forces and expects the DBA extension.  If some aspiring developer were to create a fully functional replacement for the Project Manager on VFPX (hint, hint), this (and other limitations with source control integration) could be overcome.  But as it stands, we have no control over it.

That takes care of DBCs, how about DBFs?  Well, it turns out that the code included in SCCTextX.prg for DBCs is actually pretty good for DBFs.  So, easy right? Wrong.  The first problem has to do with the Project Manager integration.  VFP doesn’t even call SCCTextX for files in the Free Tables section.  That explains why we only have the “beginnings” of DBF support.  However, we can trick VFP into calling SCCTextX by putting the DBF into the Databases section of the project.  There are three ways to do this:

  1. Add the DBF manually in the Databases section.  VFP will complain, but the file will still be there.
  2. Hack the PJX (USE MyProject.PJX) and change the type from “D” to “d” on the applicable files.
  3. If the project is open: _VFP.ActiveProject.Files(“MyTable.dbf”).Type = “d”

Once the DBF is in this section, VFP will call SCCTextX and otherwise integrate properly with source control.  SCCTextX_Data.prg is smart enough not to run GenDBC for files that don’t have a “DBC” extension.  The text file extension for both DBCs and DBFs will be “DBA”, so you can’t have a table and database of the same name, but that wasn’t a problem for me.

So far, so good, but there are other issues with DBFs.  You might want to exclude certain fields like ID fields or timestamps that change often and clutter the diffs.  Or you might want set the order for the table to get consistent results.  For this purpose, SCCTextX_Data.prg will call SCCTextX_Custom.prg if it exists in the same directory, giving you an opportunity to specify these settings.  See SCCTextX_Custom – Example.prg in the download.

So now we’ve got text files for DBCs and DBFs, integrated with the Project Manager.  Time for a quick build and… FAIL.  Ugh! VFP doesn’t like the DBFs in the Databases section.  Nothing a little project hook (included in the download) can’t fix though.  It moves all non-DBCs to the Free Tables section before the build and puts them back afterwards.

With all of these caveats, I think it is obvious why I won’t be submitting my changes to the SCCTextX project manager at VFPX.  That said, I’ll tell you it is VERY nice to finally be able to run diffs on these files.  Definitely worth the caveats and overall effort for me.  If you want to try it yourself, feel free to download SccTextX_data.zip.