简体   繁体   中英

ActiveX component can’t create object --- Excel for Mac

I'm trying to get an Excel 2011 32-bit (for Mac) spreadsheet working that contains a macro. The problem is that this macro works fine on a PC, but not on the Mac. I tried to import Tim Hall's Dictionary.cls, but it still doesn't work. Same thing for KeyValuePair.cls.

Error: Run-time error '429' ActiveX component can't create object

I'm not a programmer, so the problem is probably me, not knowing what to change to get things working. It's probably super easy for those who know what they are doing. Can anyone spend a few minutes looking at the files and tell me which parts I need to change to get this running? [I assume it does work…]

FWIW, I have tried to replace “Scripting.Dictionary” with “New.Dictionary” in two places (see below), but that didn't get it working.

Set dAttributes = CreateObject("New.Dictionary")

Set dValues = CreateObject("New.Dictionary”)

RandomiseData file:

Option Explicit
Sub GenerateResults()

Dim LO As ListObject
Dim LO2 As ListObject
Dim LR As ListRow
Dim ws As Worksheet
Dim cCount As Integer
Dim gCount As Integer
Dim dAttributes As Object
Dim dValues As Object
Dim dKey As Variant
Dim c As Range
Dim v As Variant
Dim i As Integer
Dim InsertCount As Integer

Set LO = ActiveSheet.ListObjects("Data")
If LO Is Nothing Then MsgBox "Please select the table and re-run": Exit Sub
With Application
.EnableEvents = False
.DisplayAlerts = False
.ScreenUpdating = False
End With
LO.AutoFilter.ShowAllData

Set ws = ActiveWorkbook.Sheets.Add
ws.Range("A1:C1").Value = Array("Candidate", "Attribute", "Value")
ws.ListObjects.Add xlSrcRange, Range("A1:C1"), , xlYes
Set LO2 = ws.Range("A1").ListObject

Set dAttributes = CreateObject(“New.Dictionary")
For Each c In LO.ListColumns("Attribute").DataBodyRange.Cells
If Not dAttributes.Exists(c.Value) Then dAttributes(c.Value) = c.Value
Next c

For Each dKey In dAttributes.Keys
LO.Range.AutoFilter Field:=LO.ListColumns("Attribute").Index,    Criteria1:=dKey
gCount = Evaluate("SUM(--(FREQUENCY(IF(" & LO.Name & "[Attribute]=""" & dKey & """,MATCH(" & LO.Name & "[Value]," & LO.Name & "[Value],0)),ROW(" & LO.Name & "[Value])-ROW(" & LO.Name & "[[#Headers],[Value]]))>0))")
cCount = Evaluate("SUM(--(FREQUENCY(IF(" & LO.Name & "[Attribute]=""" & dKey & """,MATCH(" & LO.Name & "[Candidate]," & LO.Name & "[Candidate],0)),ROW(" & LO.Name & "[Candidate])-ROW(" & LO.Name & "[[#Headers],[Candidate]]))>0))")
v = GenerateSplit(cCount, gCount)
Set dValues = CreateObject(“New.Dictionary")

For Each c In LO.ListColumns("Value").DataBodyRange.SpecialCells(xlCellTypeVisible)
    If Not dValues.Exists(c.Value) Then dValues(c.Value) = c.Value
Next c

InsertCount = 0
i = 1
For Each c In LO.ListColumns("Candidate").DataBodyRange.SpecialCells(xlCellTypeVisible)
TryAgain:
If i <= v(InsertCount, 2) Then
    Set LR = LO2.ListRows.Add
    LR.Range.Value = Array(c.Value, dKey, dValues.Items()(InsertCount))
    i = i + 1
Else
    i = 1
    InsertCount = InsertCount + 1
    GoTo TryAgain
End If
Next c

Next dKey
LO.AutoFilter.ShowAllData
LO.Range.Worksheet.Select

With Application
.EnableEvents = True
.DisplayAlerts = True
.ScreenUpdating = True
End With

End Sub

EDITED CODE

Option Explicit
Sub GenerateResults()

Dim LO As ListObject
Dim LO2 As ListObject
Dim LR As ListRow
Dim ws As Worksheet
Dim cCount As Integer
Dim gCount As Integer
Dim dAttributes As Object
Dim dValues As Object
Dim dKey As Variant
Dim c As Range
Dim v As Variant
Dim i As Integer
Dim InsertCount As Integer

#If Mac Then
Set dAttributes = New Dictionary
Set dValues = New Dictionary
#Else
Set dAttributes = CreateObject("Scripting.Dictionary")
Set dValues = CreateObject("Scripting.Dictionary")
#End If

Set LO = ActiveSheet.ListObjects("Data")
If LO Is Nothing Then MsgBox "Please select the table and re-run": Exit Sub
With Application
.EnableEvents = False
.DisplayAlerts = False
.ScreenUpdating = False
End With
LO.AutoFilter.ShowAllData

Set ws = ActiveWorkbook.Sheets.Add
ws.Range("A1:C1").value = Array("Candidate", "Attribute", "Value")
ws.ListObjects.Add xlSrcRange, Range("A1:C1"), , xlYes
Set LO2 = ws.Range("A1").ListObject

' Set dAttributes = CreateObject("New Dictionary")
For Each c In LO.ListColumns("Attribute").DataBodyRange.Cells
If Not dAttributes.Exists(c.value) Then dAttributes(c.value) = c.value
Next c

For Each dKey In dAttributes.Keys
LO.Range.AutoFilter Field:=LO.ListColumns("Attribute").Index,    Criteria1:=dKey
gCount = Evaluate("SUM(--(FREQUENCY(IF(" & LO.Name & "[Attribute]=""" & dKey & """,MATCH(" & LO.Name & "[Value]," & LO.Name & "[Value],0)),ROW(" & LO.Name & "[Value])-ROW(" & LO.Name & "[[#Headers],[Value]]))>0))")
cCount = Evaluate("SUM(--(FREQUENCY(IF(" & LO.Name & "[Attribute]=""" & dKey & """,MATCH(" & LO.Name & "[Candidate]," & LO.Name & "[Candidate],0)),ROW(" & LO.Name & "[Candidate])-ROW(" & LO.Name & "[[#Headers],[Candidate]]))>0))")
v = GenerateSplit(cCount, gCount)
' Set dValues = CreateObject("Scripting.Dictionary")

For Each c In  LO.ListColumns("Value").DataBodyRange.SpecialCells(xlCellTypeVisible)
    If Not dValues.Exists(c.value) Then dValues(c.value) = c.value
Next c

InsertCount = 0
i = 1
For Each c In LO.ListColumns("Candidate").DataBodyRange.SpecialCells(xlCellTypeVisible)
TryAgain:
If i <= v(InsertCount, 2) Then
    Set LR = LO2.ListRows.Add
    LR.Range.value = Array(c.value, dKey, dValues.Items()(InsertCount))
    i = i + 1
Else
    i = 1
    InsertCount = InsertCount + 1
    GoTo TryAgain
End If
Next c

Next dKey
LO.AutoFilter.ShowAllData
LO.Range.Worksheet.Select

With Application
.EnableEvents = True
.DisplayAlerts = True
.ScreenUpdating = True
End With

End Sub

New.Dictionary is not a valid classname and will fail on PC as well. Usually the construct using early binding would be:

Set obj = New Dictionary

OR using late binding:

Set obj = CreateObject("Scripting.Dictionary")

However, Mac OS does not have the Scripting Runtime librar y, so none of those things will be available to you -- Dictionary, FileSystemObject, etc.

You'll need to use a Collection or other data type in lieu of Dictionary type, or you can borrow from this other answer and implement a custom dictionary-like Class .

I tried to import Tim Hall's Dictionary.cls, but it still doesn't work. Same thing for KeyValuePair.cls.

I suspect you simply didn't know that you also need to use conditional compilation method to assign the Dictionary class on a Mac OS, and the Scripting.Dictionary class on Windows OS.

Using Conditional Compilation on Mac/Windows

Remove both of these lines:

Set dAttributes = CreateObject("New.Dictionary")
Set dValues = CreateObject("New.Dictionary")

They'll fail even in Windows, as I described above. Likewise, if you want to use this code in both Win and Mac environments, you can't use Scripting.Dictionary without taking some additional precautions to avoid errors.

You will need to implement conditional compilation using compiler directives to identify the OS. This is not overly complicated for anyone who's done it before, but it's not something that most beginners will even know is available to them, much less how to use it.

In pseudo-code, basically you're doing this:

If the operating system is Mac, then:
    Do this
ElseIf the operating system is Win, then:
    Do that instead
End If

In your code, do something like this

Assuming you've copied the KeyValuePair.cls and Dictionary.cls code from the other answer which implements the Dictionary replica in to plain text files, and imported both of the modules to your project's VBE.

#IF Mac Then
    Set dAttributes = New Dictionary
    Set dValues = New Dictionary
#Else
    Set dAttributes = CreateObject("Scripting.Dictionary")
    Set dValues = CreateObject("Scripting.Dictionary")
#End If

I would put this code just above the line:

Set LO = ActiveSheet.ListObjects("Data")

Practically speaking, as long as you place that code anywhere before you call on either of dAttributes or dValues , it doesn't matter where you put it.

This should work on both operating systems, as the Dictionary.cls mimics the Scripting.Dictionary 's methods.

NB: It's preferable to group these object assignments, rather than peppering them about haphazardly throughout the procedure, especially when you're using conditional compilation as it will be more human-readable and easier to maintain moving forward.

I see you said "I tried to import Tim Hall's Dictionary.cls, but it still doesn't work. Same thing for KeyValuePair.cls."

Tim Halls's 2016 Dictionary.cls is a complete replacement for Scripting.Dictionary and does not require KeyValuePair.cls which is a helper class I provided at https://sysmod.wordpress.com/2011/11/24/dictionary-vba-class-update/ for my 2011 Dictionary.cls. Use either his class or my pair of classes, but not both.

The advice on conditional compilation is good for writing code that works in either Mac or PC. I would suggestthat if you have your own dictionary class you don't need the Windows Scripting.Dictionary at all. I think it's better to have one class under your control than two where they might deviate in some subtle way.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM