最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python調(diào)用C++ DLL失敗的根本原因和解決方案

 更新時(shí)間:2025年10月24日 10:17:25   作者:oioihoii  
這篇文章主要介紹了Python調(diào)用C++ DLL失敗的根本原因,C++名稱修飾,并提供了三種解決方案:修改C++代碼、使用修飾后的名稱和創(chuàng)建智能解析器,同時(shí),文章還討論了調(diào)用約定、數(shù)據(jù)類型映射以及調(diào)試技巧,需要的朋友可以參考下

問題背景

在混合編程中,經(jīng)常遇到這樣的場景:C++編寫的DLL在C++項(xiàng)目中可以正常調(diào)用,但使用Python調(diào)用時(shí)卻失敗。本文深入分析這一問題的根本原因,并提供完整的解決方案。

問題現(xiàn)象

  • C++代碼靜態(tài)調(diào)用C++編寫的DLL接口:正常工作
  • Python使用ctypes調(diào)用同一個(gè)DLL:失敗

根本原因:C++名稱修飾(Name Mangling)

什么是名稱修飾?

C++編譯器為了實(shí)現(xiàn)函數(shù)重載、命名空間等特性,會(huì)對(duì)函數(shù)名進(jìn)行修飾(mangling),在編譯階段將函數(shù)名、參數(shù)類型、返回類型等信息編碼到一個(gè)唯一的名稱中。

示例對(duì)比

C++頭文件中的聲明:

BOOL InitializeDevice(HWND handle, char* config);

實(shí)際導(dǎo)出的函數(shù)名:

  • 無extern "C"(C++默認(rèn)):?InitializeDevice@@YAHPAUHWND__@@PAD@Z
  • 有extern "C"InitializeDevice

名稱修飾的影響

調(diào)用方式查找的函數(shù)名結(jié)果
C++調(diào)用?InitializeDevice@@YAHPAUHWND__@@PAD@Z? 成功
Python調(diào)用InitializeDevice? 失敗

Python的ctypes默認(rèn)按原函數(shù)名查找,無法識(shí)別經(jīng)過修飾的C++函數(shù)名。

解決方案

方案1:修改C++代碼(推薦)

在C++頭文件中添加extern "C"聲明:

#ifdef __cplusplus
extern "C" {
#endif

// 使用extern "C"導(dǎo)出所有函數(shù)
__declspec(dllexport) BOOL InitializeDevice(HWND handle, char* config);
__declspec(dllexport) BOOL ConnectDevice();
__declspec(dllexport) void GetErrorMessage(char* errorBuffer);

#ifdef __cplusplus
}
#endif

方案2:Python中使用修飾后的名稱

如果無法修改DLL源碼,可以在Python中使用實(shí)際的導(dǎo)出名稱:

import ctypes
from ctypes import wintypes

# 加載DLL
device_dll = ctypes.WinDLL("DeviceLibrary.dll")

# 使用修飾后的函數(shù)名
device_dll._?InitializeDevice@@YAHPAUHWND__@@PAD@Z.argtypes = [wintypes.HWND, c_char_p]
device_dll._?InitializeDevice@@YAHPAUHWND__@@PAD@Z.restype = wintypes.BOOL

def initialize_device(config_path):
    return device_dll._?InitializeDevice@@YAHPAUHWND__@@PAD@Z(None, config_path.encode('utf-8'))

方案3:自動(dòng)函數(shù)解析器

創(chuàng)建一個(gè)智能的Python包裝器,自動(dòng)嘗試不同的名稱變體:

import ctypes
from ctypes import wintypes, WinDLL

class DllFunctionResolver:
    def __init__(self, dll_path):
        self.dll = WinDLL(dll_path)
        self._resolved_functions = {}
    
    def resolve_function(self, base_name, argtypes, restype):
        """自動(dòng)解析函數(shù)名稱"""
        name_variants = [
            base_name,                      # 原始名稱
            f"_{base_name}",                # 前導(dǎo)下劃線
            f"_{base_name}@",               # stdcall格式
            f"?{base_name}@",               # C++修飾名稱
        ]
        
        for name in name_variants:
            try:
                # 嘗試完全匹配
                for exported_name in dir(self.dll):
                    if name in exported_name and not exported_name.startswith('_'):
                        func = getattr(self.dll, exported_name)
                        func.argtypes = argtypes
                        func.restype = restype
                        self._resolved_functions[base_name] = func
                        print(f"成功解析函數(shù): {base_name} -> {exported_name}")
                        return func
            except Exception:
                continue
        
        print(f"警告: 未找到函數(shù) {base_name}")
        return None
    
    def __getattr__(self, name):
        if name in self._resolved_functions:
            return self._resolved_functions[name]
        raise AttributeError(f"函數(shù) {name} 未解析")

# 使用示例
resolver = DllFunctionResolver("DeviceLibrary.dll")
resolver.resolve_function("InitializeDevice", [wintypes.HWND, c_char_p], wintypes.BOOL)

if hasattr(resolver, 'InitializeDevice'):
    resolver.InitializeDevice(None, b"C:\\Config")

完整的Python調(diào)用示例

import ctypes
from ctypes import wintypes, byref, c_long, c_int, create_string_buffer
import os

class DeviceController:
    def __init__(self, dll_path):
        if not os.path.exists(dll_path):
            raise FileNotFoundError(f"DLL文件不存在: {dll_path}")
            
        self.dll = ctypes.WinDLL(dll_path)
        self._setup_functions()
        
    def _setup_functions(self):
        """設(shè)置函數(shù)原型 - 假設(shè)使用extern "C"后的簡單名稱"""
        # 設(shè)備初始化函數(shù)
        self.dll.InitializeDevice.argtypes = [wintypes.HWND, c_char_p]
        self.dll.InitializeDevice.restype = wintypes.BOOL
        
        # 連接管理函數(shù)
        self.dll.ConnectDevice.argtypes = []
        self.dll.ConnectDevice.restype = wintypes.BOOL
        
        self.dll.DisconnectDevice.argtypes = []
        self.dll.DisconnectDevice.restype = wintypes.BOOL
        
        self.dll.IsConnected.argtypes = []
        self.dll.IsConnected.restype = wintypes.BOOL
        
        # 錯(cuò)誤處理函數(shù)
        self.dll.GetLastError.argtypes = [c_char_p]
        self.dll.GetLastError.restype = None
        
        # 數(shù)據(jù)獲取函數(shù)
        self.dll.GetDeviceStatus.argtypes = [ctypes.POINTER(c_int)]
        self.dll.GetDeviceStatus.restype = wintypes.BOOL
        
        self.dll.GetVersionInfo.argtypes = [c_char_p]
        self.dll.GetVersionInfo.restype = None
    
    def initialize(self, config_path):
        """初始化設(shè)備"""
        return self.dll.InitializeDevice(None, config_path.encode('utf-8'))
    
    def connect(self):
        """連接設(shè)備"""
        return self.dll.ConnectDevice()
    
    def disconnect(self):
        """斷開連接"""
        return self.dll.DisconnectDevice()
    
    def is_connected(self):
        """檢查連接狀態(tài)"""
        return self.dll.IsConnected()
    
    def get_last_error(self):
        """獲取錯(cuò)誤信息"""
        buffer = create_string_buffer(256)
        self.dll.GetLastError(buffer)
        return buffer.value.decode('utf-8')
    
    def get_device_status(self):
        """獲取設(shè)備狀態(tài)"""
        status = c_int()
        if self.dll.GetDeviceStatus(byref(status)):
            return status.value
        return -1
    
    def get_version_info(self):
        """獲取版本信息"""
        buffer = create_string_buffer(128)
        self.dll.GetVersionInfo(buffer)
        return buffer.value.decode('utf-8')

# 使用示例
if __name__ == "__main__":
    try:
        # 創(chuàng)建設(shè)備控制器實(shí)例
        controller = DeviceController("DeviceLibrary.dll")
        
        # 初始化設(shè)備
        if controller.initialize("C:\\DeviceConfig"):
            print("設(shè)備初始化成功")
            
            # 連接設(shè)備
            if controller.connect():
                print("設(shè)備連接成功")
                
                # 獲取設(shè)備信息
                status = controller.get_device_status()
                version = controller.get_version_info()
                print(f"設(shè)備狀態(tài): {status}, 版本: {version}")
                
                # 斷開連接
                controller.disconnect()
                print("設(shè)備已斷開連接")
            else:
                print(f"設(shè)備連接失敗: {controller.get_last_error()}")
        else:
            print(f"設(shè)備初始化失敗: {controller.get_last_error()}")
            
    except Exception as e:
        print(f"錯(cuò)誤: {e}")

其他注意事項(xiàng)

1. 調(diào)用約定(Calling Convention)

  • ctypes.cdll.LoadLibrary() - 用于cdecl調(diào)用約定
  • ctypes.windll.LoadLibrary() - 用于stdcall調(diào)用約定
  • ctypes.WinDLL() - Windows API的標(biāo)準(zhǔn)調(diào)用約定

2. 數(shù)據(jù)類型映射

C++ 類型Python ctypes 類型
int, BOOLctypes.c_int, ctypes.wintypes.BOOL
longctypes.c_long
char*ctypes.c_char_p
HWNDctypes.wintypes.HWND
int&ctypes.POINTER(ctypes.c_int)

3. 調(diào)試技巧

查看DLL導(dǎo)出函數(shù):

# 使用Visual Studio工具
dumpbin /exports DeviceLibrary.dll

# 使用MinGW工具
objdump -p DeviceLibrary.dll | grep "Export"

Python中檢查可用函數(shù):

import ctypes

def list_dll_exports(dll_path):
    """列出DLL中的所有導(dǎo)出函數(shù)"""
    dll = ctypes.WinDLL(dll_path)
    exports = []
    for name in dir(dll):
        if not name.startswith('_') and not name.startswith('.'):
            exports.append(name)
    return exports

# 使用
exports = list_dll_exports("DeviceLibrary.dll")
print("DLL導(dǎo)出函數(shù):", exports)

總結(jié)

Python調(diào)用C++ DLL失敗的主要原因是C++的名稱修飾機(jī)制。通過:

  1. 添加extern "C"聲明 - 最根本的解決方案,避免名稱修飾
  2. 使用修飾后的函數(shù)名 - 臨時(shí)解決方案,適用于無法修改DLL的情況
  3. 創(chuàng)建智能解析器 - 自動(dòng)化解決方案,自動(dòng)匹配函數(shù)名稱

理解C++名稱修飾機(jī)制和Python ctypes的工作原理,可以有效解決跨語言調(diào)用的兼容性問題,實(shí)現(xiàn)C++ DLL與Python程序的順暢交互。

以上就是Python調(diào)用C++ DLL失敗的根本原因和解決方案的詳細(xì)內(nèi)容,更多關(guān)于Python調(diào)用C++ DLL失敗的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Python中如何優(yōu)雅的合并兩個(gè)字典(dict)方法示例

    Python中如何優(yōu)雅的合并兩個(gè)字典(dict)方法示例

    字典是Python語言中唯一的映射類型,在我們?nèi)粘9ぷ髦薪?jīng)常會(huì)遇到,下面這篇文章主要給大家介紹了關(guān)于Python中如何優(yōu)雅的合并兩個(gè)字典(dict)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-08-08
  • 詳解python中g(shù)roupby函數(shù)通俗易懂

    詳解python中g(shù)roupby函數(shù)通俗易懂

    這篇文章主要介紹了詳解python中g(shù)roupby函數(shù)通俗易懂,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • Django添加sitemap的方法示例

    Django添加sitemap的方法示例

    這篇文章主要介紹了Django添加sitemap的方法示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • python實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)感知器算法

    python實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)感知器算法

    這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)神經(jīng)網(wǎng)絡(luò)感知器算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-12-12
  • Pandas創(chuàng)建DataFrame提示:type?object?'object'?has?no?attribute?'dtype'解決方案

    Pandas創(chuàng)建DataFrame提示:type?object?'object'?has?n

    Pandas數(shù)據(jù)幀(DataFrame)是二維數(shù)據(jù)結(jié)構(gòu),它包含一組有序的列,每列可以是不同的數(shù)據(jù)類型,這篇文章主要給大家介紹了關(guān)于Pandas創(chuàng)建DataFrame提示:type?object?‘object‘?has?no?attribute?‘dtype‘的解決方案,需要的朋友可以參考下
    2023-02-02
  • 從基礎(chǔ)操作到高級(jí)技巧解析Python字符串處理

    從基礎(chǔ)操作到高級(jí)技巧解析Python字符串處理

    字符串是編程中最基礎(chǔ)的數(shù)據(jù)類型之一,Python對(duì)其提供了豐富的操作方法,本文將通過具體案例演示字符串的創(chuàng)建、操作、格式化和高級(jí)應(yīng)用,幫助讀者系統(tǒng)掌握字符串處理的核心技能
    2025-08-08
  • Python 繪制北上廣深的地鐵路線動(dòng)態(tài)圖

    Python 繪制北上廣深的地鐵路線動(dòng)態(tài)圖

    這篇文章主要介紹了用python制作北上廣深——地鐵線路動(dòng)態(tài)圖,文中的示例代碼講解詳細(xì),對(duì)我們的工作或?qū)W習(xí)都有一定的價(jià)值,感興趣的同學(xué)可以學(xué)習(xí)一下
    2021-12-12
  • 簡單了解python PEP的一些知識(shí)

    簡單了解python PEP的一些知識(shí)

    這篇文章主要介紹了簡單了解python PEP的一些知識(shí),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • 200 行python 代碼實(shí)現(xiàn) 2048 游戲

    200 行python 代碼實(shí)現(xiàn) 2048 游戲

    2048這個(gè)小游戲大家都不陌生,應(yīng)該都玩過,之前已經(jīng)在網(wǎng)上見過各個(gè)版本的2048實(shí)現(xiàn)了,有JAVA、HTML5等,今天我就給大家來一個(gè)我200 行python 代碼實(shí)現(xiàn)的2048 游戲,感興趣的朋友一起看看吧
    2018-01-01
  • 詳解Django-channels 實(shí)現(xiàn)WebSocket實(shí)例

    詳解Django-channels 實(shí)現(xiàn)WebSocket實(shí)例

    這篇文章主要介紹了詳解Django-channels實(shí)現(xiàn)WebSocket實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08

最新評(píng)論

天峻县| 晋州市| 右玉县| 峡江县| 凌源市| 错那县| 凯里市| 闵行区| 达日县| 葵青区| 富阳市| 太康县| 洛川县| 九寨沟县| 北辰区| 新密市| 兴海县| 宁阳县| 石台县| 民权县| 汤原县| 盐山县| 深水埗区| 温州市| 昔阳县| 汉阴县| 长治市| 富平县| 正阳县| 瑞安市| 雅江县| 吉水县| 内丘县| 阿拉尔市| 黔西| 桑日县| 齐河县| 涿鹿县| 兴义市| 威宁| 松溪县|