我是编程新手,这是我在stackoverflow上的第一个问题。我试图让python打开一个.accdb文件并运行一个已经在Access中定义的子程序。我设法使用此代码使用Excel执行此操作:
import win32com.client
xl=win32com.client.Dispatch("Excel.Application")
xl.Visible=True
xl.Workbooks.Open(Filename="<mydirectory>\\open",ReadOnly=1)
xl.Application.Run("TestMe")
#...access spreadsheet data...
xl.Workbooks(1).Close(SaveChanges=0)
xl.Application.Quit()
xl=0
Sub TestMe看起来像这样:
Sub TestMe()
MsgBox "Hi there"
End Sub
运行Python代码会启动Excel,打开文件open.xlsm并显示一个消息框。到现在为止还挺好。感谢:Need skeleton code to call Excel VBA from PythonWin
我已修改代码以尝试使用Access实现相同的功能。我创建了一个名为“testdb”的新的.accdb文件,并将上面的子程序“TestMe”复制到VBA模块中。修改后的python代码如下所示:
import win32com.client
xl=win32com.client.Dispatch("Access.Application")
xl.Visible=True
xl.OpenCurrentDatabase("<mydirectory>\\testdb.accdb")
xl.Application.Run("TestMe")
#...access spreadsheet data...
xl.Workbooks(1).Close(SaveChanges=0)
xl.Application.Quit()
xl=0
主要变化是“Workbooks.Open”已更改为“OpenCurrentDatabase”。我首先尝试找到更类似的东西,比如“Databases.Open”,但没有运气。运行新代码启动Access并打开文件testdb.accdb,但就是这样,不会出现任何消息框。我唯一感兴趣的唯一控制台输出是:
xl.Application.Run("TestMe")
File "<COMObject <unknown>>", line 14, in Run
result = self._oleobj_.InvokeTypes(*(dispid, LCID, wFlags, retType, argTypes) + args)
pywintypes.com_error: (-2147352567, 'Exception occurred.', (0, None, None, None, 0, -2147352562), None)
我很茫然。任何帮助将不胜感激!
答案 0 :(得分:2)
考虑创建一个新的Access宏对象,其中RunCode
操作可以调用模块中的函数。然后,使用DoCmd.RunMacro方法在Python的Windows COM API中调用宏。
<强> MACRO 强>
Macro
RunCode: TestMe()
注意:除非您创建调用子例程的VBA模块函数,否则只能使用RunCode
而不是子例程引用函数:Call SubroutineName
:
<强>的Python 强>
import win32com.client
ac = win32com.client.Dispatch("Access.Application")
ac.Visible=True
ac.OpenCurrentDatabase("<mydirectory>\\testdb.accdb")
ac.DoCmd.RunMacro('MacroName')
ac.DoCmd.CloseDatabase
ac = None
答案 1 :(得分:1)
我绝不是Python专家,但对Access和Excel VBA非常熟悉。如果我在业余编程生涯中更好地了解Python,那么我将永远不会尝试使用任何VBA代码执行下面的操作。考虑到VBA代码的时间投入,我必须找到一种方法......
我花了最近几天/周试图找到一种方式来谈论Python-&gt;访问VBA,并满足以下要求:
我使用pythonnet和IronPython尝试使用clr,发现越来越多令人困惑和不清楚的错误消息和异常。我尝试了上面提到的DoCmd.RunMacro方法,但Access宏不返回值。尝试了一个名为数据宏的Access宏(2010)的新版本,它确实有一个名为SetReturnVar的动作,但它们不与VBA对话,除非通过更传统的宏并且如上所述,传统的宏不返回值。今天我更仔细地阅读了一些Microsoft文档(Access Application.Run方法):
我并不完全了解该声明的含义“您无法从Microsoft Access以外的任何应用程序设置对单个Microsoft Access数据库的引用”,但我发现很多Python&lt; - &gt; ;在谈论Python&lt; - &gt; Excel VBA时,Office应用程序文章似乎更成功。我推断,因为我能够运行Excel VBA&lt; - &gt;过去访问VBA,如果Python&lt; - &gt; Excel VBA和我读过的一样有效,然后一个解决方案(尽管很复杂)似乎是可能的(我认为正确的程序员称之为黑客攻击)。
在剪切/粘贴代码片段并进行调试约1.5小时后:
from win32com.client import Dispatch
FILELOC = r'C:\Users\Desktop\PyExcel.xlsm'
PROGNAME='Excel.Application'
num = 4
#open excel workbook containing VBA code
#...could do more to ensure excel isn't already running
xl = Dispatch(PROGNAME)
xl.Visible = True
#open excel file containing the VBA code
#...could do more to check if file is already open, etc
xl.Workbooks.Open(Filename=FILELOC)
#call to VBA code within excel
rtrn_int = xl.Run("RunCOMObject", num)
#print return value
print(rtrn_int)
#Quit excel-this doesn't work very well and there are articles about
#Python or the COM object not being able to actually remove Excel
#from the task manager
xl.Quit()
Option Explicit
Private Const ACCESS_FILELOC As String = "C:\Users\Desktop\Test.accdb"
Private Const TEMP_FILELOC As String = "C:\Users\Desktop\TestTemp.accdb"
Function RunCOMObject(intNum As Integer) As Integer
Dim objAcc As Object, objProject As Object
Dim accAppl As Access.Application
Dim MyAppl As String
MyAppl = "Access.Application"
If Not IsRunning(MyAppl) Then 'Access not running, simply start
'up Access and open file
Set accAppl = CreateObject(MyAppl) 'start Access
accAppl.Visible = True
accAppl.OpenCurrentDatabase (ACCESS_FILELOC) 'open file
Else: 'Access is running
On Error Resume Next
Set accAppl = GetObject(, MyAppl) 'assign the running application
'to a variable
On Error GoTo Err_File_Open 'use an error in attempting to rename
'the database of interest to determine if the open file is the
'desired file
Name ACCESS_FILELOC As TEMP_FILELOC 'rename the file of interest
Name TEMP_FILELOC As ACCESS_FILELOC 'file was successfully renamed
'therefore not open
Call NoFileOrOther(accAppl, MyAppl)
End If
Err_File_Open:
'Required Access file is open
RunCOMObject = accAppl.Run("TestLink", intNum) 'run the VBA function in
'Access
accAppl.CloseCurrentDatabase 'close database
accAppl.Quit 'quit Access
Set accAppl = Nothing
End Function
Function IsRunning(ByVal MyAppl As String) As Boolean
Dim applRef As Object
On Error Resume Next 'error occurs if GetObject is unable to find a
'running version of the application
Set applRef = GetObject(, MyAppl) 'attempt to obtain the required
'application object
If Not applRef Is Nothing Then 'if application is already running
Set applRef = Nothing
IsRunning = True
Else 'application is not running
IsRunning = False
End If
Set applRef = Nothing
End Function
Sub NoFileOrOther(accAppl As Access.Application, MyAppl As String)
On Error GoTo Err_No_FileOpen
If accAppl.CurrentProject.Name <> "" Then 'Access active with another a
'different file open
Set accAppl = CreateObject(MyAppl) 'start a new instance of Access
accAppl.Visible = True
accAppl.OpenCurrentDatabase (ACCESS_FILELOC) 'open file
End If
Exit Sub
Err_No_FileOpen:
accAppl.OpenCurrentDatabase (ACCESS_FILELOC) 'in the event of Access
'being active without a database open
End Sub
这是一个完全无关紧要的例子,它掩盖了我在Access VBA中编写的代码数量,以保证这种解决方法,但它有助于演示方法
Option Compare Database
Option Explicit
Function TestLink(intNum As Integer) As Integer
TestLink = intNum + 10
End Function
14
成功!!!!这个数字在Python中最初为4,并作为参数发送到Excel和Access,其中有10个添加到它之后再通过Excel返回到Python print(rtrn_int)= 14。
如果有人知道如何明确地(即通过上面演示的类似严格)从Python发送参数 - &gt;访问VBA并将值返回给Python而不使用Excel VBA作为中介我很乐意收到您的回复。或者,使用pythonnet引用clr的方法同样可以理解。