Microstation V8i get tag element crashes Excel macro

Hi,

I have been trying to write an Excel VBA macro which:

1. opens all .dgn files from selected location

2.scans for all cell elements in active model

3. builds an array with found cells

4. checks if cells have tags

5. if 4 true then builds arrays with tag elements for each cell

The code below works fine until I try to access any array with tag elements. Any operation like reading or writing of any tag element array causes Excel crash every time. Excel stops working and restarts with any other error message. The tag element arrays are created correctly because I can see them in watch window while I stop program before the "crashing" program line. There are some suspicious value in the tag element array like "Subtype- attempting to perform a non-graphical operation on graphical element". Any help would be appreciated.

Option Explicit
Dim directory As String, fileName As String

Sub ImportStart_Click() 'PROGRAM START
  ExportTexts
End Sub

Sub ExportTexts()
'On Error Resume Next
directory = GetFolder("c:\") & "\"
fileName = Dir(directory & "*.dgn")
Do While fileName <> ""
    obslugaDGN (directory & fileName)
    fileName = Dir
Loop
MsgBox "Done", vbOKOnly
End Sub

Sub obslugaDGN(plik As String)
Dim myDGN As DesignFile
Dim oAL As ApplicationObjectConnector
Set oAL = New MicroStationDGN.ApplicationObjectConnector
Set myDGN = oAL.Application.OpenDesignFile(plik, False)
Dim ee As ElementEnumerator
Dim es As ElementScanCriteria
Dim elArray() As element
Dim i, j As Integer

Set es = New ElementScanCriteria
es.ExcludeAllTypes
es.IncludeType msdElementTypeCellHeader
es.IncludeType msdElementTypeSharedCell
Set ee = ActiveModelReference.Scan(es)

elArray = ee.BuildArrayFromContents

For i = LBound(elArray) To UBound(elArray)
    If (elArray(i).HasAnyTags) Then
        Dim oTags() As TagElement
        oTags = elArray(i).GetTags()
        For j = LBound(oTags) To UBound(oTags)
            If oTags(j).TagDefinitionName = "TAG" Then  'here Excel stops working and restarts
                'procedure....
            End If
        Next j
    End If
Next i
myDGN.Close
Set myDGN = Nothing
Set ee = Nothing
End Sub


Function GetFolder(strPath As String) As String ' function gets folder picked by user
Dim fldr As FileDialog
Dim sItem As String
Set fldr = Application.FileDialog(msoFileDialogFolderPicker)
With fldr
    .Title = "Select a Folder"
    .AllowMultiSelect = False
    .InitialFileName = strPath
    If .Show <> -1 Then GoTo NextCode
    sItem = .SelectedItems(1)
End With
NextCode:
GetFolder = sItem
Set fldr = Nothing
End Function

Thanks,

Darius

Parents Reply Children
  • "Can't assign to array" Compile error

    Thanks,

    Darius

  • I can't speak for others but I have never had success using GetTags(). Instead I prefer not to scan for specific host elements (in your case, Normal and shared cells) but instead target the tag elements themselves i.e. instead of:

    es.IncludeType msdElementTypeCellHeader
    es.IncludeType msdElementTypeSharedCell

    try using:

    es.IncludeType msdElementTypeTag

    This will unlikely work with your existing code as is so you will probably need to edit your loop code suit the new scan element type.

  • Yes, I could use msdElementTypeTag but I do not know how to group all tags by cell they are attached to. There is the parent parameter but it seems to be empty. Please note that one drawing may have 2 or more the same cells with the same tag set attached. The goal is to update all tags of the tagset if the value of one tag meets my criteria.

  • Unknown said:
    Dim oTags() As TagElement to Dim oTags() As Variant

    No! It makes the code worse and dirty. It's defined that GetTags method returns TagElement(), so if there is an error, it will be not caused by correctly declared variable.

    To use Variant is (with a couple of exceptions) seriously bad practice and always lead to worse code.

    Unknown said:
    I can't speak for others but I have never had success using GetTags().

    I recall I used it several times in the past, both in VBA and C#, and it works fine.

    Unknown said:
    Instead I prefer not to scan for specific host elements (in your case, Normal and shared cells) but instead target the tag elements themselves

    It's different approach that provides different results (existing elements vs. existing tags) and it depends on a local conditions and requirements what is better.

    Unknown said:
    Any help would be appreciated.

    Are you sure the tag is attached to cell header? I assume it's attached to a graphics element inside the cell. Can you provide an example of the file?

    One more question, not related to the discussed issue: Is there any reason to use .BuildArrayFromContents? It increases memory requirements a lot.

    With regards,

      Jan

  • >>I assume it's attached to a graphics element inside the cell

    You are right. I found those criteria online and I am not sure they are correct.

    >>Is there any reason to use .BuildArrayFromContents?

    No there is no particular reason. As well I found it online. I tried also regular Do While oEnumerator.MoveNext with the same devastating result. As I said both arrays with cells and their tag elements are created correctly since I can look into them if I interrupt the program. I am an amateur programmer and all suggestions how to alter the program are appreciated

    Thanks

    Darius

  • Unknown said:
    You are right. I found those criteria online and I am not sure they are correct.

    The criteria itself is correct, but the next step not. You have to iterate through elements inside cell to find element(s) with tag(s). The cell header is not graphical element, so GetTags returns I guess nothing, but

    An example how your cells with tags look like would be helpful.

    Is there always one element with tag inside cell or there can be more?

    Unknown said:
    No there is no particular reason.

    In such case don't use it. To build an array upfront requires substantial more memory, is slower and is necessary only when elements during the iteration are modified in such way they have to be rewritten to the end of file. Which I suppose is not this case.

    Unknown said:
    As I said both arrays with cells and their tag elements are created correctly since

    In such case there is a contradiction, because it means the tags are attached to cell headers. not inside the cell. But if you are sure this part of the code works, it's fine.

    Unknown said:
    all suggestions how to alter the program are appreciated

    You code highly depends on the data and its structure, but you have not shared any file example, so it's hard to provide any guess why it does not work.

    Regards,

      Jan

  • Good Evening Jan

    Unknown said:
    Barry Lothian
    Dim oTags() As TagElement to Dim oTags() As Variant

    No! It makes the code worse and dirty. It's defined that GetTags method returns TagElement(), so if there is an error, it will be not caused by correctly declared variable.

    To use Variant is (with a couple of exceptions) seriously bad practice and always lead to worse code.

    Barry Lothian
    I can't speak for others but I have never had success using GetTags().

    I recall I used it several times in the past, both in VBA and C#, and it works fine.

    The earliest experience I had with VBA was extracting tag values from Excel so its a subject I'm quite familiar with. Its been a while since I encountered the problems I remember with Excel and tags so I've spent a little time this evening finding what the problem was. Using Jon's Tag Element Data Extraction example as a basis and tweaked slightly, I found the problem once again. Its not GetTags() that is the problem but it relates to the TagElement variable declaration in Excel in specific circumstances.

    Whilst this works in MicroStation it does not work in Excel:

    Set oHost = oEnumerator.Current
            If oHost.IsGraphical Then
                If oHost.HasAnyTags Then
                    Dim oTags()                       As TagElement
                    Dim oTag                          As TagElement
                    Dim nTags                         As Integer
                    Dim i                             As Integer
                    Dim vtValue                       As Variant
                    oTags = oHost.GetTags()
    
                    For i = LBound(oTags) To UBound(oTags)
                        Set oTag = oTags(i)
                        vtValue = oTag.Value
    Debug.Print "Tag [" & CStr(i) & "] =" & CStr(vtValue)
                    Next i
                End If
            End If

    This minor variation is the only way I found to get it working in Excel:

            Set oHost = oEnumerator.Current
            If oHost.IsGraphical Then
                If oHost.HasAnyTags Then
                    Dim oTags()                       As TagElement
                    Dim oTag                          As Variant
                    Dim nTags                         As Integer
                    Dim i                             As Integer
                    Dim vtValue                       As Variant
                    oTags = oHost.GetTags()
    
                    For i = LBound(oTags) To UBound(oTags)
                        Set oTag = oTags(i)
                        vtValue = oTag.Value
    Debug.Print "Tag [" & CStr(i) & "] = " & CStr(vtValue)
                    Next i
                End If
            End If

    Try it yourself, the first example will freeze your Excel instance and cause it to crash and restart.

    Apologies for being a dirty coder!

  • My VBA editor accepted oTags as Variant but unfortunatelly it didn't solve the problem.

  • Unknown said:
    Try it yourself, the first example will freeze your Excel instance and cause it to crash and restart.

    Thanks for the code snippets! Will try to test them, because I am very sure whether the code in Excel does not work, the problem is somewhere else than in the variable declaration.

    When accessing MicroStation from another application, the environment is not the same as when started from MicroStation,so in some situations an extra code or modifications can be necessary.

    With regards,

      Jan

  • Hi Barry,

    I tested it (with slightly modified code, see below) and I have to confirm the variant type has to be used, because it does not work othrwise. It seems to be a bug in VBA COM implementation that when more correct TagElement array is used, it's assigned, but cannot be accessed without the crash.

    In such case, variant type is "necessary evil", because no other solution exist ;-)

    For future reference, my testing code of Excel macro, working with already running MicroStation, is:

    Option Explicit
    
    Sub TestAccessToMicroStation()
        Dim oAL As ApplicationObjectConnector
        Dim o As MicroStationDGN.Application
        
        Set oAL = GetObject(, "MicroStationDGN.ApplicationObjectConnector")
        
        Set o = oAL.Application
        ProcessActiveModel o
    End Sub
    
    Private Sub ProcessActiveModel(app As MicroStationDGN.Application)
    
        Dim esc As ElementScanCriteria
        Set esc = app.CreateObjectInMicroStation("MicroStationDGN.ElementScanCriteria")
        esc.ExcludeNonGraphical
        
        Dim ee As ElementEnumerator
        Set ee = ActiveModelReference.Scan(esc)
        
        Do While ee.MoveNext
            If (ee.Current.HasAnyTags) Then
                ProcessElementWithTag ee.Current
            End If
        Loop
    
    End Sub
    
    Private Sub ProcessElementWithTag(el As Element)
    
        ' Used because a declaration 'As TagElement' does not work
        Dim tagElems As Variant
        tagElems = el.GetTags()
        
        Dim index As Long
        
        For index = LBound(tagElems) To UBound(tagElems)
            Dim tagElem As TagElement
            Set tagElem = tagElems(index)
    
            Dim tagValue As Variant
            tagValue = tagElem.Value
            Debug.Print "Tag [" & CStr(index) & "] =" & CStr(tagValue)
        Next index
    
    End Sub
    
    

    With regards,

      Jan