5

Why does Foo() succeed but Bar() throws a BadImageFormatException?

using System.Runtime.InteropServices;
using System.Text;

static class Program
{
    [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int sprintf([Out] StringBuilder buf, string format, __arglist);

    static void Main(string[] args)
    {
        Foo<int>(2); //Runs fine
        Bar<int>(2); //Error: "The signature is incorrect"
    }

 static void Foo<T>(int a) { sprintf(new StringBuilder(8), "%d", __arglist(a)); }
 static void Bar<T>(T   a) { sprintf(new StringBuilder(8), "%d", __arglist(a)); }
}
user541686
  • 205,094
  • 128
  • 528
  • 886

3 Answers3

0

As shown in the following code, the exception is thrown before running the method when it is compiled into native code:

var t = typeof(Program);
var m = t.GetMethod("Bar", BindingFlags.NonPublic | BindingFlags.Static);
m = m.MakeGenericMethod(typeof(int));
System.Runtime.CompilerServices.RuntimeHelpers.PrepareMethod(m.MethodHandle); //error

Also it is complaining about wrong type passed to SizeOf. I guess it might be a bug in the CLR that causes it to pass the internal handle to the generic type parameter instead of the handle of the real type passed to the method.

IS4
  • 11,945
  • 2
  • 47
  • 86
0

Try like this:

using System;
using System.Runtime.InteropServices;
using System.Text;

static class Program
{
    [DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
    static extern int sprintf([Out] StringBuilder buf, string format, params object[] args);

    static void Main(string[] args)
    {
        Foo(2);
        Bar<int>(2);
    }

    static void Foo(int a) { sprintf(new StringBuilder(8), "%d", a); }
    static void Bar<T>(T a){ sprintf(new StringBuilder(8), "%d", a); }
}
user541686
  • 205,094
  • 128
  • 528
  • 886
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • @Mehrdad, well, you are using an undocumented feature which today doesn't work with generics, tomorrow it might not work with something else and because it is undocumented there is no official answer. The way use a variable length parameters in C# is to use the `params` keyword. – Darin Dimitrov May 01 '11 at 08:06
  • Would it make any difference for you if I did this with dynamic IL generation, which *is* documented? (I have no idea if that will error or not, but at least it would get rid of this "it's undocumented" issue...) – user541686 May 01 '11 at 08:10
  • @Mehrdad, I am still trying to find out what is the actual problem you are trying to solve and why does the `params` keyword doesn't work for you? – Darin Dimitrov May 01 '11 at 08:13
  • It's not technically a "problem" anymore because I found ways around it, but it still stumped me, and I'm wondering why it happens in the first place. – user541686 May 01 '11 at 08:16
0

You are asking why an undocumented (_arglist,_makeref,_reftype,_refvalue) keyword does not work.

Well you should ask Microsoft :D

If you really want to know my take on this, it could be because generics don't know the type of T at compile time, and yet they are compiled to classes. What does __arglist take at compilation time there is a mystery. Since in the line where you declare the generic don't specify the kind of parameter to __arglist.

But all of this is as much obscure as using sprintf from C#... at least if it were _snwprintf_s or similar :D

Marino Šimić
  • 7,318
  • 1
  • 31
  • 61
  • @Marino: Not sure what you mean by "at compile time"... compile-time in .NET has pretty much no significance; JIT-time is when things become important, and at that time, `T` definitely has a type... – user541686 May 01 '11 at 07:51
  • @Marino: They're exactly the same, apart from the `int32`/`!!T` difference in exactly 1 location inside the method. – user541686 May 01 '11 at 07:54
  • yet the second generic class id badly compiled... it is not the sprintf that is failing it is the generic class that is not valid.. you cannot even enter the generic method, ... you may have found a bug that should be reported to microsoft – Marino Šimić May 01 '11 at 08:02
  • @Marino: Not sure what you mean by "badly compiled"... could you elaborate? What should it be instead? – user541686 May 01 '11 at 08:02
  • it means that the IL code cannot run, not that sprintf cannot run... what it should be you should ask microsoft, i do not know what is not stated in documentation, however i cannot even debug into the second generic method... – Marino Šimić May 01 '11 at 08:03
  • @Marino: Well yes I realized that, but the question is, what is exactly invalid about the code? What should it be in order for it to be correct? It seems perfectly correct to me... – user541686 May 01 '11 at 08:04
  • that i cannot know :/ it seems like a compiler bug to me, but since you are using undocumented features i cannot judge :( – Marino Šimić May 01 '11 at 08:04