如何将方法作为回调传递给 Windows API 调用(后续)? [英] How to pass a method as callback to a Windows API call (Follow-up)?

查看:18
本文介绍了如何将方法作为回调传递给 Windows API 调用(后续)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这篇文章是相关Ran 在此处发布的 a-callback-to-winapi">问题.

This post is a follow-up of a related question posted here by Ran.

接受的答案坚持使用通常的普通旧函数.

The accepted answer sticks to the use of the usual a plain old function.

这段摘录特别引起我的注意:

This excerpt particularly catch my attention:

一个实例方法有一个额外的、隐式的、包含实例引用,即 Self.

An instance method has an extra, implicit, parameter containing the instance reference, i.e. Self.

坚信应该有一种方法可以使用一种参数"适配器(改写摆脱不需要的 Self 隐式引用并提供指向符合的自适应回调函数的指针),我最终找到了这个文章,标题为回调类作者:彼得·莫里斯.

With the firm conviction that there should be a way to use a kind of "parameters" adapter (to rephrase get rid of the uneeded Self implicit reference and provide a pointer to a complying adapted callback function), I end up finding this article entitled Callback a class by Peter Morris.

总而言之,他使用thunking技术作为适应技巧.(免责声明:我从未测试过代码).

To sum up, he uses thunking technique as adaptation trick. (Disclaimer: I never tested the code).

我知道它作为一种解决方案不是很干净,但它允许面向对象设计具有所有假定的好处.

I know it's not very clean as a solution but it allows OO design with all the supposed benefits.

我的问题:

知道 TCallbackThunk 是基于回调函数签名的,如果像 Peter Morris 那样做是要走的路,那么上面引用的帖子的答案是什么?

Knowing that TCallbackThunk is based on the callback function signature, what would be the answer of the above refered post if doing it as Peter Morris did is the way to go?

.

推荐答案

由于 EnumWindows(参考问题中的函数)提供了一个数据参数,因此您实际上并不需要完成所有这些工作.您可以在其中放置任何您想要的值,例如答案中演示的对象引用.Morris 的技术更适合不提供任何通用数据参数的回调函数.

You don't really need to go through all that work since EnumWindows (the function in the referenced question) provides a data parameter. You can put whatever value you want there, such as the object reference demonstrated in the answer. Morris's technique is better suited for callback functions that don't provide any general-purpose data parameter.

要调整答案以使用 Morris 的代码,您首先需要确保回调方法的签名与 API 回调函数的签名相匹配.由于我们正在调用 EnumWindows,我们需要一个返回 Bool 的双参数函数.调用约定必须是stdcall(因为Morris 的代码假设了它,并且很难找到任何其他调用约定).

To adapt the answer to use Morris's code, you'll first need to make sure the signature of the callback method matches the signature of the API's callback function. Since we're calling EnumWindows, we need a two-argument function returning Bool. The calling convention needs to be stdcall (because Morris's code assumes it, and it's difficult to thunk any other calling convention).

function TAutoClickOKThread.cbEnumWindowsClickOK(
  Wnd: HWnd; Param: LParam): Bool; stdcall;
begin
  // ...
end;

接下来,我们使用所有机器代码和指向预期回调方法的跳转偏移量设置 TCallbackThunk 数据结构.

Next, we set up the TCallbackThunk data structure with all the machine code and the jump offset referring to the intended callback method.

但是,我们不使用莫里斯描述的方式.他的代码将数据结构放在堆栈上.这意味着我们将可执行代码放在堆栈上.现代处理器和操作系统不再允许这样做——操作系统将停止您的程序.我们可以通过调用 VirtualProtect 修改当前堆栈页面的权限来解决这个问题,允许它被执行,但这会使整个页面可执行,我们不想让程序保持打开状态为攻击.相反,我们将专门为 thunk 记录分配一块内存,与堆栈分开.

However, we don't use the way Morris described. His code puts the data structure on the stack. That means we're putting executable code on the stack. Modern processors and operating systems don't allow that anymore — the OS will halt your program. We could get around that by calling VirtualProtect to modify the permissions of the current stack page, allowing it to be executed, but that makes the whole page executable, and we don't want to leave the program open for attack. Instead, we'll allocate a block of memory especially for the thunk record, separate from the stack.

procedure TAutoClickOKThread.Execute;
var
  Callback: PCallbackThunk;
begin
  Callback := VirtualAlloc(nil, SizeOf(Callback^),
    Mem_Commit, Page_Execute_ReadWrite);
  try
    Callback.POPEDX := $5A;
    Callback.MOVEAX := $B8;
    Callback.SelfPtr := Self;
    Callback.PUSHEAX := $50;
    Callback.PUSHEDX := $52;
    Callback.JMP := $E9;
    Callback.JmpOffset := Integer(@TAutoClickOKThread.cbEnumWindowsClickOK)
      - Integer(@Callback.JMP) - 5;

    EnumWindows(Callback, 0);
  finally
    VirtualFree(Callback);
  end;
end;

请注意,这些是该记录中的 32 位 x86 指令.我不知道相应的 x86_64 指令是什么.

Note that those are 32-bit x86 instructions in that record. I have no idea what the corresponding x86_64 instructions would be.

这篇关于如何将方法作为回调传递给 Windows API 调用(后续)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆