【问题标题】:Custom Function Works in VBA but not when called in a Excel Cell自定义函数在 VBA 中有效,但在 Excel 单元格中调用时无效
【发布时间】:2017-04-24 01:08:26
【问题描述】:

我已经编写了一个自定义 VBA 函数,当我从 VBA 中调用它时该函数将起作用。例如

Sub test()

    Debug.Print calculate_acr_bands_data("recommended_acr", "22/11/2014", 2.67, "B23:C27", 50, 2.14, 1, 0.107, 3.89, "22/02/2021")

End Sub

输出 ...

0.01

但是,当我从 Excel 工作簿的单元格中调用完全相同的函数时。我收到错误“此公式中使用的值的数据类型错误”。这没有任何意义,因为我刚刚证明该公式适用于 VBA 中完全相同的输入。

如果您想查看源代码,我已将我的 repo 上传到我的 GitHub 帐户...

https://github.com/Joshua-W-Adams/vba-wall-loss-vs-time

感谢您的帮助。

更新

功能已在下方发布...

'Description: Calculate database of wall loss vs time information so relevant parameters can be returned
Public Function calculate_acr_bands_data(return_parameter As String, last_inspection_date As Date, last_inspection_date_wall_loss As Double, _
    acr_bands_array_text As String, nominal_wall_thickness As Double, _
    minimum_allowable_wall_thickness As Double, current_acr As Double, actual_cr As Double, current_rl As Double, current_end_of_life As Date) As Variant

    'Create copy of data range so nominal wall thickness value is not overriden
    Dim acr_bands_array As Range:                   Set acr_bands_array = ThisWorkbook.Worksheets("Wall_Loss_Vs_Time_Graph").Range(acr_bands_array_text)
    Dim n As Integer:                               n = 0
    Dim acr_bands_array_size As Integer:            acr_bands_array_size = acr_bands_array.Rows.Count
    Dim return_data_array() As BAND_ARRAY_CLASS
    Dim current_band_position As Integer
    Dim recommended_acr As Double
    Dim recommended_rl As Double
    Dim forecast_wall_loss As Double
    Dim recommended_end_of_life As Date

    'Update band array with fail FFS wall thickness of current cml
    acr_bands_array(acr_bands_array.Rows.Count, 1) = nominal_wall_thickness - minimum_allowable_wall_thickness

    'Determine current band that the last inspection date wall loss falls into
    For n = 1 To acr_bands_array_size - 1

        If last_inspection_date_wall_loss < acr_bands_array(n + 1, 1) Then

            current_band_position = n
            Exit For

        End If

    Next n

    'Push first row to array (last inspection date details)
    return_data_array = push_to_array(return_data_array, "Band Data Points", Format(last_inspection_date, "Short Date"), last_inspection_date_wall_loss, acr_bands_array(current_band_position, 2))

    'Debug.Print return_data_array(0).graph_name
    'Debug.Print return_data_array(0).date_value
    'Debug.Print return_data_array(0).wall_loss
    'Debug.Print return_data_array(0).acr

    'Push all corrosion rate band data
    If n <> 0 Then 'If a hole out is not detected

        For n = current_band_position To acr_bands_array_size - 1

            return_data_array = push_to_array(return_data_array, _
                "Band Data Points", _
                Format(DateAdd("d", (acr_bands_array(n + 1, 1) - return_data_array(UBound(return_data_array)).wall_loss) / acr_bands_array(n, 2) * 365, return_data_array(UBound(return_data_array)).date_value), "Short Date"), _
                acr_bands_array(n + 1, 1), _
                acr_bands_array(n, 2))

        Next n

    End If

    'Push data for current wall loss as of today
    For n = 1 To UBound(return_data_array) - 1

        If Now() < return_data_array(n + 1).date_value Then

            return_data_array = push_to_array(return_data_array, _
                "Band Data Points", _
                Format(Now(), "Short Date"), _
                return_data_array(n).acr * DateDiff("d", return_data_array(n).date_value, Now()) / 365 + return_data_array(n).wall_loss, _
                return_data_array(n).acr)

                'Dim rsize As Integer
                'rsize = UBound(return_data_array)
                'Debug.Print return_data_array(rsize).graph_name
                'Debug.Print return_data_array(rsize).date_value
                'Debug.Print return_data_array(rsize).wall_loss
                'Debug.Print return_data_array(rsize).acr

            Exit For

        End If

    Next n

    recommended_acr = (return_data_array(UBound(return_data_array) - 1).wall_loss - return_data_array(UBound(return_data_array)).wall_loss) _
                        / (return_data_array(UBound(return_data_array) - 1).date_value - return_data_array(UBound(return_data_array)).date_value)
    forecast_wall_loss = return_data_array(UBound(return_data_array)).wall_loss
    recommended_end_of_life = return_data_array(UBound(return_data_array) - 1).date_value
    recommended_rl = DateDiff("d", return_data_array(UBound(return_data_array) - 1).date_value, Now()) / 365

    Debug.Print recommended_acr
    Debug.Print forecast_wall_loss
    Debug.Print recommended_end_of_life
    Debug.Print recommended_rl

    'Debug.Print recommended_rl

    'Add additional graph data for reference
    return_data_array = push_to_array(return_data_array, "Today", DateValue(Format(Now(), "Short Date")), 0, 0)
    return_data_array = push_to_array(return_data_array, "Today", DateValue(Format(Now(), "Short Date")), nominal_wall_thickness, 0)

    return_data_array = push_to_array(return_data_array, "Recommended RL", DateValue(Format(recommended_end_of_life, "Short Date")), 0, 0)
    return_data_array = push_to_array(return_data_array, "Recommended RL", DateValue(Format(recommended_end_of_life, "Short Date")), nominal_wall_thickness - minimum_allowable_wall_thickness, 0)

    return_data_array = push_to_array(return_data_array, "Recommended ACR", DateValue(Format(Now(), "Short Date")), forecast_wall_loss, recommended_acr)
    return_data_array = push_to_array(return_data_array, "Recommended ACR", DateValue(Format(recommended_end_of_life, "Short Date")), nominal_wall_thickness - minimum_allowable_wall_thickness, 0)

    return_data_array = push_to_array(return_data_array, "Current RL", current_end_of_life, 0, 0)
    return_data_array = push_to_array(return_data_array, "Current RL", current_end_of_life, nominal_wall_thickness, 0)

    return_data_array = push_to_array(return_data_array, "Current ACR", DateValue(Format(last_inspection_date, "Short Date")), last_inspection_date_wall_loss, current_acr)
    return_data_array = push_to_array(return_data_array, "Current ACR", DateValue(Format(current_end_of_life, "Short Date")), nominal_wall_thickness, current_acr)

    return_data_array = push_to_array(return_data_array, "Actual CR", DateValue(Format(last_inspection_date, "Short Date")), last_inspection_date_wall_loss, actual_cr)
    return_data_array = push_to_array(return_data_array, "Actual CR", DateAdd("d", (nominal_wall_thickness - last_inspection_date_wall_loss) / actual_cr * 365, last_inspection_date), nominal_wall_thickness, 0)

    return_data_array = push_to_array(return_data_array, "Actual RL", DateAdd("d", (nominal_wall_thickness - last_inspection_date_wall_loss) / actual_cr * 365, last_inspection_date), 0, 0)
    return_data_array = push_to_array(return_data_array, "Actual RL", DateAdd("d", (nominal_wall_thickness - last_inspection_date_wall_loss) / actual_cr * 365, last_inspection_date), nominal_wall_thickness, 0)

    return_data_array = push_to_array(return_data_array, "Fail FFS", DateValue(Format(last_inspection_date, "Short Date")), nominal_wall_thickness - minimum_allowable_wall_thickness, 0)
    return_data_array = push_to_array(return_data_array, "Fail FFS", IIf(recommended_end_of_life > current_end_of_life, recommended_end_of_life, current_end_of_life), nominal_wall_thickness - minimum_allowable_wall_thickness, 0)

    return_data_array = push_to_array(return_data_array, "Nominal Wt", last_inspection_date, nominal_wall_thickness, 0)
    return_data_array = push_to_array(return_data_array, "Nominal Wt", IIf(recommended_end_of_life > current_end_of_life, recommended_end_of_life, current_end_of_life), nominal_wall_thickness, 0)

    'Return requested parameter to user
    If return_parameter = "database" Then

        calculate_acr_bands_data = return_data_array

    ElseIf return_parameter = "recommended_acr" Then

        calculate_acr_bands_data = Round(recommended_acr, 2)

    ElseIf return_parameter = "forecast_wall_loss" Then

        calculate_acr_bands_data = Round(forecast_wall_loss, 2)

    ElseIf return_parameter = "recommended_rl" Then

        calculate_acr_bands_data = Round(recommended_rl, 2)

    Else

        calculate_acr_bands_data = Null

    End If

End Function

'Description: Pass array and values to push
Function push_to_array(bands_array As Variant, graph_name As String, date_value As Date, wall_loss As Double, acr As Double) As Variant

    Dim size As Integer: size = UBound(bands_array) + 1

    ReDim Preserve bands_array(size)

    Set bands_array(size) = New BAND_ARRAY_CLASS

    With bands_array(size)
        .graph_name = graph_name
        .wall_loss = wall_loss
        .date_value = date_value
        .acr = acr
    End With

    push_to_array = bands_array

End Function

【问题讨论】:

  • 我的猜测是,您在测试中作为字符串传递的日期实际上是日期类型。
  • 如果您将代码发布到问题中,我们将有机会查看导致您的问题的原因。 (没有代码,我们只能猜测。)
  • 能贴一下calculate_acr_bands_data函数的签名吗?
  • 哪一行给出了"A value used in this formula is of the wrong data type" 错误信息? (如有必要,在可执行代码的第一行设置断点,然后从 Excel 调用 UDF,然后单步执行代码,直到生成。)
  • 除了调用 UDF 的单元格之外,您不能为任何单元格赋值(其值是通过设置函数的返回值来赋值的)。

标签: vba excel


【解决方案1】:

您的代码不能作为 UDF 工作,因为 UDF 不能为除调用 UDF 的单元格之外的任何单元格分配值(并且其值是通过设置函数的返回值来分配的)。

所以,因为你的线

acr_bands_array(acr_bands_array.Rows.Count, 1) = nominal_wall_thickness - minimum_allowable_wall_thickness

正在尝试修改 Excel 工作表的状态,它将崩溃,并且应该向调用函数的单元格返回 #VALUE! 错误。


我不确定您为什么会收到 "A value used in this formula is of the wrong data type" 错误 - UDF 返回任何类型的错误消息(仅返回错误值)是非常不寻常的。

【讨论】:

  • 谢谢,通过修改上面的代码来创建一个虚拟数组来存储和修改数据,而不是在工作表上引用和修改数组,已经确认了这一点。你给我的另一个有用的提示是在我的函数中添加断点,这样我就可以隔离导致错误的代码行,当我从 Excel 单元格调用函数时,我不知道该怎么做。非常感谢您的帮助。
猜你喜欢
  • 2015-07-31
  • 1970-01-01
  • 2010-09-22
  • 1970-01-01
  • 2013-09-24
  • 1970-01-01
  • 2021-09-07
  • 2017-04-05
  • 2015-07-26
相关资源
最近更新 更多