(Apologies if this posts twice; I outsmarted myself with mailman, and I tried to cancel the first one that mailman thought was posted by a non list member.)
Hi,
I'm having trouble invoking a static member on a class.
I've reduced the case to the following simple test case, and I suspect I may be missing somewhat simple. (I'm far from a Lisp wizard. Far...)
Basically, I'm trying to load a C# assembly, and be able to call a static method on it. I can call instance methods without issue, but make-type-from-name is not working the way I expect it should, and therefore, calling static methods fails.
Any help would be appreciated!
--Jim
Source code and sample interaction below:
Lisp interaction: (win32, using slime and clisp-2.38, but I doubt it makes any difference) Some output trimmed for legibility.
CL-USER> (load "/c/vp/devenv/lib/clisp/rdnzl-0.9.1/load.lisp") CL-USER> (in-package :rdnzl) RDNZL> (enable-rdnzl-syntax) RDNZL> (setq lisp-sample-assembly (import-assembly [System.Reflection.Assembly.LoadFrom "C:\temp\bin\Debug\LispSample1.dll"])) RDNZL> (setq p (new (import-type "LispSample1.Parrot" lisp-sample-assembly) "Bob")) #<CONTAINER LispSample1.Parrot #x2929E6A8> RDNZL> (ffi-call-with-args %invoke-instance-member p "SayHello" '(42))
47 RDNZL> (resolve-type-name "LispSample1.Parrot")
"LispSample1.Parrot, LispSample1, Version=1.0.2230.24825, Culture=neutral, PublicKeyToken=null"
;; To me, it appears that that worked.
RDNZL> (make-type-from-name (resolve-type-name "LispSample1.Parrot"))
#<CONTAINER NULL #x2929E540>
RDNZL> ;; ^^^^ This appears to be as close to the root of the problem as ;; I am able to get
RDNZL> (invoke p "SayHello" 17)
22 ;; this worked (There is stdout output, but my slime discards it.)
RDNZL> (invoke p "StaticSayHello" "Bill" 17)
; Evaluation aborted ;; failed - .NET error (System.Exception): Instance method not found: LispSample1.Parrot::StaticSayHello(System.String,System.Int32) ;; Of course, it's not an instance method...
RDNZL> (invoke "LispSample1.Parrot" "StaticSayHello" "Bill" 17) Trying to call function %INVOKE-STATIC-MEMBER with NULL object #<CONTAINER NULL #x292961E0>.
C# class is here, compiled into an assembly: C:\temp\bin\Debug\LispSample1.dll using System;
namespace LispSample1 { public class Parrot { private string m_name; public Parrot(string name) { m_name = name; } public int SayHello(int value) { Console.WriteLine("Hello {0}.", m_name); return value + 5; } static public int StaticSayHello(string name, int value) { Console.WriteLine("Hello {0}.", name); return value * 2; } } }
Hi!
On Wed, 8 Feb 2006 14:31:10 -0500 (EST), Jim Sokoloff jim@sokoloff.com wrote:
I'm having trouble invoking a static member on a class.
I've reduced the case to the following simple test case, and I suspect I may be missing somewhat simple. (I'm far from a Lisp wizard. Far...)
Basically, I'm trying to load a C# assembly, and be able to call a static method on it. I can call instance methods without issue, but make-type-from-name is not working the way I expect it should, and therefore, calling static methods fails.
Your problem can be best described with this short C# program:
using System; using System.Reflection;
public class Test { public static void Main() { // insert correct path to LispSample1.dll here Assembly a = Assembly.LoadFrom("C:\Documents and Settings\edi\Desktop\LispSample1.dll"); Type t1 = a.GetType("LispSample1.Parrot"); String s = t1.FullName; Type t2 = Type.GetType(s); try { Console.WriteLine("Success: " + t2.Name); } catch (NullReferenceException) { Console.WriteLine("Failed"); } } }
If I run this, I get "Failed" on the console. This is because Type.GetType can't create the type from the name although the name is fully qualified. This happens because you use Assembly.LoadFrom and this is obviously unrelated to RDNZL.
I can't cite chapter and verse in the .NET documentation but I immediately recognized the problem because I was bitten by it myself.
If you put your DLL into a folder where the system will find it and then use it like AproposGui is used in the RDNZL examples, everything should work fine. At least it works for me, I just tested it with LW.
HTH, Edi.
It turns out that I don't have the freedom to co-locate my .net assembly with clisp.exe (because I have to have different versions of the assembly loaded on a single machine in different processes for dev, test, load-test, and prod).
I'll propose the following patch (attached) to invoke:
Currently it takes an object (for an instance method) or a type name (for a static method).
After this patch, object can also a cons of a loaded assembly and a type name, and it will call a static method on the type from that assembly.
So, in the previous transcript, the call which failed: (invoke "LispSample1.Parrot" "StaticSayHello" "Bob" 15)
becomes: (invoke (cons lisp-sample-assembly "LispSample1.Parrot") "StaticSayHello" "Bob" 15)
which now works.
Hopefully this patch (or a variant form) will be acceptable.
Thanks for RDNZL! :) ---Jim
diff -u c:\temp\rdNZL-0.9.1\container.lisp c:\vp\devenv\lib\clisp\rdNZL-0.9.1\container.lisp --- c:\temp\rdNZL-0.9.1\container.lisp 2006-01-31 15:02:56.000000000 -0500 +++ c:\vp\devenv\lib\clisp\rdNZL-0.9.1\container.lisp 2006-02-09 13:59:03.984125000 -0500 @@ -163,6 +163,13 @@ (ffi-call-with-foreign-string* %make-type-from-name name)))
+(defun make-type-from-assembly-and-name (assembly name) + "Returns the .NET type with the name NAME from a specific assembly." + (ffi-call-with-args %invoke-instance-member + assembly + "GetType" + (list name))) + (defun get-object-as-string (container) "Get a string representation of the object denoted by CONTAINER. Uses 'ToString' internally." @@ -320,6 +327,13 @@ (make-type-from-name (resolve-type-name object)) method-name args)) + ((and (consp object) + (container-p (car object)) + (stringp (cdr object))) + (ffi-call-with-args %invoke-static-member + (make-type-from-assembly-and-name (car object) (cdr object)) + method-name + args)) (t (error "Don't know how to invoke ~A on ~S." method-name object))))) ;; if some of the arguments were pass-by-reference reset them to ;; their underlying types
On Thu, 9 Feb 2006 14:20:59 -0500 (EST), Jim Sokoloff jim@sokoloff.com wrote:
I'll propose the following patch (attached) to invoke:
Currently it takes an object (for an instance method) or a type name (for a static method).
After this patch, object can also a cons of a loaded assembly and a type name, and it will call a static method on the type from that assembly.
Thanks Jim, I've uploaded a new version (0.9.2) which incorporates your patch.
Cheers, Edi.