mail-archives/mono-list/2003-October/016551.html

302 строки
16 KiB
HTML

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<TITLE> [Mono-list] Marshaling bug?
</TITLE>
<LINK REL="Index" HREF="index.html" >
<LINK REL="made" HREF="mailto:jonpryor%40vt.edu">
<META NAME="robots" CONTENT="index,nofollow">
<LINK REL="Previous" HREF="016550.html">
<LINK REL="Next" HREF="016554.html">
</HEAD>
<BODY BGCOLOR="#ffffff">
<H1>[Mono-list] Marshaling bug?
</H1>
<B>Jonathan Pryor
</B>
<A HREF="mailto:jonpryor%40vt.edu"
TITLE="[Mono-list] Marshaling bug?">jonpryor@vt.edu
</A><BR>
<I>Sat, 25 Oct 2003 13:23:43 -0400</I>
<P><UL>
<LI> Previous message: <A HREF="016550.html">[Mono-list] Marshaling bug?
</A></li>
<LI> Next message: <A HREF="016554.html">[Mono-list] Marshaling bug?
</A></li>
<LI> <B>Messages sorted by:</B>
<a href="date.html#16551">[ date ]</a>
<a href="thread.html#16551">[ thread ]</a>
<a href="subject.html#16551">[ subject ]</a>
<a href="author.html#16551">[ author ]</a>
</LI>
</UL>
<HR>
<!--beginarticle-->
<PRE>I don't think the problem is retrieving the value from g_list_nth_data.
This is simple to check -- compare the value returned from
g_list_nth_data against the value you put into the g_list.
Or, just pass that value off to g_printf (DllImport it first):
[DllImport(...)]
static extern void g_printf (IntPtr format);
The problem, as I had mentioned, appears to be in marshaling the return
value (the unmanaged UTF-8 string) into managed memory (the char[]
array), implying that &quot;memmove&quot; isn't being marshaled correctly.
- Jon
On Sat, 2003-10-25 at 13:08, <A HREF="mailto:pbaena@uol.com.ar">pbaena@uol.com.ar</A> wrote:
&gt;<i> Thank you for all the suggestions. You're right. It seems that the problem is when retrieving from the g_list.
</I>&gt;<i>
</I>&gt;<i> The SWT API is very new so this is expected. The second approach I learned from Gtk#'s List wrapper. The intent of SWT is to be a thin layer on top of Gtk+ so I wouldn't dare to use Gtk#. All in all, Gtk# is very responsive, so I think it wouldn't hurt SWT to be a little higher level. Let's see how this evolves.
</I>&gt;<i>
</I>&gt;<i> Regards!!
</I>&gt;<i>
</I>&gt;<i> &gt; Pardon me for saying this, but your SWT code is borked. Seriously.
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; Why? Opaque pointer values should be expressed as a System.IntPtr, not
</I>&gt;<i> &gt; a System.Int32 (&quot;int&quot;). This is so that if you ever move to a platform
</I>&gt;<i> &gt; with a different sized pointer (say, 64-bit Opteron, or PowerPC 970, or
</I>&gt;<i> &gt; UltraSparc, or...), you won't kill all your pointer values.
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; Furthermore, you've got arrays where you shouldn't have arrays.
</I>&gt;<i> &gt; Consider the prototype for g_utf8_to_utf16:
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; gunichar* g_utf8_to_utf16 (const gchar *str,
</I>&gt;<i> &gt; glong len,
</I>&gt;<i> &gt; glong *items_read,
</I>&gt;<i> &gt; glong *items_written,
</I>&gt;<i> &gt; GError **error);
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; Then, consider how C code would call it:
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; int items_read, items_written;
</I>&gt;<i> &gt; const char* str = &quot;this is my utf-8 string&quot;;
</I>&gt;<i> &gt; gunichar* result =
</I>&gt;<i> &gt; g_utf8_to_utf16 (str, strlen (str),
</I>&gt;<i> &gt; &amp;items_read,
</I>&gt;<i> &gt; &amp;items_written,
</I>&gt;<i> &gt; NULL /* ignore errors */);
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; Assuming that we don't want to handle wrapping GError in C#, this would
</I>&gt;<i> &gt; be a better wrapper:
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; [DllImport(...)]
</I>&gt;<i> &gt; static extern IntPtr g_utf8_to_utf16 (byte[] str, int len,
</I>&gt;<i> &gt; out int items_read, out int items_written,
</I>&gt;<i> &gt; IntPtr error /* pass IntPtr.Zero for this */);
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; Notice that &quot;items_read&quot; and &quot;items_written&quot; are mapped to a &quot;out&quot;
</I>&gt;<i> &gt; parameter, instead of an array. This is more appropriate for this
</I>&gt;<i> &gt; function (and for any function similar to it -- items_read and
</I>&gt;<i> &gt; items_written aren't holding arrays, they're just an &quot;out&quot; pointer for
</I>&gt;<i> &gt; various data).
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; To answer your last question: what's the advantage of this:
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; string s = &quot;Let's i18n, baby...do it hard!&quot;;
</I>&gt;<i> &gt; IntPtr unmanaged_memory = Marshal.StringToHGlobalAnsi (s);
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; over the voluminous amounts of code you demonstrated before? Well, it's
</I>&gt;<i> &gt; shorter.
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; But it's also seriously broken, at least from a
</I>&gt;<i> &gt; cross-platform/portability perspective. Here's why:
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; - Not all platforms support &quot;HGLOBAL&quot;. On Unix platforms, this is
</I>&gt;<i> &gt; likely to be normal g_malloc/g_free, but on Windows, this should
</I>&gt;<i> &gt; be using the GlobalAlloc and GlobalFree Win32 APIs. Which means
</I>&gt;<i> &gt; you have different functions to call on different platforms, which
</I>&gt;<i> &gt; will be a portability headache.
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; - Even worse, StringToHGlobalAnsi creates an &quot;Ansi&quot; string. Ansi
</I>&gt;<i> &gt; IS NOT Utf-8. At least, you can't assume that it is, though it
</I>&gt;<i> &gt; *could* be. Ansi is, typically, the local code page, and if you've
</I>&gt;<i> &gt; been paying attention to the file-name handling thread on
</I>&gt;<i> &gt; mono-devel-list, you'd know that trying to mix the current code
</I>&gt;<i> &gt; page with Unicode handling is fraught with danger (and confusion,
</I>&gt;<i> &gt; and annoyance, and users with Pitchforks complaining about your
</I>&gt;<i> &gt; app not working right...).
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; So, how do you do string-interop, portably, between Mono &amp; GTK+? Well,
</I>&gt;<i> &gt; you could just use Gtk#, which will tackle this issue (eventually; it
</I>&gt;<i> &gt; appears to use Marshal.StringToHGlobalAnsi in some places, so it's
</I>&gt;<i> &gt; likely assuming that, under Mono, Ansi == UTF-8). This is certainly the
</I>&gt;<i> &gt; easiest way to go, unless you're dead set on providing *another* GTK+
</I>&gt;<i> &gt; wrapper. (Of course, this places a Gtk# dependency on SWT, which may be
</I>&gt;<i> &gt; undesirable.)
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; If you do it on your own, you're pretty much stuck doing what you're
</I>&gt;<i> &gt; doing in your first example.
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; As for why it doesn't work, it could be a regression. On my system, it
</I>&gt;<i> &gt; appears to be correctly converting the .NET UTF-16 input string &quot;str&quot;
</I>&gt;<i> &gt; into a UTF-8 string -- I'm able to pass &quot;data&quot; to g_printf and see
</I>&gt;<i> &gt; unmanaged representation.
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; It's the return trip -- converting the UTF-8 unmanaged memory and
</I>&gt;<i> &gt; copying it into the CLI char[] array, that appears to be the problem.
</I>&gt;<i> &gt; I'll need to write a small test case, and if this is a new marshalling
</I>&gt;<i> &gt; bug, I'll file it in bugzilla.
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; Thanks,
</I>&gt;<i> &gt; - Jon
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; On Sat, 2003-10-25 at 09:52, <A HREF="mailto:pbaena@uol.com.ar">pbaena@uol.com.ar</A> wrote:
</I>&gt;<i> &gt; &gt; I reported a bug (#50116) about this problem of mine (of SWT really), and I wanted to get help from the experts to see if the API can be improved.
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; SWT works this way to append and retrieve from a g_list:
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; --------------------------------CODE-------------------------
</I>&gt;<i> &gt; &gt; using System;
</I>&gt;<i> &gt; &gt; using System.Runtime.InteropServices;
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; class testbug {
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; public const string GLIB_LIBRARY = &quot;glib-2.0&quot;;
</I>&gt;<i> &gt; &gt; public const string STRLEN_LIBRARY = &quot;pango-1.0&quot;;
</I>&gt;<i> &gt; &gt; public const string MEMMOVE_LIBRARY = &quot;gtk-x11-2.0&quot;;
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; [DllImport(GLIB_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern int g_utf16_to_utf8(char[] str, int len, int[]
</I>&gt;<i> &gt; &gt; items_read, int[] items_written, int[] error);
</I>&gt;<i> &gt; &gt; [DllImport(GLIB_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern int g_utf8_to_utf16(byte[] str, int len, int[]
</I>&gt;<i> &gt; &gt; items_read, int[] items_written, int[] error);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; [DllImport(STRLEN_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern int strlen(int str);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; [DllImport(MEMMOVE_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern void memmove(int dest, int[] src, int size);
</I>&gt;<i> &gt; &gt; [DllImport(MEMMOVE_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern void memmove(int dest, byte[] src, int size);
</I>&gt;<i> &gt; &gt; [DllImport(MEMMOVE_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern void memmove(int[] dest, byte[] src, int size);
</I>&gt;<i> &gt; &gt; [DllImport(MEMMOVE_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern void memmove(byte[] dest, int src, int size);
</I>&gt;<i> &gt; &gt; [DllImport(MEMMOVE_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern void memmove(char[] dest, int src, int size);
</I>&gt;<i> &gt; &gt; [DllImport(MEMMOVE_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern void memmove(int[] dest, int src, int size);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; [DllImport(GLIB_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern void g_free(int mem);
</I>&gt;<i> &gt; &gt; [DllImport(GLIB_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern int g_malloc(int size);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; [DllImport(GLIB_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern int g_list_append(int list, int data);
</I>&gt;<i> &gt; &gt; [DllImport(GLIB_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern int g_list_nth_data(int list, int n);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; public static void Main ()
</I>&gt;<i> &gt; &gt; {
</I>&gt;<i> &gt; &gt; string str = &quot;Let's i18n, baby...do it hard!&quot;;
</I>&gt;<i> &gt; &gt; int glist = 0;
</I>&gt;<i> &gt; &gt; bool terminate = true;
</I>&gt;<i> &gt; &gt; char [] strchar = str.ToCharArray();
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; int [] items_read = new int [1], items_written = new int [1];
</I>&gt;<i> &gt; &gt; int ptr = g_utf16_to_utf8 (strchar, str.Length, items_read,
</I>&gt;<i> &gt; &gt; items_written, null);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; int written = items_written [0];
</I>&gt;<i> &gt; &gt; //TEMPORARY CODE - convertion stops at the first NULL
</I>&gt;<i> &gt; &gt; if (items_read [0] != strchar.Length) written++;
</I>&gt;<i> &gt; &gt; byte [] buffer = new byte [written + (terminate ? 1 : 0)];
</I>&gt;<i> &gt; &gt; memmove (buffer, ptr, written);
</I>&gt;<i> &gt; &gt; g_free (ptr);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; int data = g_malloc (buffer.Length);
</I>&gt;<i> &gt; &gt; memmove (data, buffer, buffer.Length);
</I>&gt;<i> &gt; &gt; glist = g_list_append (glist, data);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; data = g_list_nth_data (glist, 0);
</I>&gt;<i> &gt; &gt; int length = strlen (data);
</I>&gt;<i> &gt; &gt; byte [] buffer1 = new byte [length];
</I>&gt;<i> &gt; &gt; memmove (buffer1, data, length);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; ptr = g_utf8_to_utf16 (buffer1, buffer1.Length, null,
</I>&gt;<i> &gt; &gt; items_written, null);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; length = items_written [0];
</I>&gt;<i> &gt; &gt; char [] chars = new char [length];
</I>&gt;<i> &gt; &gt; memmove (chars, ptr, length * 2);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; Console.WriteLine (chars);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; g_free (ptr);
</I>&gt;<i> &gt; &gt; }
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; }
</I>&gt;<i> &gt; &gt; ------------------------------------------------------------------
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; That worked till mono 0.28, but doesn't work with current mono from CVS. Now I was testing things and found that this other approach to the problem works:
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; ------------------------------CODE--------------------------------
</I>&gt;<i> &gt; &gt; using System;
</I>&gt;<i> &gt; &gt; using System.Runtime.InteropServices;
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; class testbug {
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; public const string GLIB_LIBRARY = &quot;glib-2.0&quot;;
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; [DllImport(GLIB_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern int g_list_append(int list, IntPtr data);
</I>&gt;<i> &gt; &gt; [DllImport(GLIB_LIBRARY, CharSet = CharSet.Unicode)]
</I>&gt;<i> &gt; &gt; public static extern string g_list_nth_data(int list, int n);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; public static void Main ()
</I>&gt;<i> &gt; &gt; {
</I>&gt;<i> &gt; &gt; string str = &quot;Let's i18n, baby...do it hard!&quot;;
</I>&gt;<i> &gt; &gt; int glist = 0;
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; glist = g_list_append (glist, Marshal.StringToHGlobalAnsi (str));
</I>&gt;<i> &gt; &gt; string data2 = g_list_nth_data (glist, 0);
</I>&gt;<i> &gt; &gt; Console.WriteLine (data2);
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; return;
</I>&gt;<i> &gt; &gt; }
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; }
</I>&gt;<i> &gt; &gt; ----------------------------------------------------------------
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; Now I was wondering what are the advantages of the latest approach in contrast with SWT's. Can you give me some advice?
</I>&gt;<i> &gt; &gt;
</I>&gt;<i> &gt; &gt; Thank you very much!
</I>&gt;<i> &gt; &gt; Pablo
</I>&gt;<i> &gt; &gt; _______________________________________________
</I>&gt;<i> &gt; &gt; Mono-list maillist - <A HREF="mailto:Mono-list@lists.ximian.com">Mono-list@lists.ximian.com</A>
</I>&gt;<i> &gt; &gt; <A HREF="http://lists.ximian.com/mailman/listinfo/mono-list">http://lists.ximian.com/mailman/listinfo/mono-list</A>
</I>&gt;<i> &gt;
</I>&gt;<i> &gt; _______________________________________________
</I>&gt;<i> &gt; Mono-list maillist - <A HREF="mailto:Mono-list@lists.ximian.com">Mono-list@lists.ximian.com</A>
</I>&gt;<i> &gt; <A HREF="http://lists.ximian.com/mailman/listinfo/mono-list">http://lists.ximian.com/mailman/listinfo/mono-list</A>
</I>&gt;<i> _______________________________________________
</I>&gt;<i> Mono-list maillist - <A HREF="mailto:Mono-list@lists.ximian.com">Mono-list@lists.ximian.com</A>
</I>&gt;<i> <A HREF="http://lists.ximian.com/mailman/listinfo/mono-list">http://lists.ximian.com/mailman/listinfo/mono-list</A>
</I>
</PRE>
<!--endarticle-->
<HR>
<P><UL>
<!--threads-->
<LI> Previous message: <A HREF="016550.html">[Mono-list] Marshaling bug?
</A></li>
<LI> Next message: <A HREF="016554.html">[Mono-list] Marshaling bug?
</A></li>
<LI> <B>Messages sorted by:</B>
<a href="date.html#16551">[ date ]</a>
<a href="thread.html#16551">[ thread ]</a>
<a href="subject.html#16551">[ subject ]</a>
<a href="author.html#16551">[ author ]</a>
</LI>
</UL>
</body></html>