【问题标题】:Why would VBA TypeOf operator fail为什么 VBA TypeOf 运算符会失败
【发布时间】:2013-09-01 06:44:05
【问题描述】:

我这几天一直在与 Excel 2007 问题作斗争。下面列出了我能想到的所有可能相关的事实:

  1. IDetailSheet 是一个在 VBA 项目中声明的类,具有多种方法,并且在其类初始化器中引发错误,因此无法实例化(使其抽象)。

  2. Option Explicit在所有模块中设置。

  3. VBA 项目中的十个工作表实现 IDetailSheet 并干净地编译(整个项目也是如此)。

  4. CDetailSheets 是在 VBA 项目中声明的一个类,它包装了一个 Collection 对象并将 Collection 对象公开为 IDetailSheet 的一个 Collection。它还公开了一些额外的方法来对所有集合成员执行 IDetailSheet 的某些方法。

  5. 在其 Class 初始化程序(从 Workbook_Open 事件处理程序调用并分配给全局变量)中,CDetailSheet 执行以下代码填充私有集合 DetailSheets

    Dim sht as EXCEL.WorkSheet
    For Each sht in ActiveWorkbook.Worksheets
      If TypeOf sht is IDetailSheet Then
        Dim DetailSheet as IDetailSheet
        Set DetailSheet = sht
        DetailSheets.Add DetailSheet, DetailSheet.Name
      End If
    Next sht
    
  6. 在某些功能区回调中,会运行以下代码:

       If TypeOf ActiveWorkbook.ActiveSheet is IDetailSheet Then
          Dim DetailSheet as IDetailSheet
          Set DetailSheet = ActiveWorkbook.ActiveSheet
          DetailSheet.Refresh  *[correction]*
       End If
    
  7. 在确定存在其他稳定性问题(最初有几十个)之后,已从工作簿中删除所有 ActiveX 控件。已创建 Fluent 界面功能区来替换最初与 ActiveX 控件关联的功能。

  8. 企业模板中有一个 Hyperion 加载项,但本工作簿中没有使用它。

说到底,运行工作簿时出现以下症状:

  • TypeOf Is 在 CDetailSheets 初始化程序中识别任意数量的 IDetailSheet 实例,从 1(最常见)到偶尔的 2 或 3。永远不会为零,永远不会超过 3,而且肯定永远不会全部 10 个可用。 (并不总是同一个,尽管靠近片场的前面似乎会增加被认出的可能性。)
  • 在 CDetailSheets 初始化程序中发现的任何 IDetailSheet 实现实例(据我所知,只有这样的实例)在功能区回调中也被 TypeOf ... Is 识别。

谁能解释为什么大多数 TypeOf ... Is 操作都失败了?或者如何解决这个问题?

我已经求助于手动创建 v-tables(即大丑 Select Case ... End Select 语句)来使功能正常工作,但实际上我发现在旁边有我的名字相当尴尬这样的代码。除此之外,我可以看到这是未来维护的噩梦。

考虑到这可能是一个过时的 p 代码问题,我从扩展的 XLSM zip 中删除了 Project.Bin 文件,然后手动将所有 VBA 代码重新导入。没有任何变化。我还尝试将项目名称添加到 IDetailSheet 的所有用法中以使其成为 miFab.IDetailSheet,但再次无济于事。 (miFab 是项目名称。)

【问题讨论】:

  • 您的问题中是否有ForeachEndIf 拼写错误?
  • 是的,但在 VBA 中它将是 For EachEnd If
  • blog entry 建议将ImplementsWorksheet 对象一起使用会导致不稳定
  • 这当然是一个有趣的问题,而且我以前从未遇到过。如果有任何方法可以重构您的 VBA 应用程序以使用封装而不是继承,您可以定义一堆不扩展/实现任何内容的裸用户定义类型或类,并将它们声明为每个中的私有字段工作表,具有所需的属性/方法/功能来跟踪您需要的数据。这很丑,但它可能工作,因为问题似乎专门针对实现接口的工作表。
  • 不是真的...?在您的情况下,“VBA 项目中的十个工作表实现了 IDetailSheet”。我不是这里唯一认为特别是在 Worksheet 对象上使用继承/接口是问题的原因的评论者。这就是为什么我建议您创建一个实现 IDetailSheet 的每张普通旧类模块,并将 those 放入您的集合中。您可以通过多种方式编写代码以确定哪个IDetailSheet 实例属于哪个工作表,而无需使用TypeOfSelect ... End Select

标签: vba excel excel-2007


【解决方案1】:

您可以通过多种方式使用 CallByName 作弊。您将不得不以一种或另一种方式解决这个错误。

一个简单的脏例子

每个以实施行开头的工作表都应该有一个公共的 GetType 函数。 我将“TestSheet”子附加到功能区上的按钮上。它将返回的类型名称放在单元格 A1 中以演示功能。

模块1

'--- Start Module1 ---
Option Explicit

Public Sub TestSheet()
  Dim obj As Object
  Set obj = ActiveSheet
  ActiveSheet.[A1] = GetType(obj)
End Sub

Public Function GetType(obj As Object) As String
  Dim returnValue As String
  returnValue = TypeName(obj)
  On Error Resume Next
  returnValue = CallByName(obj, "GetType", VbMethod)
  Err.Clear
  On Error GoTo 0
  GetType = returnValue
End Function
'--- End Module1 ---

表 1

'--- Start Sheet1 ---
Implements Class1
Option Explicit

Public Function Class1_TestFunction()
End Function

Public Function GetType() As String
    GetType = "Class1"
End Function
'--- End Sheet1 ---

【讨论】:

  • 我以为 VBA 有一个 CallByNae 函数,但上周我找不到它。不过我现在有了;谢谢。
  • 不客气。我认为您可能可以通过 CallByName 函数完成所有工作,但这可能会令人讨厌且难以编写。这样,您仍然可以将工作表转换为相关接口,或者如果 implements 导致其他问题,则检索包装对象。
  • 我知道这已经足够了,并且比我上周构建的不那么混乱解决方法,但由于某种原因我当时找不到它。
【解决方案2】:

如果您不信任 TypeOf,请继续努力并忽略错误:

Dim sht as EXCEL.WorkSheet
For Each sht in ActiveWorkbook.Worksheets
  'If TypeOf sht is IDetailSheet Then
  Dim DetailSheet As IDetailSheet
  On Error Resume Next
  Set DetailSheet = sht
  On Error GoTo 0
  If Not DetailSheet Is Nothing Then
    DetailSheets.Add DetailSheet, DetailSheet.Name
  End If
Next sht

如果这个不起作用,至少在那个时候工作表真的不是IDetailSheet

【讨论】:

  • 这是我尝试的第一个解决方法;写原帖前几天。我最好的猜测是 STA 中存在某种时间问题/竞争条件。
【解决方案3】:

我在发布与TypeOf fails to work with Excel workbook's ActiveSheet that implements interface类似的问题后发现了这个问题

我没有明确的解释,但我认为我有一个解决方法。

我怀疑这是因为 [代码] 在 Sheet1 或 Chart 上实现了一个接口,并且正在扩展 Sheet1/Chart1,但 Sheet1 已经在扩展 Worksheet(并且 Chart1 已经在扩展 Chart)。

在我的测试中,我可以通过首先访问工作表的属性来强制 VBA 返回 TypeOf 的实际值。这意味着,做一些丑陋的事情,比如:

'Explicitly access ThisWorkbook.ActiveSheet.Name before using TypeOf
If TypeOf ThisWorkbook.Sheets(ThisWorkbook.ActiveSheet.Name) Is PublicInterface Then

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-06
    • 1970-01-01
    • 1970-01-01
    • 2017-10-11
    相关资源
    最近更新 更多