![](/img/trans.png)
[英]Trying to Call a LabVIEW dll into python that invokes a CAN protocol
[英]Call LabVIEW/Windows API DLL from Python
我正在嘗試從 Python 調用 Windows API DLL,它提供控制外部硬件的功能。 但是,如果涉及從任何上述函數傳遞或接收參數,這將不起作用。 DLL 提供了一個函數int DoProberCommand(LPCSTR Command, LPSTR Response)
,它在成功時返回零,否則返回非零值。 使用 Visual Studio C/C++ 工具,DLL 的文檔建議使用 Windows API 函數LoadLibrary
和GetProcAddress
。 在 C/C++ 中,這完美無缺。 使用了以下代碼:
#include <iostream>
#include <Windows.h>
// typedefs for DLL
typedef int (WINAPI *REGISTERPROBERAPP)(LPCSTR, LPCSTR, UINT);
typedef int (WINAPI *DOPROBERCOMMAND)(LPCSTR, LPCSTR)
int main() {
// Load DLL
HMODULE MsgServeLib = NULL;
MsgServeLib = LoadLibrary("MsgServe.dll");
// Find functions in DLL
REGISTERPROBERAPP RegisterProberApp = (REGISTERPROBERAPP)GetProcAddress(MsgServeLib, "RegisterProberApp");
DOPROBERCOMMAND DoProberCommand = (DOPROBERCOMMAND)GetProcAddress(MsgServeLib, "DoProberCommand");
FARPROC CloseProberApp = GetProcAddress(MsgServeLib, "CloseProberApp");
RegisterProberApp("AppName", "AppName", 1);
char cmdBuf[255] = "StepNextDie";
char resBuf[255];
if (DoProberCommand(cmdBuf, resBuf)) {
std::cout << "Failure" << std:endl;
} else {
std::cout << "Success" << std::endl;
}
// Free Memory
CloseProberApp();
FreeLibrary(MsgServeLib);
MsgServeLib = NULL;
return 0;
}
這按預期工作。 但是,我需要一個用於 DLL 的 Python 接口。 我的第一次嘗試是 ctypes:
import ctypes
lib = ctypes.WinDLL(r'Z:\path\to\MsgServe.dll')
_RegisterProberApp = lib.RegisterProberApp
_RegisterProberApp.restype = ctypes.c_int
_RegisterProberApp.argtypes = [ctypes.wintypes.LPCSTR, ctypes.wintypes.LPCSTR, ctypes.c_int]
_CloseProberApp = lib.CloseProberApp
_RegisterProberApp.restype = ctypes.c_int
_DoProberCommand= lib.DoProberCommand
_DoProberCommand.restype = ctypes.c_int
_DoProberCommand.argtypes = [ctypes.wintypes.LPCSTR, ctypes.wintypes.LPCSTR]
_RegisterProberApp("PyApp", "PyApp", 1); # works
_DoProberCommand("StepNextDie",""); # does not work
cmdBuf = ctypes.create_string_buffer("StepNextDie",255)
resBuf = ctypes.create_string_buffer(255)
_DoProberCommand(cmdBuf, resBuf) # does not work
cmdBuf = ctypes.create_unicode_buffer("StepNextDie",255)
resBuf = ctypes.create_unicode_buffer(255)
_DoProberCommand(cmdBuf, resBuf) # does not work
_RegisterProberApp
工作正常,但是_DoProberCommand
總是返回一個非零值,也不能觀察到與 C/C++ 版本不同的硬件的任何變化。 我還為 argtypes 嘗試了ctypes.c_char_p
和ctypes.c_wchar_p
,但這並沒有奏效。 在 Visual Studio 中,我必須在項目設置中選擇 MultiByte 選項,否則我的代碼將無法編譯。 我不知道 ctypes 是否可以處理,所以我嘗試使用Windows API提供的MultiByteToWideChar
函數圍繞MsgServe.dll
構建一個包裝 DLL。 我使用了這里給出的代碼。 C/C++ 代碼如下:
#include <Windows.h>
// defines
#define DLLEXPORT __declspec(dllexport)
// typedefs
typedef int(WINAPI *REGISTERPROBERAPP)(LPCSTR, LPCSTR, UINT);
typedef int(WINAPI *DOPROBERCOMMAND)(LPCSTR, LPSTR);
typedef int(WINAPI *CLOSEPROBERAPP)();
// prototypes
HINSTANCE g_ServiceLib = LoadLibrary((LPCWSTR)"MsgServe.dll");
REGISTERPROBERAPP g_RegisterProberApp = (REGISTERPROBERAPP) GetProcAddress(g_ServiceLib, "RegisterProberApp");
CLOSEPROBERAPP g_CloseProberApp = (CLOSEPROBERAPP) GetProcAddress(g_ServiceLib, "CloseProberApp");
DOPROBERCOMMAND g_DoProberCommand = (DOPROBERCOMMAND) GetProcAddress(g_ServiceLib, "DoProberCommand");
// helper funcs
BOOL MByteToUnicode(LPCSTR multiByteStr, LPWSTR unicodeStr, DWORD size)
{
// Get the required size of the buffer that receives the Unicode string.
DWORD minSize;
minSize = MultiByteToWideChar (CP_ACP, 0, multiByteStr, -1, NULL, 0);
if(size < minSize)
{
return FALSE;
}
// Convert string from multi-byte to Unicode.
MultiByteToWideChar (CP_ACP, 0, multiByteStr, -1, unicodeStr, minSize);
return TRUE;
}
BOOL UnicodeToMByte(LPCWSTR unicodeStr, LPSTR multiByteStr, DWORD size)
{
// Get the required size of the buffer that receives the multiByte string.
DWORD minSize;
minSize = WideCharToMultiByte(CP_OEMCP,NULL,unicodeStr,-1,NULL,0,NULL,FALSE);
if(size < minSize)
{
return FALSE;
}
// Convert string from Unicode to multi-byte.
WideCharToMultiByte(CP_OEMCP,NULL,unicodeStr,-1,multiByteStr,size,NULL,FALSE);
return TRUE;
}
extern "C" {
DLLEXPORT int RegisterProberApp() {
// 0 -> Alle Anwendungen
// 1 -> Lock auf diese Anwendung
return g_RegisterProberApp("PyMsgServe", "PyMsgServe", 1);
}
DLLEXPORT int CloseProberApp() {
return g_CloseProberApp();
}
DLLEXPORT int DoProberCommand(LPCWSTR command, LPWSTR response) {
LPSTR mCommand = NULL;
LPSTR mResponse = NULL;
UnicodeToMByte(command, mCommand, wcslen(command));
int x = g_DoProberCommand(mCommand, mResponse);
MByteToUnicode(mResponse, response, strlen(mResponse));
return x;
}
}
導出的 DLL 編譯時沒有任何缺陷,但是當使用 ctypes 從 Python 調用它時,我得到一個“訪問沖突讀數”。 我還嘗試使用 SWIG(代碼基本相同,只是包裝在一個類中),這在使用 ctypes 而沒有額外的 DLL 時出現了一些問題。
有關如何解決此錯誤的任何建議?
編輯:添加了我當時使用的代碼:
文件“LPCSTRConvert.py”
import ctypes
from ctypes import wintypes
class Converter:
# Init func
# TODO: Load conversion functions from Kernel32
def __init__(self):
# get functions
self._wideCharToMultiByte = ctypes.windll.kernel32.WideCharToMultiByte
self._multiByteToWideChar = ctypes.windll.kernel32.MultiByteToWideChar
# set return type
self._wideCharToMultiByte.restype = ctypes.c_int
self._multiByteToWideChar.restype = ctypes.c_int
# set argument types
self._wideCharToMultiByte.argtypes = [wintypes.UINT, wintypes.DWORD,
wintypes.LPCWSTR, ctypes.c_int,
wintypes.LPSTR, ctypes.c_int,
wintypes.LPCSTR,
ctypes.POINTER(ctypes.c_long)]
self._multiByteToWideChar.argtypes = [wintypes.UINT, wintypes.DWORD,
wintypes.LPCSTR, ctypes.c_int,
wintypes.LPWSTR, ctypes.c_int]
return
# WideCharToMultiByte
# TODO: Converts a WideChar character set to a MultiByte character set
# WideChar -> UTF8
def WideCharToMultiByte(self, fn):
_CP_UTF8 = 65001 # UTF-8
_CP_ACP = 0 # ANSI
codePage = _CP_UTF8
dwFlags = 0
lpWideCharStr = fn
cchWideChar = len(fn)
lpMultiByteStr = None
cbMultiByte = 0 # zero requests size
lpDefaultChar = None
lpUsedDefaultChar = None
# get size
mbcssize = self._wideCharToMultiByte(codePage,
dwFlags,
lpWideCharStr,
cchWideChar,
lpMultiByteStr,
cbMultiByte,
lpDefaultChar,
lpUsedDefaultChar)
if mbcssize <= 0:
raise WinError(mbcssize)
lpMultiByteStr = ctypes.create_string_buffer(mbcssize)
# convert
retcode = self._wideCharToMultiByte(codePage,
dwFlags,
lpWideCharStr,
cchWideChar,
lpMultiByteStr,
mbcssize,
lpDefaultChar,
lpUsedDefaultChar)
if retcode <= 0:
raise WinError(retcode)
return lpMultiByteStr.value
文件“Prober.py”
try:
from LPCSTRConvert import Converter
import ctypes
from ctypes import wintypes
except ImportError:
print("Can't import ctypes")
# import dll
DllPath = r'Z:\path\to\MsgServe.dll'
_msgServe = ctypes.WinDLL(DllPath)
# load functions
_registerProberApp = _msgServe.RegisterProberApp
_closeProberApp = _msgServe.CloseProberApp
_doProberCommand = _msgServe.DoProberCommand
# set return types
_registerProberApp.restype = ctypes.c_int
_doProberCommand.restype = ctypes.c_int
# set arguments
_registerProberApp.argtypes = [wintypes.LPCSTR, wintypes.LPCSTR, wintypes.UINT]
_doProberCommand.argtypes = [wintypes.LPCSTR, wintypes.LPCSTR]
# Registriere Prober
if _registerProberApp('PyApp'.encode('utf-8'),
'PyApp'.encode('utf-8'), 1) == 1:
print("Opened")
else:
print("Error")
conv = Converter()
resBuf = ctypes.create_string_buffer(255)
y = _doProberCommand(conv.WideCharToMultiByte('StepNextDie'),
resBuf)
if y == 0:
print("Works")
else:
print("Error Code: " + str(y))
print("ResBuf: " + str(resBuf.value))
if _closeProberApp() == 1:
print("Exit")
else:
print("Could not exit.")
這提高了輸出:
Python 3.7.2 (tags/v3.7.2:9a3ffc0492, Dec 23 2018, 22:20:52) [MSC v.1916 32 bit (Intel)]
Type "copyright", "credits" or "license" for more information.
IPython 7.3.0 -- An enhanced Interactive Python.
try:
from LPCSTRConvert import Converter
import ctypes
from ctypes import wintypes
except ImportError:
print("Can't import ctypes")
# import dll
DllPath = r'Z:\path\to\MsgServe.dll'
_msgServe = ctypes.WinDLL(DllPath)
# load functions
_registerProberApp = _msgServe.RegisterProberApp
_closeProberApp = _msgServe.CloseProberApp
_doProberCommand = _msgServe.DoProberCommand
# set return types
_registerProberApp.restype = ctypes.c_int
_doProberCommand.restype = ctypes.c_int
# set arguments
_registerProberApp.argtypes = [wintypes.LPCSTR, wintypes.LPCSTR, wintypes.UINT]
_doProberCommand.argtypes = [wintypes.LPCSTR, wintypes.LPCSTR]
# Registriere Prober
if _registerProberApp('PyApp'.encode('utf-8'),
'PyApp'.encode('utf-8'), 1) == 1:
print("Opened")
else:
print("Error")
conv = Converter()
resBuf = ctypes.create_string_buffer(255)
y = _doProberCommand(conv.WideCharToMultiByte('StepNextDie'),
resBuf)
if y == 0:
print("Works")
else:
print("Error Code: " + str(y))
print("ResBuf: " + str(resBuf.value))
if _closeProberApp() == 1:
print("Exit")
else:
print("Could not exit.")
Opened
Error Code: 509
ResBuf: b': StepNextDie'
Exit
我設法通過使用一個額外的 DLL 作為MsgServe.dll
和 Python 之間的層來解決這個問題。 根據我的在線搜索,問題是 Windows 需要文件路徑的特定字符集(在LoadLibrary()
函數調用中)。 因此,我最初選擇了 MultiByte-Charset,但因此 LabVIEW-DLL 不再起作用。 我的解決方案是tchar.h
(見下面的代碼)中的宏_T()
處理轉換。 現在一切都按預期進行。
Layer.dll 來源:
#include <Windows.h>
#include <stdio.h>
#include <tchar.h>
#define DLLEXPORT __declspec(dllexport)
// typedefs for functions
typedef int (WINAPI *REGISTERPROBERAPP)(LPCSTR, LPCSTR, UINT);
typedef int (WINAPI *DOPROBERCOMMAND)(LPCSTR, LPCSTR);
// MsgServe variables
HMODULE hMsgServe = nullptr;
REGISTERPROBERAPP mRegisterProberApp;
DOPROBERCOMMAND mDoProberCommand;
FARPROC mCloseProberApp;
extern "C" DLLEXPORT void Init() {
hMsgServe = LoadLibrary( _T("MsgServe.dll") );
mRegisterProberApp = (REGISTERPROBERAPP)GetProcAddress(hMsgServe, "RegisterProberApp");
mDoProberCommand = (DOPROBERCOMMAND)GetProcAddress(hMsgServe, "DoProberCommand");
mCloseProberApp = GetProcAddress(hMsgServe, "CloseProberApp");
mRegisterProberApp("PyProb", "PyProb", 1);
}
extern "C" DLLEXPORT void Quit() {
mCloseProberApp();
FreeLibrary(hMsgServe);
hMsgServe = nullptr;
}
extern "C" DLLEXPORT void DoProberCommand(char* Cmd) {
// buffers
char CmdBuf[256];
char ResBuf[256];
// store command
sprintf_s(CmdBuf, Cmd);
mDoProberCommand(CmdBuf, ResBuf);
}
蟒蛇源:
import ctypes
hProberBench = ctypes.WinDLL(r"path\to\Layer.dll")
_Init = hProberBench.Init
_Quit = hProberBench.Quit
_DoProberCommand = hProberBench.DoProberCommand
_Init.argtypes = None
_Init.restype = None
_Quit.argtypes = None
_Quit.restype = None
_DoProberCommand.argtypes = [ctypes.c_char_p]
_DoProberCommand.restype = None
_Init()
_DoProberCommand("StepNextDie".encode('utf-8'))
_Quit()
謝謝@CristiFati 的幫助。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.