Thread overview
Use nested functions as callbacks with Windows API functions?
Oct 01, 2018
spikespaz
Oct 01, 2018
Boris-Barboris
Oct 01, 2018
spikespaz
Oct 01, 2018
spikespaz
Oct 02, 2018
Boris-Barboris
Oct 02, 2018
John Chapman
October 01, 2018
I have the following code, this works.

================================================================

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

================================================================

I was hoping I could use something more akin to JavaScript's syntax: (void* hWnd, long) => {}.

I tried this but I'm getting errors with the signature, it says the function is a delegate and apparently Windows API can't accept a delegate.

================================================================

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

================================================================

I'm not going to even paste the compiler error because I am very clearly going about this the wrong way.

Of course there is nothing wrong with defining each callback as a separate function, but then comes the issue of naming them. I also don't like the way it makes my code look.

Thanks.
October 01, 2018
On Monday, 1 October 2018 at 20:27:43 UTC, spikespaz wrote:
> I was hoping I could use something more akin to JavaScript's syntax: (void* hWnd, long) => {}.
>
> I tried this but I'm getting errors with the signature, it says the function is a delegate and apparently Windows API can't accept a delegate.

You can make it a non-delegate by passing a pointer to hWndList in lParams as it was supposed to by WinApi devs, instead of zero, and not implicitly capturing stack pointer by referencing hWndList directly from the body.

https://run.dlang.io/is/t4k4Nc
October 01, 2018
On Monday, 1 October 2018 at 21:03:24 UTC, Boris-Barboris wrote:
> On Monday, 1 October 2018 at 20:27:43 UTC, spikespaz wrote:
>> I was hoping I could use something more akin to JavaScript's syntax: (void* hWnd, long) => {}.
>>
>> I tried this but I'm getting errors with the signature, it says the function is a delegate and apparently Windows API can't accept a delegate.
>
> You can make it a non-delegate by passing a pointer to hWndList in lParams as it was supposed to by WinApi devs, instead of zero, and not implicitly capturing stack pointer by referencing hWndList directly from the body.
>
> https://run.dlang.io/is/t4k4Nc

I don't know how to do this. I'm not the best with pointers, I'm still learning D and I'm unfamiliar with functional programming.

==============================================

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

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

    return true;
}

void main() {
    void*[] hWndList;

    EnumWindows(&callback, &hWndList);

    writeln(hWndList);
}

==============================================

Clearly I can't use &hWndList to pass the reference, how would I access the variable by the memory address inside the callback?

October 01, 2018
On Monday, 1 October 2018 at 21:03:24 UTC, Boris-Barboris wrote:
> On Monday, 1 October 2018 at 20:27:43 UTC, spikespaz wrote:
>> I was hoping I could use something more akin to JavaScript's syntax: (void* hWnd, long) => {}.
>>
>> I tried this but I'm getting errors with the signature, it says the function is a delegate and apparently Windows API can't accept a delegate.
>
> You can make it a non-delegate by passing a pointer to hWndList in lParams as it was supposed to by WinApi devs, instead of zero, and not implicitly capturing stack pointer by referencing hWndList directly from the body.
>
> https://run.dlang.io/is/t4k4Nc

The problem with the code you have is that the callback needs to be extern (Windows). I don't know how to do that with a "lambda".

===========================================

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

void main() {
    void*[] hWndList;

    EnumWindows((void* hWnd, void* lParam) nothrow {
        *(cast(void*[] *) lParam) ~= hWnd;
        return true;
    }, &hWndList);

    writeln(hWndList);
}

===========================================

source\cb.d(7): Error: function core.sys.windows.winuser.EnumWindows(extern (Windows) int function(void*, long) nothrow, long) is not callable using argument types (bool function(void* hWnd, void* lParam) pure nothrow @system, void*[]*)
source\cb.d(7):        cannot pass argument __lambda1 of type bool function(void* hWnd, void* lParam) pure
nothrow @system to parameter extern (Windows) int function(void*, long) nothrow
October 02, 2018
On Monday, 1 October 2018 at 23:07:29 UTC, spikespaz wrote:
> The problem with the code you have is that the callback needs to be extern (Windows). I don't know how to do that with a "lambda".

Neither do I actually. Apparently it is impossible.
Best I could squeeze out was this:
https://run.dlang.io/is/CpyfW3
October 02, 2018
On Monday, 1 October 2018 at 20:27:43 UTC, spikespaz wrote:
> Of course there is nothing wrong with defining each callback as a separate function, but then comes the issue of naming them. I also don't like the way it makes my code look.

I think the best you can do is something like this:

---
auto callback(T, string file = __FILE__, size_t line = __LINE__)(T handler) {
  import std.traits;

  __gshared T handler_;
  handler_ = handler;

  extern(Windows)
  ReturnType!T fn(Parameters!T args) {
    synchronized return handler_(args);
  }

  return &fn;
}

void main() {
  HWND[] list;
  EnumWindows((HWND hwnd, LPARAM lparam) {
    list ~= hwnd;
    return TRUE;
  }.callback(), 0);
  writeln(list);
}
---