1

I am trying to call an unmanaged dll. While looking for information about this and trying it, I thought I could use params object[] instead of __arglist , so I changed it like the following code, but got different results. Why does this work differently?

using System.Runtime.InteropServices;

namespace ConsoleApp1
{
    internal class Program
    {
        [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        internal static extern int printf(string format, __arglist);
        
        [DllImport("msvcrt.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
        internal static extern int printf(string format, params object[] i);

        static void Main(string[] args)
        {
            printf("Print Integer: %d\n", __arglist(10));  // Print Integer: 10
            printf("Print Integer: %d\n", 10);             // Print Integer: 1363904384
        }
    }
}
user4581301
  • 33,082
  • 7
  • 33
  • 54
Sia Luna
  • 11
  • 2

2 Answers2

2

Because they're not the same thing. __arglist is specifically and only for var args in unmanaged code. The params keyword is something else entirely and the generated code just builds an array for you from the list of parameters. All it is doing is allowing you to write MyFunc(p1,p2,p3) instead of MyFunc(new object [] { p1, p2, p3}). On your second example, that number is probably the address of the parameter array passed to printf.

John V
  • 1,286
  • 9
  • 15
0

This is a brief explanation on how params and __arglist work:
When you write

void MyFunction(int a, double b, params string[] c)
{
    some code
}

You actually just do the following:

void MyFunction(int a, double b, [ParamArrayAttribute] string[] c)
{
    some code
}

The ParamArrayAttribute just instructs the compiler to allow calls of the form:

MyFunction(3, 1.2, "foo", "bar", "baz");

That happens in addition to the "correct" way to call the method

MyFunction(3, 1.2, new string[] {"foo", "bar", "baz"});

The compiler simply convert the first call to the second one, it is just syntactic sugar and nothing more. The method MyFunction has 3 parameters:

  • int a
  • double b
  • string[] c

On the other hand, printf does not take 2 arguments, one of type string and one of type object[], printf actually does not even know what a .net string, object or object[] even is. printf is defined as: (in C)

int __cdecl printf(const char *Format, ...);

printf takes one argument of Type char * (the .net interop automatically converts the .net string object to the unmanaged char * type), and then it can optionally take any number of additional arguments of any (unmanaged) type. Here is the crucial part, this happens by pushing the additional parameters in the stack and not by pushing an array pointer with the additional parameters, which is what the inperop thinks due to your deceleration. c# does not support this functionality... well... exept if you consider the "unsupported __arglist keyword", as per Microsoft sayings (check out Compiler Error CS1952 to laugh at their correction suggestion). So, c# actually supports varargs afterall.

Now, the 1363904384 is just the pointer to an array containing the value 10, the .net inperop thinks it should pass an array as an argument (in the stack) while printf wants to have the value as an argument (in the stack).

PS: The Interop marshals the object[] as an int[] for your particular case, if you try to pass an object (besides string) you will get an exception. Object arrays do not make much sense outside the scope of .net, objects in general do not make sense (thought, arrays do make sense).

Coconut9
  • 123
  • 2
  • 7