AddVBFile getting Document and Application

AddVBFile getting Document and Application

Curtis_Waguespack
Consultant Consultant
1,015 Views
19 Replies
Message 1 of 20

AddVBFile getting Document and Application

Curtis_Waguespack
Consultant
Consultant

When using a Helper Module as in this example, is there a better way to instantiate the Document and Application than using a sub in the the Helper Module?

 

Currently, as written we need to call the "GetAppAndDoc" sub at least once in order for the RenumberNamedComponents sub to work ( see line 6 in the first screen capture).

      MyHelperFunctions.GetAppAndDoc(ThisDoc, ThisApplication)

 

 

I am wondering if there is a better way to do this. Mostly for other users less familiar with this, so that they don't have to remember to do this in future use of the helper module, but also for my future self, who seems to be getting more forgetful.

 

Thanks in advance! 

Curtis

 

AddVBFile reference link:
https://help.autodesk.com/view/INVNTOR/2025/ENU/?guid=GUID-32B66838-22E4-4A0A-B5BB-862350C76B36

 

Place and Renumber Rule

Curtis_Waguespack_0-1744816333949.png

 

Helper module

Curtis_Waguespack_1-1744816457401.png

 

 

EESignature

0 Likes
Accepted solutions (2)
1,016 Views
19 Replies
Replies (19)
Message 2 of 20

WCrihfield
Mentor
Mentor
Accepted solution

Since a Module does not get 'instantiated' like a Class, there is no need for 'requiring' the 'New' method, which leaves us with no clear or obvious way to do this.  But we still have a few options.  One is asking for, and passing them in, when we use a method from that Module.  Another is with Public properties of the Module, but that is not visible enough, or obvious enough to be very end user friendly.  Another route is to extract the needed inputs from other supplied inputs.  For example, several API objects have a property named 'Application' which will lead us to the Inventor.Application object, so we can sometimes get that by digging into the properties of the other objects that were supplied as inputs into a method.  But that will obviously only work when the right types of Inventor based 'objects' are passed in, instead of just data types like numbers and text.  Likewise sometimes we can climb back up the API Object Model ladder from a supplied Inventor object using the 'Parent' route to get to the Document object, but not always.  Sometimes it may just be simpler to ask for the Application or Document directly in the method that will need to access it, even though it may add a bit more clutter to the method.  Extension methods are pretty similar, because we are usually extending an Inventor based object, which we can sometimes inspect to find one or the other, but not always.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 3 of 20

WCrihfield
Mentor
Mentor

What if you made your method for renumbering components into an extension method of the iLogic 'Components' object?  Then, within that extension method, after that named occurrence is found, you can then get the ComponentOccurrence object.  Then you can use its ComponentOccurrence.Application property to get the Inventor.Application.  Then, you may be able to use the application to get the 'ActiveDocument' or 'ActiveEditDocument', or use its 'Parent' property to access the AssemblyComponentDefinition, then use its Document property to get to the Document.  Just throwing some alternative ideas around.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 4 of 20

jjstr8
Collaborator
Collaborator
Accepted solution

I normally implement helper functions on Inventor objects as extension methods. In your example, RenumberNamedComponents is an extension method for any ICadDoc interface implementers. Unfortunately, the iLogic editor Intellisense doesn't pick up extensions like Visual Studio would. Also, I think ThisApplication can be grabbed from ICadDoc.Document.Parent (cast to Application) in most cases. If you find a case where that isn't true, you could make your extension on Application and pass ThisDoc and the string to RenumberNamedComponents.

 

AddVbFile "MyHelperExtensions"
'place the comopoent with no browser name specified
Components.Add("", "BRACKET_00.iam")

ThisDoc.RenumberNamedComponents("BRACKET")

 

This is the MyHelperExtensions.vb

Imports System.Runtime.CompilerServices
Imports Inventor
''' <summary> Contains helper functions to use in other rule </summary>
Public Module MyHelperExtensions

	''' <summary>Renumbers all components with a specified name in the active assembly.</summary>
	''' <param name="SuppliedComponentName">The browser name to search for and renumber.</param>
	<Extension()>
	Public Sub RenumberNamedComponents(ThisDoc As ICadDoc, SuppliedComponentName As String)

		Dim oOcc As ComponentOccurrence
		Dim NewName As String
		Dim oAsmDef As AssemblyComponentDefinition

		Try
			oAsmDef = ThisDoc.Document.ComponentDefinition
		Catch
			MsgBox("Error getting document." & vblf & "You might need to use the MyHelperFunctions.GetAppAndDoc sub in your rule first ")
			Exit Sub
		End Try

		Dim i As Integer = 1

		'Temp rename
		For Each oOcc In oAsmDef.Occurrences
			If Not oOcc.Name.Contains(SuppliedComponentName) Then Continue For
			NewName = "temp_" & SuppliedComponentName & "_" & i
			oOcc.Name = NewName
			i = i + 1
		Next

		' Strip off temp prefix
		For Each oOcc In oAsmDef.Occurrences
			If Not oOcc.Name.Contains(SuppliedComponentName) Then Continue For
			NewName = Replace(oOcc.Name, "temp_", "")
			oOcc.Name = NewName
		Next
	End Sub
End Module

 

Message 5 of 20

WCrihfield
Mentor
Mentor

Making this an extension of the iLogic 'Component' object may not work that great, since it would have to obtain an instance of an occurrence first, and the name being provided here is not an exact match for any occurrence, but a portion if their names.  However, the iLogic 'ThisAssembly' object (IManagedAssembly) works pretty well here.  One of its direct properties is Document, which specifically returns an AssemblyDocument.  From that we can get the AssemblyComponentDefiinition, then the Application from that.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

0 Likes
Message 6 of 20

jjstr8
Collaborator
Collaborator

I'm not sure I follow you. The original helper function operated on ThisDoc. What I posted does the same thing. "oAsmDef = ThisDoc.Document.ComponentDefinition" gets the AsemblyComponentDefinition so you can access Occurrences.

 

0 Likes
Message 7 of 20

Curtis_Waguespack
Consultant
Consultant

Thanks for the replies!

Unfortunately I am not seeing extensions helping in this case,  due to the lack of Intellisense tooltip support as mentioned. It's a shame the tooltip doesn't display, or this would probably be the way I would go for this.

I think handing over ThisDoc as a parameter to the helper module sub accomplishes the same thing and will be a bit more intuitive or/or discoverable for my target users, since the Intellisense tooltip does tell them it expects the document to be passed in as an argument.

Curtis_Waguespack_0-1744833616910.png


Getting the application from the document works well. 


So passing the document in isn't a bad solution, I just wanted to ensure I wasn't missing some other way to do it from the module or something. 
 

AddVbFile "MyHelperFunctions"

'place the comopoent with no browser name specified
Components.Add("", "BRACKET_00.iam")

RenumberNamedComponents(ThisDoc, "BRACKET")

 

Imports Inventor
''' <summary> Contains helper functions to use in other rules </summary>
Public Module MyHelperFunctions

	''' <summary>Renumbers all components with a specified name in the active assembly.</summary>
	''' <param name="SuppliedComponentName">The browser name to search for and renumber.</param>
	''' <param name="ThisDoc">Enter "ThisDoc".</param>
	Public Sub RenumberNamedComponents(ThisDoc As ICadDoc, SuppliedComponentName As String)

		Dim ThisApp As Inventor.Application = ThisDoc.Document.Parent
		Dim oAsmDef As AssemblyComponentDefinition = ThisDoc.Document.ComponentDefinition
		Dim Collection As ObjectCollection = ThisApp.TransientObjects.CreateObjectCollection
		Dim oOcc As ComponentOccurrence

		Dim i As Integer = 1

		'Temp rename
		For Each oOcc In oAsmDef.Occurrences
			If Not oOcc.Name.Contains(SuppliedComponentName) Then Continue For
			oOcc.Name = "temp_" & SuppliedComponentName & "_" & i
			Collection.add(oOcc)
			i = i + 1
		Next

		' Strip off temp prefix
		For Each oOcc In Collection
			oOcc.Name = Replace(oOcc.Name, "temp_", "")
		Next

	End Sub
End Module

 

EESignature

Message 8 of 20

Curtis_Waguespack
Consultant
Consultant

@WCrihfield wrote:

Making this an extension of the iLogic 'Component' object may not work that great, since it would have to obtain an instance of an occurrence first, and the name being provided here is not an exact match for any occurrence, but a portion if their names.  However, the iLogic 'ThisAssembly' object (IManagedAssembly) works pretty well here.  One of its direct properties is Document, which specifically returns an AssemblyDocument.  From that we can get the AssemblyComponentDefiinition, then the Application from that.


 

I like that idea for this specific renumber routine, but this was just an example I pulled for this question. I was looking for a way to do this more generally for different types of functions, to keep things as consistent and straightforward as possible for newer users. I think passing in the document when needed, and getting the application from that, should give access to most everything needed.

EESignature

0 Likes
Message 9 of 20

jjstr8
Collaborator
Collaborator

Yeah, the Intellisense is disappointing. This is the best you get.

jjstr8_0-1744833305963.png

 

0 Likes
Message 10 of 20

WCrihfield
Mentor
Mentor

Hi @jjstr8.  My first comment in my last reply was pertaining to what I suggested in my previous reply, about making this method an extension method of the iLogic 'Component' rule object, and basically explaining that I tested doing it that way, and I no longer think it would be a good idea, due to its dependency on an exact component name.  I know the original helper function was using the iLogic 'ThisDoc' object, but it was not an 'extension' method, so it had to get those two references (document and application) from another method setting them to shared variables.  The second part of my last reply was just pointing out another perfectly viable alternative iLogic rule object which that routine could be an 'extension method' of, and would give us easy access to all those references, as requested.  I'm not saying there is anything wrong with using the 'ThisDoc' object, just that there are still other possibilities, and the 'ThisAssembly' object is more closely related to the task of renaming component occurrences within an assembly.  More possibilities & options are usually better than fewer.  I made a version of the original Module, and the method, using the 'ThisAssemby' object, and that seemed to work just fine for me, and seemed like a viable and somewhat intuitive alternative to mention.

I will post an example of what I am talking about here, just for reference, and show that it can be simpler than you may be thinking.

Here is the Module's code:

Imports Inventor
''' <summary> Contains helper functions to use in other rule </summary>
Public Module MyHelperFunctions

    ''' <summary>Renumbers all components with a specified name in the active assembly.</summary>
    ''' <param name="SuppliedComponentName">The browser name to search for and renumber.</param>
	<System.Runtime.CompilerServices.Extension>
	Public Sub RenumberNamedComponents(ThisAssembly As Autodesk.iLogic.Interfaces.IManagedAssembly, SuppliedComponentName As String)

		Dim oAsmDef As AssemblyComponentDefinition = ThisAssembly.Document.ComponentDefinition
		Dim ThisApplication As Inventor.Application = oAsmDef.Application
		Dim oOcc As ComponentOccurrence
		Dim NewName As String
		Dim i As Integer = 1

		'Temp rename
		For Each oOcc In oAsmDef.Occurrences
			If Not oOcc.Name.Contains(SuppliedComponentName) Then Continue For
			NewName = "temp_" & SuppliedComponentName & "_" & i
			oOcc.Name = NewName
			i = i + 1
		Next

		' Strip off temp prefix
		For Each oOcc In oAsmDef.Occurrences
			If Not oOcc.Name.Contains(SuppliedComponentName) Then Continue For
			NewName = Replace(oOcc.Name, "temp_", "")
			oOcc.Name = NewName
		Next
	End Sub
End Module

And here is an example of another rule using it.  In this case, both rules were 'internal', so using AddVbRule instead of AddVbFile, but they work the same way.

AddVbRule "Extension Method"
ThisAssembly.RenumberNamedComponents("MyComponentBaseName")

As you can see, I am not even using the "MyHelperFunctions" term here to access that helper method, but just the 'ThisAssembly' object directly, because that is the object that we added the 'extension' to.  When it is an extension method, it is actually simpler to use, because you are using things that already exist, and things that we are already used to using on a regular basis.  And that method shows up just fine for me in Intellisense right after I type the dot (.) after that object.

WCrihfield_1-1744889245389.png

WCrihfield_2-1744890193779.png

 

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 11 of 20

jjstr8
Collaborator
Collaborator

My apologies, that was confusion on my part. I didn't see your prior post before writing mine. I agree with using ThisAssembly since that's the only type it would apply to. No matter what I try, I cannot get Intellisense to see the extension method. Could it be an Inventor version? I'm on 2024. Also, I couldn't figure out where to put the extension method rule other that in with the document rules. 

0 Likes
Message 12 of 20

Curtis_Waguespack
Consultant
Consultant

@WCrihfield, your last post gave me the idea to just import the iLogic interfaces as shown below with: Imports Autodesk.iLogic.Interfaces

That imports statement connects the renumber extension to ThisDoc as you mentioned, and shows it in the Intellisense.

I think this creates an intuitive experience for the users I was aiming this at. 

 

Curtis_Waguespack_0-1744896400836.png

 

Internal or External calling rule:

AddVbFile "MyHelperFunctions"
'place the comopoent with no browser name specified
Components.Add("", "BRACKET_00.iam")

Thisdoc.RenumberNamedComponents("BRACKET")

 

External MyHelperFunctions.iLogicVb utility rule:

Imports Inventor
Imports System.Runtime.CompilerServices
Imports Autodesk.iLogic.Interfaces
''' <summary> Contains helper functions to use in other rule </summary>
Public Module MyHelperFunctions

	''' <summary>Renumbers all components with a specified name in the active assembly.</summary>
	''' <param name="ThisDoc">Enter "ThisDoc".</param>
	''' <param name="SuppliedComponentName">The browser name to search for and renumber.</param>
	<Extension()>
	Public Sub RenumberNamedComponents(ThisDoc As ICadDoc, SuppliedComponentName As String)

		Dim ThisApp As Inventor.Application = ThisDoc.Document.Parent
		Dim Collection As ObjectCollection = ThisApp.TransientObjects.CreateObjectCollection
		Dim oAsmDef As AssemblyComponentDefinition = ThisDoc.Document.ComponentDefinition		
		Dim oOcc As ComponentOccurrence				

		Dim i As Integer = 1

		'Temp rename
		For Each oOcc In oAsmDef.Occurrences
			If Not oOcc.Name.Contains(SuppliedComponentName) Then Continue For
			oOcc.Name = "temp_" & SuppliedComponentName & "_" & i
			Collection.add(oOcc)
			i = i + 1
		Next

		' Strip off temp prefix
		For Each oOcc In Collection
			oOcc.Name = Replace(oOcc.Name, "temp_", "")
		Next

	End Sub

End Module

 

EESignature

Message 13 of 20

WCrihfield
Mentor
Mentor

No apology necessary.  Were all just trying to help others here the best we can given our current situations.  The same happens to me relatively often, because I often take a long time typing my replies, when they get posted to the forum, there may already be 2 other replies since the last one I saw.  🙂

I am also using 2024.3.4 right now, so there is something you can check out.  In an empty iLogic rule, within the iLogic rule editor, just type in a single dot (.), with nothing in front of it, then pay attention to the small icons at the bottom of that Intellisense pop-up.  You will usually see 3 of them...one for 'iLogic', one for 'Inventor', and one for 'Standard .NET'.  Make sure all three are turned on (highlighted) (see image below).

WCrihfield_0-1744896810811.png

As for not knowing where to put that extension method...I should have stayed with the original poster's example scenario, and placed it within an external iLogic rule.  However, since I was just doing quickie local testing, I created both rules within 'internal' (saved within a document) rules, within the same Document.  So, my extension module within one local rule (with 'Straight VB Code' option turned on), and the rule using that module was in another local rule, both within the same document.  This is most likely a pretty rare way to do things.  When both rules are within the same document, we can use the 'AddVbRule' line in the rule's header, and just specify the name of the other rule.  However, in most normal situations, helper Classes and helper Modules are stored within 'external' iLogic rules, with the rule option named "Straight VB Code" turned on, and most other rule options turned off, then within the rule(s) that are referencing/using those Classes/Modules, the Header line starts with 'AddVbFile', followed by not only the name of the external rule, but also its file extension.  I think this is because of there being 3 possible file extensions for external rules, to make it easier for it to find the correct file, and avoid duplicate files with different file extensions type issues.  Most of my external resources used that way are Classes, instead of Modules, but I do have a couple Modules also, mainly for true 'extension' methods, rather than just regular helper methods.

Edit:  Once again, another reply was posted while I was typing, so I didn't see it until I posted my reply.  That last reply makes a good point to add here.  Using the 'Imports' statements within the header of our rules helps Intellisense recognize stuff also, but is more for allowing shortcut access to deeper objects within an API's object model.  The AddReference line is what really adds object recognition of things defined within outside DLL's, but those seem to be difficult to use within external rules that are set to 'Straight VB Code' like this, for some reason.  Every time I use AddReference lines in the header of those types of rules, it leads to errors when referencing those helper rules later, which leads to be either commenting them out, or deleting them.  I can add those AddReference lines in my 'regular' rule (the one referencing/using the helper) just fine though.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 14 of 20

jjstr8
Collaborator
Collaborator

@WCrihfield : It looks like it's a quirk of the iLogic editor. It doesn't complain about just "ICadDoc" or "IManagedAssembly" by itself without an Imports for the namespace, but it should. It needed to be a fully qualified namespace, either in the method declaration like you had or the Imports statement that @Curtis_Waguespack used. 

 

The only way I could get a common location to work was to put in a full path. Unfortunately, system variables like %appdata% don't work.

 

AddVbFile "C:\Users\Public\Documents\Autodesk\iLogicVb\ThisAssemblyExtensions.iLogicVb"

 

EDIT: I did see in the Help that there are a few default imports, including Autodesk.iLogic.Interfaces. It just doesn't handle them the same way as if you typed it in yourself.

Message 15 of 20

WCrihfield
Mentor
Mentor

OK.  I thing I understand what you meant now.  In that case, you may simply need look into your iLogic configuration settings.  On the Tools tab, and within the Options panel (may need to be expanded down to see it), it a button named "iLogic Configuration".  Click on that, then within that pop-up dialog labeled "Advanced iLogic Configuration", make sure that exact, lowest level folder is listed directly in the 'External Rule Directories' section of that dialog.  This tells it where to look for your external iLogic rule files.  I have one main folder, with a bunch of sub folders.  Pretty much all of my external rules are within the sub folders, but each sub folder must be put into that list directly.  It will not find rules within sub folders otherwise.  And you may need to make sure your external rule file extension matches what you are specifying.  If you ever change that, then you may have to rename all your pre-existing external rule files.

WCrihfield_0-1744901104713.png

Once that is set-up property, you should no longer need to specify the full file path of your external rules anywhere.

 

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 16 of 20

Curtis_Waguespack
Consultant
Consultant

edit: it looks like WCrihfield already replied about the config path while I was typing. 🙂

 

@jjstr8 , Just to add clarification on the paths, and in case it helps someone in the future,  I just have the external rule directory set in the iLogic Configuration paths, and then just used AddVbFile "MyHelperFunctions" as shown in the earlier screen shot to point to the external helper rule. I tried it with MyHelperFunctions.txt and MyHelperFunctions.iLogicVB and both worked.

 

Curtis_Waguespack_0-1744901605634.png

 

@WCrihfield , I'll keep and eye out for the errors you mention concerning the AddReference in the helper rule.

 

Thanks again, guys!

 

EESignature

Message 17 of 20

jjstr8
Collaborator
Collaborator

I just wish there were pre-defined folders like they have for addins, so the end-user doesn't have to do anything with the iLogic Configuration.

Message 18 of 20

WCrihfield
Mentor
Mentor

I agree that would simplify things.  But I would still like for that folder location to be specified in a relatively easy to find place, and be able to change it, if needed.  Even though we still do not have Vault yet (they keep saying 'soon'), we store pretty much everything (even settings) in a local server on the network.  That way everyone in the whole Engineering department is using accessing the same files, and mostly all the same settings (except for a few customizations).  And the buttons we put into our ribbons all point to the same external iLogic rules & global iLogic forms.  They did include the 'iLogic configuration' settings within the ones that get exported with the application options, but they did not combine the dialog for accessing those settings into the application options, since iLogic is still considered an add-in.  Maybe one day they will make it part of the main application, instead of just an add-in, then merge the settings into one place, to make them easier to find.  I'm not going to hold my breath for that one though.  They seem to cater to everyone's pre-existing code based solutions that such a change might break the functionality of.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)

Message 19 of 20

Curtis_Waguespack
Consultant
Consultant

@jjstr8 , in case it's of interest, we can set the path programmatically, see examples below. You can have this set from an addin that runs on startup, or have it done as a "precheck" for any rule that depends on the path, or use a PowerShell script, etc.

 

There's another workaround that I've used in some cases, where we utilize the Startup action template to run a rule (or rules) internal to that file to set the path, and do other admin tasks. Going from memory, but I think we used this to create a file from the specified template, triggered the internal rule with the On New Event Trigger, and then we just closed the new file after, so the user just saw a blip when starting Inventor, but then everything was as if there was no startup action.

 

 

Curtis_Waguespack_2-1744906857322.png

 

Add External iLogic Rule Path to Configuration

'Add path to External iLogic rule folder for the project
oPath = "C:\Temp\iLogic Rules"

'Get current list of External iLogic directories
Dim oExternalRuleDirectories As New List(Of String)
oExternalRuleDirectories.AddRange(iLogicVb.Automation.FileOptions.ExternalRuleDirectories)

If oExternalRuleDirectories.Contains(oPath) Then Exit Sub	

If System.IO.Directory.Exists(oPath) Then
	oExternalRuleDirectories.Add(oPath)
End If

iLogicVb.Automation.FileOptions.ExternalRuleDirectories = oExternalRuleDirectories.ToArray



Add External iLogic Rule Path Sub Directories to Configuration

'Add path to External iLogic rule folder for the project
oPath = "C:\Temp\iLogic Rules"

'Get current list of External iLogic directories
Dim oExternalRuleDirectories As New List(Of String)
oExternalRuleDirectories.AddRange(iLogicVb.Automation.FileOptions.ExternalRuleDirectories)

If oExternalRuleDirectories.Contains(oPath) Then Exit Sub	

If System.IO.Directory.Exists(oPath) Then
	oExternalRuleDirectories.Add(oPath)
End If

iLogicVb.Automation.FileOptions.ExternalRuleDirectories = oExternalRuleDirectories.ToArray"

'Get current list of External iLogic directories
Dim oExternalRuleDirectories As New List(Of String)

'optional remove all existing paths
iLogicVb.Automation.FileOptions.ExternalRuleDirectories = oExternalRuleDirectories.ToArray

oExternalRuleDirectories.AddRange(iLogicVb.Automation.FileOptions.ExternalRuleDirectories)

Dim subdirs As IEnumerable(Of String) = System.IO.Directory.EnumerateDirectories(oPath, "*", System.IO.SearchOption.TopDirectoryOnly)
subdirs = subdirs.Prepend(oPath)

For Each oSubDir In subdirs
	
	If oExternalRuleDirectories.Contains(oSubDir) Then Continue For

	If System.IO.Directory.Exists(oSubDir) Then
		oExternalRuleDirectories.Add(oSubDir)
	End If
Next

iLogicVb.Automation.FileOptions.ExternalRuleDirectories = oExternalRuleDirectories.ToArray

 

EESignature

Message 20 of 20

WCrihfield
Mentor
Mentor

Nice.  I have a similar one.  It finds my main folder for all things 'external iLogic', then makes sure that folder, and all of its sub folders are included in that configuration list.  And Optionally sort the folders alphabetically, to make the categories easier to navigate in the iLogic DockableWindow.  Much faster than manually doing it, but very seldomly used.

Wesley Crihfield

EESignature

(Not an Autodesk Employee)