[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