I have had to step away from this project for a little while to work on other projects. However, I do plan to return to complete the version @A.Acheson suggests that does not require a second "flag" in the iProperties to help find the folder the actual assembly lives in.
In the meantime, I will post the version I have that is working and explain how to implement it so others can use it, if they desire.
The code is heavily commented for users like me that don't really know what they are doing (creating those comments helped me to understand what the code is doing). Next there is a section with general notes. After the general notes is a copy of the code (commented out) that has minimal comments for those that want a cleaner version to work with.
Here is the code:
'[ Heavily commented version with Logger.Info() outputs for debugging. Logger.Info() calls are commented out.
Sub Main()
' Declaring variable "oAsmDoc" and setting its datatype to Assembly Document object.
Dim oAsmDoc As AssemblyDocument
' Setting oAsmDoc equal to the currently opened document. "This Application" represents Inventor,
'"Active Document" represents the document that is currently active in Inventor.
oAsmDoc = ThisApplication.ActiveDocument
'Iterate through all of the occurrences
' Declaring variable "oOcc" and setting its datatype to be an occurrence of a component.
Dim oOcc As ComponentOccurrence
' "For Each" will look at all component occurrences ("oOcc") in the currently active assembly document ("oAsmDoc")
For Each oOcc In oAsmDoc.ComponentDefinition.Occurrences
' Declaring a variable "oOccDoc" and setting its datatype to be a document.
Dim oOccDoc As Document
' Setting the variable "oOccDoc" equal to the definition of the document so we can check if it is a part or not.
oOccDoc = oOcc.Definition.Document
' Verify that the document is a part and carry out the task if it is.
If oOccDoc.DocumentType = DocumentTypeEnum.kPartDocumentObject Then
' Declaring a variable "oPartDoc" and setting its datatype to be a Part Document, then setting the "oPartDoc" variable
' equal to the occurrence "oOccDoc"
Dim oPartDoc As PartDocument = oOccDoc
' Declaring variables "check", "AssyNum" and "FileLoc" that will be used to call functions and store their returned
' values as strings.
Dim check As String
Dim AssyNum As String
Dim FileLoc As String
'Call the function to check for the flag, passing to the function oPartDoc (the variable that represents a specifc
' occurrence a part in the active assembly).
check = CC_AssyCheck(oPartDoc)
' If the returned value from the function ("Ans" which was set eaual to "check" by the function call) is set to Yes
' do the following ....
If check = "Yes" Then
'PUll the Part Name field from iProperties.
' Call the function that will pull the part number (which is the file name of the assembly to replace with) from the
' iProperties. Pass "oPartDoc" to this function as the work needs to be done on the document (digging through iProp).
AssyNum = PullNum(oPartDoc)
'Pull the folder name
' Call the function that will pull the folder name (a [custom] value in the iProperties) just like the PullNum()
' function. Pass "oPartDoc" to this function as the work needs to be done on the document (digging through iProp).
FoldL = PullFolder(oPartDoc)
Logger.Info("AssyN Main Sub: " & AssyNum)
Logger.Info("//////Fodler Name: " & FoldL)
'Find the desired assembly file to replace with.
' Call the function that will check a specific directory to try and find the desired assembly to replace with.
' Pass to this function "AssyNum" (the file name of the assembly to replace with found by PullNum() function),
' "FoldL" (the folder name pulled from the iProperties by the PullFolder() function), and "oPartDoc" (the
' representative part file that will be replaced with the desired assembly. The returned value from this function
' is set equal to "FileLoc" which really doesn't matter becasue this variable is not used anywhere.
FileLoc = FindAssyFile1(AssyNum, FoldL, oPartDoc)
Logger.Info("AssyN Main Sub: " & AssyNum)
End If
' There is nothing for if "check" comes back as "No" or null becasue we don't want the code to do anything in that
' case. We only want the code to do something if the CC_AssyCheck() function returns a "Yes" value (check = "Yes")
End If
' There is nothing for if oOccDoc comes back as a document type other than a part file becuase we only want to look
' at part files, ... skip everything else.
Next
End Sub
' This function looks for the flag to see if "CC Assy?" is set to Yes or No. "CC Assy?" is a custom iProperty.
' "Yes" and "No" must have first letter capitalized in iProperties for code to recognize the flag.
Function CC_AssyCheck(oPartDoc) As String 'Function output is defined to be a string.
Try
' Get the User Defined property set.
' Decalring variable "UserDefinedPropSet" to be a Property Set object.
' This is the first step in drilling down to the desired level of the API Object Model which is needed to pull properties.
' Go to this link to see the Inventor API Object Model:
' https://damassets.autodesk.net/content/dam/autodesk/www/pdfs/Inventor2022ObjectModel.pdf
Dim UserDefinedPropSet As PropertySet
' Setting the "UserDefinedPropSet" equal to the a custom property. So, basically, looking at the part document (oPartDoc),
' and then the property sets "Inventor User Defined Properties". "Inventor User Defined Properties" is where you go to
' access custom iProperties.
UserDefinedPropSet = oPartDoc.PropertySets.Item("Inventor User Defined Properties")
' Get the custom property "CC Assy?" value.
' Now that we are at the custom property level, we tell the code to look at the specific custom property "CC Assy?"
' Declaring a new variable "AssyCheck" and setting it to be an Inventor property.
Dim AssyCheck As Inventor.Property
' Setting "AssyCheck" equal to the value contained in the custom iProperty "CC Ass?".
' UserDefinedPropSet is already at the Custom (or Inventor User Defined Properties) level. Now we need to tell it which
' custom property to look at.
AssyCheck = UserDefinedPropSet.Item("CC_Assy?")
' Now "AssyCheck" is looking at the custom property "CC Assy?"
' Declare a new variable to hold the value stored under the custom iProperty "CC Assy?"
Dim Ans As String
' Set the new variable ("Ans") equal to the value stored in iProperties uncer "CC Assy?"
Ans = AssyCheck.Value
' Now "Ans" holds whatever was typed into the "CC Assy?" field in the iProperties of part. This should be a "Yes" or "No".
' If value in "CC Assy?" is set to "Yes", then do the following ....
If Ans = "Yes" Then
Logger.Info("Yes: " & oPartDoc.DisplayName)
' Function returns "Yes"
Return Ans
ElseIf Ans = "No" Then
' If Value in "CC Assy?" is set to "No", do nothing.
Logger.Info("No: " & oPartDoc.DisplayName)
End If
Catch
' We only want the code to act if it finds a "Yes" in the "CC Assy?" field. So, leave "Catch" blank so the code does
' nothing if if finds anything other than "Yes" in the "CC Assy?" field.
End Try
End Function
' This function pulls the part number for the assembly from the "Part Name" field (this is the title field in
' Iventor iProperties).
Function PullNum(oPartDoc) As String 'Function output is defined to be a string.
Try
' Declare a variable "AssyN" to be a string
Dim AssyN As String
' Get the PropertySets object.
' Declare a variable "oPropSets" to be a PropertySets object.
' This is the first step in drilling down to the desired level of the API Object Model which is needed to pull properties.
' Go to this link to see the Inventor API Object Model:
' https://damassets.autodesk.net/content/dam/autodesk/www/pdfs/Inventor2022ObjectModel.pdf
Dim oPropSets As PropertySets
' Setting "oPropSets" equal to a property set in the document (oPartDoc)
oPropSets = oPartDoc.PropertySets
' Get the design tracking property set.
' Declare a new variable "oPropSet" and set it to be a Property Set object.
Dim oPropSet As PropertySet
' Setting oPropSet equal to the Inventor Summary Information (drilling into the API to get to the iProp field we want.
oPropSet = oPropSets.Item("Inventor Summary Information")
' Get the part number iProperty.
' Declaring a new variable "oPartNumiProp" and setting its datatype to be and Inventor property
Dim oPartNumiProp As Inventor.Property
' The Inventor property "oPartNumiProp" will hold is the "Title" property from the iProperties
oPartNumiProp = oPropSet.Item("Title")
Logger.Info("Part Number: " & oPartNumiProp.Value)
' Decalring a new variable "AssyN" and setting it equal to the valu stored in the title field of the iProperties.
' This is the value we want from the function
AssyN = oPartNumiProp.Value
Logger.Info("----AssyN in Function is: " & AssyN)
' Output an error message if there is no assembly number stored in the representative part file. This means the part file
' did not have its iProperties properly filled out before it was published. The part will need to be fixed (add in assembly
' part number un the "Title" field of iProperties ("Part Name" field in iPropWiz).
' The error checking will look for null value.
If AssyN = ""
MessageBox.Show(oPartDoc.DisplayName &" does not contain the part number of the desired assembly",
"No Assembly Number Found")
' Error checking: If there is a space and no characters
ElseIf AssyN = " " Then
MessageBox.Show(oPartDoc.DisplayName &" does not contain the part number of the desired assembly",
"No Assembly Number Found")
' Error checking: If there are 2 spaces and no characters
ElseIf AssyN = " " Then
MessageBox.Show(oPartDoc.DisplayName &" does not contain the part number of the desired assembly",
"No Assembly Number Found")
End If
' Return the desired value from the function. In this case, the value stored in the Title field of the iProperties which
' will be the part number for the assembly we want.
Return AssyN
Catch
' If the try fails, do nothing. There is error checking in the Try statement
End Try
End Function
'This function pulls the folder name the assembly is located in, which is a user input in the iProperties.
' oPartDoc is passed to this function as the work needs to be done on this variable (i.e. we are looking at
' document properties).
Function PullFolder(oPartDoc) As String
Try
' The next 8 lines (including blank lines) are drilling down the API to get to the Custom iProperty "Assy Folder"
' the same as the CC_AssyCheck() function and PullNum() function above.
Dim UserDefinedPropSet2 As PropertySet
UserDefinedPropSet2 = oPartDoc.PropertySets.Item("Inventor User Defined Properties")
Dim FolderN As Inventor.Property
FolderN = UserDefinedPropSet2.Item("Assy Folder")
Dim FolderLoc As String
FolderLoc = FolderN.Value
Logger.Info("----Folder Name in Function is: " & FolderLoc)
' Error checking the same as the PullNum() function.
If FolderLoc = "" Then
MessageBox.Show(oPartDoc.DisplayName &" does not contain the folder name the desired assembly resides in",
"No Folder Name Found")
ElseIf FolderLoc = " " Then
MessageBox.Show(oPartDoc.DisplayName &" does not contain the folder name the desired assembly resides in",
"No Folder Name Found")
ElseIf FolderLoc = " " Then
MessageBox.Show(oPartDoc.DisplayName &" does not contain the folder name the desired assembly resides in",
"No Folder Name Found")
End If
' Return the value that was pulled from the custom iProperty "Assy Folder".
Return FolderLoc
Catch
' If the Try fails, do nothing. There is error checking the Try statement.
End Try
End Function
' This function will perform a search in the specified directory to try and located the assembly file.
' Once the search finds the assembly file the replace command will be executed.
' Pass to this function the variable "AssyNum" (the file name (or part number for the assembly) we want to find),
' the variable "FolderL" (the folder name in which the assembly file resides. We will need this to build the search directory)
' and the variable "oPartDoc" (the part document we want to replace. Needed for the replace command).
Function FindAssyFile1(AssyNum, FolderL, oPartDoc)
' Declare a variable "SearchLocation" to be equal to the directory (one folder above the folder name stored in the
' custom iProperty "Assy Folder". The directory has to be built using the variable "FolderL"
Dim SearchLocation As String = "R:\__RND CUSTOM & PACKAGING\Purchased\" & FolderL & "\"
' Declare a new variable "FN" and set its datatype to be a string
Dim FN As String
' Build the full assembly file name with extension.
FN = AssyNum & ".iam"
Logger.Info("FN Value is: " & FN)
Logger.Info("Search Directory is: " & SearchLocation)
Logger.Info("----+++++Folder Name in Function is: " & FolderL)
Try
' Declare a new variable "dirs" and set its datatype to be an array of strings.
Dim dirs As String()
' Find and populate the "dirs" array with all the matching criteria. If none are found, the Try fails and moves to the Catch
dirs = System.IO.Directory.GetFiles(SearchLocation, FN)
' Declare a new variable "fullPath" and set its datatype to be a string.
Dim fullPath As String
' Set "fullPath" equal to the first string in the array of strings stored in "dirs". This will be the
fullPath = dirs.GetValue(0)
Logger.Info("~~~~~~ Bottom ~~~~~~ FullPath Value is: " & fullPath)
Catch
FileLoc2 = FindAssyFile2(AssyNum, FolderL, oPartDoc)
Return FileLoc
End Try
Dim strRelativeFileLocation As String = SearchLocation & FN
Component.Replace(oPartDoc.DisplayName & ":1", strRelativeFileLocation, True)
Logger.Info("Complete path is: " & strRelativeFileLocation)
End Function
Function FindAssyFile2(AssyNum, FolderL, oPartDoc)
Dim SearchLocation2 As String = "R:\__RND CUSTOM & PACKAGING\Assembly\" & FolderL & "\"
Dim FN As String
FN = AssyNum & ".iam"
Logger.Info("2nd Fnctn FN Value is: " & FN)
Logger.Info("2nd Fnctn Search Directory is: " & SearchLocation2)
Logger.Info("----2nd Fnctn+++++Folder Name in Function is: " & FolderL)
Try
Dim dirs2 As String ()
dirs2 = System.IO.Directory.GetFiles(SearchLocation2, FN)
Dim fullPath2 As String
fullPath2 = dirs2.GetValue(0)
Catch
' This is a more complicated error message box. The Catch puts this message box out if the try fails. If the Try fails
' that means the assembly file could not be found.
MessageBox.Show("The File " & FN & " cannot be found in Purchased Folder Or Assembly Folder" _
& vbNewLine & "" _
& vbLf & "Please make sure an assembly file exsists in the Purchased or Assembly Folder" _
& vbNewLine & "" _
& vbCrLf & "Assembly file not found", "Cannot Find Assembly in Folders",MessageBoxButtons.OK,MessageBoxIcon.Error)
Return FileLoc2
End Try
Dim strRelativeFileLocation2 As String = SearchLocation2 & FN
Component.Replace(oPartDoc.DisplayName & ":1", strRelativeFileLocation2, True)
Logger.Info("Complete path is: " & strRelativeFileLocation2)
End Function
']
'[ General Notes:
' The flag in the iProperties ("CC Assy?") must be a string value and connot be boolean (Yes/No) because the API
' will try to write to the iProperties when it reads the boolean values for some reason. A CC part placed as standard
' cannot be written to as it will be a read-only file. Due to the naute of the read-only state of standard CC files,
' Boolean cannot be used for this application.
' Logger.Info() is for debugging purposes only and outputs data to the "iLogic Log" bowser tree in Inventor.
' If additional directories need to be searched, simplay add another FindAssyFile#(AssyNum, FolderL, oPartDoc) function and
' place its call in the Catch of the last FindAssyFile#(AssyNum, FolderL, oPartDoc) function. Keep the error message in the
' Catch of the last FindAssyFile#(AssyNum, FolderL, oPartDoc) function.
'To see the Autodesk forum post that concerns the creation of this rule, go to this link:
'
'https://forums.autodesk.com/t5/Inventor-ilogic-api-vba-forum/work-around-To-Get-assemblies-In-content-center-Using-quot-flags/td-p/10796274/highlight/False
']
'*******************************************************************************************************************
'[ Clean version of the code with very few comments. Highlight everything and click "Uncomment the selected lines" button once.
'Sub Main()
' Dim oAsmDoc As AssemblyDocument
' oAsmDoc = ThisApplication.ActiveDocument
''Iterate through all of the occurrences
'Dim oOcc As ComponentOccurrence
'For Each oOcc In oAsmDoc.ComponentDefinition.Occurrences
' Dim oOccDoc As Document
' oOccDoc = oOcc.Definition.Document
' If oOccDoc.DocumentType = DocumentTypeEnum.kPartDocumentObject Then
' Dim oPartDoc As PartDocument = oOccDoc
' Dim check As String
' Dim AssyNum As String
' Dim FileLoc As String
' ' Check the flag.
' check = CC_AssyCheck(oPartDoc)
' If check = "Yes" Then
' 'PUll the Part Name field from iProperties.
' AssyNum = PullNum(oPartDoc)
' 'Pull the folder name from the iProperties.
' FoldL = PullFolder(oPartDoc)
' 'Find the desired assembly file to replace with.
' FileLoc = FindAssyFile1(AssyNum, FoldL, oPartDoc)
' End If
' End If
'Next
'End Sub
'' Check the flag "CC_Assy?" to see if it stores a "Yes" or a "No"
'Function CC_AssyCheck(oPartDoc) As String
' Try
' ' Get the User Defined property set.
' Dim UserDefinedPropSet As PropertySet
' UserDefinedPropSet = oPartDoc.PropertySets.Item("Inventor User Defined Properties")
' ' Get the custom property "CC Assy?" value.
' Dim AssyCheck As Inventor.Property
' AssyCheck = UserDefinedPropSet.Item("CC_Assy?")
' Dim Ans As String
' Ans = AssyCheck.Value
' If Ans = "Yes" Then
' Return Ans
' ElseIf Ans = "No" Then
' End If
' Catch
' End Try
'End Function
'' Get the part number of the assembly
'Function PullNum(oPartDoc) As String
' Try
' ' Declare a variable "AssyN" to be a string
' Dim AssyN As String
' ' Get the PropertySets object.
' Dim oPropSets As PropertySets
' oPropSets = oPartDoc.PropertySets
' ' Get the design tracking property set.
' Dim oPropSet As PropertySet
' oPropSet = oPropSets.Item("Inventor Summary Information")
' ' Get the part number iProperty.
' Dim oPartNumiProp As Inventor.Property
' oPartNumiProp = oPropSet.Item("Title")
' AssyN = oPartNumiProp.Value
' If AssyN = ""
' MessageBox.Show(oPartDoc.DisplayName &" does not contain the part number of the desired assembly",
' "No Assembly Number Found")
' ElseIf AssyN = " " Then
' MessageBox.Show(oPartDoc.DisplayName &" does not contain the part number of the desired assembly",
' "No Assembly Number Found")
' ElseIf AssyN = " " Then
' MessageBox.Show(oPartDoc.DisplayName &" does not contain the part number of the desired assembly",
' "No Assembly Number Found")
' End If
'Return AssyN
' Catch
' End Try
'End Function
''This function pulls the folder name the assembly is located in, which is a user input in the iProperties.
'Function PullFolder(oPartDoc) As String
' Try
' Dim UserDefinedPropSet2 As PropertySet
' UserDefinedPropSet2 = oPartDoc.PropertySets.Item("Inventor User Defined Properties")
' Dim FolderN As Inventor.Property
' FolderN = UserDefinedPropSet2.Item("Assy Folder")
' Dim FolderLoc As String
' FolderLoc = FolderN.Value
' If FolderLoc = "" Then
' MessageBox.Show(oPartDoc.DisplayName &" does not contain the folder name the desired assembly resides in",
' "No Folder Name Found")
' ElseIf FolderLoc = " " Then
' MessageBox.Show(oPartDoc.DisplayName &" does not contain the folder name the desired assembly resides in",
' "No Folder Name Found")
' ElseIf FolderLoc = " " Then
' MessageBox.Show(oPartDoc.DisplayName &" does not contain the folder name the desired assembly resides in",
' "No Folder Name Found")
' End If
' Return FolderLoc
' Catch
' End Try
'End Function
'Function FindAssyFile1(AssyNum, FolderL, oPartDoc)
' Dim SearchLocation As String = "R:\__RND CUSTOM & PACKAGING\Purchased\" & FolderL & "\"
' Dim FN As String
' FN = AssyNum & ".iam"
' Logger.Info("FN Value is: " & FN)
' Logger.Info("Search Directory is: " & SearchLocation)
'Logger.Info("----+++++Folder Name in Function is: " & FolderL)
' Try
' Dim dirs As String ()
' dirs = System.IO.Directory.GetFiles(SearchLocation, FN)
' Dim fullPath As String
' fullPath = dirs.GetValue(0)
' Catch
' FileLoc2 = FindAssyFile2(AssyNum, FolderL, oPartDoc)
' Return FileLoc
' End Try
'Dim strRelativeFileLocation As String = SearchLocation & FN
' Component.Replace(oPartDoc.DisplayName & ":1", strRelativeFileLocation, True)
'End Function
'Function FindAssyFile2(AssyNum, FolderL, oPartDoc)
' Dim SearchLocation2 As String = "R:\__RND CUSTOM & PACKAGING\Assembly\" & FolderL & "\"
' Dim FN As String
' FN = AssyNum & ".iam"
' Logger.Info("2nd Fnctn FN Value is: " & FN)
' Logger.Info("2nd Fnctn Search Directory is: " & SearchLocation2)
' Try
' Dim dirs2 As String ()
' dirs2 = System.IO.Directory.GetFiles(SearchLocation2, FN)
' Dim fullPath2 As String
' fullPath2 = dirs2.GetValue(0)
' Catch
' MessageBox.Show("The File " & FN & " cannot be found in Purchased Folder Or Assembly Folder" _
' & vbNewLine & "" _
' & vbLf & "Please make sure an assembly file exsists in the Purchased or Assembly Folder" _
' & vbNewLine & "" _
' & vbCrLf & "Assembly file not found", "Cannot Find Assembly in Folders",MessageBoxButtons.OK,MessageBoxIcon.Error)
' Return FileLoc2
' End Try
'Dim strRelativeFileLocation2 As String = SearchLocation2 & FN
' Component.Replace(oPartDoc.DisplayName & ":1", strRelativeFileLocation2, True)
'End Function
']
Here is the idea and how it works:
First, create two custom iProperties. The first custom iProperty will be called "CC_Assy?" and it takes a string of text input ("Yes" or "No"). As @A.Acheson pointed out in an earlier post, there was a problem using boolean for the Yes and No, so Use a sting. The second custom iProperty will be called "Assy Folder" and it holds a string of text. This string will be the folder name the actual assembly live in.
Here is an example:

Now that you have created the new custom iProperties, you need to create a representative part (a simple .ipt that will go into the CC and represent the assembly you want).
Here is a good way to create one of those parts:
Create a simple .ipt (thin extruded rectangle, for example) and place a screen shot of the assembly this .ipt will represent in a sketch on the front face of the part. ... Maybe create another sketch that has the text "RUN CONTENT CENTER ASSEMBLY RULE" to remind users what they need to do.
Here is an example of one I made to represent an electrical box.

Now that we have a representative part, we need to fill out the iProperties so the code will find what it needs to carry out the replace.
Let's say the assembly you want to put in the Vault has filename "A10001003.iam" and it lives in a folder called "A10001000-10001999"

The part of the directory in the yellow box is hard coded into the rule. You will have to find that line of code and replace it with your actual directory.
Go back to your representative part and fill in the iProperties:
Put the filename (without the extension) In the "Title" field under the "Summary" tab

Now click over to the "Custom" tab and fill in the two custom iPropeties you created.
"CC_Assy?" gets a "Yes" value and in the "Assy Folder" field, input the folder name.

Now, publish the representative part to the content center.
Now you are ready to use it and "pull" and assembly from the CC.
Place your representative part from the CC into an assembly and run the rule.
Your part will be automatically replaced with the assembly you assigned to the representative part.
I hope this makes sense and is useful to someone!!
As I mentioned earlier in this post, I do plan to return to this project at some point to finish the other version that searches all folders and sub folders so you don't need to create an iProperty for the folder name. Right now, that code works, but is very slow as it searches EVERYTHING. I cannot use that version as it is due to the time it takes to run and find the correct files; my team will complain!! ... It just needs to be tuned up a little to work quicker. Once I finish that, or make any adjustments, I will post it to this thread.
..... Before I finish the next version, I will probably need to work on getting this version to work with Vault.
Updates to come (probably not very soon, though).
Extra special thanks to @A.Acheson for all the help!!!!
Also, thanks to @theo.bot for helpful information and resources!!!!