COM Interop is rarely fun, but it looks like it's getting to be less and less useful as time goes on and new .NET Runtime features come along that don't work well over COM.
It appears that Generic types can't be exported over COM and be usable to a client like Visual FoxPro. When I create a class like this:
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Person
{
public string Name
{
get { return _Name; }
set { _Name = value; }
}
private string _Name = "Rick";
public DateTime Entered
{
get { return _Entered; }
set { _Entered = value; }
}
private DateTime _Entered = DateTime.Now;
public List<string> NickNames
{
get { return _NickNames; }
set { _NickNames = value; }
}
private List<string> _NickNames = new List<string>();
}
and pull this up in VFP I can't see the Nicknames List<string> property:
The generic interface is not exported - at least not to IDispatch.
Accessing the Nicknames property does return an Object to me, but I can't seem to do anything with this object. It's not NULL, but also doesn't have any properties that should be there like Count or in case of conversion to an array a length or any array elements for that matter.
It's not showing up as Collection or array, and basically any operation against it over COM fails. Apparently this is true of any Generic type which of course are pretty common in the .NET 2.0 framework and most collection types that are publicly exposed somewhere in their class hierarchy are based on one of the generic types.
If I replace the code above with an ArrayList instead I can see the list fine and COM Interop transforms that array list into a SafeArray that is visible in VFP as a standard array. This change works, but of course requires that you actually have control over the object.
Good ole' Reflection to the Rescue
So is it possible to make this work? Yes, but it's hideous. Basically you can use Reflection in a helper class to provide the type management so that .NET rather than the COM client code is accessing the collection directly.
As part of a wwDotNetBridge class I've created I got methods like this:
public object InvokeMethod(object Instance, string Method)
{
return wwUtils.CallMethod(Instance,Method);
}
public object InvokeMethod_OneParm(object Instance, string Method, object Parm1)
{
return wwUtils.CallMethod(Instance, Method,Parm1);
}
public object InvokeMethod_TwoParms(object Instance, string Method, object Parm1, object Parm2)
{
return wwUtils.CallMethod(Instance, Method, Parm1, Parm2);
}
public object GetProperty(object Instance, string Property)
{
return wwUtils.GetProperty(Instance, Property);
}
public void SetProperty(object Instance, string Property, object Value)
{
wwUtils.SetProperty(Instance, Property, Value);
}
where these wwUtils methods are Reflection helpers. With this code I can then do:
loPerson = loBridge.GetPerson()? loPerson.ToString()
? loPerson.Name
loPerson.Name = "John"
loNick = loPerson.NickNames
? loBridge.InvokeMethod_OneParm(loNick,"Add","ricky")
? loBridge.InvokeMethod_OneParm(loNick,"Add","richard")
? loBridge.InvokeMethod_OneParm(loNick,"Add","ricci")
? loBridge.InvokeMethod(loNick,"ToString")
? loBridge.GetProperty(loNick,"Count") && 3
As I said that's pretty ugly but I have a few wrappers around this on the VFP end so the code at least doesn't need to use the _OneParam, TwoParms etc. nastiness. It works and get around some of the issues but as I said it's ugly.
In most situations when working in an interop scenario where types are encountered that just don't want to pass the COM boundary I find it best to just build a wrapper function in a .NET class that can act as a proxy and pass the data back in a COM capable format.
Can we make COM just go away for good? Please? <s>
Other Posts you might also like