Utilizing MakePath in SelectSet.Select

Utilizing MakePath in SelectSet.Select

Anders_Olsen
Contributor Contributor
543 Views
13 Replies
Message 1 of 14

Utilizing MakePath in SelectSet.Select

Anders_Olsen
Contributor
Contributor

I'm trying to feed a list of part numbers from an input to the SelectSet.Select-function, but I can't get it to work.

 

This is what I have currently: 

oDoc.SelectSet.Select(Component.InventorComponent(MakePath("75430:2", "75424:1")))

I've checked the MakePath-function in another context, so I know I'm using it correctly.

 

I've also tried the SelectSet.Select-function with the input being on the first level:

oDoc.SelectSet.Select(Component.InventorComponent("75430:2"))

which also works as expected.

 

But the combination of the two is not working.

 

What am I doing wrong?

0 Likes
Accepted solutions (1)
544 Views
13 Replies
Replies (13)
Message 2 of 14

Anders_Olsen
Contributor
Contributor

By the way, just to clarify - I'm aware that, in order to select multiple components, I will have to use SelectSet.SelectMultiple, but right now I'm just focused on using entities in deeper levels than the top level as the function argument.

0 Likes
Message 3 of 14

WCrihfield
Mentor
Mentor

Hi @Anders_Olsen.  I could be wrong, because I rarely use that specific uniquely iLogic snippet, but I think you may need to enclose the two quoted component names within the { & } characters, to simulate them being two elements of an 'Array' ({"75430:2", "75424:1"}).  Of course, there are also alternative ways to get the lower level component occurrence, such as the example below which starts from a uniquely iLogic object, then transitions to Inventor API code for the rest.

ThisAssembly.Document.ComponentDefinition.Occurrences("75430:2").Definition.Occurrences("75424:1")

 ICadComponent.InventorComponent Property 

LmiMath.MakePath Method 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 4 of 14

Anders_Olsen
Contributor
Contributor

Hi @WCrihfield,

 

Thank you for the fast response and suggestion.

I had the same idea and already tried the curly brackes, without luck.

I also tried your alternative suggestion (with a small tweak, adding "ItembyName" before the occurrence-argument):

 

oDoc.SelectSet.Select(ThisAssembly.Document.ComponentDefinition.Occurrences.ItembyName("75430:2").Definition.Occurrences.ItembyName("75424:1"))

Outside the SelectSet.Select-function, this also works - I can get the occurrence object and display it's name in a messagebox for instance - same story as with the MakePath-function. But inside the SelectSet.Select-function, it just does not work. The error just says that "The parameter is incorrect".

Message 5 of 14

WCrihfield
Mentor
Mentor

Ah, I see.  I see that sort of thing a lot.  Sometimes we just need to separate complex lines of code out a bit, and do a better job of defining their Types to get things to work.  And yes, I forgot that the 'ItemByName' property is not he 'default' one when trying to shorten the line of code to eliminate a couple steps.  I tend to do most things through Inventor API code, even though it usually requires more code, because it usually makes more logical sense to me or provides better/clearer control.  I too went through a long phase where I was attempting to make everything as condensed as possible, but I got over it, and opted for code that had better performance and was easier to read through and understand again at a later date.  My 'production' related codes and the stuff I post online are usually pretty different.  My production related codes usually contain lots of comments, error avoidance checks with some form(s) of feedback, are designed for best performance...and usually get tweaked lots of times as my knowledge and coding style changes.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 6 of 14

Anders_Olsen
Contributor
Contributor

I agree, I also use the Inventor API as much as possible. I'm using the Component.InventorComponent and MakePath-function here, because I don't know what else to do. I'm open to alternative (more complex) solutions that fully use the Inventor API, if you have any. 🙂

0 Likes
Message 7 of 14

WCrihfield
Mentor
Mentor

If you are iterating through a list (or dictionary, or similar) of String type values, and will be getting several different component occurrences during that process, I would suggest getting the ComponentOccurrences collection to a variable first (before the iteration starts), from the AssemblyComponentDefinition.Occurrences property.  Also get the SelectSet object to a variable before the iteration starts.  Those steps will eliminate a lot of little API calls later, making it more efficient.  Then I would suggest declaring a ComponentOccurrence Type variable that you will later be setting the value of, so that it has a predefined Type, no matter how you set its value.  Then, after you have set the value of that variable in a later line of code, put the code for selecting it on the next line, where the input into that select method is that component variable.  Then whatever you are planning on doing with it in other lines of code after that point.

But I simply do not know enough yet about the details of your overall design intent or plans to suggest much else yet, or provide a working code example.  If your list only contains the part number (or file name) of the lower level component, but not the name of its parent sub assembly, then a different strategy may be necessary.  Maybe get the referenced document with that part number (or file name) first, then use the ComponentOccurrences.AllReferencedOccurrences method to get all of the occurrences throughout the entire assembly structure that reference it, then iterate through them, selecting each one and doing your thing with them.  That way you are not iterating through all occurrences in every level recursively looking for them, but just going directly to the ones you want to do something with.  Each occurrence object has the OccurrencePath property also, which returns an ComponentOccurrencesEnumerator that we can convert to an array of Strings or ArrayList, if necessary, but that may be regressing.

Edit:  Here is a 'shot in the dark' example code, without knowing what your full plans are yet.

Sub Main
	Dim oADoc As AssemblyDocument = TryCast(ThisApplication.ActiveDocument, Inventor.AssemblyDocument)
	If oADoc Is Nothing Then
		Logger.Debug("Rule named '" & iLogicVb.RuleName & "' did not run because no assembly obtained!")
		Return
	End If
	
	Dim oSS As Inventor.SelectSet = oADoc.SelectSet
	Dim oOccs As ComponentOccurrences = oADoc.ComponentDefinition.Occurrences
	Dim oAllRefDocs As DocumentsEnumerator = oADoc.AllReferencedDocuments
		
	Dim oListOfPNs As List(Of String)
	oListOfPNs.Add("75430")
	oListOfPNs.Add("75424")

	For Each sPN As String In oListOfPNs
		'get the referenced Document with this Part Number (or FileName)
		For Each oRefDoc As Inventor.Document In oAllRefDocs
			Dim sPartNumber As String = oRefDoc.PropertySets.Item(3).Item(2).Value
			If sPartNumber = sPN Then
				Dim oRefOccs As ComponentOccurrencesEnumerator = oOccs.AllReferencedOccurrences(oRefDoc)
				If (oRefOccs Is Nothing) OrElse (oRefOccs.Count = 0) Then Continue For
				For Each oRefOcc As Inventor.ComponentOccurrence In oOccs
					'If oRefOcc.Name.StartsWith(sPN) Then
					oSS.Select(oRefOcc)
					'<<< DO SOMETHING WITH IT >>>
				Next 'oRefOcc
			End If
		Next 'oRefDoc
	Next 'sPN
	oADoc.Update2(True)
End Sub

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 8 of 14

Anders_Olsen
Contributor
Contributor

Still no luck. Your sample still only selects components on the first level. I need the code to add selections across the entire hierarchy.

I should probably elaborate the purpose: My company has a lot of legacy Inventor data, which does not comply with current metadata standards. I've written an iLogic-rule (updateComponents), which automatically takes care of 90 % of the compliancy by just selecting the component, and running the rule. Moreover, I've written another rule (screenComponents), which analyzes an entire assembly, and fills out an excel sheet with all noncompliant properties of each component. Using the data from that sheet, I can generate a list of components (across the entire assembly hierarchy), which should be updated, using the aforementioned iLogic rule.

 

I want to use that list of noncompliant components as an input to a new function, which should automatically select only the components that need to be updated. Once selected, the "updateComponents" iLogic-rule goes through the selectset one by one.

Alternatively, I would have to manually dig down to each noncompliant component, and manually multiselecting every single one of them, at which point I would be able to run the "updateComponents" iLogic-rule.

 

I hope it makes sense. Here is my current full sample code - note that I'm simply adding each correctly flagged occurrence as if it's on the top level - I assume I need to apply the MakePath-function, when the occurrence is not in the first level.

 

Sub Main
	oDoc = ThisDoc.Document
	
	Dim oComponentsString As String = InputBox("Input part numbers separated by comma", "Select components")
	
	oComponentsList = oComponentsString.Split(","c).ToList
	
	oObjCol = ThisApplication.TransientObjects.CreateObjectCollection
	assemblyRunner(oDoc)
	
	oDoc.SelectSet.SelectMultiple(oObjCol)
End Sub

Dim oDoc As AssemblyDocument
Dim oComponentsList As List(Of String)
Dim alreadyInList As Boolean
Dim occList As New List(Of ComponentOccurrence)
Dim oObjCol As ObjectCollection

Sub assemblyRunner(aDoc As AssemblyDocument)
	
	For Each oOcc In aDoc.ComponentDefinition.Occurrences
		Dim oOccDoc As Document = oOcc.Definition.Document
		Dim oPartNo As String = oOccDoc.PropertySets.Item(3).Item("Part Number").Value
		
		If oComponentsList.Contains(oPartNo) Then
			For Each comp In occList
				compPartNo = comp.Definition.Document.PropertySets.Item(3).Item("Part Number").Value
				If compPartNo = oPartNo Then
					alreadyOnList = True
					Exit For
				End If
			Next
			If alreadyOnList Then
				alreadyOnList = False
				Continue For
			Else
				occList.Add(oOcc)
				oObjCol.Add(oOcc)
			End If
		End If
		If oOccDoc.DocumentType = kAssemblyDocumentObject Then
			assemblyRunner(oOccDoc)
		End If
	Next
End Sub

 

0 Likes
Message 9 of 14

WCrihfield
Mentor
Mentor

Hi @Anders_Olsen.  I had no idea what you were planning on doing with each component just after selecting them, but attempting to get them into a collection to then run some other iLogic rule on later was not on my radar.  Using the SelectSet is usually not a good fit for something like that.  It has some restrictions and can be a bit finicky.  Using the other collection types (ObjectCollection, List(Of ), etc) should be OK though.  However, if you need a means to capture multiple objects that will stay in place between running one rule, to running another rule, then some additional strategy would be required.  There are ways for one iLogic rule to 'send' data to another iLogic rule, when it calls that other rule to run.  And a way to 'receive' data back from the other rule, after the other rule has finished running, if both rules are set-up to do so.  (RuleArguments)  There is also a way to store data from one rule up into an Inventor 'session' memory variable, where later rule(s) can later access that data from (the uniquely iLogic SharedVariable).  One of the restrictions of the SelectSet is that it can only 'select' things that exist within the 'context' (definition & model coordinate space) of the Document they are associated with.  For example, the SelectSet from Document A can not select something that is within Document B.  And if Document A is not the 'active' document, you will not be able to use the DocumentA.SelectSet.Select() method on anything in it anyways, because true selection only works on the 'active' document.  I think that may be the problem in this case also, because some of the other components that may be referencing some of the referenced documents may be down within other sub assemblies, which means they would not be in the 'context' of the 'main/active' assembly directly.  With that in mind, the SelectSet object that the SelectSet.Select() method being used in that last example I posted could be switched out for something like the List(Of ComponentOccurrence) type collection instead, which may enable it to capture all the components you want.  However, I still don't know how your second rule will be using them, so still not sure if the context from where they are being gathered will be the best fit either.

 

When iterating through assembly components (instead of just referenced documents), and wanting to do so 'recursively' (keeps stepping down into lower levels on its own), there are two ways to do that, which each work differently, as far as 'context' is concerned.  When stepping down through the 'definitions' of sub assemblies to access their sub components, that is one way, but the lower level components it accesses will not be in the 'context' of the main / active assembly.  When stepping down through the ComponentOccurrence.SubOccurrences collection, the association with the parent 'context' is maintained, so that the lower level components are still in the context of the main / active assembly.  This second way is sort of similar to navigating through the model browser tree's nodes, with the lower level nodes still being associated with the highest level node, due to the 'tree' structure.  This second way sounds like what you likely need in your situation.

 

Within the example code that you posted above, I see a variable misspelling or difference in how it is spelled in multiple locations ("alreadyInList" vs "alreadyOnList").  It also looks like your variable named 'occList' may not be getting properly 'initialized'.  When declaring a variable for a reference type between methods, we can not initialize it or set an initial value to it until it is accessed within one of the methods.  It looks like you are using the 'New' keyword directly in the declaration line, then just adding values to it within the method, without first initializing the variable.  The 'New' keyword is used to initialize the List object, before entries can be added to it, but that step should be done within the method, and only when it is encountered or the first time, so avoid resetting its contents.  I also see that the 'assemblyRunner' method you are using, is also being used for 'recursion' down into lower levels of the assembly, but it is doing so through the 'definitions', which does not maintain contextual lineage with the 'active' assembly.  That is not necessarily a good or bad thing, depending on what you are trying to do with those components later.  If you are planning on making changes to those components later, then how you gathered them will determine the 'scope' (area of effect) of the changes.  If gathered through their definitions, then changes will effect the independent referenced documents.  If gathered through SubOccurrences properties, then changes may only be relevant to those occurrences from the perspective of the active assembly, depending on the types of changes.  For example, if you wanted to change the colors of all components in an assembly, but do not want the color changes to actually effect the files that the components reference, but only want the colors to be in effect while viewing them from within the active assembly, then you would access them through the SubOccurrences property, instead of through their definitions.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 10 of 14

WCrihfield
Mentor
Mentor

Here is another example of the rule that just 'finds and collects' the components with matching Part Number values, but does not do anything else with them except 'sharing them' for a later rule or process to use.  It is recursive, and broken up into 3 routines.  The first routine gets user input, then calls the second routine to run, while supplying it with this assembly's components collection, and instructions about how they should be dealt with.  Then the second routine is just for the recursion process, and should not need to be customized.  Then the third routine is what actually does a custom task with each occurrence.  In this case, that task is just getting the value of the Part Number iProperty from its referenced document, then seeing if that is one we are looking for.  If so, add it to the List.  Then, back in the 'Main' routine, after the recursion is done, it shares the List up into Inventor's session memory, so that a later rule can access it and do a custom task on its contents.

Sub Main
	Dim oADoc As AssemblyDocument = TryCast(ThisDoc.Document, Inventor.AssemblyDocument)
	If oADoc Is Nothing Then
		Logger.Debug("The iLogic rule named '" & iLogicVb.RuleName & "' exited, because no AssemblyDocument was obtained.")
		Exit Sub
	End If
	
	'gather input from the user
	Dim sPartNumbersToFind As String = InputBox("Input part numbers separated by comma", "Select components")
	'if nothing entered, or is empty, exit the rule
	If String.IsNullOrWhiteSpace(sPartNumbersToFind) Then Return
	'attempt to put values into List
	If sPartNumbersToFind.Contains(",") Then
		oPartNumbersToFind = sPartNumbersToFind.Split(","c).ToList()
	Else
		If (oPartNumbersToFind Is Nothing) Then oPartNumbersToFind = New List(Of String)
		oPartNumbersToFind.Add(sPartNumbersToFind)
	End If
	'get assembly occurrrences collection
	Dim oOccs As ComponentOccurrences = oADoc.ComponentDefinition.Occurrences
	'call custom Sub routine to recurse this assemblies components
	'True means maintain context with parent assemblies
	RecurseComponents(oOccs, True, AddressOf ProcessComponent)
	
	'share the resulting List(Of ComponentOccurrence) object to Inventor's session memory
	SharedVariable.Value("FoundComponentsList") = oPartNumbersToFind
	'a later rule can retrieve this by: 
	'Dim FoundOccsList As List(Of ComponentOccurrence) = SharedVariable.Value("FoundComponentsList")
	
	'optionally update this assembly
	'oADoc.Update2(True)
	'optionally save this assembly
	'If oADoc.Dirty Then oADoc.Save2(True)
End Sub

'<<< declare 'global' variables here >>>
Dim oPartNumbersToFind As List(Of String)
Dim oFoundOccs As List(Of Inventor.ComponentOccurrence)

'just for assembly component recursion process (no need to customize)
Sub RecurseComponents(oComps As ComponentOccurrences, _
	preserveContext As Boolean, _
	ComponentProcess As Action(Of ComponentOccurrence))
	'validate input collection
	If (oComps Is Nothing) OrElse (oComps.Count = 0) Then Return
	'start iterating input collection
	For Each oComp As ComponentOccurrence In oComps
		'call custom task to run on this instance
		ComponentProcess(oComp)
		'if this occurrence is suppressed, we can not iterate through its sub occurrences
		If oComp.Suppressed Then Continue For
		'if this occurrrence is not an assembly, we do not want to iterate its sub occurrences
		If Not oComp.DefinitionDocumentType = DocumentTypeEnum.kAssemblyDocumentObject Then Continue For
		'attempt to recurse down into its sub-components, if possible, and it has some
		If preserveContext Then
			'<<< edits (can be) constrained to context of 'parent' assembly >>>
			RecurseComponents(oComp.SubOccurrences, preserveContext, ComponentProcess)
		Else
			'<<< edit each component's definition &/or referenced file independently >>>
			RecurseComponents(oComp.Definition.Occurrences, preserveContext, ComponentProcess)
		End If
	Next 'oComp
End Sub

'what to do to/with each occurrence
Sub ProcessComponent(oComp As ComponentOccurrence)
	If (oComp Is Nothing) OrElse oComp.Suppressed Then Return
	'skip virtual components
	'If (TypeOf oComp.Definition Is VirtualComponentDefinition) Then Return
	'skip welds within weldment type assemblies
	'If (TypeOf oComp.Definition Is WeldsComponentDefinition) Then Return
	'get referenced Document
	Dim oCompDoc As Inventor.Document = Nothing
	Try : oCompDoc = oComp.Definition.Document : Catch : End Try
	If oCompDoc Is Nothing Then Return
	'get Part Number iProperty value
	Dim sPN As String = oCompDoc.PropertySets.Item(3).Item(2).Value
	'if Part Number value is empty, then skip this component
	If String.IsNullOrWhiteSpace(sPN) Then Return
	'if this is 'NOT' one of the Part Number values we are looking for, then skip it
	If Not oPartNumbersToFind.Contains(sPN) Then Return
	'initialize the List, if necessary
	If (oFoundOccs Is Nothing) Then oFoundOccs = New List(Of Inventor.ComponentOccurrence)
	'meeds our requirements, so add it to our List
	If Not oFoundOccs.Contains(oComp) Then oFoundOccs.Add(oComp)
End Sub

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 11 of 14

Anders_Olsen
Contributor
Contributor

Very thorough explanation, thank you. 
It is on purpose that I’m working with the definitions, and not just the components within the context of the assembly. 

If the components on lower levels not being the active document should explain why they can’t be part of the selectSet, then by that logic, the first first level components couldn’t be part of it either. And I can manually select any component I want - anywhere in the hierarchy - and subsequently retrieve the selections from the selectSet-method. So I’m still not convinced it can’t be done the other way around. 

I appreciate your help, though. Your suggestion could be a reasonable plan b, but I would prefer the selectSet.Select method, if at all possible.

0 Likes
Message 12 of 14

marcin_otręba
Advisor
Advisor
Accepted solution

hi,

 

you can select occ proxy instead:

 

Dim odoc As AssemblyDocument = ThisDoc.Document
Dim occ_proxy As ComponentOccurrenceProxy = odoc.ComponentDefinition.Occurrences.Item(2).SubOccurrences.Item(1)
odoc.SelectSet.Select(occ_proxy)
Logger.Info(odoc.SelectSet(1).name)

 

 

Hi, maybe you want to vote my:

Ideas

or check my apps:

DrawingTools   View&ColoringTools   MRUFolders

Message 13 of 14

WCrihfield
Mentor
Mentor

As Marcin pointed out, the term "proxy" has been left out of our earlier conversations, but is very appropriate to include here, because it helps explain the whole 'context' thing, and why the selection will only work under certain conditions.  They can be difficult to explain though.  A proxy is essentially some geometry that has been (copied, moved, and reoriented) from within the context of a child level component in an assembly, up into the context of its parent assembly.  The parent assembly can then select and interact with the proxy geometry, where it could not select or interact with the original geometry that existed in a different coordinate system.  The concept of the ComponentOccurrence object itself is very similar to the idea of proxies, because it is also essentially a copied and transformed version of the geometry from within some other external file, that has been placed into the context of an assembly.  Its geometry can be accessed by the assembly, as long as we are not stepping down into the definition of that component to access it.  Proxies are often created/retrieved for the purposes of enabling measurements, constraints, and similar functionalities between the geometry of that proxy, and something else that is already in the appropriate context.  Proxies are pretty much only necessary to work with and talk about when attempting to do things within assemblies by code, since Autodesk handles tons of these finer details for us automatically, behind the scenes, as we interact with things in the user interface.  Even the 'Pick' method, which pauses a code to allow manual user selection of a single object, seems to do some stuff for us to help eliminate the need for dealing with proxies, because it always seems to return an object that is in the active document's context, even if that means that it had to create a quick proxy of it.  The AssemblyComponentDefinition.AdjustProxyContext method seems to be one of the tools to help enable such interactions, along with the ComponentOccurrence.CreateGeometryProxy method.  When we step down to lower level components using the ComponentOccurrence.SubOccurrences route, we are then accessing the proxies of those lower level components, which are still accessible by the parent level assembly (the assembly that directly contains the ComponentOccurrence object that the method is being called from).  But when accessing lower level components through the ComponentOccurrence.Definition.Occurrences route, we are reaching down into a different document's coordinate space, breaking the connection to the parent level.

There are tons of official 'proxy' object Types, which are usually derived from the original object Type.  If you go into the online help area, under the 'Programming Interface', then under 'Inventor API Reference Manual', then under 'Objects', and do Ctrl+F search for the text "Proxy", you will find around 270 object Types which include that term at the end of their names.  That can be seen as an indicator of how important and widely used they are.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 14 of 14

Anders_Olsen
Contributor
Contributor

@marcin_otręba thank you so much - it works! And @WCrihfield thanks, for the explanation.

 

I have a utility now, which does exactly what I wanted. This is the code sample, if anyone's interested:

 

Sub Main
	oDoc = ThisDoc.Document
	
	Dim oComponentsString As String = InputBox("Insert part numbers, separated by comma", "Select components")
	
	oComponentsList = oComponentsString.Split(","c).ToList
	
	oObjCol = ThisApplication.TransientObjects.CreateObjectCollection
	
	' On the first level, run through all occurrences, and add those found in the list to the object collection
	firstLevelRunner(oDoc)
	
	oDoc.SelectSet.SelectMultiple(oObjCol)
End Sub

Dim oDoc As AssemblyDocument
Dim oComponentsList As List(Of String)
Dim alreadyOnList As Boolean
Dim aOcc As ComponentOccurrence
Dim oOccProxy As ComponentOccurrenceProxy
Dim oPartNo As String
Dim oObjCol As ObjectCollection

Sub firstLevelRunner(aDoc As AssemblyDocument)
	Dim aOccDoc As Document
	
	' Loop through all first level occurrences, ignoring those already found using the "alreadyOnList"-variable
	For Each aOcc As ComponentOccurrence In aDoc.ComponentDefinition.Occurrences
		aOccDoc = aOcc.Definition.Document
		oPartNo = aOccDoc.PropertySets.Item(3).Item("Part Number").Value
		
		If oComponentsList.Contains(oPartNo) Then
			For Each comp In oObjCol
				compPartNo = comp.Definition.Document.PropertySets.Item(3).Item("Part Number").Value
				If compPartNo = oPartNo Then
					alreadyOnList = True
					Exit For
				End If
			Next
			If alreadyOnList Then
				alreadyOnList = False
				Continue For
			Else
				oObjCol.Add(aOcc)
			End If
		End If
		
		' If the current occurence is an assembly, loop through its lower levels using occurrence proxies
		If aOccDoc.DocumentType = kAssemblyDocumentObject Then
			occRunner(aOcc)
		End If
	Next
End Sub

Sub occRunner(oOcc As ComponentOccurrence)
	Dim occProxyDoc As Document
	
	For Each occProxy As ComponentOccurrenceProxy In oOcc.SubOccurrences
		occProxyDoc = occProxy.Definition.Document
		oPartNo = occProxyDoc.PropertySets.Item(3).Item("Part Number").Value
		
		If oComponentsList.Contains(oPartNo) Then
			For Each comp In oObjCol
				compPartNo = comp.Definition.Document.PropertySets.Item(3).Item("Part Number").Value
				If compPartNo = oPartNo Then
					alreadyOnList = True
					Exit For
				End If
			Next
			If alreadyOnList Then
				alreadyOnList = False
				Continue For
			Else
				oObjCol.Add(occProxy)
			End If
		End If
		If occProxyDoc.DocumentType = kAssemblyDocumentObject Then
			occRunner(occProxy)
		End If
	Next
End Sub
0 Likes