如何在 Access VBA 中设置对正在运行的对象的引用

Posted

技术标签:

【中文标题】如何在 Access VBA 中设置对正在运行的对象的引用【英文标题】:How to set a reference to a running object in Access VBA 【发布时间】:2018-08-07 11:50:09 【问题描述】:

我尝试使用 GetObject 在另一个数据库中打开一个表单。不幸的是,我必须打开数据库的第二个实例,但我想改用该数据库的活动实例(如果已加载)。为此,我需要为该数据库的运行实例设置一个对象引用。

我目前使用的是下面的功能。此函数首先尝试使用其屏幕名称激活正在运行的数据库实例,如果这产生错误,则加载数据库和表单。但是,如果数据库已经加载,我也希望能够加载表单。

较小的问题是如果加载数据库和表单的错误过程产生错误,则不遵循错误程序。我应该如何处理?

有人有想法吗?

我正在使用 Access 2016

谢谢。

彼得

Public Function AppDbOpen(strAppExec As String, strAppName As String, strOpenForm As String) As Boolean
    On Error GoTo Err_Proc
    Dim objDb As Object

    'Activate DB if open
    AppActivate strAppName
    AppDbOpen = True

Exit_Err_Proc:
    Set objDb = Nothing
    Exit Function

Err_Proc:
    Select Case Err.Number
        Case 5 'Open Db if not open
            Set objDb = GetObject(strAppExec, "Access.Application")
            If Nz(strOpenForm, "") <> "" Then
                objDb.DoCmd.OpenForm strOpenForm
            End If
            AppDbOpen = True
        Case Else
            MsgBox "Error: " & Trim(Str(Err.Number)) & vbCrLf & _
            "Desc: " & Err.description & vbCrLf & vbCrLf & _
            "Module: Mod_GeneralFunctions" & vbCrLf & _
            "Function: AppDbOpen", _
            vbCritical, "Error!"
    End Select
    Resume Exit_Err_Proc
End Function

【问题讨论】:

【参考方案1】:

这不是一件容易的事,但可以通过使用一些 WinAPI 窗口函数来完成。

本质上,您希望通过使用窗口标题来获取 Access Application 对象。

我将假设您在该窗口标题中没有任何 unicode 字符,否则,我们需要一些更复杂的东西。

首先,声明我们的 WinAPI 函数:

Declare PtrSafe Function FindWindowExA Lib "user32" (Optional ByVal hWndParent As LongPtr, Optional ByVal hwndChildAfter As LongPtr, Optional ByVal lpszClass As String, Optional ByVal lpszWindow As String) As LongPtr
Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" (ByVal hWnd As LongPtr, ByVal dwId As Long, riid As Any, ppvObject As Object) As Long

FindWindowExA 用于查找具有指定标题的窗口。 AccessibleObjectFromWindow 用于获取该窗口的 COM 对象。

然后,我们声明一些用于 AccessibleObjectFromWindow 的常量:

Const strIID_IDispatch As String = "00020400-0000-0000-C000-000000000046" 'To identify the IDISPATCH COM interface
Const OBJID_NATIVEOM As Long = &HFFFFFFF0 'To identify the object type

然后,我们就可以编写函数了

Public Function AppDbOpen(strAppExec As String, strAppName As String, strOpenForm As String) As Boolean
    On Error GoTo Err_Proc
    Dim objDb As Object

    'Activate DB if open
    AppActivate strAppName
    AppDbOpen = True
    Dim hwndAppDb As LongPtr
    hwndAppDb = FindWindowExA (,,,strAppName) 'Find the window handle (hWnd)
    If hwndAppDb <> 0 Then 'If it's 0, something went wrong, check the title
          Dim guid() As Byte
          guid = Application.GuidFromString(strIID_IDispatch)
          'Get the IDispatch object associated with that handle
          AccessibleObjectFromWindow hwndAppDb, OBJID_NATIVEOM, guid(0), objDb 
    End If
    If Nz(strOpenForm, "") <> "" Then
         objDb.DoCmd.OpenForm strOpenForm
    End If
Exit_Err_Proc:
    Set objDb = Nothing
    Exit Function

Err_Proc:
    Select Case Err.Number
        Case 5 'Open Db if not open
            Set objDb = GetObject(strAppExec, "Access.Application")
            If Nz(strOpenForm, "") <> "" Then
                objDb.DoCmd.OpenForm strOpenForm
            End If
            AppDbOpen = True
        Case Else
            MsgBox "Error: " & Trim(Str(Err.Number)) & vbCrLf & _
            "Desc: " & Err.description & vbCrLf & vbCrLf & _
            "Module: Mod_GeneralFunctions" & vbCrLf & _
            "Function: AppDbOpen", _
            vbCritical, "Error!"
    End Select
    Resume Exit_Err_Proc
End Function

我不打算讨论链式错误处理程序的意义,但您可以查看this answer。请注意,重置错误处理程序也会重置 Err 对象,因此如果要使用它,您可能首先需要存储错误编号和描述。

【讨论】:

【参考方案2】:

这就像一个魅力,非常感谢你!我自己从来没有想到过。

似乎在调整代码后也没有与嵌套错误相关的问题。我需要添加一个最大化调用,因为 mu 表单显示与屏幕相关,当其他数据库最小化时,这会导致一个不可见的表单。现在是最终代码

Option Compare Database
Option Explicit
Declare PtrSafe Function FindWindowExA Lib "user32" (Optional ByVal hWndParent As LongPtr, _
        Optional ByVal hwndChildAfter As LongPtr, Optional ByVal lpszClass As String, _
        Optional ByVal lpszWindow As String) As LongPtr
Declare PtrSafe Function AccessibleObjectFromWindow Lib "oleacc" (ByVal hWnd As LongPtr, _
        ByVal dwId As Long, riid As Any, ppvObject As Object) As Long
Const strIID_IDispatch As String = "00020400-0000-0000-C000-000000000046" 'To identify the IDISPATCH COM interface
Const OBJID_NATIVEOM = &HFFFFFFF0 'To identify the object type

Public Function AppDbOpen(strAppExec As String, strAppName As String, strOpenForm As String) As Boolean
    On Error GoTo Err_Proc
    Dim objDb As Object
    Dim hwndAppDb As LongPtr

    'Find the Db handle
    hwndAppDb = FindWindowExA(, , , strAppName) 'Find the window handle (hWnd)
    If hwndAppDb <> 0 Then 'If it's 0, something went wrong, check the title
        'Activate DB if open
        Dim guid() As Byte
        guid = Application.GUIDFromString(strIID_IDispatch)
        'Get the IDispatch object associated with that handle
        AccessibleObjectFromWindow hwndAppDb, OBJID_NATIVEOM, guid(0), objDb
    Else
        'Open Db if not open
        Set objDb = GetObject(strAppExec, "Access.Application")
    End If

    If Nz(strOpenForm, "") <> "" Then
        objDb.RunCommand acCmdAppMaximize
        objDb.DoCmd.OpenForm strOpenForm
        objDb.Run "CenterForm", strOpenForm, False, False, False, 0
    End If
    AppDbOpen = True

Exit_Err_Proc:
    Set objDb = Nothing
    Exit Function

Err_Proc:
    Select Case Err.Number
        Case Else
            MsgBox "Error: " & Trim(Str(Err.Number)) & vbCrLf & _
            "Desc: " & Err.description & vbCrLf & vbCrLf & _
            "Module: Mod_OpenExtDb" & vbCrLf & _
            "Function: AppDbOpen", _
            vbCritical, "Error!"
    End Select
    Resume Exit_Err_Proc
End Function

再次感谢您!

彼得

【讨论】:

以上是关于如何在 Access VBA 中设置对正在运行的对象的引用的主要内容,如果未能解决你的问题,请参考以下文章

在项目控制中设置对新项目的关注

如何在此示例中设置对节点的引用

有没有办法在 Android 中设置对加密数据的随机访问?

在iptables防火墙中设置对yum放行

2010 Access vba如何将Excel中的displaygridlines设置为false?

如何在 Access VBA 中运行附加查询以根据表单字段中的数量创建一定数量的相同记录?