Message 1 of 4
Apprentice Server with powershell, working prefix copy design
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
I am posting this so the community can figure out more items related to this so we can expand what we can do with this. I still need to figure out the renaming structure, but that should be easy. Now copy design should be possible from inventor as it reuses the current session for this.
powershell script:
# --------------------------------------------------------------------------------
# CopyDesign Script
#
# DESCRIPTION
# Copies an Inventor assembly (.iam), its referenced parts (.ipt/.iam), and
# drawing files (.idw/.dwg/.ipn) into a new folder, renames them with a user-
# specified prefix, relinks all references, suppresses iLogic rule execution,
# and updates the Part Number iProperty on each copied file.
#
# NAMING CONVENTION REQUIRED
# • Model files (.iam, .ipt):
# – Base name must start with '0' and end with '-<digits>'
# e.g. 0001-1.ipt, 0001-501.iam
# – OR base name must start with one of your custom prefixes
# (e.g. WIP_, WIP2_)
# • Drawing files (.idw):
# – Base name must start with '0' and end with a digit
# e.g. 0001.idw
# – OR start with one of your custom prefixes
# • Other drawing types (.dwg, .ipn) are always copied regardless of name.
# • Enter any extra prefixes when prompted; leave blank to skip.
#
# NOTE
# Inventor must be running. The script attaches to the existing session.
# --------------------------------------------------------------------------------
$ErrorActionPreference = 'Stop'
#
# ←—— USER INPUT Prompts ————————————————————————————————————————
#
# Prompt for the folder containing the top-level assembly
$asmDir = Read-Host 'Enter the directory containing the top-level assembly'
if (-not (Test-Path $asmDir)) {
Write-Error "Directory not found: $asmDir"
exit 1
}
# Prompt for the assembly filename (with or without .iam)
$inputName = Read-Host 'Enter the top-level assembly filename (with or without .iam extension)'
if ($inputName -match '\.iam$') {
$asmName = $inputName
} else {
$asmName = "$inputName.iam"
}
# Build the full assembly path
$asmPath = Join-Path $asmDir $asmName
if (-not (Test-Path $asmPath)) {
Write-Error "Assembly not found: $asmPath"
exit 1
}
# Prompt for the destination folder
$destDir = Read-Host 'Enter the destination directory for copied files'
if (-not (Test-Path $destDir)) {
New-Item -ItemType Directory -Path $destDir | Out-Null
}
# Prompt for the filename prefix
$prefix = Read-Host 'Enter the prefix to prepend to copied filenames (e.g. Copy_)'
if (-not $prefix) {
Write-Error "Prefix cannot be empty."
exit 1
}
#
# ←—— Existing logic below unchanged, using $asmPath, $destDir, $prefix ——————————————————
#
# Prompt for any existing filename prefixes (e.g. WIP_, WIP2_) that should be allowed
# even if they don’t follow the “0…-digit” naming rule
$custom = Read-Host 'Enter any any existing filename prefixes if they do not follow the naming rule, to include in the copy, (comma-separated for different ones), or press Enter to skip'
if ($custom) { $extraPrefixes = $custom -split ',' | ForEach-Object { $_.Trim() } } else { $extraPrefixes = @() }
# ApprenticeServer for CAD-only copy
$appr = New-Object -ComObject Inventor.ApprenticeServer
$asm = $appr.Open($asmPath)
$save = $appr.FileSaveAs
function Pref([string]$path) {
$prefix + ([IO.Path]::GetFileName($path))
}
# 1) queue the main assembly
$save.AddFileToSave(
$asm,
(Join-Path $destDir (Pref $asmPath))
)
# 2) queue filtered parts & sub-assemblies
foreach ($doc in $asm.AllReferencedDocuments) {
if ($doc.NeedsMigrating) { continue }
$fileName = [IO.Path]::GetFileName($doc.FullFileName)
$baseName = [IO.Path]::GetFileNameWithoutExtension($fileName)
$ext = [IO.Path]::GetExtension($fileName).ToLowerInvariant()
$matchesPattern = $baseName -match '^0.*-\d+$'
$matchesExtra = $false
foreach ($p in $extraPrefixes) {
if ($p -and $baseName.StartsWith($p)) { $matchesExtra = $true; break }
}
if (($ext -in '.iam','.ipt') -and ($matchesPattern -or $matchesExtra)) {
$out = Join-Path $destDir (Pref $doc.FullFileName)
$outDir = Split-Path $out
if (-not (Test-Path $outDir)) {
New-Item -ItemType Directory -Path $outDir -Force | Out-Null
}
$save.AddFileToSave(
$doc,
$out
)
}
else {
Write-Host "Skipping $fileName (naming filter)" -ForegroundColor DarkYellow
}
}
# 3) perform CAD copy
$save.ExecuteSaveCopyAs()
# 4) copy drawings from disk, filtered
$exts = '.idw','.dwg','.ipn'
function CopyDrawings($folder) {
foreach ($e in $exts) {
Get-ChildItem -Path $folder -Filter "*$e" -File -ErrorAction SilentlyContinue |
Where-Object {
if ($_.Extension -ieq '.idw') {
$b = $_.BaseName
$okPattern = $b -match '^0.*\d$'
$okExtra = $false
foreach ($p in $extraPrefixes) {
if ($p -and $b.StartsWith($p)) { $okExtra = $true; break }
}
$okPattern -or $okExtra
}
else { $true }
} |
ForEach-Object {
Copy-Item $_.FullName (Join-Path $destDir ($prefix + $_.Name)) -Force
}
}
}
CopyDrawings (Split-Path $asmPath -Parent)
foreach ($doc in @($asm) + $asm.AllReferencedDocuments) {
CopyDrawings (Split-Path $doc.FullFileName -Parent)
}
# 4.5) remove Read-Only attribute
Get-ChildItem -Path $destDir -Recurse -File |
Where-Object { $_.Extension -in '.iam','.ipt','.idw' } |
ForEach-Object {
if ($_.IsReadOnly) {
$_.IsReadOnly = $false
Write-Host "Removed ReadOnly: $($_.FullName)"
}
}
# 5) first relink pass via ApprenticeServer
function RelinkDrawings {
param($folder, $filePrefix)
$invApp = [Runtime.InteropServices.Marshal]::GetActiveObject("Inventor.Application")
Get-ChildItem -Path $folder -Include '*.idw','*.dwg' -File | ForEach-Object {
$draw = $invApp.Documents.Open($_.FullName, $true)
foreach ($desc in $draw.ReferencedDocumentDescriptors) {
$old = $desc.ReferencedFileDescriptor.FullFileName
$base = [IO.Path]::GetFileName($old)
$new = Join-Path $folder ($filePrefix + $base)
if (Test-Path $new) {
$desc.ReferencedFileDescriptor.Path = $new
}
}
$draw.Update(); $draw.Save(); $draw.Close()
}
}
RelinkDrawings -folder $destDir -filePrefix $prefix
# 6) open result folder
Start-Process explorer.exe -ArgumentList $destDir
# 7) full-session ReplaceReference relink (suppress pop-ups & iLogic)
Write-Host "Preparing full-session relink with iLogic/events off…" -ForegroundColor Cyan
# attach to running Inventor
$inv = [Runtime.InteropServices.Marshal]::GetActiveObject("Inventor.Application")
# allow alerts, but run silently
try { $inv.DisplayAlerts = $true } catch {}
try { $inv.SilentOperation = $true } catch {}
# disable VB iLogic automation mode
try {
$iLogicVB = $inv.ApplicationAddIns.ItemById('{3BDD8D79-2179-4B11-8A5A-257B1C0263AC}')
if (-not $iLogicVB.Activated) { $iLogicVB.Activate() }
$iLogicVB.Automation.RulesEnabled = $false
$iLogicVB.Automation.RulesOnEventsEnabled = $false
$iLogicVB.Automation.SilentOperation = $true
$iLogicVB.Automation.CallingFromOutside = $true
Write-Host "iLogic VB automation mode ON."
}
catch { Write-Warning "VB iLogic disable failed: $($_.Exception.Message)" }
# build source->dest map
$map = @{}
$allDocs = New-Object System.Collections.Generic.List[Object]
$queue = New-Object System.Collections.Generic.Queue[Object]
$queue.Enqueue($asm)
while ($queue.Count -gt 0) {
$d = $queue.Dequeue()
if (-not ($allDocs -contains $d)) {
$allDocs.Add($d)
foreach ($child in $d.AllReferencedDocuments) {
if ($child.FullFileName) { $queue.Enqueue($child) }
}
}
}
foreach ($doc in $allDocs) {
if ($doc.FullFileName) {
$map[$doc.FullFileName.ToLower()] = Join-Path $destDir ($prefix + [IO.Path]::GetFileName($doc.FullFileName))
}
}
foreach ($file in Get-ChildItem -Path $destDir -Filter "$prefix*.idw" -File) {
$orig = $file.Name.Substring($prefix.Length)
$map[(Join-Path (Split-Path $asmPath -Parent) $orig).ToLower()] = $file.FullName
}
# ReplaceReference relink loop
Get-ChildItem -Path $destDir -Filter "$prefix*.idw" -Recurse -File | ForEach-Object {
Write-Host " • $($_.Name)"
$dw = $inv.Documents.Open($_.FullName, $false)
foreach ($r in $dw.ReferencedFileDescriptors) {
$k = $r.FullFileName.ToLower()
if ($map.ContainsKey($k)) {
$r.DocumentDescriptor.ReferencedFileDescriptor.ReplaceReference($map[$k])
}
}
$dw.Update(); $dw.Save2(); $dw.Close($true)
}
# 8) update Part Number iProperties
Write-Host "Updating Part Number iProperties…" -ForegroundColor Cyan
$inv2 = [Runtime.InteropServices.Marshal]::GetActiveObject("Inventor.Application")
Get-ChildItem -Path $destDir -Filter "$prefix*" -File | ForEach-Object {
Write-Host " • $($_.Name)"
$doc2 = $inv2.Documents.Open($_.FullName, $false)
try {
$ps = $doc2.PropertySets.Item("Design Tracking Properties")
$ps.Item("Part Number").Value = [IO.Path]::GetFileNameWithoutExtension($_.Name)
$doc2.Update(); $doc2.Save2()
}
catch {
Write-Warning " ! Part Number failed on $($_.Name): $($_.Exception.Message)"
}
finally {
$doc2.Close($true)
}
}
Write-Host "Part Number update complete." -ForegroundColor Green
here is the ilogic code to run the PowerShell but using system forms. anything is now possible.
AddReference "System.IO"
AddReference "System.Windows.Forms"
AddReference "System.Drawing"
Imports Inventor
Imports IO = System.IO
Imports WF = System.Windows.Forms
Imports SD = System.Drawing
Imports System.Text
Imports System.Diagnostics
Public Class CopyDesignRule
Public Sub Main()
' 1) Source assembly path from the active document
Dim asmPath As String = ThisDoc.ModelDocument.FullFileName
If String.IsNullOrEmpty(asmPath) OrElse Not IO.File.Exists(asmPath) Then
WF.MessageBox.Show("Active document path not found or file does not exist.", _
"Copy Design", WF.MessageBoxButtons.OK, WF.MessageBoxIcon.Error)
Exit Sub
End If
' 2) Build the form
Dim frm As New WF.Form With {
.Text = "Copy Design (PowerShell)",
.AutoScaleMode = WF.AutoScaleMode.Dpi,
.Size = New SD.Size(760, 340),
.Font = New SD.Font("Segoe UI", 10.0!),
.StartPosition = WF.FormStartPosition.CenterScreen,
.FormBorderStyle = WF.FormBorderStyle.FixedDialog,
.MaximizeBox = False,
.MinimizeBox = False
}
AddHandler frm.KeyDown, Sub(s, E) If E.KeyCode = WF.Keys.Escape Then frm.Close()
Dim y As Integer = 18
Dim gap As Integer = 40
' — Source (readonly)
frm.Controls.Add(New WF.Label With {.Text = "Source assembly:", .Location = New SD.Point(15, y), .AutoSize = True})
Dim tbSrc As New WF.TextBox With {.Location = New SD.Point(180, y - 3), .Width = 530, .Text = asmPath, .ReadOnly = True}
frm.Controls.Add(tbSrc)
y += gap
' — New sub-folder name
frm.Controls.Add(New WF.Label With {.Text = "New folder name:", .Location = New SD.Point(15, y), .AutoSize = True})
Dim tbFolder As New WF.TextBox With {.Location = New SD.Point(180, y - 3), .Width = 300, .Text = "CopyDesign"}
frm.Controls.Add(tbFolder)
y += gap
' — Destination root (optional)
frm.Controls.Add(New WF.Label With {.Text = "Destination root (optional):", .Location = New SD.Point(15, y), .AutoSize = True})
Dim tbRoot As New WF.TextBox With {.Location = New SD.Point(180, y - 3), .Width = 530, .Text = ""}
frm.Controls.Add(tbRoot)
y += gap
' — Copy prefix
frm.Controls.Add(New WF.Label With {.Text = "Copy prefix (e.g. Copy_):", .Location = New SD.Point(15, y), .AutoSize = True})
Dim tbPrefix As New WF.TextBox With {.Location = New SD.Point(180, y - 3), .Width = 180, .Text = "Copy_"}
frm.Controls.Add(tbPrefix)
y += gap
' — Existing prefixes to allow (comma-separated)
frm.Controls.Add(New WF.Label With {
.Text = "Existing prefixes to allow (comma-separated, e.g. WIP_,WIP2_):",
.Location = New SD.Point(15, y), .AutoSize = True})
Dim tbExtra As New WF.TextBox With {.Location = New SD.Point(15, y + 22), .Width = 695, .Text = ""}
frm.Controls.Add(tbExtra)
y += 70
' — Buttons
Dim btnStart As New WF.Button With {.Text = "Start", .Location = New SD.Point(520, y), .Width = 90, .DialogResult = WF.DialogResult.OK}
Dim btnClose As New WF.Button With {.Text = "Close", .Location = New SD.Point(620, y), .Width = 90, .DialogResult = WF.DialogResult.Cancel}
frm.Controls.AddRange({btnStart, btnClose})
frm.AcceptButton = btnStart
frm.CancelButton = btnClose
If frm.ShowDialog() <> WF.DialogResult.OK Then Exit Sub
' 3) Validate inputs and compute destination folder
Dim baseFolder As String = IO.Path.GetDirectoryName(asmPath)
Dim newFolder As String = tbFolder.Text.Trim()
If newFolder = "" Then
WF.MessageBox.Show("You must enter a folder name.", "Copy Design", WF.MessageBoxButtons.OK, WF.MessageBoxIcon.Warning)
Exit Sub
End If
Dim rootPath As String = tbRoot.Text.Trim()
Dim destDir As String
If rootPath = "" Then
' default: new sub-folder next to the source assembly
destDir = IO.Path.Combine(baseFolder, newFolder)
Else
' user-supplied root path
destDir = IO.Path.Combine(rootPath, newFolder)
End If
Dim prefix As String = tbPrefix.Text.Trim()
If prefix = "" Then prefix = "Copy_"
Dim extraText As String = tbExtra.Text.Trim()
Dim extraList As New List(Of String)
If extraText <> "" Then
For Each tok In extraText.Split(","c)
Dim p = tok.Trim()
If p <> "" Then extraList.Add(p)
Next
End If
' 4) Generate and run the latest PowerShell script
GenerateAndRunPS(asmPath, destDir, prefix, extraList)
End Sub
Private Sub GenerateAndRunPS(asmPath As String, destDir As String, prefix As String, extraPrefixes As List(Of String))
' Build PowerShell array literal for extra prefixes, with safe quoting
Dim psExtra As String
If extraPrefixes IsNot Nothing AndAlso extraPrefixes.Count > 0 Then
Dim esc = extraPrefixes.Select(Function(s) s.Replace("'", "''"))
psExtra = "@('" & String.Join("','", esc) & "')"
Else
psExtra = "@()"
End If
' Create temp .ps1
Dim psFile As String = IO.Path.Combine(IO.Path.GetTempPath(), "CopyDesign_" & Guid.NewGuid().ToString("N") & ".ps1")
Dim sb As New StringBuilder()
' NOTE: This is the latest baseline logic (no Read-Host; uses values from the form)
With sb
.AppendLine("$ErrorActionPreference = 'Stop'")
.AppendLine("")
.AppendLine("# Inputs from iLogic")
.AppendLine("$asmPath = '" & asmPath.Replace("'", "''") & "'")
.AppendLine("$destDir = '" & destDir.Replace("'", "''") & "'")
.AppendLine("$prefix = '" & prefix.Replace("'", "''") & "'")
.AppendLine("$extraPrefixes = " & psExtra)
.AppendLine("")
.AppendLine("# ensure output folder exists")
.AppendLine("if (-not (Test-Path $destDir)) { New-Item -ItemType Directory -Path $destDir | Out-Null }")
.AppendLine("")
.AppendLine("# ApprenticeServer for CAD-only copy")
.AppendLine("$appr = New-Object -ComObject Inventor.ApprenticeServer")
.AppendLine("$asm = $appr.Open($asmPath)")
.AppendLine("$save = $appr.FileSaveAs")
.AppendLine("")
.AppendLine("function Pref([string]$path) { $prefix + ([IO.Path]::GetFileName($path)) }")
.AppendLine("")
.AppendLine("# 1) queue the main assembly")
.AppendLine("$save.AddFileToSave($asm, (Join-Path $destDir (Pref $asmPath)))")
.AppendLine("")
.AppendLine("# 2) queue parts & sub-assemblies, filtered")
.AppendLine("foreach ($doc in $asm.AllReferencedDocuments) {")
.AppendLine(" if ($doc.NeedsMigrating) { continue }")
.AppendLine(" $fileName = [IO.Path]::GetFileName($doc.FullFileName)")
.AppendLine(" $baseName = [IO.Path]::GetFileNameWithoutExtension($fileName)")
.AppendLine(" $ext = [IO.Path]::GetExtension($fileName).ToLowerInvariant()")
.AppendLine(" $matchesPattern = $baseName -match '^0.*-\d+$'")
.AppendLine(" $matchesExtra = $false")
.AppendLine(" foreach ($p in $extraPrefixes) { if ($p -and $baseName.StartsWith($p)) { $matchesExtra = $true; break } }")
.AppendLine(" if (($ext -in '.iam','.ipt') -and ($matchesPattern -or $matchesExtra)) {")
.AppendLine(" $out = Join-Path $destDir (Pref $doc.FullFileName)")
.AppendLine(" $outDir = Split-Path $out")
.AppendLine(" if (-not (Test-Path $outDir)) { New-Item -ItemType Directory -Path $outDir -Force | Out-Null }")
.AppendLine(" $save.AddFileToSave($doc, $out)")
.AppendLine(" } else {")
.AppendLine(" Write-Host ('Skipping ' + $fileName + ' (naming filter)') -ForegroundColor DarkYellow")
.AppendLine(" }")
.AppendLine("}")
.AppendLine("")
.AppendLine("# 3) perform CAD copy")
.AppendLine("$save.ExecuteSaveCopyAs()")
.AppendLine("")
.AppendLine("# 4) copy drawings from disk, filtered")
.AppendLine("$exts = '.idw','.dwg','.ipn'")
.AppendLine("function CopyDrawings($folder) {")
.AppendLine(" foreach ($e in $exts) {")
.AppendLine(" Get-ChildItem -Path $folder -Filter ""*$e"" -File -ErrorAction SilentlyContinue |")
.AppendLine(" Where-Object {")
.AppendLine(" if ($_.Extension -ieq '.idw') {")
.AppendLine(" $b = $_.BaseName")
.AppendLine(" $okPattern = $b -match '^0.*\d$'")
.AppendLine(" $okExtra = $false")
.AppendLine(" foreach ($p in $extraPrefixes) { if ($p -and $b.StartsWith($p)) { $okExtra = $true; break } }")
.AppendLine(" $okPattern -or $okExtra")
.AppendLine(" } else { $true }")
.AppendLine(" } |")
.AppendLine(" ForEach-Object { Copy-Item $_.FullName (Join-Path $destDir ($prefix + $_.Name)) -Force }")
.AppendLine(" }")
.AppendLine("}")
.AppendLine("CopyDrawings (Split-Path $asmPath -Parent)")
.AppendLine("foreach ($doc in @($asm) + $asm.AllReferencedDocuments) { CopyDrawings (Split-Path $doc.FullFileName -Parent) }")
.AppendLine("")
.AppendLine("# 4.5) remove Read-Only attribute")
.AppendLine("Get-ChildItem -Path $destDir -Recurse -File | Where-Object { $_.Extension -in '.iam','.ipt','.idw' } | ForEach-Object {")
.AppendLine(" if ($_.IsReadOnly) { $_.IsReadOnly = $false; Write-Host ('Removed ReadOnly: ' + $_.FullName) }")
.AppendLine("}")
.AppendLine("")
.AppendLine("# 5) first relink pass via Inventor.Application (.Path)")
.AppendLine("function RelinkDrawings {")
.AppendLine(" param($folder, $filePrefix)")
.AppendLine(" $invApp = [Runtime.InteropServices.Marshal]::GetActiveObject('Inventor.Application')")
.AppendLine(" Get-ChildItem -Path $folder -Include '*.idw','*.dwg' -File | ForEach-Object {")
.AppendLine(" $draw = $invApp.Documents.Open($_.FullName, $true)")
.AppendLine(" foreach ($desc in $draw.ReferencedDocumentDescriptors) {")
.AppendLine(" $old = $desc.ReferencedFileDescriptor.FullFileName")
.AppendLine(" $base = [IO.Path]::GetFileName($old)")
.AppendLine(" $new = Join-Path $folder ($filePrefix + $base)")
.AppendLine(" if (Test-Path $new) { $desc.ReferencedFileDescriptor.Path = $new }")
.AppendLine(" }")
.AppendLine(" $draw.Update(); $draw.Save(); $draw.Close()")
.AppendLine(" }")
.AppendLine("}")
.AppendLine("RelinkDrawings -folder $destDir -filePrefix $prefix")
.AppendLine("")
.AppendLine("# 6) open result folder")
.AppendLine("Start-Process explorer.exe -ArgumentList $destDir")
.AppendLine("")
.AppendLine("# 7) full-session ReplaceReference relink with iLogic suppression")
.AppendLine("Write-Host 'Preparing full-session relink with iLogic/events off…' -ForegroundColor Cyan")
.AppendLine("$inv = [Runtime.InteropServices.Marshal]::GetActiveObject('Inventor.Application')")
.AppendLine("try { $inv.DisplayAlerts = $true } catch {}")
.AppendLine("try { $inv.SilentOperation = $true } catch {}")
.AppendLine("")
.AppendLine("try {")
.AppendLine(" $iLogicVB = $inv.ApplicationAddIns.ItemById('{3BDD8D79-2179-4B11-8A5A-257B1C0263AC}')")
.AppendLine(" if (-not $iLogicVB.Activated) { $iLogicVB.Activate() }")
.AppendLine(" $iLogicVB.Automation.RulesEnabled = $false")
.AppendLine(" $iLogicVB.Automation.RulesOnEventsEnabled = $false")
.AppendLine(" $iLogicVB.Automation.SilentOperation = $true")
.AppendLine(" $iLogicVB.Automation.CallingFromOutside = $true")
.AppendLine(" Write-Host 'iLogic VB automation mode ON.'")
.AppendLine("} catch { Write-Warning ('VB iLogic disable failed: ' + $_.Exception.Message) }")
.AppendLine("")
.AppendLine("# build source->dest map")
.AppendLine("$map = @{}")
.AppendLine("$allDocs = New-Object System.Collections.Generic.List[Object]")
.AppendLine("$queue = New-Object System.Collections.Generic.Queue[Object]")
.AppendLine("$queue.Enqueue($asm)")
.AppendLine("while ($queue.Count -gt 0) {")
.AppendLine(" $d = $queue.Dequeue()")
.AppendLine(" if (-not ($allDocs -contains $d)) {")
.AppendLine(" $allDocs.Add($d)")
.AppendLine(" foreach ($child in $d.AllReferencedDocuments) { if ($child.FullFileName) { $queue.Enqueue($child) } }")
.AppendLine(" }")
.AppendLine("}")
.AppendLine("foreach ($doc in $allDocs) {")
.AppendLine(" if ($doc.FullFileName) { $map[$doc.FullFileName.ToLower()] = Join-Path $destDir ($prefix + [IO.Path]::GetFileName($doc.FullFileName)) }")
.AppendLine("}")
.AppendLine("foreach ($file in Get-ChildItem -Path $destDir -Filter ""$prefix*.idw"" -File) {")
.AppendLine(" $orig = $file.Name.Substring($prefix.Length)")
.AppendLine(" $map[(Join-Path (Split-Path $asmPath -Parent) $orig).ToLower()] = $file.FullName")
.AppendLine("}")
.AppendLine("")
.AppendLine("Get-ChildItem -Path $destDir -Filter ""$prefix*.idw"" -Recurse -File | ForEach-Object {")
.AppendLine(" Write-Host (' • ' + $_.Name)")
.AppendLine(" $dw = $inv.Documents.Open($_.FullName, $false)")
.AppendLine(" foreach ($r in $dw.ReferencedFileDescriptors) {")
.AppendLine(" $k = $r.FullFileName.ToLower()")
.AppendLine(" if ($map.ContainsKey($k)) { $r.DocumentDescriptor.ReferencedFileDescriptor.ReplaceReference($map[$k]) }")
.AppendLine(" }")
.AppendLine(" $dw.Update(); $dw.Save2(); $dw.Close($true)")
.AppendLine("}")
.AppendLine("")
.AppendLine("# 8) update Part Number iProperties")
.AppendLine("Write-Host 'Updating Part Number iProperties…' -ForegroundColor Cyan")
.AppendLine("$inv2 = [Runtime.InteropServices.Marshal]::GetActiveObject('Inventor.Application')")
.AppendLine("Get-ChildItem -Path $destDir -Filter ""$prefix*"" -File | ForEach-Object {")
.AppendLine(" Write-Host (' • ' + $_.Name)")
.AppendLine(" $doc2 = $inv2.Documents.Open($_.FullName, $false)")
.AppendLine(" try {")
.AppendLine(" $ps = $doc2.PropertySets.Item('Design Tracking Properties')")
.AppendLine(" $ps.Item('Part Number').Value = [IO.Path]::GetFileNameWithoutExtension($_.Name)")
.AppendLine(" $doc2.Update(); $doc2.Save2()")
.AppendLine(" } catch {")
.AppendLine(" Write-Warning (' ! Part Number failed on ' + $_.Name + ': ' + $_.Exception.Message)")
.AppendLine(" } finally { $doc2.Close($true) }")
.AppendLine("}")
.AppendLine("Write-Host 'Part Number update complete.' -ForegroundColor Green")
End With
IO.File.WriteAllText(psFile, sb.ToString())
' Launch PowerShell so progress/errors are visible
Dim psi As New ProcessStartInfo() With {
.FileName = "powershell.exe",
.Arguments = "-NoProfile -ExecutionPolicy Bypass -File """ & psFile & """",
.UseShellExecute = True,
.WindowStyle = ProcessWindowStyle.Normal
}
Dim proc As Process = Process.Start(psi)
' Optional: wait for completion
' proc.WaitForExit()
' (Optional) delete temp script afterward
' Try
' IO.File.Delete(psFile)
' Catch ex As Exception
' WF.MessageBox.Show("Could not delete temp script:" & vbCrLf & ex.Message, "Copy Design", WF.MessageBoxButtons.OK, WF.MessageBoxIcon.Warning)
' End Try
End Sub
End Class