65.9K
CodeProject 正在变化。 阅读更多。
Home

为 VBA 和 Python 用户创建 DLL

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2022年12月5日

CPOL

3分钟阅读

viewsIcon

11753

downloadIcon

225

扩展 Microsoft Office 和 Python 的功能。

下载

引言

有时,我们需要为 VBA (Visual Basic for Application) 用户开发 DLL 库,以扩展 Microsoft Office 套件(Excel、Word、PPT 和 Outlook)的功能。

如果我们需要为 Python 开发具有类似功能的库怎么办?

我只是想知道是否可以为 VBA 和 Python 都使用同一个库。

这样做的好处很明显,在一个项目中维护代码很容易。

经过一番探索,我发现 Robert Giesecke 的 Unmanaged Exports Nuget 包是实现此目的的正确工具。

我已将一些有用的功能打包到此 DLL 中,它也可以为其他人节省一些宝贵的时间。

该库仍在开发中,如果您有任何建议,请在此文章下方留言。 例如,如果 Excel 或 Python 中缺少任何需要的功能等。

Using the Code

请下载示例并解压整个文件夹 "PythonVbaDemo"。

  • 在 Excel 中进行测试:打开 "VbaCallDLL.xlsm"
  • 在 Python 中进行测试:使用 Visual Studio Code 打开文件夹 "PythonVbaDemo"

VBA:只需运行 VbaTestDllDemo Sub

Python:运行 "PythonCallDLL.py"

关注点

1. 在 VBA 中使用

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

  1. 确定现有 Office 是 32 位还是 64 位:(感谢 Daniel Pineault)
    Function IsOffice32Bit() As Boolean
    'https://docs.microsoft.com/en-us/office/troubleshoot/office-suite-issues/numbering-scheme-for-product-guid
    '   p => 0=32-bit, 1=64-bit
        IsOffice32Bit = (Mid(Application.ProductCode, 21, 1) = 0)
    End Function    
    
  2. 现在我们可以使用“更改当前目录”来让 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

有三种类型的接口

  1. 不返回任何数据的子程序
  2. 返回原始数据类型的函数,例如 IntegerDouble 等。
  3. 返回非原始数据类型的函数,例如 String

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

'
' Interface 1 & 2 declarations:
'
#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
    
    Private Declare PtrSafe Function BytesToBase64 Lib "ExtLib4VbaPython.dll" _
            (bytes() As Byte) As String
    Private Declare PtrSafe Function Base64toBytes Lib "ExtLib4VbaPython.dll" _
            (ByVal sBase64String As String) As Byte()
    
    Private Declare PtrSafe Function EncryptStringTripleDES Lib _
            "ExtLib4VbaPython.dll" (ByVal sPlainText As String) As String
    Private Declare PtrSafe Function DecryptStringTripleDES Lib _
            "ExtLib4VbaPython.dll" (ByVal sEncryptedText As String) As String    
    
    Private Declare PtrSafe Sub Vba_ReloadMediaFile Lib _
            "ExtLib4VbaPython.dll" (ByVal sMediaFileName As String)
    Private Declare PtrSafe Sub StartPlayingMediaFile Lib "ExtLib4VbaPython.dll" ()
    Private Declare PtrSafe Sub StopPlayingMediaFile Lib "ExtLib4VbaPython.dll" ()

    Private Declare PtrSafe Function InstantiateExtLib Lib _
            "ExtLib4VbaPython.dll" (ByVal text As String) As Object
#Else
    Private Declare Function InstantiateExtLib Lib _
            "ExtLib4VbaPython.dll" (ByVal text As String) As Object    
    
    Private Declare Sub Vba_PlotFreeSpaceChart Lib "ExtLib4VbaPython.dll" _
      (ByVal sSpaceCheckPathsInCsv As String, ByVal nMinFreeSpacePercent As Integer)
    Private Declare Function Vba_Echo Lib "ExtLib4VbaPython.dll" _
      (ByVal sName As String) As String
    Private Declare Function Sum Lib "ExtLib4VbaPython.dll" _
      (ByVal nNum1 As Integer, ByVal nNum2 As Integer) As Integer
    
    Private Declare Function BytesToBase64 Lib "ExtLib4VbaPython.dll" _
     (bytes() As Byte) As String
    Private Declare Function Base64toBytes Lib "ExtLib4VbaPython.dll" _
     (ByVal sBase64String As String) As Byte()
    
    Private Declare Function EncryptStringTripleDES Lib "ExtLib4VbaPython.dll" _
     (ByVal sPlainText As String) As String
    Private Declare Function DecryptStringTripleDES Lib "ExtLib4VbaPython.dll" _
     (ByVal sEncryptedText As String) As String
    
    Private Declare Sub Vba_ReloadMediaFile Lib "ExtLib4VbaPython.dll" _
      (ByVal sMediaFileName As String)
    Private Declare Sub StartPlayingMediaFile Lib "ExtLib4VbaPython.dll" ()
    Private Declare Sub StopPlayingMediaFile Lib "ExtLib4VbaPython.dll" ()
#End If

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

'
' Interface 3 calling:
'
    Dim oExtLib As Object
    Set oExtLib = InstantiateExtLib("Alarm01.wav")
    Debug.Print oExtLib.Vba_Hello(Application.UserName)
    Debug.Print oExtLib.MediaFileName
    
    Debug.Print "Lib Name: " & oExtLib.Vba_GetLibName()
    Debug.Print "Lib Version: " & oExtLib.Vba_GetLibVersion()
    Debug.Print "Lib EmpID: " & oExtLib.Vba_GetEmpIDByUserID("Your User ID")
    Debug.Print "Lib UserID: " & oExtLib.Vba_GetUserIDByEmpID("Your Emp ID")

2. 在 Python 中使用

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

  • Py_PlotFreeSpaceChart

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

有三种类型的接口

  1. 不返回任何数据的子程序
  2. 返回原始数据类型的函数,例如 IntegerDouble 等。
  3. 返回非原始数据类型的函数,例如 String

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

#
# Interface 1 & 2 declarations:
#
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 接口,我们必须使用以下方法来调用它们

#
# Interface 3 calling:
#
import ctypes
import os
from ctypes import *
from comtypes.automation import VARIANT

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 中无效,如果有人知道原因,请指出,非常感谢。

#
# Known issues: Same function in VBA call, it will play the music, but not in 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() 

致谢

历史

  • 2022 年 12 月 5 日:初始版本

<object data-extension-version="1.2.0.158" data-supports-flavor-configuration="true" id="__symantecMPKIClientMessenger" style="display: none;">__PRESENT

<object data-extension-version="1.2.0.158" data-supports-flavor-configuration="true" id="__symantecMPKIClientMessenger" style="display: none;">__PRESENT

© . All rights reserved.