[Mono-list] Need help with DllImport (P/Invoke) and UCS-4 unicode

Jonathan Pryor jonpryor at vt.edu
Mon Jul 16 12:12:23 EDT 2007


On Mon, 2007-07-16 at 17:06 +0200, Christian Heimes wrote:
> Your code works like a charm and I got most unit tests running. I'm
> still having a small problem. How can I apply a custom marshaler to the
> return value of a pinvoke? I like to replace some code with the custom
> marshaler.
> 
>     [DllImport(Runtime.dll, CallingConvention = CallingConvention.Cdecl,
>            EntryPoint = "PyUnicodeUCS4_AsUnicode",
>            ExactSpelling = true)]
>     internal unsafe static extern char*
>     PyUnicode_AsUnicode(IntPtr ob);
> 
>     internal unsafe static string GetManagedString(IntPtr op) {
>         IntPtr type = PyObject_TYPE(op);
> 
>         ...
> 
>         if (type == Runtime.PyUnicodeType) {
>             char *p = Runtime.PyUnicode_AsUnicode(op);
>             int size = Runtime.PyUnicode_GetSize(op);
>             return new String(p, 0, size);
>         }
> 
>         return null;
>     }

In theory, you could probably supply a custom marshaler to the return
value, e.g.

	[return:MarshalAs (...)]
	[DllImport (...)]
	internal unsafe static extern char* ...

(The "return:" prefix specifies that the custom attribute applies to the
return value.)

I don't know if this will work though, as I've never tried it before.

I would instead suggest that you use IntPtr for the return type, if for
no other reason that reference types (e.g. string) require that the
unmanaged code use a specific memory allocator as the runtime will free
the memory.  For example, under Win32 PyUnicode_AsUnicode() would need
to allocate its return value with CoTaskMemAlloc(), as .NET will use
CoTaskMemFree() to free the memory returned.  Using IntPtr avoids this
default behavior.  (Of course, Mono on Linux will use a different memory
allocator than the COM task allocator, thus leading to less portable
unmanaged code.)

Furthermore, with your GetManagedString() code above it'll break if
Runtime.PyUnicode_AsUnicode() returns UTF-32 strings, as the `char*'
will still refer to UTF-16 strings.

If the string returned by Runtime.PyUnicode_AsUnicode() is
NULL-terminated, you could do this:

    internal unsafe static string GetManagedString(IntPtr op)
    {
        IntPtr type = PyObject_TYPE(op);

        ...

        if (type == Runtime.PyUnicodeType) {
            IntPtr p = Runtime.PyUnicode_AsUnicode(op);
            return UnixMarshal.PtrToString (p, Encoding.UTF32);
        }

        return null;
    }

This works as UnixMarshal.PtrToString() will look for a UTF-32 NULL
character to terminate the string, do the UTF-32 -> UTF-16 conversion,
and return the resulting string.

 - Jon




More information about the Mono-list mailing list