可以在显示“插入函数/函数参数”对话框时禁用VBA UDF计算吗?

时间:2014-01-01 07:23:41

标签: excel vba

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

有没有办法可以将函数设置为不计算何时用户具有“插入函数”对话框(或“函数参数”对话框,这是在已经提供函数名称时显示的内容)打开?我想只在用户在单元格中输入公式或刷新工作表时运行该功能。

2 个答案:

答案 0 :(得分:1)

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

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

如果正在使用功能向导,它将退出UDF

答案 1 :(得分:0)

在一种情况下,Charles Williams 提供的“CommandBars”解决方案会失败,错误地表明功能向导处于活动状态,而实际上并非活动状态。

当您在 Excel 中打开逗号分隔的文本文件时会发生这种情况,在这种情况下,所有打开的 Excel 工作簿都会重新计算,即使 Excel 计算设置为手动也是如此。如果您打开工作簿并使用计算速度较慢的 VBA UDF,而这些 VBA UDF 使用 CommandBars 测试在向导被认为处于活动状态时提前退出,那么这将是非常具有破坏性的。

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

仅在英文版 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