[Openmcl-devel] RDNZL in CCL: Exception occurred while executing foreign code

John Miller millejoh at mac.com
Sat Oct 17 13:15:33 UTC 2009


On Oct 16, 2009, at 11:54 PM, Gary Byers wrote:

> R. Matthew Emerson wrote:
>>
>>
>> On Oct 16, 2009, at 5:28 PM, John Miller wrote:
>>
>>> The problem looks to be one of passing the wrong type of  
>>> argument.  The C function I am calling is defined as
>>>
>>> void* invokeInstanceMember(const __wchar_t *methodName, void  
>>> *target, int nargs, void *args[])
>>>
>>> h-to-ffi.sh interprets that to be
>>>
>>> (function ("/cygdrive/d/rdnzl-cpp-0.7.1/RDNZL/ffi-headers.h" 55)
>>>  "invokeInstanceMember"
>>>  (function
>>>   ((pointer (unsigned-short ())) (pointer (void ())) (int ())  
>>> (pointer (pointer (void ()))) )
>>>   (pointer (void ()))) (extern))
>>>
>>> Then, given the following definitions:
>>>
>>> (defun %invoke-instance-member (method-name type nargs args)
>>>   (ccl::with-native-utf-16-cstr (mn method-name)
>>>     (#_invokeInstanceMember mn type nargs args)))
>>>
>>> (defun test-invoke-2 ()
>>>   (let ((obj (make-type-from-name "System.Reflection.Assembly")))
>>>     (%invoke-instance-member "ToString" (pointer obj) 0 (ccl:%null- 
>>> ptr))))
>>>
>>> (defun test-invoke-3 ()
>>>   (let ((obj (make-type-from-name "System.Reflection.Assembly"))
>>>  (type (box* "System.Reflection.Assembly")))
>>>     (ccl:rletz ((ptr (:array :address 1)))
>>>       (setf (ccl:paref ptr (:array :address) 0) type)
>>>       (%invoke-instance-member "GetType" (pointer obj) 1 ptr))))
>>>
>>> I call (test-invoke-2) and all is hunky-dorey, but I try (test- 
>>> invoke-3) and all heck breaks loose.  I have imagined, and tried a  
>>> couple different ways of representing an array of pointers, but  
>>> clearly I am not imaginative enough.
>>>
>>> It would be interesting to know if anyone else has tried calling  
>>> foreign functions that have arguments of type void *args[].  I  
>>> mean other than the code in cocoa-ide/start.lisp that I based the  
>>> code in test-invoke-3 off of.
>>
>> It seems to me that you're stack-allocating the array of pointers  
>> correctly.
> Agreed.  That makes me wonder what BOX* does, whether it's doing it  
> right, and whether it's the right thing to do here.  I assume that  
> we're trying to call the second method listed at:
>
> <http://msdn.microsoft.com/en-us/library/system.reflection.assembly.gettype.aspx 
> >
>
> - the one that takes a String argument - and that BOX* of a lisp  
> string returns a pointer to a .NET String.  Is this assumption  
> correct, and if so, are we sure that it works (e.g., can we do  
> something like return a lisp string from that .NET String, or  
> otherwise test that) ?  This might turn out to be a dead end, but if  
> we believe that we're passing the array of pointers correctly one of  
> the next things to look at would seem to be the contents of that  
> array ...
>

You are right, Gary.  (box* <String>) turns into a call to

(defun %make-dot-net-container-from-string (s)
   (ccl::with-native-utf-16-cstr (us s)
     (#_makeDotNetContainerFromString us)))

There is another function

(defun get-object-as-string (container)
   "Get a string representation of the object denoted by CONTAINER.
Uses 'ToString' internally."
(let ((object0 (pointer container)))
   (ccl:rlet ((temp1 (:* t)))
     (%get-dot-net-container-object-as-string object0 temp1)
     (ccl::%get-native-utf-16-cstring temp1)))

The (pointer container) function call is expecting container to be a  
lisp structure which encapsulates the pointer to the .Net container.   
If I call (box <string>) instead of (box* <string>) I get back the  
structure.  So, in the below code:

RDNZL> (get-object-as-string (box "A string"))
"A string"

That seems to be okay.

>
>>
>> I would try running the lisp under gdb.  Load the shared library  
>> that contains
>> #_invokeInstanceMember, and then set a gdb breakpoint on it.  Then  
>> evaluate (test-invoke-3), and see if the arguments provided from  
>> lisp look right.
>>
>> Here's a trivial example I just came up with and ran on my Mac. (I  
>> tried to do it on Windows, but I couldn't figure out at a quick  
>> glance how to build a working dll with cygwin tools.)
> [...]
>
> I'm fairly sure that the library that John's trying to call into is  
> written in "Managed C++"; if so, tools like GDB may have trouble  
> making sense out any symbolic information that's available (MS  
> compilers generate debugging info that's in an undocumented  
> proprietary format).  I don't know for sure; GDB might be able to at  
> least find #_invokeInstanceMember, but things might get weird  
> quickly.  (Weirder than usual, I mean.)
>
>

Yes, the RDNZL.dll library is sort of a thin C++ wrapper on top  
of .Net with an even thinner C interface on top.  The C part of  
%invoke-instance-member looks like

__declspec(dllexport) void* invokeInstanceMember(const __wchar_t  
*methodName, void *target, int nargs, void *args[]) {
   DotNetContainer *container = static_cast<DotNetContainer *>(target);
   Type ^t = container->getContainerType();
   Object ^o = container->getContainerObject();
   return InvokeMember::invokeMethod(o, t, methodName, nargs, args,  
static_cast<BindingFlags>(BindingFlags::Instance |  
BindingFlags::Public));
}

While the C++ declaration for InvokeMember::invokeMethodDirectly looks  
like

void* InvokeMember::invokeMethod(Object ^o, Type ^t, const __wchar_t  
*methodName, int nargs, void *args[], BindingFlags bindingAttr)

I was a little suspicious of that bindingAttr variable, but I can  
invoke-instance-member as long as nargs is 0 and args is a null-ptr.   
I would think the problem is with the C++ code, but it has been well  
tested under various other Lisp implementations in windows and works  
fine with those.  Most probably I am doing something wrong, but it  
certainly isn't obvious what at the moment.



>
>>
>> #include <stdio.h>
>>
>> void testing(char *name, void *target, int nargs, void *args[])
>> {
>>   int i;
>>
>>   fprintf(stderr, "name = %s\n", name);
>>   fprintf(stderr, "target = %p\n", target);
>>   fprintf(stderr, "nargs = %d\n", nargs);
>>
>>   for (i = 0; i < nargs; i++)
>>     fprintf(stderr, "args[%d] = %p\n", i, args[i]);
>> }
>>
>> Compile that as a shared library, e.g., with cc -arch x86_64 - 
>> shared ffi.c -o ffi.dylib or whatever the right thing is for your  
>> system.  Sorry to hand-wave like this.
>>
>> With that in hand:
>>
>> ? (open-shared-library "ffi.dylib")
>> #<SHLIB ffi.dylib #x300041594C2D>
>>
>> Now:
>>
>> (defun testing ()
>>   (rlet ((args (:array :address 3)))
>>     (setf (paref args (:array :address) 0) (%int-to-ptr 9)
>>    (paref args (:array :address) 1) (%int-to-ptr 99)
>>    (paref args (:array :address) 2) (%int-to-ptr 999))
>>     (with-cstrs ((s "hello"))
>>       (external-call "testing" :address s :address (%null-ptr) :int 3
>>       :address args :void))))
>>
>> ? (testing)
>> name = hello
>> target = 0x0
>> nargs = 3
>> args[0] = 0x9
>> args[1] = 0x63
>> args[2] = 0x3e7
>>
>> I don't know if that helps or not.
>>
>>

Thanks.  I will see if I remember how to create DLL's in Mingw and  
give the above a try.


>>
>> _______________________________________________
>> Openmcl-devel mailing list
>> Openmcl-devel at clozure.com
>> http://clozure.com/mailman/listinfo/openmcl-devel
>>
>

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.clozure.com/pipermail/openmcl-devel/attachments/20091017/90d15230/attachment.html>


More information about the Openmcl-devel mailing list