C# 不能使用 params object[] 代替 __arglist

发布于 2025-01-20 09:48:50 字数 873 浏览 5 评论 0原文

我正在尝试调用非托管 dll。 在寻找有关此内容并尝试的信息时,我认为我可以使用 params object[] 而不是 __arglist ,所以我像下面的代码一样更改它,但得到了不同的结果。为什么这会产生不同的效果?

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
        }
    }
}

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
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

倾城花音 2025-01-27 09:48:50

因为他们不是同一回事。 __ arglist是专门的,仅适用于非托管代码中的var args。参数关键字完全是其他内容,而生成的代码仅从参数列表中为您构建一个数组。它要做的就是允许您编写myFunc(p1,p2,p3)而不是myfunc(new Object [] {p1,p2,p3})。在您的第二个示例中,该数字可能是传递给printf的参数阵列的地址。

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.

如歌彻婉言 2025-01-27 09:48:50

这是关于 params__arglist 如何工作的简要说明:
当您编写时,

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

您实际上只需执行以下操作:

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

ParamArrayAttribute 只是指示编译器允许以下形式的调用:

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

除了调用该方法的“正确”方式之外,还会发生这种情况

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

编译器只需将第一个调用转换为第二个调用,它只是语法糖而已。 MyFunction 方法有 3 个参数:

  • int a
  • double b
  • string[] c

另一方面,printf 确实采用 2 个参数,一个是字符串类型,一个是对象类型[]、printf 实际上甚至不知道 .net stringobjectobject[] 是什么。 printf 定义为:(在 C 语言中)

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

printf 采用一个 char * 类型的参数(.net 互操作自动转换 .net >string 对象转换为非托管 char * 类型),然后它可以选择采用任何(非托管)类型的任意数量的附加参数。这是关键部分,这是通过将附加参数压入堆栈而不是通过将附加参数压入数组指针来发生的,这是 inperop 由于减速而认为的。 c# 不支持此功能...好吧...除非您考虑“不受支持的 __arglist 关键字”,按照 Microsoft 的说法(查看 编译器错误 CS1952 嘲笑他们的纠正建议)。所以,C# 毕竟还是支持 varargs 的。

现在, 1363904384 只是指向包含值 10 的数组的指针,.net inperop 认为它应该传递一个数组作为参数(在堆栈中),而 printf 希望将该值作为参数(在堆栈中)。

PS:如果您尝试传递一个对象(除了 string )你会得到一个例外。对象数组在 .net 范围之外没有多大意义,对象通常没有意义(认为,数组确实有意义)。

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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文