跨平台扩展库:VBA与Python的桥梁

在软件开发中,经常需要为Microsoft Office套件(如Excel、Word、PowerPoint和Outlook)扩展功能。通常,Visual Basic for Applications (VBA) 是实现这些扩展的首选语言。然而,随着Python的流行,开发者可能希望使用Python来实现类似的功能。那么,是否有可能创建一个同时支持VBA和Python的库呢?

这种跨平台库的优势显而易见:代码维护更为简单,因为所有代码都在一个项目中。经过一番探索,发现Robert Giesecke的Unmanaged Exports Nuget包是实现这一目标的理想工具。将一些有用的功能打包到DLL中,这可能会为其他人节省宝贵的时间。这个库仍在开发中,如果有任何建议,请在文章下方留言。

如何使用代码

要测试这个库,请下载演示文件并解压整个文件夹 "PythonVbaDemo"。

在Excel中测试:打开 "VbaCallDLL.xlsm" 文件。

在Python中测试:使用Visual Studio Code打开 "PythonVbaDemo" 文件夹。

VBA:运行 "VbaTestDllDemo Sub"。

Python:运行 "PythonCallDLL.py" 脚本。

要点

1. 在VBA中的使用

存在两个版本的DLL(32位和64位)。如何让VBA代码自动处理这两个版本,请参考以下代码片段。

Function IsOffice32Bit() As Boolean ' 参考链接:https://docs.microsoft.com/en-us/office/troubleshoot/office-suite-issues/numbering-scheme-for-product-guid ' p => 0=32位, 1=64位 IsOffice32Bit = (Mid(Application.ProductCode, 21, 1) = "0") End Function

现在可以使用 "更改当前目录" 让VBA知道它应该引用哪个DLL。

Sub ChangeCurDirToDllFolder() Dim sSubfolderName As String If IsOffice32Bit() Then sSubfolderName = "x86" Else sSubfolderName = "x64" End If ChDir ThisWorkbook.Path & "\" & sSubfolderName End Sub

以下是VBA可以使用的函数列表:

  • Vba_PlotFreeSpaceChart
  • ReloadMediaFile
  • StartPlayingMediaFile
  • StopPlayingMediaFile
  • Sum
  • InstantiateExtLib
  • Vba_Echo
  • EncryptStringTripleDES
  • DecryptStringTripleDES
  • GetLibName
  • GetLibVersion
  • GetEmpIDByUserID
  • GetUserIDByEmpID

有三种类型的接口:

  • 不返回任何数据的子程序
  • 返回原始数据类型(如Integer、Double等)的函数
  • 返回非原始数据类型(如String)的函数

对于类型1和2,可以直接在VBA中使用以下声明来调用它们。

#If VBA7 Then Private Declare PtrSafe Sub Vba_PlotFreeSpaceChart Lib "ExtLib4VbaPython.dll" (ByVal sSpaceCheckPathsInCsv As String, ByVal nMinFreeSpacePercent As Integer) Private Declare PtrSafe Function Vba_Echo Lib "ExtLib4VbaPython.dll" (ByVal sName As String) As String Private Declare PtrSafe Function Sum Lib "ExtLib4VbaPython.dll" (ByVal nNum1 As Integer, ByVal nNum2 As Integer) As Integer ' ... 其他声明 #Else ' ... 其他声明 #End If

对于类型3接口,必须使用以下方法来调用它们:

Dim oExtLib As Object Set oExtLib = InstantiateExtLib("Alarm01.wav") Debug.Print oExtLib.Vba_Hello(Application.UserName) Debug.Print oExtLib.MediaFileName ' ... 其他调用

2. 在Python中的使用

以下是Python可以使用的函数列表:

  • Py_PlotFreeSpaceChart
  • ReloadMediaFile
  • StartPlayingMediaFile
  • StopPlayingMediaFile
  • Sum
  • InstantiateExtLib
  • Py_Echo
  • Py_EncryptStringTripleDES
  • Py_DecryptStringTripleDES
  • Py_GetLibName
  • Py_GetLibVersion
  • Py_GetEmpIDByUserID
  • Py_GetUserIDByEmpID

有三种类型的接口:

  • 不返回任何数据的子程序
  • 返回原始数据类型(如Integer、Double等)的函数
  • 返回非原始数据类型(如String)的函数

对于类型1和2,可以直接在Python中使用以下声明来调用它们。

import ctypes import os from ctypes import * from comtypes.automation import VARIANT full_path = os.path.realpath(__file__) sPath, sFilename = os.path.split(full_path) sDllPath = sPath + "\\x64\\ExtLib4VbaPython.dll" oExtLib = ctypes.cdll.LoadLibrary(sDllPath) print("3 + 5 = " + str(oExtLib.Sum(3, 5))) oExtLib.Py_PlotFreeSpaceChart('C:', 10)

对于类型3接口,必须使用以下方法来调用它们:

def Hello(name, oExtLib): oExtLib.Py_Hello.argtypes = [VARIANT, POINTER(VARIANT)] v = VARIANT() oExtLib.Py_Hello(name, v) return v.value full_path = os.path.realpath(__file__) sPath, sFilename = os.path.split(full_path) sDllPath = sPath + "\\x64\\ExtLib4VbaPython.dll" oExtLib = ctypes.cdll.LoadLibrary(sDllPath) print(Hello('Wayne', oExtLib))

已知问题

在VBA中播放媒体文件可以正常工作,但在Python中不行。如果有人知道原因,请指出,非常感谢。

import ctypes import os from ctypes import * from comtypes.automation import VARIANT full_path = os.path.realpath(__file__) sPath, sFilename = os.path.split(full_path) sDllPath = sPath + "\\x64\\ExtLib4VbaPython.dll" oExtLib = ctypes.cdll.LoadLibrary(sDllPath) oExtLib.Py_ReloadMediaFile('') oExtLib.StartPlayingMediaFile()
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485