- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I have many assemblies and simplified parts with broken references due to renamed or relocated files.
Most of the time, these broken references are due to a restructured file organization, where about 80% of all created parts are now stored in a single folder (let’s say the path is "C:\BigFolder") without much change to their names.
When a file has lost a reference due to a name change, it's primarily because the naming format was originally like this:
[Partnumber as numbers only] [Partname]
And now it’s structured like this:
[Partnumber as numbers only]_[Partname]
As you can see, the only change is that the seperator is now an underscore instead of a space.
My idea was to let the macro search for the first part of the name (the part number) in "C:\BigFolder" and attempt to find the closest match. If nothing is found, the macro should skip to the next broken reference.
Since the name is very similar and the new path is known, I think automating this should be possible.
Mmaybe with a VBA macro, iLogic or something similar.
But I'm unsure how to start—both in terms of accessing the reference data and implementing the macro.
Does anyone have ideas on automating this?
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Hi @torben_schroederXDK86. I don't think I have ever created a whole automation solution for a situation like this, but I know that I have touched on this general subject on multiple occasions in the past. If I remember correctly the following method was involved in that process.
DesignProjectManager.ResolveFile
As you can see, that method is associated with the DesignProjectManager Inventor API object. That object can be found directly under the Application object (ThisApplication). I have not really use that method that much myself, because I always resolve files manually, using the user interface dialogs, so I can not offer much as far as working examples of using it, or more detailed explanations about how it works beyond what its help page offers.
Another method that may get involved is this one:
FileDescriptor.ReplaceReference
As you can see, it is associate with the FileDescriptor Inventor API object. And that FileDescriptor object can be obtained from 4 different possible routes, as you can see under its 'Accessed From' portion of its online help page.
But you should understand the difference between an Inventor.Document and an Inventor.File first. The File is essentially only the data that has been written to a long term storage medium, like a hard drive, or USB drive. The Document is essentially an organized group of data being held within Inventor's session memory. The document can exist before or after a File exists for it, because we can create new documents that have not been 'saved' yet. And when a Document does have a File associated with it, it may contain different data than what is in the File, because we may have made changes to the Document that have not yet been saved to that file. There are several different derived types of Documents, and each of them has a ReadOnly Property named File, that is supposed to return an Inventor.File type object, if it exists. That File object has the File.ReferencedFileDescriptors property, which will return a FileDescriptorsEnumerator object. You may also notice that the File has a File.HasLoadedDocuments property, which tells us that it is possible for a single File to contain the required data for defining multiple Documents. Good to keep in mind. Pay no attention to the HasReferencingFiles property, or the ReferencingFiles properties, because they will only be able to contain anything when those other Files &/or documents are currently loaded into Inventor's session memory, other wise they can not be found by Inventor. That's something that Vault can likely help with, if you have it. Back to the FileDescriptorsEnumerator...as you iterate through that collection, you encounter individual FileDescriptor objects, like we mentioned earlier.
Wesley Crihfield
(Not an Autodesk Employee)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Thank you for your detailed answer. It seems like I’ll need to look for it myself, for now.
What I still don’t quite understand is how to interact with the resolve-file dialog. My knowledge of this area in VBA is quite limited. Could you explain what I can do to make a macro run before the file resolve dialog appears? I imagine that once it pops up, I won’t be able to run my macro until the window is closed...
Maybe there’s something I could try using a trigger when opening a document.
Do you have any other ideas?
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Hi @torben_schroederXDK86. After seeing your response this morning, and thinking about it, I thought I remembered there being an 'Event' related to this 'action' , so I looked around a bit and found it. There is an object called FileAccessEvents right under the main Application object, and it has an Event named OnFileResolution which seems to be what you are looking for. Its online help page actually contains quite a bit of useful information that you should review.
I would recommend that you avoid creating new automation content within VBA, and stick with iLogic rules. The folks at Autodesk stopped including VBA in their standard installations back around 2021, partially due to security issues (among others). This is partially because Microsoft stopped maintaining & developing that coding resource several years ago. It is also pretty outdated now. However, because it is 'free' to use, readily available to Inventor users, and its user interface is more advanced than the iLogic rule editor dialog, it can still be valuable resource to keep handy. Many people like its visual Object Browser dialog, and the information that its Locals window can provide, when researching new objects or new functionality. The iLogic add-in allows us to use the newer VB.NET coding system, and iLogic rules are much easier to 'trigger' to run when events happen. However, when it comes to Inventor API Events (as opposed to just using the iLogic Event Triggers dialog), these are usually best managed by an Inventor add-in than with iLogic rules.
Since you seem relatively new to coding with VBA (and possibly with iLogic too), then you may also be relatively new to 'handling events' by code, without relying on the iLogic Event Triggers dialog, so I will attach a PDF document that you can review, which may help familiarize you with this concept. It can be a bit advanced, and challenging to manage properly at first. That document is several years old now, and could likely use some updating, but it will at least get you started in that direction.
Wesley Crihfield
(Not an Autodesk Employee)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
You're a saint—thank you!
The solution I’ve come up with so far avoids interacting with the resolve-file dialog entirely. Instead, I run a macro that replaces all parts with missing references (ActiveDocument.File.ReferencedFileDescriptors.ReferenceMissing).
What it essentially does is loop through every file in the active assembly (or drawing for that matter, if I set the FileReference as ReferencedObjects(1)) and check if the file has a reference. If it doesn’t, it replaces it; otherwise, it moves to the next file.
I’ll look into the PDF and the event you mentioned—thank you again.
You guessed right that I’m still fairly new to VBA. I didn’t realize it was so… “frowned upon,” it seems. Besides VB.NET being newer, are there any other major differences from VBA? I’ll make sure to dive deeper into it.
edit:
would you be interested in the code at all? (is it normal in this forum that if somebody has come up with his own solution that the code is posted as a solution?)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Hi @torben_schroederXDK86. I'm glad that I was able to help you develop a working solution.
Using VBA (Visual Basic for Applications) is not really frowned upon. It is just highly recommended (not just by me, but by the majority of the coding/automation community) that, because VBA has not been maintained in several years, and there are newer/better options readily available to us, that any 'new' automation solution development be done using one of the newer systems. Since Inventor's iLogic system has been around for a great many years now, and it has been using the newer VB.NET coding language for most of those years, it just seems like the next logical step in that direction. And since VB.NET sort of shares some of the same ancestry as VBA, stepping forward into VB.NET is 'relatively' easy. Likely easier for those with less experience using VBA, than for those with lots of experience using it. Many of the VBA sample programs that Autodesk has published in its online help, can be pretty easily converted to VB.NET so that they can be used in iLogic rule, by removing certain keywords (such as 'Set', 'Let', & 'Call'), and fixing a few minor formatting differences. VB.NET also lets you 'declare' and 'set' the value of a variable naturally in one line, while VBA generally requires 2 lines, so equivalent vb.net code is often shorter, or requires less code. There are too many differences & benefits to mention here. VB.NET offers many new features/functionality that were not present back in the VBA era also (Link). If you look at the history of VB.NET versions, there have been several 'branches' of it (Standard, Framework, Core), and lots of versions over the years, so it is not actually that 'new', just 'newer' than VBA. In fact, even the VB part of VB.NET may be nearing the end of its 'new development' phase, while other forms of the .NET platform (C#.Net, F#.NET, ASP.NET, and others) will likely continue forward if VB development ceases.
Yes, it is common or forum users to post the final code that they consider to be 'the solution' to their question/problem/challenge. However, it is not necessarily required. If your finished/final code contains anything that is personal/confidential/proprietary, then you can either change that part to something 'neutral/generic' or simply not post it at all, if you do not want to. It is relatively common for someone else to find a forum topic that closely matches what they are searching for, and appears to be 'solved', but the 'solution' is not included in the forum discussion, so they may ask for the solution to be posted, so they can benefit from it, or use it themselves to further their own automation goals. Some folks may simply not visit this forum much after asking a question and getting an answer though, so it is very possible that the 'solution' may never get posted, it was not posted originally. And yes, if you eventually post your own 'solution' to your own question/problem/challenge, it is OK to mark your own forum reply as the/a 'accepted solution'.
Wesley Crihfield
(Not an Autodesk Employee)
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
for everyone interested in my solution i came up with. its part german part english, sry.
Sub ResolveParts()
Dim oFile As Inventor.file
Dim oFileDescriptor As FileDescriptor
Dim oFileDescriptor2 As FileDescriptor
Dim oFileName As String
Dim PartNumber As String
Dim SearchFolder As String
Dim FileFound As String
Dim FileFoundTemp As String
Dim FileType As String
Dim RefMis As String
Dim Response As VbMsgBoxResult
Dim MacroRun As VbMsgBoxResult
Dim pos As Integer
Dim MatchingFiles() As String
Dim FileIndex As Integer
Dim test As String
Dim UnresolvedParts() As String ' Array for unresolved parts
Dim UnresolvedIndex As Integer
Dim MaxUnresolved As Integer
Dim UnresolvedIndex2 As Integer
Dim FoundButUnresolvableParts() As String ' Array for parts that were found but could not be resolved
Dim FoundButUnresolvableIndex As Integer
Dim ReferenceCount As Integer
Dim CurrentPartCounter As Integer
Dim oDoc As Document
Dim oApp As Application
Set oApp = ThisApplication
Set oDoc = oApp.ActiveDocument
' Set the search folder path
SearchFolder = "C:\BigFolder"
' Initialize array for unresolved parts
ReDim UnresolvedParts(0)
UnresolvedIndex = 0
MaxUnresolved = 50 ' Maximum unresolved parts before displaying a message
' Initialize array for parts that were found but not resolvable
ReDim FoundButUnresolvableParts(0)
FoundButUnresolvableIndex = 0
CurrentPartCounter = 0
If Not oDoc.DocumentType = kPartDocumentObject Then
replaceit:
' Get the active document's file reference
If oDoc.DocumentType = kDrawingDocumentObject Then
Set oFile = oDoc.ReferencedDocuments(1).file
Else
Set oFile = oDoc.file
End If
' Loop through each referenced file descriptor
For Each oFileDescriptor In oFile.ReferencedFileDescriptors
' Check if the reference is missing
If oFileDescriptor.ReferenceMissing Then
If Not nrr = 1 Then
nrr = 1
For Each oFileDescriptor2 In oFile.ReferencedFileDescriptors
If oFileDescriptor2.ReferenceMissing Then
ReferenceCount = ReferenceCount + 1
End If
Next
MacroRun = MsgBox("Would you like to try resolving the " & ReferenceCount & " parts?", vbYesNo + vbQuestion)
End If
If MacroRun = vbYes Then
RefMis = oFileDescriptor.FullFileName
RefMis = Right(RefMis, Len(RefMis) - InStrRev(RefMis, "\"))
' Extract the part number from the missing reference's filename
If InStr(RefMis, " ") > 1 Then
PartNumber = Left(RefMis, InStr(RefMis, " ") - 1)
ElseIf InStr(RefMis, "_") > 1 Then
PartNumber = Left(RefMis, InStr(RefMis, "_") - 1)
ElseIf InStr(RefMis, "-") > 1 Then
PartNumber = Left(RefMis, InStr(RefMis, "-") - 1)
Else
' No delimiter found, skip this iteration
GoTo NextFileDescriptor
End If
' Initialize the array for storing potential matches
FileIndex = 0
ReDim MatchingFiles(0)
' Search for a matching file in the specified folder
FileType = Right(RefMis, 4)
FileFoundTemp = SearchFolder & PartNumber & "*" & FileType
FileFound = Dir(FileFoundTemp, vbNormal)
' Collect all matching files in the array
Do While FileFound <> ""
ReDim Preserve MatchingFiles(FileIndex)
MatchingFiles(FileIndex) = FileFound
FileIndex = FileIndex + 1
FileFound = Dir
Loop
' If potential matches were found, prompt the user
If FileIndex > 0 Then
CurrentPartCounter = CurrentPartCounter + 1
For FileIndex = LBound(MatchingFiles) To UBound(MatchingFiles)
Response = MsgBox("The missing part was found:" & vbCrLf & vbCrLf & _
RefMis & " (old)" & vbCrLf & MatchingFiles(FileIndex) & " (new)" & vbCrLf & vbCrLf & _
"Would you like to resolve it?", _
vbYesNoCancel + vbExclamation, "Part " & CurrentPartCounter & "/" & ReferenceCount & " Resolve")
If Response = vbYes Then
' Resolve the reference using the selected file
On Error Resume Next
Dim TryCount As Integer
TryCount = 0
Do
Err.Clear
oFileDescriptor.ReplaceReference SearchFolder & MatchingFiles(FileIndex)
If Err.Number = 5 Then
TryCount = TryCount + 1
If TryCount >= 3 Then
' If not resolvable, add to array
ReDim Preserve FoundButUnresolvableParts(FoundButUnresolvableIndex)
FoundButUnresolvableParts(FoundButUnresolvableIndex) = RefMis
FoundButUnresolvableIndex = FoundButUnresolvableIndex + 1
MsgBox "Reference could not be replaced for: " & vbCrLf & RefMis, vbCritical
Exit Do
End If
Else
Exit Do ' Successfully replaced, exit loop
End If
Loop
On Error GoTo 0
Exit For
ElseIf Response = vbCancel Then
' Cancel the entire operation if the user selects Cancel
MsgBox "Procedure cancelled.", vbCritical
Exit Sub
End If
Next FileIndex
Else
' If no match was found, add to UnresolvedParts array
ReDim Preserve UnresolvedParts(UnresolvedIndex)
UnresolvedParts(UnresolvedIndex) = RefMis
UnresolvedIndex = UnresolvedIndex + 1
UnresolvedIndex2 = UnresolvedIndex2 + 1
' Check if 20 unresolved parts have been found
If UnresolvedIndex >= MaxUnresolved Then
MsgBox UnresolvedIndex2 & " unresolved parts were found:" & vbCrLf & Join(UnresolvedParts, vbCrLf), vbCritical, "Unresolved Parts"
UnresolvedIndex = 0
End If
End If
End If
If oDoc.DocumentType = kDrawingDocumentObject Then
oDoc.ReferencedDocuments(1).Update
oDoc.ReferencedDocuments(1).Update2
End If
End If
NextFileDescriptor:
Next
' Ausgabe der verbleibenden nicht auflösbaren Teile
If UnresolvedIndex > 0 Then
MsgBox "Die folgenden Teile konnten nicht in" & vbCrLf & vbCrLf & SearchFolder & vbCrLf & vbCrLf & "aufgelöst werden:" & vbCrLf & vbCrLf & Join(UnresolvedParts, vbCrLf), vbInformation, "Nicht auflösbare Teile"
Erase UnresolvedParts
UnresolvedIndex = 0
End If
' Ausgabe der Teile, die gefunden aber nicht auflösbar waren
' Zweiter Suchdurchgang in anderem Ordner
If Not nr = 2 Then
nr = 2
SearchFolder = Left(oDoc.FullFileName, InStrRev(oDoc.FullFileName, "\"))
GoTo replaceit
End If
If FoundButUnresolvableIndex > 0 Then
MsgBox "Die folgenden Teile wurden gefunden, konnten aber nicht aufgelöst werden:" & vbCrLf & vbCrLf & Join(FoundButUnresolvableParts, vbCrLf), vbCritical, "Gefunden aber nicht auflösbar"
End If
ThisApplication.ActiveDocument.Update
ThisApplication.ActiveDocument.Update2
Else
MsgBox "Bitte nur aus einer Baugruppe oder aus einer Zeichnung starten. Abgeleitete Baugruppen können nicht mit diesem Makro aufgelöst werden.", vbExclamation
End If
End Sub