From matthew.oconnor at calyptech.com Thu Apr 3 00:18:52 2008 From: matthew.oconnor at calyptech.com (Matthew O'Connor) Date: Thu, 03 Apr 2008 00:18:52 +0000 Subject: [rdnzl-devel] .NET error : Ambiguous match found. Message-ID: <1207181932.6712.8.camel@localhost> Hi All, I am getting a .NET error whilst simply trying to get access to the Controls property of a TableLayoutPanel in System.Windows.Forms. I have included a little test application below which demonstrates the issue. I am using Lispworks 5.0 and RDNZL 0.12.0. I am running Visual Studio 2008 so I think I've got version 3.5 of .NET. I've tried to have a look at it but don't understand enough about the ffi to come up with a possible cause. I did look at the System.Windows.Form assembly through the Visual Studio object viewer and I can't see any other definition of Controls. So I really can't see where the ambiguity would come from. Regards, Matthew ;; Bug ;; .NET error (System.Reflection.AmbiguousMatchException): Ambiguous match found. ;; NOTE - Make this point to your RDNZL implementation. (load "../RDNZL/rdnzl-0.12.0/load.lisp") (rdnzl:enable-rdnzl-syntax) (rdnzl:import-types "System.Windows.Forms" "TableLayoutPanel") (rdnzl:use-namespace "System.Windows.Forms") (setf tlp (rdnzl:new "TableLayoutPanel")) (rdnzl:property tlp "Controls") From iverodin at gmail.com Thu Apr 3 09:08:18 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Thu, 3 Apr 2008 11:08:18 +0200 Subject: [rdnzl-devel] .NET error : Ambiguous match found. In-Reply-To: <1207181932.6712.8.camel@localhost> References: <1207181932.6712.8.camel@localhost> Message-ID: > I can't see any other definition of Controls. So I really can't see > where the ambiguity would come from. There is something weird going on here, if you do this: (pprint (mapcar (lambda (x) (invoke x "ToString")) (rdnzl-array-to-list (invoke (invoke tlp "GetType") "GetProperties")))) the first entries are "System.Windows.Forms.TableLayoutControlCollection Controls" "ControlCollection Controls" so there is actually an ambiguity here, where the first property is the one documented. I don't understand what the second one is. Of course, these two properties are only differentiated by their return-value which I didn't think was actually possible. And look at this: RDNZL-USER(202): (invoke tlp "get_Controls") # That's the expected result, and there are actually two get_Controls-method as well, so that shouldn't really have worked either. From iverodin at gmail.com Thu Apr 3 09:35:04 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Thu, 3 Apr 2008 11:35:04 +0200 Subject: [rdnzl-devel] .NET error : Ambiguous match found. In-Reply-To: References: <1207181932.6712.8.camel@localhost> Message-ID: > "System.Windows.Forms.TableLayoutControlCollection Controls" > "ControlCollection Controls" It seems the second Controls is from System.Windows.Forms.Control, from which TableLayout.. inherits. I think that's supposed to be wrong. From iverodin at gmail.com Thu Apr 3 10:17:38 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Thu, 3 Apr 2008 12:17:38 +0200 Subject: [rdnzl-devel] .NET error : Ambiguous match found. In-Reply-To: References: <1207181932.6712.8.camel@localhost> Message-ID: > I think that's supposed to be wrong. .. or possibly not. This error can be reproduced with a pair of classes like this: public class TestPropertiesA { public int thisthing = 1; public int MyProperty { get { return thisthing; } set { thisthing = value; } } } public class TestPropertiesB : TestPropertiesA { public String thisstring = "hello"; public new String MyProperty { get { return thisstring; } set { thisstring = value; } } } Notice the "new" keyword in there. When instantiated, a TestPropertiesB-object will have both properties, and GetProperty will indeed report an ambiguos match even though the first property was supposed to be 'hidden'. Pretty silly. GetMethod does the correct thing wrt the accessor method, which is why 'get_PropertyName' works. For this case, Type.GetProperty has an overloaded form that takes the expected return argument so probably this could be accounted for in that way. From iverodin at gmail.com Thu Apr 3 12:12:40 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Thu, 3 Apr 2008 14:12:40 +0200 Subject: [rdnzl-devel] .NET error : Ambiguous match found. In-Reply-To: References: <1207181932.6712.8.camel@localhost> Message-ID: Browsing through MSDN a bit more, I find this in Type.GetProperty: ===> Situations in which AmbiguousMatchException occurs include the following: * A type contains two indexed properties that have the same name but different numbers of parameters. To resolve the ambiguity, use an overload of the GetProperty method that specifies parameter types. * A derived type declares a property that hides an inherited property with the same name, by using the new modifier (Shadows in Visual Basic). To resolve the ambiguity, use the GetProperty(String, BindingFlags) method overload and include BindingFlags..::.DeclaredOnly to restrict the search to members that are not inherited. ===> The last situation is what we have ('new' being used because the return-value is more specific), and it appears specifying the return type is then unneccessary. This works: (INVOKE (INVOKE TLP "GetType") "GetProperty" "Controls" (OR-ENUMS (FIELD "BindingFlags" "Instance") (FIELD "BindingFlags" "Public") (FIELD "BindingFlags" "DeclaredOnly"))) perhaps adding this flag in an exception-handler for this case would be ok, as the first kind of ambiguity should not be possible as all parameter types are passed. Otherwise, one would need to either add the return-type somehow or write a specialized Binder-subclass. It's kind of strange that this seems to only affect properties; for instance both get_Controls methods were present, but the default GetMethod-method had no problem with that. From iverodin at gmail.com Thu Apr 3 12:20:16 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Thu, 3 Apr 2008 14:20:16 +0200 Subject: [rdnzl-devel] .NET error : Ambiguous match found. In-Reply-To: References: <1207181932.6712.8.camel@localhost> Message-ID: > perhaps adding this flag in an exception-handler for this case would > be ok, as the first kind of ambiguity should not be possible as all > parameter types are passed. Otherwise, one would need to either add > the return-type somehow or write a specialized Binder-subclass. Come to think of it, both those properties could be inherited, so the advice in GetProperty doesn't actually help. From iverodin at gmail.com Thu Apr 3 12:20:16 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Thu, 3 Apr 2008 14:20:16 +0200 Subject: [rdnzl-devel] .NET error : Ambiguous match found. In-Reply-To: References: <1207181932.6712.8.camel@localhost> Message-ID: > perhaps adding this flag in an exception-handler for this case would > be ok, as the first kind of ambiguity should not be possible as all > parameter types are passed. Otherwise, one would need to either add > the return-type somehow or write a specialized Binder-subclass. Come to think of it, both those properties could be inherited, so the advice in GetProperty doesn't actually help. From matthew.oconnor at calyptech.com Fri Apr 4 04:22:54 2008 From: matthew.oconnor at calyptech.com (Matthew O'Connor) Date: Fri, 04 Apr 2008 04:22:54 +0000 Subject: [rdnzl-devel] .NET error : Ambiguous match found. In-Reply-To: References: <1207181932.6712.8.camel@localhost> Message-ID: <1207282974.6421.8.camel@localhost> Thanks Iver, > > RDNZL-USER(202): (invoke tlp "get_Controls") > # > > Your direct invocation of the "get_Controls" method gets me going again. Cheers, Matthew From iverodin at gmail.com Tue Apr 8 14:29:47 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Tue, 8 Apr 2008 16:29:47 +0200 Subject: [rdnzl-devel] .NET error : Ambiguous match found. In-Reply-To: <1207282974.6421.8.camel@localhost> References: <1207181932.6712.8.camel@localhost> <1207282974.6421.8.camel@localhost> Message-ID: > Your direct invocation of the "get_Controls" method gets me going again. I'm glad that works, but reflecting on this it seems that one pretty much *have* to offer the return-type to PROPERTY/GetProperty when there are ambiguities of this sort, which there will be. That's a bit iffy syntactically, even though the implementation is easy enough. And of course the error-message would still be pretty confusing. C#'s type inference mechanism tells me that in the example, if foo has type TestPropertiesB, then bar in var bar = foo.MyProperty(); has type String, this being an error: var bar = (int) foo.MyProperty(); whereas in var bar = ((TestPropertiesA) foo).MyProperty(); bar is an int. I guess one could write a custom Binder that would implement a 'most specific property' thing, but that does kind of feel like overkill. Regards, Iver From iverodin at gmail.com Fri Apr 25 08:14:56 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Fri, 25 Apr 2008 10:14:56 +0200 Subject: [rdnzl-devel] A bug in *setDotNetContainerType ( CAST) Message-ID: I was working a bit on the 'ambiguous match'-thing for properties and ran into a weird thing using CAST, where I couldn't cast from a subclass to its super, both being quite vanilla. The problematic line is this from InvocationResult.cpp if (oldType->IsAssignableFrom(newType)) { I'm pretty sure this is supposed to be if (newType->IsAssignableFrom(oldType)) { This fixes the case I had problems with, but ufortunately reveals another problem in the Excel example, where now this no longer works: (cast [get_Item worksheets 1] "Worksheet") The object returned is a com-object returned as a System.Object, and the cast fails with "object must implement IConvertible". It's possible to cast the worksheet to System.__ComObject, but that doesn't help. This seemed to work earlier only because the first test was inverted, thus returning true in this case and forcing the container-type to the correct value. I'll try to find the source of this. The 'fix' for the ambiguos-match-thing, by the way, tries to simulate what C# does by searching the base classes of the object queried when the exception occured, thus letting one retrieve the 'other' property by casting to the appropriate superclass. That is, if one has classes A, B and C, with A > B > C, and A and B each defining a property with different return-type, then (property C) will return the one defined in B, whereas (property (cast C A)) will return the one defined in A. And that is how I found the issue with CAST. I put 'fix' in quotes because this is obviously not really neccessarily how things should work; C# is one thing but the .Net methods would more naturally just take the desired return-type as an argument where the property is ambiguous. But I think perhaps this is more intuitive (given C#'s behaviour), syntactically simpler, and also it seemed like good excercise. Regards, Iver From iverodin at gmail.com Fri Apr 25 08:24:07 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Fri, 25 Apr 2008 10:24:07 +0200 Subject: [rdnzl-devel] Re: A bug in *setDotNetContainerType ( CAST) In-Reply-To: References: Message-ID: > The object returned is a com-object returned as a System.Object, and > the cast fails with "object must implement IConvertible". It's > possible to cast the worksheet to System.__ComObject, but that doesn't > help. This seemed to work earlier only because the first test was > inverted, thus returning true in this case and forcing the > container-type to the correct value. I'll try to find the source of > this. Very preliminary searching seems to indicate that System.__ComObjects have a 'real type' one has to somehow get to, and that this has to be handled especially by applications lucky enough to recieve them. From iverodin at gmail.com Fri Apr 25 15:05:02 2008 From: iverodin at gmail.com (Iver Odin Kvello) Date: Fri, 25 Apr 2008 17:05:02 +0200 Subject: [rdnzl-devel] Re: A bug in *setDotNetContainerType ( CAST) In-Reply-To: References: Message-ID: > Very preliminary searching seems to indicate that System.__ComObjects > have a 'real type' one has to somehow get to, and that this has to be > handled especially by applications lucky enough to recieve them. Some more reading, and it appears that __ComObjects doesn't really have a "real" type, but rather implements a set of interfaces one has to query the old way. Approximating this using Marshal:IsComObject and simply assuming the user knows what he is doing, I found that the excel example still wouldn't work because excel cheerfully also returns a non-COM object - an array - as a System.Object. The 'real' type of the object is available, and changing oldType to be container->getContainerObject()->GetType(); makes the excel example work again, but I wonder if it wouldn't be easier to just add something like UNSAFE-CAST or FORCE-CAST or something for working with Interop and similar situations. I'll fix it somehow anyway. Accessing properties of COM-objects (without using the get_-methods) is also sketchy; but I don't think this could reasonably be called RDNZL's fault. Regards, Iver