0

我是 Excel 中的宏和 vba 的新手。目前,我正在为工作中的发票模板开发 vba 宏。但是,我在除以零错误的情况下运行,我无法追踪其原因。
有时会弹出两行特定的代码。
第一部分:

    VATRMB = 0

第二部分:

    VATRMB = VATRMB + (0.0593 * (ActiveSheet.Range("I" & i).Value / (1 + 0.0593)))

Dim VATRMB 存储如下:

        Dim startRow As Integer, endRow As Integer, VATRMB As Single, VATEUR As Single, VATUSD As Single, VATRMBCell As Range, VATEURCell As Range, VATUSDCell As Range

在我看来,这些行不应该引发除以零错误。在第一种情况下,没有任何除数,而在第二种情况下,它总是正数。
你们中有人知道为什么这可能会导致错误吗?是否与 sub 被多次调用,重复使用相同的 VATRMB Dim 的事实有关?每次调用 sub 后都应该重置它,对吗?还是与我将 VATRMB 指定为 Single 的事实有关?这适用于“小”(低于 1,000,000)浮点数,对吗?

编辑:
1. 添加了用于调用 Dim storage 的确切行

2. 这是使用的完整代码块,也许有助于澄清一两件事:

'Debug.Print Tab(10); ("Items will be searched in rows " & startRow & " thru " & endRow) 'Serves for debugging and testing
For i = startRow To endRow 'Loop the following code through all rows mentioned above
    If ActiveSheet.Range("B" & i).Find("Membership") Is Nothing Then 'If nothing is returned when searching for "Membership"; i.e. if the item in this row is not a membership payment
        If Not ActiveSheet.Range("H" & i).Find("RMB") Is Nothing Then 'If the value for this item is RMB denoted
            'Debug.Print Tab(20); "Item on Row " & i & " is RMB denoted, VAT = " & ((ActiveSheet.Range("I" & i).Value / (1 + 0.0593)) * 0.0593) 'Serves for debugging and testing
            VATRMB = VATRMB + (0.0593 * (ActiveSheet.Range("I" & i).Value / (1 + 0.0593))) 'Add row's VAT to VAT total
        End If
        If Not ActiveSheet.Range("H" & i).Find("EUR") Is Nothing Then 'If the value for this item is EUR denoted
            'Debug.Print Tab(20); "Item on Row " & i & " is EUR denoted, VAT = " & ((ActiveSheet.Range("I" & i).Value / (1 + 0.0593)) * 0.0593)  'Serves for debugging and testing
            'MsgBox VATEUR + 0.0593 * ActiveSheet.Range("I" & i).Value / (1 + 0.0593)
            VATEUR = VATEUR + (0.0593 * (ActiveSheet.Range("I" & i).Value / (1 + 0.0593))) 'Add row's VAT to VAT total
        End If
        If Not ActiveSheet.Range("H" & i).Find("USD") Is Nothing Then 'If the value for this item is USD denoted
            'Debug.Print Tab(20); "Item on Row " & i & " is USD denoted, VAT = " & ((ActiveSheet.Range("I" & i).Value / (1 + 0.0593)) * 0.0593)  'Serves for debugging and testing
            VATUSD = VATUSD + (0.0593 * (ActiveSheet.Range("I" & i).Value / (1 + 0.0593))) 'Add row's VAT to VAT total
        End If
    Else 'Else, i.e. if the row contains a membership payment, then essentially nothing happens
        'Debug.Print Tab(20); ("Item on Row " & i & " is a membership payment; no VAT paid.") 'Serves for debugging and testing
    End If
Next

所以我要做的基本上是遍历发票中的所有项目,从 startRow 到 endRow,并通过解析“类型”字符串(B 列)确定该项目是否为会员付款。然后,根据是否为会员付款确定增值税,还要检查支付的货币。付款金额以浮点数形式存储在 I 列中。

4

1 回答 1

0

Not sure if this is the answer to your problems since you would need to provide the entirety of your workbooks etc to confirm. Nevertheless, we can create this type of "it should be impossible" situation with 100% reproducibility for not only Div0, but also for pretty much any error, with a line like:

VarX = 10  ' we can make this fail with Div0, Overflow or whatever

In our test, the problem is not actually the "direct" or "explicit" code where the error is reported, but rather, the error occurs elsewhere, and VBA in its infinite wisdom just happens to "report" the error in an odd way at an odd time (in fact it should not be reporting certain errors at all, see below).

Does your package involve any external executables, dll's, addins', etc?

If so, then that is likely the place to start.

If not, the error may actually be occurring directly or indirectly in the Range you are accessing, but not necessarily in the cell currently accessed.

Here is an example creating a "Div0" via a DLL accessed in VBA as an addin: Suppose you write a bit of code in another language, here Fortran (we use Implicit None everywhere, and everything is declared correctly etc.):

Pure Subroutine FortDLL(x, U)
:
Real(DP), Intent(In)    :: x
Real(DP), Intent(Out)   :: U
:
Real(DP)                :: QQ
:
:
QQ = Log10(x) ! Notice this is not the return var and has nothing to do with anything on the VBA side (or so you would think)
:
U = 10.D0   ! Notice, this is the return var, and is a constant = 10.D0
:
End Subroutine FortDLL

compile as DLL and access in the usual way.

Then suppose you have some VBA as:

Function VBAFunc(x) as Variant
:
Call FortDLL(x, U)
:
Dim VarU as Variant
:
VarU = U   ; you can replace this with VarU = 10, or whatever, and will get same result/failure

Now, if x < 0, then the DLL will crap out since Log10 is not defined for x < 0. This will throw a Fortran run time error, and depending on how you set this up, you can get it to throw a Div0, an Overflow (e.g. on the Fortran side the MaxExponent for a Double here is 1024, whereas on the VBA side it is around 308 depending on a number of things, etc. etc. etc. )

Now even though QQ has nothing at all to do with the VBA side, when the VBA code executes FortDLL(), it returns U = 10, and it actually does that part correctly.

HOWEVER, the DLL would have thrown a Div0 (or whatever you desire to create) error, and that "error message" is/can be buried in the return to the Call FortDLL().

If you are not using DLL's etc, it is possible that something comparable is happening in your "range" or elsewhere during you looping etc.

We have not performed explicit tests as to why the Dim as Currency "fix" works, but we are guessing that as Currency is a very special Type (it is actually a structured type with at least TWO fields), the "error message" may be buried in one of those "fields", and which may not be required/relevant to the size of number you are using, and obviating the crash by "fluke" (i.e. a kind of "lucky KLUDGE". You can test this by putting in numbers too large for Double, and requiring the full machinery of the Currency Type. If it is a "lucky KLUDGE", then one day when you are not around and somebody else is using your code, and they enter a number requiring the full Currency machinery, then it will likely crash, and nobody will know why.

Here is an alternate test, suppose you have the crash on a line like VarX = 10, then replace/amend as follows:

:
On Error Resume Next
VarX = 10
VarX = 10
:

... if this "works" (i.e. obviates the error/crash), then your problem is likely along the lines explained above (whether "external" or "internal"). In this case, basically, the "Div0 problem" is treated as a VBA error on the first time VarX is assigned 10, since the Error Trap is set, that "first time" catches and ignores the "DLL side error", and moves on.

... clearly this is just a TEST, not a solution.

This may also be Excel/Win/Compiler (and especially with GCC, compiler VERSION also since they have some pretty wacky things/changes sometimes) dependent and so the reproducibility and exact behaviour may vary.

于 2017-08-14T16:31:10.983 回答