簡體   English   中英

帶回調的ctypes:退出時訪問沖突

[英]ctypes with callback: access violation on exit

我之前沒有回復就問過這個問題。 我再次問它,這次更加簡化了。

我有一個由Python ctypes調用的dll,帶有一個回調函數。 回調一直正常工作(如果我在Visual Studio中逐步執行該程序,我可以在操作中看到它),但在退出時,Visual Studio會拋出“訪問沖突”異常。 但是如果我從dll中刪除對回調的調用,則它會在沒有訪問沖突的情況下正常退出。

還有什么我必須做的事情來退出帶回調的DLL嗎? 我已經研究了幾個小時了,我還沒有在網上找到解決這個問題的東西。

這是ctypes代碼。 我省略了dll代碼來保持這個簡短(它是用NASM編寫的)但是如果需要的話我也可以發布它。

def SimpleTestFunction_asm(X):

    Input_Length_Array = []
    Input_Length_Array.append(len(X)*8)

    CA_X = (ctypes.c_double * len(X))(*X)

    length_array_out = (ctypes.c_double * len(Input_Length_Array))(*Input_Length_Array)

    hDLL = ctypes.WinDLL("C:/Test_Projects/SimpleTestFunction/SimpleTestFunction.dll")
    CallName = hDLL.Main_Entry_fn
    CallName.argtypes = [ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_longlong)]
    CallName.restype = ctypes.POINTER(ctypes.c_int64)
    #__________
    #The callback function

    LibraryCB = ctypes.WINFUNCTYPE(ctypes.c_double, ctypes.c_double)

    def LibraryCall(ax):
        bx = math.ceil(ax)
        return (bx)

    lib_call = LibraryCB(LibraryCall)
    lib_call = ctypes.cast(lib_call,ctypes.POINTER(ctypes.c_longlong))

    #__________

    ret_ptr = CallName(CA_X,length_array_out,lib_call)

我真的非常感謝有關如何解決這個問題的任何想法。 我希望這篇簡化的帖子會有所幫助。

非常感謝。

我對您的代碼進行了一些小的更改以實際運行(導入)並添加了一個打印以查看傳遞的對象的地址和返回值,並創建了一個等效的C DLL以確保指針正確傳遞並且回調有效。

蟒蛇:

import ctypes
import math

def SimpleTestFunction_asm(X):
    Input_Length_Array = []
    Input_Length_Array.append(len(X)*8)

    CA_X = (ctypes.c_double * len(X))(*X)

    length_array_out = (ctypes.c_double * len(Input_Length_Array))(*Input_Length_Array)

    hDLL = ctypes.WinDLL('test')
    CallName = hDLL.Main_Entry_fn
    CallName.argtypes = [ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_longlong)]
    CallName.restype = ctypes.POINTER(ctypes.c_int64)

    LibraryCB = ctypes.WINFUNCTYPE(ctypes.c_double, ctypes.c_double)

    def LibraryCall(ax):
        bx = math.ceil(ax)
        return (bx)

    lib_call = LibraryCB(LibraryCall)
    lib_call = ctypes.cast(lib_call,ctypes.POINTER(ctypes.c_longlong))

    ret_ptr = CallName(CA_X,length_array_out,lib_call)
    print('{:016X} {:016X} {:016X} {}'.format(ctypes.addressof(CA_X),ctypes.addressof(length_array_out),ctypes.addressof(lib_call.contents),ret_ptr.contents))

SimpleTestFunction_asm([1.1,2.2,3.3])

Test.DLL源碼:

#include <inttypes.h>
#include <stdio.h>

typedef double (*CB)(double);

__declspec(dllexport) int64_t* __stdcall Main_Entry_fn(double* p1, double* p2, long long* p3)
{
    static int64_t x = 123;
    double out = ((CB)p3)(1.1);
    printf("%p %p %p %lf\n",p1,p2,p3,out);
    return &x;
}

輸出:

0000021CC99B23A8 0000021CCBADAC10 0000021CCBC90FC0 2.000000
0000021CC99B23A8 0000021CCBADAC10 0000021CCBC90FC0 c_longlong(123)

您可以看到指針是相同的,並且回調返回值和函數返回值是正確的。

很可能你的NASM代碼沒有正確實現調用約定或破壞訪問數組的堆棧。 我只是盡力使你的Python代碼工作。 我確實認為, length_array_out總是長度為1的雙數組,其值為輸入數組X長度的8倍。 NASM代碼如何知道數組的長度?

您可以更加類型更正並聲明以下內容而不是將回調轉換為long long *

CALLBACK = ctypes.WINFUNCTYPE(ctypes.c_double, ctypes.c_double)

CallName.argtypes = [ctypes.POINTER(ctypes.c_double),ctypes.POINTER(ctypes.c_double),CALLBACK]
CallName.restype = ctypes.POINTER(ctypes.c_int64)


@CALLBACK
def LibraryCall(ax):
    bx = math.ceil(ax)
    return (bx)

ret_ptr = CallName(CA_X,length_array_out,LibraryCall)

@Mark Tolonen,非常感謝您的詳細分析。 我發布這個作為答案,因為代碼的格式不會在評論中正確出現 - 但我選擇你的答案作為最佳答案。

我懷疑堆棧對齊可能是問題,你消除了ctypes作為源,所以我專注於堆棧。 這就是我做的工作。

在NASM代碼中,我在輸入時推送rbp和rdi,然后在退出時恢復它們。 這里,在調用之前,我通過從堆棧中彈出rbp和rdi來設置堆棧狀態。 然后我從rsp中減去32個字節(不是40個)。 調用完成后,我恢復堆棧狀態:

pop rbp
pop rdi
sub rsp,32
call [CB_Pointer] ; The call to the callback function
add rsp,32
push rdi
push rbp

對於外部函數調用(比如C庫函數),我必須減去40個字節,但對於這個回調,我只需要32個字節。 在你的回答之前我嘗試了40個字節並且它不起作用。 我想原因是因為它沒有調用外部庫,它是對ctypes代碼的回調,它首先調用了dll。

另一件事。 調用發送一個浮點值(xmm0)並返回一個整數值,但整數值在xmm0寄存器中返回,而不是rax。 將ctypes中的原型設置為整數返回不會這樣做。 它必須保持這樣:

LibraryCB = ctypes.WINFUNCTYPE(ctypes.c_double, ctypes.c_double)

再次感謝你的回復。 你告訴我在哪里看。

PS length_array_out將輸入數組的長度傳遞給NASM。 如果我傳遞多個數組,則length_array_out將更長,每個長度只有一個qword; 目前我在輸入時將qword轉換為整數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM