[Gtk-sharp-list] Getting Application.Init (args) to work

Peter Williams peter@newton.cx
Tue, 02 Dec 2003 21:57:37 -0500


--=-9ARav0W4C1CHyonDG5Vz
Content-Type: text/plain
Content-Transfer-Encoding: 7bit

Hi,

	I've been working on getting Application.Init () to work when given
commandline arguments. The current code just tries to use a "ref
string[] args" parameter when calling gtk_init (), but that doesn't
work, because the runtime doesn't support passing references to arrays
into unmanaged code. Or at least, if you try to use the code right now,
it totally breaks. (Another problem is that gtk_init() expects argv[0]
to be the program name, which is not the case if you pass it the
arguments given to your Main() function.)

	Anyway, I cooked up a patch to get this working, based on the advice
here: 

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconarrayssample.asp

Unfortunately, the result is nasty, and then there's the issue of 64-bit
safety, which I have attempted to address. There's probably several ways
to go about it; I chose the one that I thought created the least
confusing code. (It works on my 32-bit system; the 64-bit version is
untested.)

	Mike and I would like to find a neater solution to the problem, so I'd
be interested if someone more familiar with the runtime had an idea
about a better approach.

	Peter

-- 
Peter Williams                          peter@newton.cx

"[Ninjas] are cool; and by cool, I mean totally sweet."
                              -- REAL Ultimate Power

--=-9ARav0W4C1CHyonDG5Vz
Content-Disposition: attachment; filename=gtkinit.diff
Content-Type: text/x-patch; name=gtkinit.diff; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit

Index: ChangeLog
===================================================================
RCS file: /cvs/public/gtk-sharp/ChangeLog,v
retrieving revision 1.573
diff -u -r1.573 ChangeLog
--- ChangeLog	1 Dec 2003 04:43:20 -0000	1.573
+++ ChangeLog	1 Dec 2003 07:48:18 -0000
@@ -1,3 +1,16 @@
+2003-12-01  Peter Williams  <peter@newton.cx>
+
+	* gtk/Application.cs (do_init): New helper function to handle
+	passing args to Gtk. Not very pleasant, but this seems to be
+	the only way to pass ref arrays to unmanaged code.
+	(check_sixtyfour): Helper function for the helper function.
+	(make_buf_32): Ditto.
+	(make_buf_64): Ditto.
+	(unmarshal_32): Ditto.
+	(unmarshal_64): Ditto.
+	(Init): Use do_init, take a new progname parameter.
+	(InitCheck): Same.
+
 2003-11-30  Mike Kestner  <mkestner@speakeasy.net>
 
 	* art/art-symbols.xml : add some simple types to clean up generation.
Index: gtk/Application.cs
===================================================================
RCS file: /cvs/public/gtk-sharp/gtk/Application.cs,v
retrieving revision 1.11
diff -u -r1.11 Application.cs
--- gtk/Application.cs	25 Mar 2003 16:57:05 -0000	1.11
+++ gtk/Application.cs	1 Dec 2003 07:48:18 -0000
@@ -19,29 +19,147 @@
 		{
 		}
 		
+		// this is annoyingly complex
+
+		[DllImport("libglib-2.0-0.dll")]
+		static extern IntPtr g_malloc(ulong size);
+
+		[DllImport("libglib-2.0-0.dll")]
+		static extern void g_free(IntPtr mem);
+
 		[DllImport("libgtk-win32-2.0-0.dll")]
-		static extern void gtk_init (int argc, IntPtr argv);
+		static extern void gtk_init (ref int argc, ref IntPtr argv);
+
+		[DllImport("libgtk-win32-2.0-0.dll")]
+		static extern bool gtk_init_check (ref int argc, ref IntPtr argv);
 
 		public static void Init ()
 		{
-			gtk_init (0, new IntPtr(0));
+			IntPtr argv = new IntPtr(0);
+			int argc = 0;
+
+			gtk_init (ref argc, ref argv);
 		}
 
-		[DllImport("libgtk-win32-2.0-0.dll")]
-		static extern void gtk_init (ref int argc, ref String[] argv);
-		[DllImport("libgtk-win32-2.0-0.dll")]
-		static extern bool gtk_init_check (ref int argc, ref String[] argv);
+		// we need all this crap to pass an array reference to
+		// unmanaged code: the runtime doesn't have any way of knowing
+		// that "ref int argc" is size of the argv array. Basic idea of
+		// how to solve this problem demonstrated (in much simpler
+		// circumstances) at:
+		//
+		// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconarrayssample.asp
+		//
+		// Combine this with attempts at 64-bit cleanliness and this 
+		// is ugly ugly ugly.
+
+		static bool check_sixtyfour () {
+			int szint = Marshal.SizeOf (typeof (int));
+			int szlong = Marshal.SizeOf (typeof (long));
+			int szstr = Marshal.SizeOf (typeof (string));
+
+			if (szstr == szint)
+				return false;
+			if (szstr == szlong)
+				return true;
+
+			throw new Exception ("Pointers are neither int- nor long-sized???");
+		}
+
+		static IntPtr make_buf_32 (string[] progargs) 
+		{
+			int[] ptrs = new int[progargs.Length];
+
+			for (int i = 0; i < progargs.Length; i++)
+				ptrs[i] = (int) Marshal.StringToHGlobalAuto (progargs[i]);
+				
+			IntPtr buf = g_malloc ((ulong) Marshal.SizeOf(typeof(string)) * (ulong) progargs.Length);
+			Marshal.Copy (ptrs, 0, buf, ptrs.Length);
+			return buf;
+		}
+
+		static IntPtr make_buf_64 (string[] progargs) 
+		{
+			long[] ptrs = new long[progargs.Length];
+
+			for (int i = 0; i < progargs.Length; i++)
+				ptrs[i] = (long) Marshal.StringToHGlobalAuto (progargs[i]);
+				
+			IntPtr buf = g_malloc ((ulong) Marshal.SizeOf(typeof(string)) * (ulong) progargs.Length);
+			Marshal.Copy (ptrs, 0, buf, ptrs.Length);
+			return buf;
+		}
+
+		static string[] unmarshal_32 (IntPtr buf, int argc)
+		{
+			int[] ptrs = new int[argc];
+			string[] args = new string[argc - 1];
+
+			Marshal.Copy (buf, ptrs, 0, argc);
+
+			for (int i = 1; i < ptrs.Length; i++)
+				args[i - 1] = Marshal.PtrToStringAuto ((IntPtr) ptrs[i]);
+			
+			return args;
+		}
+
+		static string[] unmarshal_64 (IntPtr buf, int argc)
+		{
+			long[] ptrs = new long[argc];
+			string[] args = new string[argc - 1];
+
+			Marshal.Copy (buf, ptrs, 0, argc);
+
+			for (int i = 1; i < ptrs.Length; i++)
+				args[i - 1] = Marshal.PtrToStringAuto ((IntPtr) ptrs[i]);
+			
+			return args;
+		}
+
+		static bool do_init (string progname, ref string[] args, bool check)
+		{
+			bool res = false;
+			string[] progargs = new string[args.Length + 1];
+			bool sixtyfour = check_sixtyfour ();
+
+			progargs[0] = progname;
+			args.CopyTo (progargs, 1);
+
+			IntPtr buf;
+
+			if (sixtyfour)
+				buf = make_buf_64 (progargs);
+			else
+				buf = make_buf_32 (progargs);
+
+			int argc = progargs.Length;
+
+			if (check)
+				res = gtk_init_check (ref argc, ref buf);
+			else
+				gtk_init (ref argc, ref buf);
+
+			// copy back the resulting argv, minus argv[0], which we're
+			// not interested in.
+
+			if (argc == 0)
+				args = new string[0];
+			else if (sixtyfour)
+				args = unmarshal_64 (buf, argc);
+			else
+				args = unmarshal_32 (buf, argc);
+
+			g_free (buf);
+			return res;
+		}
 
-		public static void Init (ref string[] args)
+		public static void Init (string progname, ref string[] args)
 		{
-			int argc = args.Length;
-			gtk_init (ref argc, ref args);
+			do_init (progname, ref args, false);
 		}
 
-		public static bool InitCheck (ref string[] args)
+		public static bool InitCheck (string progname, ref string[] args)
 		{
-			int argc = args.Length;
-			return gtk_init_check (ref argc, ref args);
+			return do_init (progname, ref args, true);
 		}
 
 		[DllImport("libgtk-win32-2.0-0.dll")]
Index: doc/ChangeLog
===================================================================
RCS file: /cvs/public/gtk-sharp/doc/ChangeLog,v
retrieving revision 1.175
diff -u -r1.175 ChangeLog
--- doc/ChangeLog	23 Nov 2003 00:56:01 -0000	1.175
+++ doc/ChangeLog	1 Dec 2003 07:48:18 -0000
@@ -1,3 +1,8 @@
+2003-11-23  Peter Williams  <peter@newton.cx>
+
+	* en/Gtk/Application.xml: Update Init, InitCheck functions
+	for new progname argument. Fix a paste-o in the docs for InitCheck.
+
 2003-11-22  John Luke  <jluke@cfl.rr.com>
 
 	* en/Gnome/Print.xml: add example
Index: doc/en/Gtk/Application.xml
===================================================================
RCS file: /cvs/public/gtk-sharp/doc/en/Gtk/Application.xml,v
retrieving revision 1.18
diff -u -r1.18 Application.xml
--- doc/en/Gtk/Application.xml	28 Oct 2003 00:48:06 -0000	1.18
+++ doc/en/Gtk/Application.xml	1 Dec 2003 07:48:19 -0000
@@ -142,16 +142,18 @@
       </Docs>
     </Member>
     <Member MemberName="Init">
-      <MemberSignature Language="C#" Value="public static void Init (ref string [] args);" />
+      <MemberSignature Language="C#" Value="public static void Init (string progname, ref string [] args);" />
       <MemberType>Method</MemberType>
       <ReturnValue>
         <ReturnType>System.Void</ReturnType>
       </ReturnValue>
       <Parameters>
+        <Parameter Name="progname" Type="System.String" />
         <Parameter Name="args" Type="System.String []&amp;" />
       </Parameters>
       <Docs>
         <summary>Initializes Gtk# for operation.</summary>
+        <param name="progname">The name of your program</param>
         <param name="args">The arguments to pass to the toolkit</param>
         <remarks>
           <para>
@@ -163,7 +165,7 @@
 	    This function will terminate your program if it was unable
 	    to initialize the GUI for some reason. If you want your
 	    program to fall back to a textual interface you want to
-	    call <see cref="M:Gtk.Application.InitCheck(System.String []&amp;)" /> instead.
+	    call <see cref="M:Gtk.Application.InitCheck(System.String, System.String []&amp;)" /> instead.
 	  </para>
           <para>
 	    The args values will be modified after Gtk has removed the
@@ -173,18 +175,20 @@
       </Docs>
     </Member>
     <Member MemberName="InitCheck">
-      <MemberSignature Language="C#" Value="public static bool InitCheck (ref string [] args);" />
+      <MemberSignature Language="C#" Value="public static bool InitCheck (string progname, ref string [] args);" />
       <MemberType>Method</MemberType>
       <ReturnValue>
         <ReturnType>System.Boolean</ReturnType>
       </ReturnValue>
       <Parameters>
+        <Parameter Name="progname" Type="System.String" />
         <Parameter Name="args" Type="System.String []&amp;" />
       </Parameters>
       <Docs>
         <summary>Initializes Gtk# for operation, probes window system.</summary>
         <returns>true if the toolkit was initialized, false if the
         windowing system can not be initilized.</returns>
+        <param name="progname">The name of your program</param>
         <param name="args">The arguments to pass to the toolkit</param>
         <remarks>
           <para>
@@ -195,12 +199,6 @@
 	    toolkit could not be initialized.  If you do not want to
 	    do dual GUI/text applications, you can use <see cref="M:Gtk.Application.Init()" /> instead. 
 	  </para>
-          <para>
-	    This function will terminate your program if it was unable
-	    to initialize the GUI for some reason. If you want your
-	    program to fall back to a textual interface you want to
-	    call <see cref="M:Gtk.Application.InitCheck(System.String []&amp;)" /> instead.
-	  </para>
         </remarks>
       </Docs>
     </Member>
@@ -223,11 +221,11 @@
 	    This function will terminate your program if it was unable
 	    to initialize the GUI for some reason. If you want your
 	    program to fall back to a textual interface you want to
-	    call <see cref="M:Gtk.Application.InitCheck(System.String []&amp;)" /> instead.
+	    call <see cref="M:Gtk.Application.InitCheck(System.String, System.String []&amp;)" /> instead.
 	  </para>
           <para>
 	    If you want to pass arguments from the command line use
-	    the <see cref="T:Gtk.Application.Init(System.String []&amp;)" />
+	    the <see cref="T:Gtk.Application.Init(System.String, System.String []&amp;)" />
 	    method instead. 
 	  </para>
         </remarks>
@@ -267,4 +265,4 @@
       </Docs>
     </Member>
   </Members>
-</Type>
\ No newline at end of file
+</Type>

--=-9ARav0W4C1CHyonDG5Vz--