访问:在程序重置期间保留 COM 引用?

发布于 2024-09-25 19:25:41 字数 358 浏览 3 评论 0原文

Access VBA (2003) 是否有方法将 COM 引用转换为整数,并调用 AddRef/Release? (这会出现错误“函数或接口标记为受限制,或者函数使用 Visual Basic 中不支持的自动化类型”)

我使用的是第三方 COM 对象,该对象无法处理在单个进程中实例化两次的情况(这是一个已知的错误)。因此,我考虑将引用存储为隐藏窗体上控件的标题,以保护它免受程序重置清除所有 VB 变量的影响。

编辑:我认为可以使用未记录的 ObjPtr 来完成对 int 的转换,然后使用 CopyMemory API 再次返回,并且可以隐式调用 AddRef/Release 。但还有更好的办法吗?加载项是否受到程序重置的保护?

Are there ways in Access VBA (2003) to cast a COM reference to an integer, and to call AddRef/Release? (which give the error "Function or interface marked as restricted, or the function uses an Automation type not supported in Visual Basic")

I'm using a third-party COM object which doesn't handle being instantiated twice in a single process (this is a known bug). I therefore thought of storing the reference as the caption of a control on a hidden form to protect it from Program Reset clearing all VB variables.

Edit: I think the cast to int can be done with the undocumented ObjPtr, and back again with the CopyMemory API, and AddRef/Release can be called implicitly. But is there a better way? Are add-ins protected from Program Reset?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

遗忘曾经 2024-10-02 19:25:41

问题是代码重置后仍然存在,还是代码重置后就无法重新初始化?

对于第一个问题,将顶级对象包装在函数中,并在内部使用 STATIC 变量来缓存引用。如果 STATIC 变量为 Nothing,则重新初始化。这是我用于缓存对本地数据库的引用的函数:

  Public Function dbLocal(Optional bolInitialize As Boolean = True) +
     As DAO.Database
  ' 2003/02/08 DWF added comments to explain it to myself!
  ' 2005/03/18 DWF changed to use Static variable instead
  ' uses GoTos instead of If/Then because:
  '  error of dbCurrent not being Nothing but dbCurrent being closed (3420)
  '  would then be jumping back into the middle of an If/Then statement
  On Error GoTo errHandler
    Static dbCurrent As DAO.Database
    Dim strTest As String

  If Not bolInitialize Then GoTo closeDB

  retryDB:
    If dbCurrent Is Nothing Then
       Set dbCurrent = CurrentDb()
    End If
    ' now that we know the db variable is not Nothing, test if it's Open
    strTest = dbCurrent.Name

  exitRoutine:
    Set dbLocal = dbCurrent
    Exit Function

  closeDB:
    If Not (dbCurrent Is Nothing) Then
       Set dbCurrent = Nothing
    End If
    GoTo exitRoutine

  errHandler:
    Select Case err.Number
      Case 3420 ' Object invalid or no longer set.
        Set dbCurrent = Nothing
        If bolInitialize Then
           Resume retryDB
        Else
           Resume closeDB
        End If
      Case Else
        MsgBox err.Number & ": " & err.Description, vbExclamation, "Error in dbLocal()"
        Resume exitRoutine
    End Select
  End Function

在代码中的任何地方:

  Dim db As DAO.Database
  Set db = CurrentDB()
  Set db = DBEngine(0)(0)
  db.Execute "[SQL DML]", dbFailOnError

...您可以将整个内容替换为:

  dbLocal.Execute "[SQL DML]", dbFailOnError

...并且您不必担心在以下情况下初始化它:您的应用程序打开,或者在代码重置后 - 它是自我修复的,因为它会检查静态内部变量并在需要时重新初始化。

唯一需要注意的是,当您关闭应用程序时,您需要将 bolInitialize 参数设置为 False 进行调用,因为这会清理引用,因此当应用程序关闭时超出范围时,您的应用程序不会有挂起的风险。

对于另一个问题,我真的怀疑 VBA 中是否有任何解决方案,除非您可以进行 API 调用并终止外部进程。但我认为这是一个不太可能的事情。

Is the problem with surviving the code reset or is it that once the code is reset it can't be re-initialized?

For the first problem, wrap your top-level object in a function and use a STATIC variable internally to cache the reference. If the STATIC variable Is Nothing, re-initialize. Here's the function I use for caching a reference to the local database:

  Public Function dbLocal(Optional bolInitialize As Boolean = True) +
     As DAO.Database
  ' 2003/02/08 DWF added comments to explain it to myself!
  ' 2005/03/18 DWF changed to use Static variable instead
  ' uses GoTos instead of If/Then because:
  '  error of dbCurrent not being Nothing but dbCurrent being closed (3420)
  '  would then be jumping back into the middle of an If/Then statement
  On Error GoTo errHandler
    Static dbCurrent As DAO.Database
    Dim strTest As String

  If Not bolInitialize Then GoTo closeDB

  retryDB:
    If dbCurrent Is Nothing Then
       Set dbCurrent = CurrentDb()
    End If
    ' now that we know the db variable is not Nothing, test if it's Open
    strTest = dbCurrent.Name

  exitRoutine:
    Set dbLocal = dbCurrent
    Exit Function

  closeDB:
    If Not (dbCurrent Is Nothing) Then
       Set dbCurrent = Nothing
    End If
    GoTo exitRoutine

  errHandler:
    Select Case err.Number
      Case 3420 ' Object invalid or no longer set.
        Set dbCurrent = Nothing
        If bolInitialize Then
           Resume retryDB
        Else
           Resume closeDB
        End If
      Case Else
        MsgBox err.Number & ": " & err.Description, vbExclamation, "Error in dbLocal()"
        Resume exitRoutine
    End Select
  End Function

Anywhere you'd either of these in code:

  Dim db As DAO.Database
  Set db = CurrentDB()
  Set db = DBEngine(0)(0)
  db.Execute "[SQL DML]", dbFailOnError

...you can replace the whole thing with:

  dbLocal.Execute "[SQL DML]", dbFailOnError

...and you don't have to worry about initializing it when your app opens, or after a code reset -- it's self-healing because it checks the Static internal variable and re-initializes if needed.

The only caveat is that you need to make a call with the bolInitialize argument set to False when you shut down your app, as this cleans up the reference so there's no risk of your app hanging when it goes out of scope as the app closes.

For the other problem, I really doubt there's any solution within VBA, unless you can make an API call and kill the external process. But that's something of a longshot, I think.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文