【问题标题】:Can I disable a VBA UDF calculation when the Insert Function/Function Arguments dialogs are displayed?当显示插入函数/函数参数对话框时,我可以禁用 VBA UDF 计算吗?
【发布时间】:2021-07-27 01:28:39
【问题描述】:

我有一个 Excel VBA UDF,它执行一些昂贵的计算。目前,当用户单击“插入函数”对话框(公式栏旁边的“fx”按钮)时,Excel 会尝试运行该函数,这会导致我的代码出现问题。

当用户打开“插入函数”对话框(或“函数参数”对话框,当函数名称已提供时显示)时,有没有办法可以将函数设置为不计算?我希望该函数仅在用户在单元格中输入公式或刷新工作表时运行。

【问题讨论】:

    标签: excel vba


    【解决方案1】:

    尝试将此代码添加到函数的开头:

    If (Not Application.CommandBars("Standard").Controls(1).Enabled) Then Exit Function
    

    如果正在使用函数向导,它将退出您的 UDF

    【讨论】:

    • 谢谢查尔斯。在 Excel 2010 中工作。有一个关于为什么这样工作的问题,但在你的博客上找到了答案:)。有没有办法直接检查向导的可见性而不是检查标准控件是否被禁用?
    • 您也可以使用 Windows API 检查功能向导窗口是否正在显示并且是否具有与当前 Excel 进程相同的进程 ID,但检查命令栏要容易得多。
    【解决方案2】:

    在一种情况下,Charles Williams 提供的“CommandBars”解决方案会失败,错误地指示函数向导在未激活时处于活动状态。

    当您在 Excel 中打开逗号分隔的文本文件时会发生这种情况,在这种情况下,所有打开的 Excel 工作簿都会重新计算,即使 Excel 计算设置为手动也是如此。如果您打开的工作簿带有计算缓慢的 VBA UDF,如果认为该向导处于活动状态,则使用 CommandBars 测试提前退出,这会造成很大的破坏。

    Charles 进一步建议可以使用 Windows API 作为替代方法。我无法在其他地方找到这样的代码,所以这是我尝试实施 Charles 的建议。

    仅在英语 64 位 Excel 365 上测试。

    Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
    Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As LongPtr, ByVal lpString As String, ByVal cch As Long) As Long
    Private Declare PtrSafe Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As LongPtr) As Long
    Private Declare PtrSafe Function GetWindow Lib "user32" (ByVal hwnd As LongPtr, ByVal wCmd As Long) As Long
    
    Private Declare PtrSafe Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As LongPtr, ByRef lpdwProcessId As Long) As Long
    Private Declare PtrSafe Function GetCurrentProcessId Lib "kernel32" () As Long
    Private Const GW_HWNDNEXT = 2
    
    
    Function FunctionWizardActive() As Boolean
    
        Dim ExcelPID As Long
        Dim lhWndP As LongPtr
        Dim WindowTitle As String
        Dim WindowPID As Long
        Const FunctionWizardCaption = "Function Arguments" 'This won't work for non English-language Excel
        
        If TypeName(Application.Caller) = "Range" Then
            'The "CommandBars test" below is usually sufficient to determine that the Function Wizard is active,
            'but can sometimes give a false positive. Example: When a csv file is opened (via File Open) then all
            'active workbooks are calculated (even if calculation is set to manual!) with
            'Application.CommandBars("Standard").Controls(1).Enabled being False
            'So apply a further test using Windows API to loop over all windows checking for a window with title "Function  Arguments", checking also the process id.
            If Not Application.CommandBars("Standard").Controls(1).Enabled Then
                ExcelPID = GetCurrentProcessId()
                lhWndP = FindWindow(vbNullString, vbNullString) 'PARENT WINDOW
                Do While lhWndP <> 0
                    WindowTitle = String(GetWindowTextLength(lhWndP) + 1, Chr$(0))
                    GetWindowText lhWndP, WindowTitle, Len(WindowTitle)
                    WindowTitle = Left$(WindowTitle, Len(WindowTitle) - 1)
                    If WindowTitle = FunctionWizardCaption Then
                        GetWindowThreadProcessId lhWndP, WindowPID
                        If WindowPID = ExcelPID Then
                            FunctionWizardActive = True
                            Exit Function
                        End If
                    End If
                    lhWndP = GetWindow(lhWndP, GW_HWNDNEXT)
                Loop
            End If
        End If
    End Function
    

    使用该功能,您可以使用以下代码修改慢速 VBA UDF:

    If FunctionWizardActive() Then Exit Function
    

    【讨论】:

      猜你喜欢
      • 2015-05-24
      • 1970-01-01
      • 2021-07-24
      • 1970-01-01
      • 1970-01-01
      • 2014-07-09
      • 1970-01-01
      • 2017-06-22
      • 1970-01-01
      相关资源
      最近更新 更多