通过指针调用具有未知参数计数的函数的简单方法

发布于 2024-12-01 02:50:59 字数 662 浏览 2 评论 0原文

我有以下代码:

typedef void * (__stdcall * call_generic)(...);
typedef void * (__stdcall * call_push2)(unsigned long,unsigned long);

void * pfunc;
// assume pfunc is a valid pointer to external function

// this is a logically correct way of calling, however this includes:
// add esp, 8
// after the call, and that breaks my stack.
((call_generic)pfunc)(1,1);

// however, if i use this call:
((call_push2)pfunc)(1,1);
// this does not happen and code works properly.

手动跟踪所有调用并计算参数是很痛苦的(前面有很多这样的调用),我更喜欢宏或其他东西,但由于该错误,这是不可能的。

有解决办法吗?是否有另一种创建 call_generic 类型来执行此类操作的方法?

我真的不明白为什么它会进行“清理”,但这会严重破坏我的堆栈,导致以前定义的变量丢失。

I have the following code:

typedef void * (__stdcall * call_generic)(...);
typedef void * (__stdcall * call_push2)(unsigned long,unsigned long);

void * pfunc;
// assume pfunc is a valid pointer to external function

// this is a logically correct way of calling, however this includes:
// add esp, 8
// after the call, and that breaks my stack.
((call_generic)pfunc)(1,1);

// however, if i use this call:
((call_push2)pfunc)(1,1);
// this does not happen and code works properly.

It's a pain to track all the calls and count args manually (there are lots of such calls ahead), I'd prefer a macro or something for this, but with that bug it's not possible.

Is there a solution? Is there another way of creating call_generic type to do such things?

I do not really understand why exactly it does that "cleanup" but that breaks my stack badly, causing previously defined variables to be lost.

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

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

发布评论

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

评论(3

走过海棠暮 2024-12-08 02:50:59

((call_generic)pfunc)(1,1); 仅当 pfunc 指向的函数实际上具有您转换为的签名时,才是逻辑上正确的调用方式,<代码>void *(...)。您的代码告诉编译器进行可变参数调用,因此它会进行可变参数调用。对不是 varargs 函数的函数进行 varargs 调用不起作用(在这种情况下,对于谁必须清理堆栈存在分歧,并且完成了两次)。

没有办法免费做到这一点。在调用之前,您必须以某种方式将函数指针转换为正确的签名,否则调用代码不知道如何以被调用者代码可以使用的方式传递参数。

一种选择是确保 pfunc 可能指向的所有被调用函数具有相同的签名,然后转换为该类型。例如,您可以将它们设为所有可变参数函数,尽管我并不特别推荐它。做您不想做的事情会更安全 - 确保此处可能出现的所有函数都采用两个 unsigned long ,并强制转换为 call_push2

((call_generic)pfunc)(1,1); is only a logically correct way of calling if the function pointed to by pfunc actually has the signature you cast to, void *(...). Your code tells the compiler to make a varargs call, so it makes a varargs call. A varargs call to a function that isn't a varargs function doesn't work (in this case, there's disagreement who has to clean up the stack, and it gets done twice).

There's no way to do this for free. You must somehow cast the function pointer to the correct signature before calling it, otherwise the calling code doesn't know how to pass the parameters in a way that the callee code can use.

One option is to ensure that all the called functions that pfunc might point to have the same signature, then cast to that type. For example, you could make them all varargs functions, although I don't particularly recommend it. It would be more type safe to do what you don't want to - make sure that all the functions that might appear here take two unsigned long, and cast to call_push2.

茶底世界 2024-12-08 02:50:59

call_generic 的技巧不适用于应使用 __stdcall 调用约定调用的函数。这是因为 __stdcall 暗示函数应该清理堆栈,OTOH 可变参数函数(带有 ... 参数的函数)可能不会这样做,因为它们不知道论据。

因此,使用 __stdcall 调用约定来标记可变参数函数就像搬起石头砸自己的脚。

在你的具体情况下,我会进入宏写作方向。我看不出有什么小技巧可以满足您的需求。

编辑

其中一种技术可能是使用模板类。例如:

// any __stdcall function returning void taking 2 arguments
template <typename T1, typename T2>
struct FuncCaller_2
{
    typedef void * (__stdcall * FN)(T1, T2);

    static void Call(PVOID pfn, T1 t1, T2 t2)
    {
        ((FN) pfn)(t1, t2);
    }
};

// call your function
FuncCaller_2<int, long>::Call(pfn, 12, 19);

您需要为每个参数数量(0、1、2、3、...)创建这样一个类。

不用说这个方法是“不安全的”——即没有对函数调用的正确性进行编译时验证。

The trick with call_generic won't work with functions that should be called with __stdcall calling convention. This is because __stdcall implies that the function should clean the stack, OTOH variadic functions (those with ... arguments) may not do this, since they are not aware of the arguments.

So that marking a variadic function with __stdcall calling convention is like shooting yourself in the foot.

In your specific case I'd go in the macro writing direction. I don't see a trivial trick that'd acomplish what you need.

EDIT

One of the techniques may be using template classes. For instance:

// any __stdcall function returning void taking 2 arguments
template <typename T1, typename T2>
struct FuncCaller_2
{
    typedef void * (__stdcall * FN)(T1, T2);

    static void Call(PVOID pfn, T1 t1, T2 t2)
    {
        ((FN) pfn)(t1, t2);
    }
};

// call your function
FuncCaller_2<int, long>::Call(pfn, 12, 19);

You'll need to create such a class for every number of arguments (0, 1, 2, 3, ...).

Needless to say this method is "unsafe" - i.e. there is no compile-time validation of the correctness of the function call.

初懵 2024-12-08 02:50:59

在我看来,您想要一种动态函数绑定机制,它将指针绑定到从调用中推导的原型。这通常需要元编程,为此 boost::bind或者来自 boost 函数库的其他东西是你最好的选择(如果他们没有它,我怀疑我能否做到)。

seems to me you want a form of dynamic function binding mechanism that will bind a pointer to a protoype deducted from an invocation. This generally requires metaprogramming, for this boost::bind or something else from the boost functions library is your best bet (and if they don't have it, its doubtful that i can be done).

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