Debugging COM objects in VFP in real time as an application runs is a real pain, but with a little trickery you can make this work. I’ve been working extensively on a Visual Studio .Net 2003 Add-in for Help Builder which allows you to basically right click on a method or property and import the method directly into Help Builder.
The Help Builder VS.Net Add-In collects all the relevant information about the Code element (Name, parameters, return type, static etc.) and sticks it into an object that is passed back to Help Builder to process using COM Interop to do so.
Works great, but the debugging process for this scenario is pretty hairy – you’re basically working blind when the Fox code in Help Builder gets control and receives this exported object from .Net. There's no other way to create this object, which means the only way to get there is through the Add-In.
Enter the VFP Automation model to help out and provide an easier way to build the VFP server code. Rather than call the method directly you can use the Visual FoxPro as an Automation Server to instantiate Visual FoxPro pass it the parameter value and then execute the code inside the VFP IDE where you can debug it.
Here’s a small snippet from the Add-In that is relevant for the call to VFP:
public void UpdateHelpBuilderMethodCSharpComment(CodeFunction element)
CodeFunction func = element as CodeFunction;
ObjectMethod Method = new ObjectMethod();
Method.cName = func.Name;
Method.bStatic = func.IsShared;
Method.cReturnType = func.Type.AsString;
if (func.Access == vsCMAccess.vsCMAccessProtected)
Method.cScope = "protected";
else if (func.Access == vsCMAccess.vsCMAccessPrivate)
Method.cScope = "private";
else if (func.Access == vsCMAccess.vsCMAccessProject)
Method.cScope = "internal";
Method.cScope = "public";
… More code to set properties of the Method object
Type loT = Type.GetTypeFromProgID("visualFoxpro.application.8");
object VFP = Activator.CreateInstance(loT);
int x = 0;
The ‘real’ COM call is using a late binding call using Reflection with some wrappers I use frequently to simplify dynamically accessing the late bound object’s properties and methods. You can see how late binding works in the #else code that instead instantiates the VFP development environment.
The idea is pretty simple: You instantiate VFP, and set up your environment – mainly making sure that you change path to your work directory from which to run your app. Make VFP visible and use the SetVar method to assign variables to VFP – these vars become PUBLIC vars that you can reference from VFP and they are ideal for passing objects. If you need to pass simple parameters you can expand them into a string using DoCmd and WITH parameters or .Eval() with the appropriate parameters.
Then I run a small stub program that I call wwHelpDebug which is simply a loader that performs code similar to what the actual method call does, but using plain VFP code:
SET PATH TO \wwapps\wc3\classes; \wwapps\common; \wwapps\wc3\; \wwapps\wc3\tools; .\Code
SET RESOURCE ON
*** Load Class Libs
DO WWHELP WITH "LOAD"
LOCAL oHelp as wwHelp
oHelp = CREATEOBJECT("wwhelp")
llResult = oHelp.OpenForm(goElement.cSignature,4)
oHelp.oForm.AlwaysOnTop = .f.
loHelp = oHelp.oForm.oHelp
*** For now create a new topic always - must check later
loHelp.oTopic.Pk = ""
o = CREATEOBJECT("wwHelpEvents")
loTopic = o.ImportCSharpComment(goElement,oHelp.oForm.oHelp.oTopic)
The key for this stub program is to set up your environment – set paths, set settings, and most importantly here load any class libs and procedures.
When this method runs goElement will be in scope because we passed it in with the SetVar() method call from the .Net code. So we’ve efficiently moved into a debuggable VFP environment. What’s nice now is that VFP is up and running and you can step through your code and change it and even re-run the VFP portion of the application as long as you make sure that you don’t CLEAR ALL or RELEASE ALL or otherwise remove the reference for goElement.
This is a huge timesaver especially in a scenario like this where you cannot fake the object being passed to you any other way. The Add-In creates this object that’s passed and there’s no easy way to instantiate and populate this object any other way.
Other Posts you might also like