[Mono-dev] Calling unmanaged dll from C#

Jonathan Pryor jonpryor at vt.edu
Tue Jun 20 07:28:21 EDT 2006


On Mon, 2006-06-19 at 09:49 -0400, romyd misc wrote:
> I want to use DllImport to call a C function that allocates and
> returns an array of strings. I'm not sure if this is the right place
> to ask this question,

mono-list is the appropriate place for this question (yet I'll answer
anyway...)

>  but my sample code works with windows .NET
> compiler, so either there is a different way to call unmanaged dlls in
> mono or may be i'm not implementing it right?

Or it's (C): None of the above. :-)

You're hitting three issues, two of which I wouldn't expect you to know
about, and one of which confuses me (as I don't see why it works
on .NET).

The first issue is this: C's `sizeof(wchar_t)' is NOT equal to C#'s
sizeof(System.Char).  System.String is an array of System.Char structs,
and a System.Char is an unsigned 16-bit value.  `wchar_t' on Unix
platforms, on the other hand, is a signed 32-bit value.  Consequently,
the use of mbrtowc(3) is incorrect.

The second issue is that Mono takes "CharSet.Auto" to be "ANSI" by
default (where for Mono "ANSI" is really UTF-8).  So your DllImport says
Func() should be in the "auto" charset, but your C code is wchar_t,
which wouldn't be right anyway....

The third issue is that your code confuses me. :-)

In particular, Func() is similar to this:

	void Func (wchar_t** ipadds)
	{
		(*ipadds) = malloc (10);
	}

Yet your DllImport says that `ipadds' should be a `string[]'.  That code
looks like it should be a `ref string', not a `string[]', since it would
only be modifying the first element of the array...

I'm surprised this works under .NET, actually.

Regardless, the following code works:

        /*
         * KeyServerUsage.c: Unmanaged Library.
         *
         * Compile as:
         *  gcc -shared -o libKeyServerUsage.so KeyServerUsage.c
         */
        #include <stdio.h>
        #include <stdlib.h>
        #include <string.h>
        
        void Func(char** ipadds)
        {
          char mbBuf[] = "1.1.1.1";
          size_t len = strlen (mbBuf);
        
          *ipadds = malloc (len+1);
          strcpy (*ipadds, mbBuf);
        }
        
        void* Func2 (void)
        {
          char mbBuf[] = "1.1.1.1";
          size_t len = strlen (mbBuf);
        
          char* ipadds = malloc (len+1);
          strcpy (ipadds, mbBuf);
          return ipadds;
        }
        
        
        /*
         * KeyServerUsage.cs
         *
         * Compile as: mcs -r:Mono.Posix.dll KeyServerUsage.cs
         */
        using System;
        using System.Collections;
        using System.Runtime.InteropServices;
        
        using Mono.Unix;
        
        class Test {
          [DllImport("KeyServerUsage.dll")]
          private static extern void Func(out string name);
        
          public static string GetIPAdd ()
          {
            string ipadds;
            Func (out ipadds);
            return ipadds;
          }
        
          [DllImport("KeyServerUsage.dll")]
          private static extern IntPtr Func2 ();
        
          public static string GetIPAdd2 ()
          {
            IntPtr p = Func2 ();
            string s = Marshal.PtrToStringAnsi (p);
            UnixMarshal.FreeHeap (p);
            return s;
          }
        
          public static void Main ()
          {
            string l = GetIPAdd ();
            Console.WriteLine (l);
            l = GetIPAdd2 ();
            Console.WriteLine (l);
          }
        }

Note the addition of the Func2() and GetIPAdd2() methods, which uses a
manually freed return value instead of setting the `out' parameter to
malloc(3)'d memory.  This should probably be preferred in order to avoid
memory leaks (as .NET will _never_ call free(3), using CoTaskMemFree()
instead, and I don't know if .NET will call CoTaskMemFree() on [Out]
parameters...)

 - Jon





More information about the Mono-devel-list mailing list