[Mono-list] DllImport and extern declarations for values

Jonathan Pryor jonpryor@vt.edu
Tue, 30 Sep 2003 07:47:13 -0400


Responses inline...

On Tue, 2003-09-30 at 05:07, Thomas Sondergaard wrote:
> I have written a ruby extension module in managed C++ that allows ruby to
> interop with .net. I want this to work with mono on linux and since the
> managed C++ is not portable I am rewriting as much as possible to C# using
> P/Invoke. Ideally I would only have to write a small stub of a dynamic link
> library that exports the init function called by ruby, which uses the Mono
> embedding API and then I can do the rest from C#.
> 
> I have made a C# class called RubyAPI, where I have manually entered all the
> ruby functions I use from the ruby api. It looks something like this:
> 
> 
> namespace RubyDotNet.Common {
>     public class RubyAPI {
> 
>         [DllImport(@"c:/ruby180/bin/msvcrt-ruby18.dll")]
>         public static extern void ruby_init();
> 
>         [DllImport(@"c:/ruby180/bin/msvcrt-ruby18.dll")]
>         public static extern void ruby_finalize();
> 
>         [DllImport(@"c:/ruby180/bin/msvcrt-ruby18.dll")]
>         public static extern uint
> rb_eval_string([MarshalAs(UnmanagedType.LPStr)] string s);
> 
>         ...
>     }
> }

I would suggest that you leave out the pathnames in the DllImport
statements, unless you want to guarantee that all users (on all
platforms!) will have it in the same location.... :-)

> The thing is, in C++ I also use a few macros, which I won't be able to use
> either, but I can probably manage that by duplicating it in C# (yuck,
> duplication).

I miss macros as well.  Though a lisp-like macro system would be an
interesting addition to C#....  Which will never happen, obviously.

>  I do however need to reference som global constants defined in
> ruby.h like this:
> 
> typedef unsigned long VALUE;
> 
> EXTERN VALUE rb_mKernel;
> EXTERN VALUE rb_mComparable;
> EXTERN VALUE rb_mEnumerable;
> EXTERN VALUE rb_mPrecision;
> EXTERN VALUE rb_mErrno;
> EXTERN VALUE rb_mFileTest;
> EXTERN VALUE rb_mGC;
> EXTERN VALUE rb_mMath;
> EXTERN VALUE rb_mProcess;
> 
> How can I DllImport these? I tried:
> 
> [DllImport(@"c:/ruby180/bin/msvcrt-ruby18.dll")]
> public static extern /* VALUE */ uint rb_cModule;
> 
> but that doesn't work.

You can't DllImport a variable.  Primarily because the PE file format
doesn't support exporting variables (IIRC).

> Is there a smarter way to do this, where I don't have to manually duplicate
> all this? Like a utility program that will generate a managed wrapper class
> with static methods given a .h file.

You can take a look at the gtk-sharp CVS module, which contains a Perl
script to parse C code and generate XML files (containing classes,
enumerations, constants, etc.), which are then used to generate the C#
class definitions.

Though that might be overkill.  Yes, you should use a utility program. 
It'll probably be easiest to just custom write your own in Ruby.  It
should be easy enough to create a regex that will grab the variable
name, attach a suffix, use that for a function, and implement it.  

> What is the performance overhead of using P/Invoke instead of IJW (It Just
> Works) in managed C++?

IJW is basically an implicit P/Invoke.  It uses the same runtime
mechanism to call native functions.

At least, that was my impression reading the documentation last year.

> What is the state of Mono P/Invoke, DllImport, MarshalAs etc.

It mostly works.  I ran across one unimplemented feature for marshaling
LPWStr types, but that's not particularly common under Unix anyway.

> What about
> System.Reflection and System.Reflection.Emit?

Mono arguably has a better implementation than .NET. :-)

MCS uses System.Reflection.Emit, and a couple enhancements needed to be
made so that all of C# could be easily implemented using S.R.E.

 - Jon