[Mono-list] more problems with C and C# strings

Jonathan Pryor jonpryor@vt.edu
Mon, 20 Dec 2004 19:50:44 -0500


On Tue, 2004-12-21 at 10:15 +1000, Bryan Buchanan wrote:
<snip/>

The crux of your problem is you have this C function:

	void QMCall(char * subrname, int argc, ...);

And this C# declaration:

	[DllImport("mylib.so")]
	public static extern void QMCall(string subrname, int argc, 
		params StringBuilder [] str);

While syntactically these functions may be called similarly, they're
completely different.  The "problem" is that arrays are marshaled as
arrays.  The "params" modifier is completely ignored by the marshaler.
Consequently, Mono will call QMCall as if it were declared:

	void QMCall (char *subrname, int argc, char **arguments);

Which, if you know anything about the stdarg/cdecl/vararg calling
conventions, is completely different.

The solution?  Don't do that.  It's not necessarily portable.  For
example, in late November Mono couldn't call stdarg functions that had
floating-point parameters on AMD64, as it didn't follow the correct
calling convention.  This was later fixed, but calling stdarg functions
is likely to always be somewhat iffy.  See: 

	http://lists.ximian.com/archives/public/mono-list/2004-November/024390.html

The "correct" solution is likely to use __arglist in C#: this will map
to a stdarg C function, properly passing arguments on the stack.
However, it's support is incomplete (IIRC), and may never be an answer.

Another alternative is to use System.Reflection.Emit to take a parameter
list and generate a DllImport statement at runtime with the correct
number of parameters.  This way you'll always place the arguments on the
stack.  However, it's not certain to be portable (see the aforementioned
ABI issues AMD64 had), but this technique is being used in Cocoa# and
Mono.Posix (for wrapping Stdlib.printf, arguably a bad idea but a fun
hack nonetheless).  See:

	http://mono.myrealbox.com/source/trunk/mcs/class/Mono.Posix/Mono.Unix/CdeclFunction.cs

 - Jon