Rick Strahl's FoxPro and Web Connection Web Log
White Papers | Products | Message Board | News |

COM Interop and .NET Guids


Thursday, April 9, 2009, 5:31:00 PM

I ran into a nasty problem with a Web Service the other day that expects a GUID as part of its parameters. I’m using the West Wind Web Service Proxy Generator to generate a .NET service proxy for the service for use in Visual FoxPro. The service expects a GUID which in the .NET service proxy is then represented as a System.Guid object.

 

System.Guid is a value type in .NET which means it’s not based on a class but on a struct. This struct is a ‘by value’ passed object and unless .NET provides custom marshalling for the type, COM access to this type fails. Guid happens to have no custom COM marshaller and so passing it over COM to VFP fails.

 

A simple example of what doesn’t work are these two methods:

 

        public Guid GetGuid()

        {

            return Guid.NewGuid();

        }

 

        public string SetGuid(Guid guid)

        {

            return guid.ToString();

        }

 

If create these now through COM Interop there’s no way to call the first method and get a result, and because you effectively can’t create a new instance of a GUID in VFP, no way to even think about calling the second one. So the following sequence of Fox code fails miserably:

 

o = CREATEOBJECT("westwind.wwDotNetBridge")

loVal =  o.GetGuid()

? o.GetSetGuid(loVal)

 

You end up getting an error: Function argument value, type or count is invalid.

 

That sucks royally! Especially since there’s really no way around this because no matter how you twist this with direct COM interop there’s no way to create a .NET System.Guid instance in FoxPro. I played around with Reflection and some internal mapping which works at the .NET end but always fails when the guid in anyway is exposed to FoxPro code. Bummer.

Injection

After giving this some more thought and testing a few dead ends I finally managed to find -  an admittedly ugly -  solution that might be of interest for a host of other types that might not work inside of Visual FoxPro via COM interop.

 

The concept is based on a façade and call interjection, that relies on indirect calls to the COM server. Some time back I’ve built a DotNet Interop tool for Visual FoxPro called the wwDotNetBridge which is as the name suggests a bridge interface to .NET. Among its features is the ability to create .NET Components directly without having to rely on COM invokation. The library provides non COM-registered activation of .NET components as well as a host of helper methods and functions that allow access to types that typically cannot be accessed over COM interop. In fact only a small percentage of types in .NET are COM accessible and with wwDotNetBridge you can access many more of them directly as well as access arrays more easily, access static properties and methods, access enum values and so on.

 

Much of what wwDotNetBridge does is accomplished by indirect invokation: So rather than directly operating on the COM instance that VFP has access to an indirect mechanism that uses Reflection inside of .NET to act s a proxy for the FoxPro instance. This has the effect that you have access to much more functionality than VFP has directly against the .NET component.

 

The upshoot of this process is that this is a controlled process – I have control over parameters that are passed in and return values returned back out. Which brings us to the solution to the problem I mentioned.

The idea is that I can now use indirect calls using InvokeMethod to do parameter and result value fixups and if the value is a GUID it’s converted into custom object provided in wwDotNetBridge that is accessible in VFP:

 

CLEAR

DO wwDotnetBridge

 

LOCAL oBridge as wwDotNetBridge

oBridge = CREATEOBJECT("wwDotNetBridge")

 

*** create our .NET object to call method on

loInst = oBridge.CreateInstance("Westwind.WebConnection.TestType")

 

* loInst.GetGuid()   && fails

 

*** Call method that returns a GUID: Return a custom object ComGuid

loGuid = oBridge.InvokeMethod(loInst,"GetGuid")

 

*** Custom object contains string version of Guid

? loGuid.GuidString  && Print a a new Guid

 

*** You can assign to it as a string and it will set an internal GUID

loGuid.GuidString    && assign a new Guid

 

*** Call method that expects a guid with the wrapper object

? oBridge.InvokeMethod(loInst,"SetGuid",loGuid)

 

*** Create a new Guid with a new Guid value

loGuid = oBridge.CreateInstance("Westwind.WebConnection.ComGuid")

loGuid.New()  && another new value

 

? oBridge.InvokeMethod(loInst,"SetGuid",loGuid)

 

 

Notice that the calls to the Guid related methods are implicit using InvokeMethod. InvokeMethod uses Reflection but internally checks parameters and if a GUID (or a couple of other types) are found, fixes up these parameters/result values and turns them into something that VFP can deal with. A few other things I’ve done is turn DataSets into XML automatically and fix up COM SafeArrays that can’t be accessed as byte arrays in .NET to mention a few.

 

The result above is that in the code above I get not a GUID object returned but an instance of a custom type I created ComGuid which wraps the .NET Guid instance and provide a string based interface to VFP. The simple .NET ComGuid implementation looks like this:

 

    /// <summary>

    /// COM wrapper for the .NET Guid control that allows

    /// </summary>

    [ComVisible(true)]

    [ClassInterface(ClassInterfaceType.AutoDual)]

    [ProgId("Westwind.ComGuid")]

    public class ComGuid

    {

       

        public Guid Guid

        {

            get { return _Guid; }

            set { _Guid = value; }

        }

        private Guid _Guid = Guid.Empty;

 

       

        public string GuidString

        {

            get

            {

                return this.Guid.ToString();

            }

            set

            {

                this.Guid = new Guid(value);

            }

        }

 

 

        public void New()

        {

            this.Guid = Guid.NewGuid();

        }

    }

 

This class is a simple wrapper around a GUID instance and allows you to manipulate the Guid instance via strings that you can use in VFP. If you set the string GuidString property it internally updates the Guid instance which when passed back to .NET is used as the actual GUID parameter as long as you’re using InvokeMethod() to do it.

 

The Guid string format is this and comes from the Guid.ToString() method in .NET:

 

3484b4dc-841d-408d-8de3-0cfb1b6582bb

 

The way the intercept works as follows. All methods that set and retrieve values have calls to FixupXXX methods and look something like this:

 

        protected object InvokeMethod_Internal(object Instance, string Method,
                                               params object[] args)

        {

           object result = ReflectionUtils.CallMethod(Instance, Method, args);

           return this.FixupReturnValue(result);

        }

 

Where the actual FixupReturnValue() and FixupParameter() methods look like this:

 

        /// <summary>

        /// Fixes up a return value based on its type

        /// </summary>

        /// <param name="val"></param>

        /// <returns></returns>

        private object FixupReturnValue(object val)

        {

            if (val == null)

                return null;

 

            // *** Need to figure out a solution for value types

            Type type = val.GetType();

           

            if (type == typeof(Guid))

            {

                ComGuid guid = new ComGuid();

                guid.Guid = (Guid) val;

                return guid;

            }

           

            return val;

        }

        private object FixupParameter(object val)

        {

            if (val == null)

                return null;

 

            Type type = val.GetType();

 

            // *** Fix up binary SafeArrays into byte[]

            if (type.Name == "Byte[*]")

                return ConvertObjectToByteArray(val);

 

            if (type == typeof(ComGuid))

                return ((ComGuid)val).Guid;

 

            return val;

        }

 

Notice that the FixupParameters fixes up another parameter type – a Binary input from FoxPro (CreateBinary or CAST(x as Q) which also doesn’t work any other way when you pass a binary value from VFP to .NET. These Fixup methods ensure that there’s a point of interception for these types of conversion problems so that FoxPro code can reasonably cleanly call a .NET component with a problem type.

 

 

 

All of this is handled automatically by wwDotNetBridge, but I suspect as time goes on I’ll end up adding more custom ‘conversions’ into these Fixup methods. With these hooks in place it’s likely that just about any kind of type that can be fixed up in some way that it can be consumed and sent from VFP. It’s a sort of custom marshaller.

 

Although I’m describing this as part of wwDotNetBridge which is part of the West Wind Client Tools you can apply this same functionality in your own components (or create a simple Reflection helper in .NET that you call from your COM calls). I talked about the core concepts of wwDotNetBridge here some time ago.

Posted in:

 



© Rick Strahl, West Wind Technologies, 2003 - 2018