sprintf

Is there any chance to have sprintf in C#? I have now:

  [DllImport("msvcrt", 
      CharSet = CharSet.Unicode,
      CallingConvention = CallingConvention.Cdecl)]
  
  static extern int swprintf( StringBuilder buffer,
                              string format,
                              __arglist);
  //...
  swprintf( str, "test %d", __arglist(100) );

But running the code gives me foll. error:

InvalidProgramException: Invalid IL code in XXXX:Start (): IL_001d: call      0x0a000021

That should work. I’ll test this out in Unity later tonight.

why?

whats wrong with string.Format

It does, indeed, look like a compiler bug. I tested this with Mono 2.10.8. Small modification of your program, just playing around with different calls, it doesn’t actually seem to matter all that much. The program either fails on the delegate call or on the method return.

// dlltest.cs
using System.Runtime;
using System.Runtime.InteropServices;
using System.Text;

public class DllTest
{
    [DllImport("msvcrt", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
    static extern int sprintf(StringBuilder buffer, string format, __arglist);
    
    static public void Main()
    {
        StringBuilder str = new StringBuilder();
        DllTest.sprintf(str, "Hello %s", __arglist("World"));
    }
}

e:\Program Files (x86)\Mono-2.10.8\bin>gmcs dlltest.cs

e:\Program Files (x86)\Mono-2.10.8\bin>mono dlltest.exe

Unhandled Exception: System.InvalidProgramException: Invalid IL code in DllTest:Main (): IL_0017: ret
[ERROR] FATAL UNHANDLED EXCEPTION: System.InvalidProgramException: Invalid IL code in DllTest:Main (): IL_0017: ret

Here’s the disassembled exe…

e:\Program Files (x86)\Mono-2.10.8\bin>monodis dlltest.exe --output=dlltestIL.txt

e:\Program Files (x86)\Mono-2.10.8\bin>view dlltestIL.txt
‘view’ is not recognized as an internal or external command,
operable program or batch file.

e:\Program Files (x86)\Mono-2.10.8\bin>more dlltestIL.txt

.assembly extern mscorlib
{
  .ver 2:0:0:0
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
}
.assembly 'dlltest'
{
  .custom instance void class [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::'.ctor'() =  (
                01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78   // ....T..WrapNonEx
                63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01       ) // ceptionThrows.

  .hash algorithm 0x00008004
  .ver  0:0:0:0
}
.module dlltest.exe // GUID = {80996749-6E58-498A-A2DA-7372ADD10FE0}

.module extern 'msvcrt'

  .class public auto ansi beforefieldinit DllTest
        extends [mscorlib]System.Object
  {

    // method line 1
    .method public hidebysig specialname rtspecialname
           instance default void '.ctor' ()  cil managed
    {
        // Method begins at RVA 0x20ec
        // Code size 7 (0x7)
        .maxstack 8
        IL_0000:  ldarg.0
        IL_0001:  call instance void object::'.ctor'()
        IL_0006:  ret
    } // end of method DllTest::.ctor

    // method line 2
    .method private static hidebysig pinvokeimpl ("msvcrt" as "sprintf" ansi cdecl )
           vararg int32 sprintf (class [mscorlib]System.Text.StringBuilder buffer, string format)  cil managed preservesig
    {
        // Method begins at RVA 0x0
    } // end of method DllTest::sprintf

    // method line 3
    .method public static hidebysig
           default void Main ()  cil managed
    {
        // Method begins at RVA 0x20f4
        .entrypoint
        // Code size 24 (0x18)
        .maxstack 4
        .locals init (
                class [mscorlib]System.Text.StringBuilder       V_0)
        IL_0000:  newobj instance void class [mscorlib]System.Text.StringBuilder::'.ctor'()
        IL_0005:  stloc.0
        IL_0006:  ldloc.0
        IL_0007:  ldstr "Hello %s"
        IL_000c:  ldstr "World"
        IL_0011:  call vararg int32 class DllTest::sprintf(class [mscorlib]System.Text.StringBuilder, string, ..., string)
        IL_0016:  pop
        IL_0017:  ret  <----------- Invalid Instruction???
    } // end of method DllTest::Main

  } // end of class DllTest

I’ll go ahead and toss this up on Mono’s bug tracker.

Bug filed…
https://bugzilla.xamarin.com/show_bug.cgi?id=6346

I’m unable to output something like:
sprintf(“%+7.5f”, 0.5);
And I hate to specify the argument number, especially when I want to insert!

Thank you!

Well the Mono version Unity is using is not very generous when it comes to C-API access… I also had a bunch of very weird crashes where it worked in a Visual Studio standalone but failed with my Unity project… So basically, you have to use the simplest binding possible. “Ref” and some basic array to pointer conversions seem to work well though, and I stick with it ^^.

granted my c/c++ is rusty, so Im going to assume Ive missed something, but…

char buffer[50];
sprintf(buffer, "%+7.5f", 1234567.89);

produces the same result as…

double value = 1234567.89;
string test = value.ToString("+0000000.00000");

Well almost. Now let’s output something like this:

Matrix4x4 m;
char str[255];
sprintf(str, "[%+5.3f,%+5.3f,%+5.3f,%+5.3f][%+5.3f,%+5.3f,%+5.3f,%+5.3f][%+5.3f,%+5.3f,%+5.3f,%+5.3f][%+5.1f,%+5.1f,%+5.1f,%+5.3f]", 
  m[0][0],m[0][1],m[0][2],m[0][3],
  m[1][0],m[1][1],m[1][2],m[1][3],
  m[2][0],m[2][1],m[2][2],m[2][3],
  m[3][0],m[3][1],m[3][2],m[3][3]  );

And now let us assume I want to add an additional parameter in front of the list… no comment…

You havn’t given an example that’s any more difficult to format in c#…

well, if you think so, you must be right :wink:

I got a reply from Mono dev’s regarding this issue…

COM interop functionality in Mono apparently isn’t entirely complete. Calling into methods with variable argument lists is not currently supported. So printf(), sprintf(), scanf(), presumably et. al. will fail. Unfortunately, they fail in a very ugly way with an obscure compiler exception which means very little to the programmer. :\