在较大的项目上使用接口时出现错误 [英] Bug when using interfaces on larger projects

查看:103
本文介绍了在较大的项目上使用接口时出现错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于较大的VBA项目(40,000行代码),我不能正确使用接口,因为应用程序(我主要使用Excel)会经常崩溃.显然,这是因为代码无法保持编译状态(据我了解,VBA代码被编译为P代码,以后再进行解释).当VBA项目受密码保护时,我主要会遇到这种情况.

For larger VBA projects (40,000+ lines of code) I cannot properly use interfaces because the Application (I mainly use Excel) will crash quite often. Apparently, this is because the code cannot remain compiled (from my understanding VBA code gets compiled to P-code which is later interpreted). I mainly get this behavior when the VBA Project is password protected.

调试/编译"菜单几乎永远不会灰显".当我打开托管文档时:

The Debug/Compile menu is almost never "greyed out" when I open the hosting document:

This article describes the same behavior. Go to section 2.3

例如:
IClass界面:

For example:
IClass interface:

Option Explicit

Public Property Get SomeProperty() As Double
End Property

Class1:

Option Explicit

Implements IClass

Private Property Get IClass_SomeProperty() As Double
    IClass_SomeProperty = 0
End Property

标准模块中的代码

Option Explicit

Sub TestInterface()
    Dim obj As IClass
    
    Set obj = New Class1
    Debug.Print obj.SomeProperty 'Crashes here on large projects only
End Sub

如预期的那样,Debug.Print obj.SomeProperty行工作正常,如果项目较小,则在立即"窗口中显示0.但是,在大型项目中,调用此行时,应用程序崩溃.不能达到IClass_SomeProperty(登录到文件可以澄清这一点).

As expected the Debug.Print obj.SomeProperty line works fine and prints 0 in the Immediate window if the project is small. However, on a large project the application crashes when this line is called. The IClass_SomeProperty is not reached (logging to a file clarifies this).

与上述文章一样,有一些方法可以暂时避免该问题:

As in the above-mentioned article, there are ways to temporarily avoid the issue:

  1. 重新编译可以解决此问题(并非总是如此),但是崩溃可能在下次打开文档时发生,或者可能会使事情持续几天(假定每天都打开文件)
  2. 在当前选项1不工作,停用一切<4>用查找/替换窗口,然后编译和然后重新激活该语句后面在整个项目中的语句.再说一次,这可以工作几个小时或几天,但是不可避免地会在不久的将来发生崩溃

由于VBA项目受密码保护,并且由于许多人正在使用这些具有VBA功能的文档(在我的情况下为工作簿),因此应用临时修补程序根本无济于事.

Since the VBA Project is password protected and because many people are using these VBA-capable documents (Workbooks in my case), applying the temporary fixes does not help at all.

我发现避免崩溃并仍然获得接口好处的唯一方法是使用条件编译.基本上,我仅将接口用于开发,然后切换到后期绑定进行生产.显然,这会带来很多麻烦.

The only way that I've found to avoid the crashes and still get the benefits of the interfaces is to use conditional compilation. Basically, I use interfaces only for development and then switch to late-binding for production. Obviously, this comes with a lot of headaches.

上面的示例变为:
Class1:

The example above becomes:
Class1:

Option Explicit

#Const USE_INTERFACES = True

#If USE_INTERFACES Then
Implements IClass
#End If

Private Property Get IClass_SomeProperty() As Double
    IClass_SomeProperty = Me.SomeProperty
End Property

Public Property Get SomeProperty() As Double
    SomeProperty = 0
End Property

请注意,所有接口方法都必须复制并公开,以便可以选择后期绑定.

Notice that all interface methods must be duplicated and made public so that late-binding is an option.

标准模块中的代码

Option Explicit

#Const USE_INTERFACES = True

Sub TestInterface()
    #If USE_INTERFACES Then
        Dim obj As IClass
    #Else
        Dim obj As Object
    #End If
    
    Set obj = New Class1
    Debug.Print obj.SomeProperty
End Sub

开发新功能时,请按照以下步骤操作:

When developing new features, I follow these steps:

  1. 使用查找/替换"将所有出现的事件都转为#Const USE_INTERFACES = True
  2. 添加新功能
  3. 转动#Const USE_INTERFACES = False,以便代码在后期绑定上运行并且不会崩溃
  1. turn #Const USE_INTERFACES = True for all occurrences using Find/Replace
  2. add new features
  3. turn #Const USE_INTERFACES = False so that code runs on late-binding and doesn't crash


我已经经历了至少3年的错误.如果可以的话,我显然会避免有条件的编译方法.


I've been experiencing this bug for at least 3 years. I would obviously avoid the conditional compilation workaround if I could.

是否可以在不访问 VBA项目对象模型的情况下保持VBA项目的编译状态(例如,打开文档时运行一个过程)?对我来说,打开对VBA项目对象模型的信任访问权限是不可行的.

Is there a way to keep the VBA Project compiled (let's say running a procedure when opening the document) without access to the VBA project object model? It is not an option for me to have the Trust access to the VBA project object model turned on.

除非您碰巧手头有一个大型VBA项目,否则我不容易重新创建此错误.

I appreciate this bug is not easy to re-create unless you happen to have a large VBA project at hand.

编辑1

@PEH在评论中提出的一个很好的观点:此问题适用于xlsmxlsb文件(Excel).

A nice point raised by @PEH in the comments: this issue is applicable for both xlsm and xlsb files (Excel).

推荐答案

我没有40K线性项目进行测试,但是众所周知,VBE调试器的编辑并继续 >功能会破坏内部存储流,从而导致重影断点".

I don't have a 40K-liner project to test it, but it is known that the VBE debugger's edit-and-continue feature can corrupt the internal storage streams, resulting in "ghost breakpoints".

如果有足够的时间和足够的调试器会话(以及适当数量的pixie灰尘),则无论有无接口,VBE本身都会破坏VBA项目.

The VBE itself will corrupt a VBA project, given enough time and enough debugger sessions (and the right amount of pixie dust) - with or without interfaces involved.

自从我意识到VBA具有此功能以来,我就一直在针对VBA中的接口进行编码,而我发现的唯一问题是直接由Implements语句引起的,是如果您制作了文档模块(例如Sheet1ThisWorkbook),实现了一个接口,那么您将100%破坏您的项目并使Excel崩溃.但是使用普通的用户类别?不,这些从来没有问题.

I've been coding against interfaces in VBA ever since I've realized that VBA had that capability, and the only problem I've found that was directly caused by Implements statements, was that if you make a document module (e.g. Sheet1, or ThisWorkbook), implement an interface, then you're 100% going to corrupt your project and crash Excel. But with normal user classes? Nope, never had a problem with those.

我绝对会认为编辑并继续是VBA项目中发生的任何流损坏的主要嫌疑人-如果内部ITypeInfo在类型为Implements的接口时被损坏,则这可能是由于 edit-and-continue 的错误以及Implements语句"cause it"引起的.真的只是一种症状.

I would definitely consider edit-and-continue the primary suspect for any stream corruption happening in a VBA project - if the internal ITypeInfo gets corrupted when the type Implements an interface, then that would be because of the bug with edit-and-continue, and the Implements statement "causing it" is really just a symptom.

项目规模也是一个重要因素:40K LoC确实是一个非常大的VBA项目,并且大型VBA项目也倾向于更容易遭到破坏.通常可以通过定期删除+导出所有代码文件并将其重新导入到项目中来缓解这种情况(我猜这将强制对内部存储进行干净的重建")-如果您的大型项目处于源代码之下控制,那么该应该定期发生.

Project size is also an important factor: 40K LoC is indeed a very large VBA project, and large VBA projects have a tendency to more easily get corrupted too. This can usually be mitigated by regularly removing+exporting all code files and re-importing them back into the project (which forces a "clean rebuild" of the internal storage, I'm guessing) - if your very large project is under source control, then this should be happening regularly.

VBE不希望您编写OOP.它正在积极地与之抗争:它希望您将尽可能多的代码塞入尽可能少的模块中,并且由于其导航工具客观地令人讨厌,因此您不会找到所有实现".命令以快速找到您接口的具体实现,因此,VBE可以定义"带您进入一个空的方法存根-猜猜是什么,去定义" Microsoft Visual Studio 2019中的功能完全相同(尽管它也确实为您提供了实施").

The VBE does not want you to write OOP. It's actively fighting it: it wants you to cram as much code as possible into as few modules as possible, and since its navigation tooling objectively sucks, you don't get a "find all implementations" command to quickly locate the concrete implementations for your interfaces, so yes the VBE's "go to definition" takes you to an empty method stub - guess what, "go to definition" in Microsoft Visual Studio 2019 does exactly the same (it does give you "go to implementation" too though).

我不建议在没有Rubberduck的情况下在VBA中编写OOP以辅助导航(以及其他所有操作),但是具有讽刺意味的是,Rubberduck在具有大量后期绑定的非常大的旧代码库中表现不佳(是否隐含).

Writing OOP in VBA without Rubberduck to assist with navigation (and everything else) isn't something I would recommend, but then irony has it that Rubberduck doesn't perform very well with very large legacy code bases with lots of late binding (implicit or not).

如果项目规模与问题有关,那么使用条件编译实际上会使事情变得更糟,这会使项目变得更大,然后让Rubberduck无法看到".几乎一半的代码(无法解析后期绑定的成员调用,因此我们无法跟踪在什么地方使用了什么),...这实际上使代码瘫痪了.

If project size is related to the problem, then using conditional compilation is actually making things worse, by making the project even larger, and then making Rubberduck fail to "see" pretty much half of the code (late bound member calls can't be resolved, so we lose track of what's being used where), ...which essentially cripples it.

我没有解决方案,只是预感 edit--continue 可能在此背后,因为它会即时重写p代码的大部分,并且这已经众所周知引起问题.如果我的预感是正确的,那么请定期导出&重新导入所有代码文件应有助于防止损坏(然后,如果任何事情无法挽回地损坏,则可以很容易地将项目置于源代码控制之下).执行此操作时,请尽可能避免在文档模块中隐藏代码,并且从不执行任何操作都应使工作表/文档模块Implements具有任何接口. Rubberduck的工具可以快速&轻松地一次将多个代码文件导出/从给定文件夹导入/导出.

I don't have a solution, only a hunch that edit-and-continue is very likely behind this, since it rewrites chunks of p-code on the fly, and that is already known to be causing problems. If my hunch is correct, then regularly exporting & reimporting all code files should help keep the corruption at bay (and then it makes it easy to put the project under source control, should anything ever become irreparably broken). Avoid code-behind in document modules as much as possible when you do this, and whatever you do NEVER make a worksheet/document module Implements any interface. Rubberduck has tooling to quickly & easily export/import multiple code files at once to/from a given folder.

这篇关于在较大的项目上使用接口时出现错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆