This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Author David Heffernan
Recipients David Heffernan
Date 2019-11-08.16:57:33
SpamBayes Score -1.0
Marked as misclassified Yes
Message-id <1573232254.38.0.286098798289.issue38748@roundup.psfhosted.org>
In-reply-to
Content
Starting with Python 3.8 certain ctypes callbacks fail to restore the stack pointer.

In the repo below, when the DLL is compiled with MSVC under default debug settings, running the Python script leads to a debug error dialog which says:


Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call.  This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.


It appears that when the C code calls the callback function, the value of ESP is 4 greater than it should be.

This problem does not occur with older versions of Python.


**DLL code**

#include <Windows.h>

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

typedef void (__stdcall *MYCALLBACK)(int, double);

extern "C"
{
    __declspec(dllexport) void __stdcall foo(MYCALLBACK callback)
    {
        callback(1, 11);
        callback(2, 21);
        callback(3, 31);
    }
}

**Python code**

import ctypes
import ctypes.wintypes

def CallbackType(restype, *argtypes):

    def from_param(cls, obj):
        if obj is None:
            return obj
        return ctypes._CFuncPtr.from_param(obj)

    result = ctypes.WINFUNCTYPE(restype, *argtypes)
    result.from_param = classmethod(from_param)
    return result

MYCALLBACK = CallbackType(
    None,
    ctypes.c_int,
    ctypes.c_double
)

def callback(handle, time):
    print(handle, time)
mycallback = MYCALLBACK(callback)

lib = ctypes.WinDLL(r'path\to\dll\foo.dll')
func = getattr(lib, '_foo@4')
func.restype = None
func.argtypes = MYCALLBACK,
func(mycallback)
History
Date User Action Args
2019-11-08 16:57:34David Heffernansetrecipients: + David Heffernan
2019-11-08 16:57:34David Heffernansetmessageid: <1573232254.38.0.286098798289.issue38748@roundup.psfhosted.org>
2019-11-08 16:57:34David Heffernanlinkissue38748 messages
2019-11-08 16:57:33David Heffernancreate