Unsafe mono Aktuell Seminare Reports Homepage Software
printer / text mode version
university-logo
draheim
@informatik.hu-berlin.de

Reports
- postindustr.CC
- XML/Ti Report
- pTA StudienArbeit  .
- sch_llf study
- Geschichte des PC

TechDocs
- Perl Objects
- Installing Oracle
- shell cmds in python
- Using css for xml
    defs   tricks
- Unsafe mono  [x]  !
- Docbook Manpages
- Java Bean   Code
rpm-suse
 
- schema-mappingen
  ig cv hg re dv ev
  zz mk pr
- java problemsen
  lang swing ext gtk jjtree xul
 
boot
-grub-netboot
-grub-gtk
-partclone freshmeat
-partimage links
 
-releaseuploader


sitemap


-guidod-pygtk
sitemap             *offsite link

2005-03-23
(C) Guido Draheim
guidod@gmx.de

 
generated by mksite.sh

Unsafe Mono C# Code

Target of this documentation: learn how write/compile/run C# code that calls C/C++ functions in libraries (or kernel) of the underlying operating system - looking at the mono/linux platform for unsafe code.

Preface

There is a lot false documentation floating around. Many state that mono can not compile unsafe code. Wrong, it can always do it. The problem is not the compiler but the runtime. Mono ships with a runtime and its abilities to call into libraries were limited. The biggest problem: each native code platform (linux or windows) differs heavily in the way to specify the dependencies. And that is the hard piece to learn about.

Unsafe Code

There is a short Unsafe CSharp Introduction that can help to get started. It is actually the preface to a coursework at a UK university. Basically, you need the mono framework along with the gtksharp assembly. The "Game" example over there uses pairs of "unsafe" / "fixed" to optimize the code for speed: the C# runtime will usually check sizes and object types of multi-dimensional arrays. And sure computer programmers know that it is about the small foreach loops on data that is the real time-consuming thing making the big program to feel slow in the end.

Here we go: the example code shows how to create an "unsafe class Board". Yes, there is such a thing, basically it is a refcounted unmanaged C++ class definition (It won't be moved suddenly?). Everywhere that you create an object of that class or usethat object... you must wrap the portion containing such an object reference in "unsafe { ... }" braces. Furthermore, every argument that is pushed down into a method of that "unsafe class" must be "fixed". That looks like this:

  public static void initialize_board(Config cf) {
    unsafe {
       int width = cf.Nb_cols;
       int height = cf.Nb_rows;
       b = new Board(&width, &height);
    }
    for(int i = 0; i < cf.Nb_cols; ++i) {
      for(int j = 0; j < cf.Nb_rows; ++j) {
        unsafe {
          fixed (Cell* c = &b.grid[i, j])  {
            c->count = sum_up (&b, i, j);
          }
        }
      }
    }

  unsafe class Board {
    public Cell[,] grid;
    public Board(int* width, int* height) {
      grid = new Cell[*width, *height];
    }
  }

The example presents the Board onto the screen using the GTK# libraries. The full code is in Game.cs and the compiler is being called along with `-r:gtk-sharp`. If the assembly is not in the default search path then you need to set `-L path` to point to the path. Remember to set "--unsafe" during compiling but there is no need to say that upon runing the compiled executable.

On Suse Linux, the "gtk-sharp"
package exists. But to compile
you need to add the option

-L /usr/lib/mono/gtk-sharp/
  1. To compile the program, type:
    mcs -r:gtk-sharp --unsafe Game.cs
  2. To run the program, type:
    mint Game.exe

When the program would not contain any unsafe keywords, one can remove the --unsafe switch from step 1. At that point the program may also work with the Mono JIT so you can try also `mono Game.exe`. Unsafe code is unlikely to work with the Mono JIT. This paragraph is word from that coursework introduction [UnsafeCSharp] which also contains hint for compiling the program under windows:

  1. Download and install the Mono and GTK# installer.
  2. Open a command-line prompt and change to the directory containing the program file.
  3. . Set the path environment variable so that the Mono command-line tools are available by typing:
    set PATH=%PATH%;"C:\Program Files\Mono-1.0.5\bin\"
  4. To compile the program, type:
    mcs -pkg:gtk-sharp -target:winexe -unsafe Game.cs
  5. To run the program, type:
    mono Game.exe

When the program code no longer contains any unsafe keywords, you can remove the -unsafe switch from step 4.

Importing Native Functions

Java has JINI, CSharp has DllImport. I have some experiences with the java native interface support it is has a big problem: there compiled java objects ("*.class") do not have RPATHs or even any other dependency hint. When running a "*.class" or "*.jar" you have to specify all native code dependencies on the command line. The CSharp folks have solved it - using function attributes.

Later Java has learned about generic attributes as well. We will see whether it helps with the native code import problems. In C# it does exist from day 1 simply because microsoft has so many native dlls. It was not a good idea to even try to recreated them from square 1 in "pure" managed code. Therefore we can be strict sure that the native function import works. Always. In our later "tomboy"-like gnome panel example it looks like:

   // preload /opt/gnome/lib/libpanel-applet-2.so
   [DllImport("panel-applet-2")] 
   // import this C symbol from the environment
   static extern void panel_applet_factory_main(IntPtr raw,
       out int size_hints, int n_elements, int base_size);

That should give you some clues. First of all, it easy to transfer integral types as arguments (and return values) since they identical in C/C++ and C#. For an external struct object just use "IntPtr" to get a generic pointer thing. The "out" parameter hint ensures that the address of object is being transfered. That object is automatically "fixed" of course. - Let's have a look a more problematic thing:

  [DllImport("panel-applet-2")]
  static extern IntPtr panel_applet_new();

  [DllImport("panel-applet-2")]
  static extern unsafe int panel_applet_gconf_get_int(IntPtr raw, 
    string key, out IntPtr opt_error);

  public unsafe int GconfGetInt(string key) {
    IntPtr error = IntPtr.Zero;
    int ret = panel_applet_gconf_get_int(Handle, key, out error);
    if (error != IntPtr.Zero) throw new GLib.GException (error);
    return ret;
  }

The "unsafe" on the GconfGetInt ensure that the "string" key is being fixed. Marking the import "gconf_get_int" as "unsafe" will remind us of that as well. Well, that's about it with library imports. A different thing might be when importing a functions from the kernel.

When trying to call a function from a kernel API (like the "dvb" API or the "videodev" API) one has to remember that they look like function references but they are not. The header files for that will define an "inline function" or "parametric define" that maps the arguments on to an "ioctl" call - often along with an argument buffer instead of using a stack parameter block. That's why you have to write your own little C library that turns those inline function into real function references first. Then DllImport.

One last note - apart from "unsafe" classes, "unsafe" functions and "static extern" functions there is also support for "internal" clsses, "internal" delegates, objects and a lot more. I have seen those on the results of the "gtk#" generator that turns C libraries in the gtk object-oriented style into wrapper C# assembly source. (I guess they have to do with `mono_add_internal_call ("Hello::Sample", sample);` that allows to export a function from the runtime. Instead of specifying a DllImport one just says "internal".)

Building Wrapper Assemblies

Remember that the base case to write optimized code is having a C library with simple code that convert complicated objects and API usages into a format that can be easily imported by a C# source DllImport. Theoretically there is "managed C++" as well but the mono compiler suite does not (yet) have it. In any case, the underlying "#define" code would need to be compiled ahead of time anyway.

Anyway, if you want to have something redistributable you should remember the mono way of expressing complicated library dependencies. In the first attempt one would use function attributes or class attributes. That did work for windows and linux but it did fail on the next platform that the original developer of an assembly was not thinking of: Mac OS-X. Therefore, the best thing is to put it in an extra file that can be edited manually. Here is the tomboy xml file named Tomboy.exe.config

<configuration>
  <dllmap dll="libglib-2.0-0.dll" target="libglib-2.0.so.0"/>
  <dllmap dll="libgobject-2.0-0.dll" target="libgobject-2.0.so.0"/>
  <dllmap dll="libgtkspell" target="libgtkspell.so.0"/>
  <dllmap dll="libtomboy" target="libtomboy.so"/>
  <dllmap dll="panel-applet-2" target="libpanel-applet-2.so.0"/>
</configuration>

Now watch for the "libtomboy" entry which differs from the other ones. Yes, most imports are referencing "ldd" system libraries but for libtomboy it is a dynamic shared object shipped by the project itself. It is not being installed in the public /usr/lib path but in the package-local /usr/lib/tomboy module path. A little wrapper script will ensure it is present in LD_LIBRARY_PATH.

........... to be continued

CSharp Gnome Panel Applet

The tomboy note applet is widely used already. Hence, it is a sophisticated example to have a look at.

........... to be continued