如何在 MS ACCESS 中打开定时消息框而不创建其他窗口
Posted
技术标签:
【中文标题】如何在 MS ACCESS 中打开定时消息框而不创建其他窗口【英文标题】:How to open a timed Message Box in MS ACCES without creating additional window 【发布时间】:2019-03-30 10:38:48 【问题描述】:按下form
上的Save
按钮时,我想运行一个定时消息框,它会在1 秒后自动关闭。在用户按下OK
或Exit
之前,默认的MsgBox
命令不会消失。
到目前为止,我从网上搜索得到了一个解决方案:
Public Sub Timed_Box (dur AS Long)
Dim WSH AS IWshRuntimeLibrary.WshShell
Dim Res AS Long
Set WSH = IWshRuntimeLibrary.WshShell
Res = WSH.PopUp(Text:="Record Updated", secondstowait:=dur, _
Title:="Update", Type:=vbOKOnly)
End Sub
它工作正常。但是,问题在于它会在桌面任务栏上创建一个临时窗口,持续时间对于用户来说非常烦人。无论如何,我可以隐藏此窗口,使其不显示在任务栏上,同时仍显示类似于MsgBox
的消息?
【问题讨论】:
看看这个而不是 MsgBox:***.com/questions/39224308/… @Andre,看起来很有希望。我花了一些时间来理解它,但我不知道如何将 .dll 库加载到我的 Access 项目中。 【参考方案1】:我写了一个额外的答案而不仅仅是评论,因为它似乎对请求的上下文太重要了。
Lone 写了关于 MatteoNNZ 的回答:
感谢分享,结果与我使用现有代码实现的结果没有什么不同。您的代码还在任务栏上生成了一个临时窗口。
但这距离您的需求仅一步之遥!
只需将您的 Microsoft Access 窗口 (Application.hWndAccessApp
) 的句柄提供给 Api,即可让生成的消息框“视觉绑定”到 Microsoft Access:
MsgBoxTimeout Application.hWndAccessApp, "This message box will be closed after 1 second ", "Automatically closing MsgBox", vbInformation, 0, 1000
2019-04-05 更新
这是 MessageBoxTimeout 的包装器,用于简化调用。
参数的顺序和它们的默认值遵循原来的MsgBox
函数。
它使用原始 API 函数 namens 为用户定义的过程释放此名称。
我为超时返回值32000
添加了一个枚举。
您应该注意添加适当的错误处理。
#If VBA7 Then
Private Declare PtrSafe Function MessageBoxTimeoutA Lib "user32" (ByVal hwnd As LongPtr, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As VbMsgBoxStyle, ByVal wlange As Long, ByVal dwTimeout As Long) As Long
#Else
Private Declare Function MsgBoxTimeout Lib "user32" Alias "MessageBoxTimeoutA" (ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As VbMsgBoxStyle, ByVal wlange As Long, ByVal dwTimeout As Long) As Long
#End If
Public Enum VbMsgBoxTimeoutResult
Timeout = 32000
End Enum
'// If parameter msgTimeoutMilliseconds < 1 then the message box will not close by itself.
'// There is one additional return value to the values of VbMsgBoxResult:
'// If the message box timed out it returns 32000 (VbMsgBoxTimeoutResult.Timeout).
Public Function MsgBoxTimeout(ByVal msgText As String, Optional ByVal msgButtons As VbMsgBoxStyle = vbOKOnly, Optional ByVal msgTitle As String = vbNullString, Optional ByVal msgTimeoutMilliseconds As Long = 0) As VbMsgBoxResult
MsgBoxTimeout = MessageBoxTimeoutA(Application.hWndAccessApp, msgText, msgTitle, msgButtons, 0, msgTimeoutMilliseconds)
End Function
一个用法示例:
Select Case MsgBoxTimeout("Foo", vbYesNo + vbQuestion, "Bar", 5000)
Case VbMsgBoxTimeoutResult.Timeout
Debug.Print "MessageBox timed out."
Case vbYes
Debug.Print "User selected 'Yes'."
Case Else
Debug.Print "User selected 'No'."
End Select
【讨论】:
太棒了!这就像一个魅力......只是一个好处,你知道当这个消息弹出时我怎样才能关闭通知声音吗? 我希望这只能在操作系统 (Windows) 的设置中实现,所以它是系统范围的。 好吧,处理起来会很痛苦。顺便说一句,你能想出一种方法让'Application.hWndAccessApp
作为MsgBoxTimout
函数的一部分,这样我在跨modules/ classes
调用custom MsgBox
函数时就不必输入它了吗?
我为您的回答添加了一个包装器和一个示例调用。
酷。被选为答案!【参考方案2】:
一个选项是创建您自己的消息框。这个你可以用超时打开:
' API call for sleep function.
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Function OpenFormDialog( _
ByVal FormName As String, _
Optional ByVal TimeOut As Long, _
Optional ByVal OpenArgs As Variant = Null) _
As Boolean
' Open a modal form in non-dialogue mode to prevent dialogue borders to be displayed
' while simulating dialogue behaviour using Sleep.
' If TimeOut is negative, zero, or missing:
' Form FormName waits forever.
' If TimeOut is positive:
' Form FormName exits after TimeOut milliseconds.
Const SecondsPerDay As Single = 86400
Dim LaunchTime As Date
Dim CurrentTime As Date
Dim TimedOut As Boolean
Dim Index As Integer
Dim FormExists As Boolean
' Check that form FormName exists.
For Index = 0 To CurrentProject.AllForms.Count - 1
If CurrentProject.AllForms(Index).Name = FormName Then
FormExists = True
Exit For
End If
Next
If FormExists = True Then
If CurrentProject.AllForms(FormName).IsLoaded = True Then
' Don't reopen the form should it already be loaded.
Else
' Open modal form in non-dialogue mode to prevent dialogue borders to be displayed.
DoCmd.OpenForm FormName, acNormal, , , , acWindowNormal, OpenArgs
End If
' Record launch time and current time with 1/18 second resolution.
LaunchTime = Date + CDate(Timer / SecondsPerDay)
Do While CurrentProject.AllForms(FormName).IsLoaded
' Form FormName is open.
' Make sure form and form actions are rendered.
DoEvents
' Halt Access for 1/20 second.
' This will typically cause a CPU load less than 1%.
' Looping faster will raise CPU load dramatically.
Sleep 50
If TimeOut > 0 Then
' Check for time-out.
CurrentTime = Date + CDate(Timer / SecondsPerDay)
If (CurrentTime - LaunchTime) * SecondsPerDay > TimeOut / 1000 Then
' Time-out reached.
' Close form FormName and exit.
DoCmd.Close acForm, FormName, acSaveNo
TimedOut = True
Exit Do
End If
End If
Loop
' At this point, user or time-out has closed form FormName.
End If
' Return True if the form was not found or was closed by user interaction.
OpenFormDialog = Not TimedOut
End Function
然而,要获得消息框的全部功能需要更多的代码,但我的文章中对其进行了仔细的描述和下载:
Modern/Metro style message box and input box for Microsoft Access 2013+
代码也在 GitHub:VBA.ModernBox
【讨论】:
嗯,由于安全限制,我无法从公司笔记本电脑访问 GitHub。 哦。然后使用 Expert Exchange 链接。它附上了一个带有演示和所有代码的 zip。 我先试过了,但它不是免费的。注册需要订阅,我付不起。到目前为止,我一直只通过开源学习 VBA-Access。 该页面上有一个链接,例如“单击此处获取完整文章的访问权限”。或者,试试这个 link 到我的 OneDrive 文件夹。 感谢 Gustav 的持续支持。它确实有效。完整的代码对我来说太多了,但我会以此为基础来设计一个可行的解决方案。【参考方案3】:您可以使用Windows的user32
库中提供的MsgBoxTimeout
函数。
在模块顶部声明以下内容:
#If Win64 Then 'If the system is in 64b
Private Declare PtrSafe Function MsgBoxTimeout _
Lib "user32" _
Alias "MessageBoxTimeoutA" ( _
ByVal hwnd As LongPtr, _
ByVal lpText As String, _
ByVal lpCaption As String, _
ByVal wType As VbMsgBoxStyle, _
ByVal wlange As Long, _
ByVal dwTimeout As Long) _
As Long
#Else 'if it's in 32b
Private Declare Function MsgBoxTimeout _
Lib "user32" _
Alias "MessageBoxTimeoutA" ( _
ByVal hwnd As Long, _
ByVal lpText As String, _
ByVal lpCaption As String, _
ByVal wType As VbMsgBoxStyle, _
ByVal wlange As Long, _
ByVal dwTimeout As Long) _
As Long
#End If
然后像这样使用它:
MsgBoxTimeout 0, "This message box will be closed after 1 second ", "Automatically closing MsgBox", vbInformation, 0, 1000
一些有用的注释:
#If Win64 Then
部分是一个宏,在编译时确定要使用什么声明。事实上,在 64b 系统中,外部库声明的每个函数都应该使用 32b 系统中不存在的 PtrSafe
(指针安全)关键字。
您以毫秒为单位传递超时,这就是为什么当您希望它等待 1 秒时参数为 1000
。
【讨论】:
感谢分享,结果与我使用现有代码实现的结果没有什么不同。您的代码还在任务栏上生成了一个临时窗口。 @Lone,您需要记住 VBA 在单线程进程上运行,并且 MsgBox 旨在在关闭之前等待用户操作。实际上没有办法在同一个线程中拥有一个 MsgBox 和另一个关闭 MsgBox 的进程,您必然需要 2 个线程(一个显示 MsgBox,另一个关闭它),并且 Office 应用程序不是为此而设计的,所以不可能没有另一个进程(你提到的任务栏上的一个窗口)来做你想做的事情。 @Lone,如果我可以问,为什么这个其他进程打开几秒钟对你来说是个问题,知道它会在 1 秒后关闭? 嗯,对我来说,即使是 MsgBox 也做得很好。但是我的老板希望该应用程序的行为类似于一个 Web 界面,如果执行一个操作,则会弹出一个小通知,然后自动关闭。你的解释很有道理,让我试着在这个问题上说服我的老板。 @Lone 否则,如果这个额外的窗口确实是个问题,那么您将不得不用真正的编程语言(例如 C#,它已经提供与 Access 数据库的轻松交互)并在那里编写应用程序您将能够在同一个应用程序中拥有 2 个线程 - 关键是,迁移整个应用程序而不在任务栏中看到一个额外的程序打开一秒钟真的有意义吗?【参考方案4】:这是我的 MessageBoxTimeout 包装器,用于简化调用。我需要返回默认按钮值,而不是返回超时信息。参数和默认值的顺序遵循原来的 MsgBox 函数,以便更好地使用。
Option Compare Database
#If VBA7 Then
Private Declare PtrSafe Function MessageBoxTimeoutA Lib "user32" (ByVal hwnd As LongPtr, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As VbMsgBoxStyle, ByVal wlange As Long, ByVal dwTimeout As Long) As Long
#Else
Private Declare Function MsgBoxTimeout Lib "user32" Alias "MessageBoxTimeoutA" (ByVal hwnd As Long, ByVal lpText As String, ByVal lpCaption As String, ByVal wType As VbMsgBoxStyle, ByVal wlange As Long, ByVal dwTimeout As Long) As Long
#End If
Public Enum vbMsgBoxTimeoutResult
vbTimeout = 32000
End Enum
'// If parameter msgTimeoutMilliseconds < 1 then the message box will not close by itself.
'// The default timeout is set to 15 sec
'//
Public Function MsgBoxTimeout(ByVal msgText As String, Optional ByVal msgButtons As VbMsgBoxStyle = vbOKOnly, Optional ByVal msgTitle As String = vbNullString, Optional ByVal msgTimeoutMilliseconds As Long = 15000) As VbMsgBoxResult
'Always set minimal timeout to 1 sec
If msgTimeoutMilliseconds < 1000 Then msgTimeoutMilliseconds = 1000
MsgBoxTimeout = MessageBoxTimeoutA(Application.hWndAccessApp, msgText, msgTitle, msgButtons, 0, msgTimeoutMilliseconds)
'timeout action
If MsgBoxTimeout = VbMsgBoxTimeoutResult_Timeout Then
Dim defaultButtonFlag
'get default button
defaultButtonFlag = vbDefaultButton1
If msgButtons And vbDefaultButton4 Then defaultButtonFlag = vbDefaultButton4
If msgButtons And vbDefaultButton3 Then defaultButtonFlag = vbDefaultButton3
If msgButtons And vbDefaultButton2 Then defaultButtonFlag = vbDefaultButton2
'get only buttons information
msgButtons = msgButtons And 7
'return default value
If msgButtons = vbYesNo Then
If defaultButtonFlag = vbDefaultButton2 Then
MsgBoxTimeout = vbNo
Else
MsgBoxTimeout = vbYes
End If
ElseIf msgButtons = vbYesNoCancel Then
If defaultButtonFlag = vbDefaultButton3 Then
MsgBoxTimeout = vbCancel
ElseIf defaultButtonFlag = vbDefaultButton2 Then
MsgBoxTimeout = vbNo
Else
MsgBoxTimeout = vbYes
End If
ElseIf msgButtons = vbAbortRetryIgnore Then
If defaultButtonFlag = vbDefaultButton3 Then
MsgBoxTimeout = vbIgnore
ElseIf defaultButtonFlag = vbDefaultButton2 Then
MsgBoxTimeout = vbRetry
Else
MsgBoxTimeout = vbAbort
End If
ElseIf msgButtons = vbOKCancel Then
If defaultButtonFlag = vbDefaultButton2 Then
MsgBoxTimeout = vbCancel
Else
MsgBoxTimeout = vbOK
End If
ElseIf msgButtons = vbOKOnly Then
MsgBoxTimeout = vbOK
Else
'do nothing, already MsgBoxTimeout = vbMsgBoxTimeoutResult.vbTimeout
End If
End If
End Function
【讨论】:
以上是关于如何在 MS ACCESS 中打开定时消息框而不创建其他窗口的主要内容,如果未能解决你的问题,请参考以下文章
如何从javascript(nodejs)程序调用python,而不创建子进程