Generics and COM Interop don't mix
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
- Map Physical Paths with an HttpContext.MapPath() Extension Method in ASP.NET
- Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
- Getting the Client IP Address in ASP.NET Core
- Resolving Paths To Server Relative Paths in .NET Code
- Getting the ASP.NET Core Server Hosting Urls at Startup and in Requests
The Voices of Reason
# re: Generics and COM Interop don't mix
Actually I think it's interesting that you can pass types back that arent explicitly marked as COM visible. It looks like you can pass just about anything and in most cases you can even access these object properties directly without requiring Reflection. Not quite sure how that works since there's no type library for the COM client to look at. <shrug>
# re: Generics and COM Interop don't mix
As I think about this more there is no reason that a closed(I think this is the term they are called) generic shouldn't be accesible via COM. As long as the type is specified such as in your example List<string> should be visible.
I think the best answer is get away from COM;-)
# re: Generics and COM Interop don't mix
Not so pretty, but usable.
[ComVisible(true)] public interface IPerson { string Name { get;set;} DateTime Entered { get;set;} IList NickNamesList { get;} ICollection NickNamesCollection { get;} } [ComVisible(true)] [ClassInterface(ClassInterfaceType.AutoDual)] [ComDefaultInterface(typeof(IPerson))] public class Person:IPerson { [ComVisible(false)] public List<string> NickNames { get { return _NickNames; } set { _NickNames = value; } } private List<string> _NickNames = new List<string>(); #region IPerson Members IList IPerson.NickNamesList { get { return this.NickNames; } } ICollection IPerson.NickNamesCollection { get { return this.NickNames; } } #endregion .... }
# re: Generics and COM Interop don't mix
The story is as:
I have created one C# component which is consumed by another C# dll. Both of them are COM compliant. When I am directly using the second C# dll in ATL C++ exe, I can build the ATL C++ project in Release but not in Debug. In Debug, while building, it gives the following error: #import referenced a type from a missing type library; '__missing_type__' used as a placeholder
In ATL C++ application both the C# tlbs are correctly #imported along with mscorlib.tlb.
Please let me know if you know about this error.
Thanks a lot in advance for your help.
# re: Generics and COM Interop don't mix
Type library exporter warning processing 'GenericsCOMInterop.IClass1.GetNickName
s(#0), GenericsCOMInterop'. Warning: Type library exporter encountered a generic
type instance in a signature. Generic code may not be exported to COM.
I find it interesting you were able to still access the property since the property is not defined within the IDL created when exporting the type library.
I aggree it would be nice to see the end of COM but since COM is so ingrained, I doubt we will see the end of it for a very long time. The .net runtime is a COM server, so is visual studio;-)