【问题标题】:Use nested functions as callbacks with Windows API functions?使用嵌套函数作为 Windows API 函数的回调?
【发布时间】:2018-10-01 20:31:11
【问题描述】:

我有以下代码,这行得通。

import core.sys.windows.windows: EnumWindows;
import std.stdio: writeln;

void*[] hWndList;

extern (Windows) int callback(void* hWnd, long /* lParams */ ) nothrow {
    hWndList ~= hWnd;

    return true;
}

void main() {
    EnumWindows(&callback, 0);

    writeln(hWndList);
}

我希望我可以使用更类似于 JavaScript 的语法:(void* hWnd, long) => {}

我试过了,但我收到了签名错误,它说函数是一个委托,显然 Windows API 不能接受委托。

import core.sys.windows.windows: EnumWindows;
import std.stdio: writeln;

void main() {
    void*[] hWndList;

    EnumWindows((void* hWnd, long /* lParams */ ) nothrow {
        hWndList ~= hWnd; return true;
    }, 0);

    writeln(hWndList);
}

我什至不打算粘贴编译器错误,因为我很清楚这是错误的方式。

当然,将每个回调定义为单独的函数并没有错,但随之而来的是命名它们的问题。我也不喜欢它使我的代码看起来的方式。

谢谢。

【问题讨论】:

  • 我建议你尝试一个嵌套的静态函数。我可以稍后再写,不确定它是否允许 extern(Windows) 作为嵌套函数,但它可能会,然后您可以在调用之前的行上定义并在此处使用它
  • 回调必须严格遵守预期的签名。您的嵌套(如 c++ lamda)函数与此签名不匹配。因为嵌套函数实际上是某个未命名对象上的成员函数,并且它具有额外的隐藏参数(如 this ) - 你真的如何在回调中访问 hWndList ?因为指向此局部变量的指针保存在您本地回调的隐藏本地对象中。无论如何,您需要静态函数作为回调。可能在本地对象上,但静态

标签: function winapi callback window d


【解决方案1】:

我发现我可以将委托(或 lambda)转换为 MSDN 文档为 EnumWindowsProc 指定的正确签名。

我也没有意识到通过在全局范围内隐式使用 hWndList 来访问 D 程序的框架是非常糟糕的做法。

这是我用来创建正确签名的。

alias EnumWindowsProc = extern (Windows) int function(HWND, LPARAM) nothrow;

然后我发现别名已经作为 ENUMWINDOWSPROC 存在于 MinGW 标头中 core.sys.windows.windows 的第 2483 行(在撰写本文时)。

extern (Windows) nothrow {
    ...
    alias BOOL function(HWND, LPARAM) ENUMWINDOWSPROC;
    ...
}

为了解决隐式传递D帧的问题,我在EnumWindows中使用了cast(LPARAM) &hWndList作为lParam

这变成了现在使用指针的问题。我知道这可能是拙劣的,欢迎提出任何建议,但我将其转换为指针。

*(cast(HWND[]*) lParam)

完整的代码看起来像这样。显然,这只是一个开始的最小示例,因此您可能希望将转换后的指针分配给某个东西,以便在使用来自 lParams 的变量时不那么混乱。

import core.sys.windows.windows: EnumWindows, ENUMWINDOWSPROC;
import std.stdio: writeln;

void main() {
    HWND[] hWndList;

    EnumWindows(cast(ENUMWINDOWSPROC) (HWND hWnd, LPARAM lParam) {
        *(cast(HWND[]*) lParam) ~= hWnd;
        return true;
    }, cast(LPARAM) &hWndList);

    writeln(hWndList);
}

我希望这对某人有所帮助,因为这让我感到非常困惑(仍然不确定我是否理解指针逻辑)。

感谢 D 论坛上的 Boris-Barboris 为我提供了一些工作。

https://forum.dlang.org/post/xxklxaajptppockvazeo@forum.dlang.org

【讨论】:

  • 你是为 64 位编译的吗?你的代码我很确定它在 32 位上是错误的 - C long 和 D's long 通常实际上并不兼容(LPARAM 实际上更像是 void* 而不是 long 本身)
  • @AdamD.Ruppe 不,这是 32 位的。
  • 是的,那肯定是错误的,它只是碰巧现在可以工作,因为 32 位指针将适合 long 的前几个字节,但这很可能导致稍后的堆栈损坏,因为该函数试图清理 8 个字节,但没有意识到实际上有 12 个字节。您需要将其更改为位于import core.stdc.config; 中的c_longvoid*
  • @AdamD.Ruppe 工作正常,没有参数,-m32-m64。这是一个 Windows 函数,所以无论如何我都无法更改为 void 指针,我试过了。
  • 我刚试过,你的代码甚至不能在 32 位上编译。 (这很好,比堆栈损坏更好!)。您的设置一定很奇怪,也许 m32 在某处被全局配置覆盖。 cannot pass argument cast(long)& hWndList of type long to parameter int.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-27
  • 2020-11-22
  • 2014-01-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多