C# P/Invoke:Varargs 委托回调
我只是想进行一些托管/非托管互操作。为了获得扩展的错误信息,我决定注册 dll 提供的日志回调:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void LogCallback(void* arg1,int level,byte* fmt);
此定义有效,但我得到类似“Format %sprobed with size=%d and Score=%d”的字符串。 我尝试添加 __arglist 关键字,但委托不允许这样做。
好吧,这对我来说并不是那么戏剧性,但我只是好奇是否可以在 C# 中获取 varargs 参数。 我知道我可以使用 C++ 进行互操作。 那么:有没有一种方法可以纯粹用 C# 来做到这一点,并付出合理的努力?
编辑:对于那些仍然不明白的人:我不导入一个可变参数函数而是将其导出为回调,然后调用它我的本机代码。我一次只能指定一个 ->只有一种重载可能,并且 __arglist 不起作用。
I was just trying to do some managed/unmanaged interop. To get extended error information I decided to register a log callback offered by the dll:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public unsafe delegate void LogCallback(void* arg1,int level,byte* fmt);
This definition works, but i get strings like "Format %s probed with size=%d and score=%d".
I tryed to add the __arglist keyword, but it is not allowed for delegates.
Well, it is not so dramatic for me, but I am just curious wether one could get the varargs parameters in C#.
I know that I could use c++ for interop.
So: Is there a way to do this purely in C#, with reasonable efford?
EDIT: For those who still did not get it: I am NOT IMPORTING a varargs function BUT EXPORTING it as a callback, which is then called my native code. I can specify only one at a time -> only one overload possible and __arglist does NOT work.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
这是处理它的方法。它可能适用也可能不适用于您的情况,具体取决于您的回调参数是否要与 printf 系列函数一起使用。
首先,从
msvcrt
导入vsprintf
和_vscprintf
:接下来,使用
IntPtr
args 指针声明您的委托:现在,当您的委托是通过本机代码调用的,只需使用
vsprintf
正确格式化消息:Here is the way to deal with it. It may or may not be applicable to your case, depending on whether your callback arguments are meant to be used with
printf
family of functions.First, import
vsprintf
and_vscprintf
frommsvcrt
:Next, declare your delegate with
IntPtr
args pointer:Now when your delegate is invoked via native code, simply use
vsprintf
to format the message correctly:不,没有办法做到这一点。这是不可能的原因是因为变量参数列表在 C 中的工作方式。
在 C 中,变量参数只是作为额外参数推送到堆栈(在我们的例子中是非托管堆栈)。 C 不会在任何地方记录堆栈上的参数数量,被调用函数采用其最后一个形式参数(可变参数之前的最后一个参数)获取其位置并开始从堆栈中弹出参数。
它知道从堆栈中弹出多少变量的方式完全基于约定 - 其他一些参数指示堆栈上有多少变量参数。对于 printf 来说,它通过解析格式字符串并在每次看到格式代码时从堆栈中弹出来实现这一点。看来你的回调是相似的。
为了让 CLR 处理这个问题,它必须能够知道正确的约定来确定需要拾取多少个参数。您无法编写自己的处理程序,因为它需要访问您无权访问的非托管堆栈。所以你无法从 C# 中做到这一点。
有关这方面的更多信息,您需要阅读 C 调用约定。
No there is no possible way to do it. The reason it is impossible is because of the way variable argument lists work in C.
In C variable arguments are just pushed as extra parameters on to the stack (the unmanaged stack in our case). C does not anywhere record the number of parameters on the stack, the called function takes its last formal parameter (the last argument before the varargs) gets its location and starts popping arguments off the stack.
The way that it knows how many variables to pop off the stack is completely convention based - some other parameter indicates how many variable arguments are sitting on the stack. For printf it does that by parsing the format string and popping off the stack every time it sees a format code. It seems like your callback is similar.
For the CLR to handle that, it would have to be able to know the correct convention to determine how many arguments it needed to pickup. You can't write your own handler, because it would require access to the unmanaged stack which you don't have access to. So there is no way you can do this from C#.
For more information on this you need to read up on C calling conventions.
我不同意@shf301,这是可能的。
对于 PInvoke,您可以使用 __arglist,如下所示:
调用:
PrintFormat("Hello %d", __arglist(2019));
对于委托和回调:
定义以下结构:
定义您的委托
用于调用
用于实现
format
才能知道推入堆栈的内容,然后使用binary
读取参数。例如,如果您知道已推送 int32,则可以使用binary.ReadInt32()
读取它。如果您不明白这部分内容,请在评论中告诉我,以便我为您提供更多信息。I disagree with @shf301, It's possible.
You can use
__arglist
in case of PInvoke, like this:Calling:
PrintFormat("Hello %d", __arglist(2019));
In the case of delegates and callbacks:
Define the following struct:
Define your delegate
For calling
For implementing
format
to know what is pushed into the stack, and then read arguments usingbinary
. For example, if you know an int32 is pushed, you can read it usingbinary.ReadInt32()
. If you don't understand this part, please tell me in comments so I can provide you more info.实际上在CIL中是可以的:
Actually it is possible in CIL:
以下文章介绍了略有不同的方案,可能会有所帮助:
如何在中 P/Invoke VarArgs(变量参数) C#
The following article covers a slightly different scenario and may be helpful:
How to P/Invoke VarArgs (variable arguments) in C#
您需要 P/invoke 编组器的支持才能实现这一点。编组器不提供此类支持。因此这是不可能完成的。
You'd need support from the P/invoke marshaller for this to be possible. The marshaller does not provide such support. Thus it cannot be done.