21

I want to pass a lambda function pointer, which nested in a class, to the Windows API callback function. I found there is no place for me to specify the __stdcall keyword. Some people told me the compile only support __cdecl, but after I used nm command to dump the obj file, I found the compile will generate three helper function (__stdcall, __cdecl, __fastcall) concurrently. So my problem is, how can I specify the calling convention?

Those following code are my test code.

#include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
    auto func = [](){};
    return 0;
}
00000000 t ?<helper_func_cdecl>@<lambda_5738939ec88434c53e1a446c47cf2db6>@@CAXXZ
00000000 t ?<helper_func_fastcall>@<lambda_5738939ec88434c53e1a446c47cf2db6>@@CIXXZ
00000000 t ?<helper_func_stdcall>@<lambda_5738939ec88434c53e1a446c47cf2db6>@@CGXXZ
00000000 t ??B<lambda_5738939ec88434c53e1a446c47cf2db6>@@QBEP6AXXZXZ
00000000 t ??B<lambda_5738939ec88434c53e1a446c47cf2db6>@@QBEP6GXXZXZ
00000000 t ??B<lambda_5738939ec88434c53e1a446c47cf2db6>@@QBEP6IXXZXZ
00000000 t ??R<lambda_5738939ec88434c53e1a446c47cf2db6>@@QBEXXZ
ildjarn
  • 62,044
  • 9
  • 127
  • 211
andrew young
  • 231
  • 2
  • 5

1 Answers1

23

Cast it:

WinApiFunc(static_cast<void(__stdcall *)()>(func));

Or store it into a local variable first:

void (__stdcall *funcp)() = func;
WinApiFunc(funcp);
ildjarn
  • 62,044
  • 9
  • 127
  • 211
  • 1
    And now i meet another problem: When I use this syntax, I cannot catch any variables, like this: `auto func = [this](){}; void (__stdcall * funcp)() = func;` – andrew young Jan 05 '13 at 07:38
  • 7
    @user1948596 : That's entirely unrelated to calling conventions – only _captureless_ lambdas are convertible to function pointers (how else could state be maintained?). WinAPI functions that take callbacks always also have a `void*`/`LPVOID` state parameter; use that argument to pass `this` (`static_cast` yet again). – ildjarn Jan 05 '13 at 07:40
  • thanks, I use `DWORD` type to transfer `this` pointer, and succeed. – andrew young Jan 05 '13 at 08:12
  • 1
    @user1948596 : `DWORD` will only work on 32-bit platforms – _not_ portable. Which API specifically are you working with? There may be a more straightforward approach... – ildjarn Jan 05 '13 at 08:15
  • `RasDial` API and store `this` pointer in `RASDIALPARAMS.dwCallbackId` – andrew young Jan 05 '13 at 08:23
  • @user1948596 : `dwCallbackId` is a `ULONG_PTR` (which _is_ 64-bit portable), not a `DWORD` – if your WinAPI shows it as a `DWORD` then your WinAPI is **very** outdated. :-] – ildjarn Jan 05 '13 at 08:33
  • Yeah, i see `dwCallbackId` is defined as `ULONG_PTR` in the struct defination. However, the callback function define `dwCallbackId` as `DWORD`.. MS is out of dated! not my WinAPI.. :) REF:http://msdn.microsoft.com/en-us/library/windows/desktop/aa377236(v=vs.85).aspx – andrew young Jan 05 '13 at 08:43
  • @andrewyoung : Fortunately, it turns out to only be a documentation bug – in the actual Windows SDK (at least as of the Windows 8.0 SDK, which is all I have installed at the moment; hopefully in earlier SDKs as well), the first parameter of `RASDIALFUNC2` is indeed also `ULONG_PTR`. :-] – ildjarn Jan 07 '13 at 19:34
  • _"WinAPI functions that take callbacks always also have a `void*`/`LPVOID` state parameter; use that argument to pass `this` (`static_cast` yet again)."_ - But I can't find `void*`/`LPVOID` parameter, e.g. [this](https://learn.microsoft.com/en-us/windows/console/setconsolectrlhandler) or [this](https://learn.microsoft.com/en-us/windows/console/handlerroutine). Could you please elaborate more? – starriet Jul 16 '23 at 03:38
  • 1
    @starriet : It should read 'nearly always'; some poorly-designed APIs lack this. – ildjarn Jul 16 '23 at 08:41