# 家具导出插件 - 安装包生成脚本 # 生成一个“解压后双击 安装.bat 即可安装”的简化安装包。 # # 关键设计: # - 设计师侧仍然只接触 .bat(双击即可),但核心逻辑用 PowerShell 执行。 # - 原因:部分机器的 AutoCAD Profile 名称可能包含 << >>,在 cmd/bat 的 for /f 子命令里 # 很容易触发重定向解析,导致 TRUSTEDPATHS 写入失败 -> 插件无法自启动(每次都要 NETLOAD)。 $ErrorActionPreference = "Stop" function Write-TextFileUtf8NoBom { param( [Parameter(Mandatory = $true)][string]$Path, [Parameter(Mandatory = $true)][string]$Content ) # Windows 下的脚本/文本统一 CRLF,避免 cmd/某些编辑器对 LF-only 解析异常 $normalized = $Content.Replace("`r`n", "`n").Replace("`n", "`r`n") $utf8NoBom = New-Object System.Text.UTF8Encoding($false) [System.IO.File]::WriteAllText($Path, $normalized, $utf8NoBom) } function Write-TextFileUtf8Bom { param( [Parameter(Mandatory = $true)][string]$Path, [Parameter(Mandatory = $true)][string]$Content ) # Windows PowerShell 5.1 对“UTF-8 无 BOM”的脚本兼容性很差(常见表现:中文乱码甚至解析报错)。 # 设计师侧会通过 powershell.exe 执行 install.ps1 / uninstall.ps1,因此这里强制写入 UTF-8 BOM。 $normalized = $Content.Replace("`r`n", "`n").Replace("`n", "`r`n") $utf8Bom = New-Object System.Text.UTF8Encoding($true) [System.IO.File]::WriteAllText($Path, $normalized, $utf8Bom) } function Write-TextFileGbk { param( [Parameter(Mandatory = $true)][string]$Path, [Parameter(Mandatory = $true)][string]$Content ) # cmd 解析 bat 对编码/换行很敏感:使用 GBK + CRLF 兼容中文环境 $normalized = $Content.Replace("`r`n", "`n").Replace("`n", "`r`n") $gbk = [System.Text.Encoding]::GetEncoding(936) [System.IO.File]::WriteAllText($Path, $normalized, $gbk) } Write-Host "========================================" -ForegroundColor Cyan Write-Host " 家具导出插件 - 安装包生成器" -ForegroundColor Cyan Write-Host "========================================" -ForegroundColor Cyan Write-Host "" # 路径设置 $PluginName = "FurnitureExporter" $Version = "2.6.0" $SourceDir = Join-Path $PSScriptRoot "FurnitureExporter" $InstallerDir = Join-Path $PSScriptRoot "installer" $OutputDir = Join-Path $PSScriptRoot "dist" $OutputName = "家具导出插件-v${Version}-一键安装版" $DllSourcePath = Join-Path $SourceDir "bin\\Release\\net48\\FurnitureExporter.dll" $PdbSourcePath = Join-Path $SourceDir "bin\\Release\\net48\\FurnitureExporter.pdb" if (-not (Test-Path $DllSourcePath)) { Write-Host "❌ 未找到编译好的 DLL:$DllSourcePath" -ForegroundColor Red Write-Host "请先运行 build.ps1(或 dotnet build -c Release)编译插件" -ForegroundColor Yellow exit 1 } # 清理并创建安装目录 if (Test-Path $InstallerDir) { Remove-Item -Path $InstallerDir -Recurse -Force } New-Item -ItemType Directory -Path $InstallerDir -Force | Out-Null Write-Host "📦 正在准备安装包文件..." -ForegroundColor Yellow # 1) 创建 Bundle 目录结构 $BundleDir = Join-Path $InstallerDir "${PluginName}.bundle" $ContentsDir = Join-Path $BundleDir "Contents" New-Item -ItemType Directory -Path $ContentsDir -Force | Out-Null # 2) 复制 DLL / PDB Copy-Item -Path $DllSourcePath -Destination $ContentsDir -Force if (Test-Path $PdbSourcePath) { Copy-Item -Path $PdbSourcePath -Destination $ContentsDir -Force } # 3) PackageContents.xml $PackageXml = @" "@ Write-TextFileUtf8NoBom -Path (Join-Path $BundleDir "PackageContents.xml") -Content $PackageXml # 4) 安装/卸载 PowerShell 脚本(核心逻辑) $InstallPs1 = @' # 家具导出插件 - 一键安装脚本(PowerShell) # 目标: # 1) 复制 bundle 到当前用户:%AppData%\Autodesk\ApplicationPlugins # 2) 写入 AutoCAD 2024 自启动注册表(避免每次 NETLOAD) # 3) 追加可信路径 TRUSTEDPATHS(SECURELOAD=1 时必须,否则不自动加载) $ErrorActionPreference = "Stop" function Write-Step { param([string]$Text) Write-Host $Text -ForegroundColor Cyan } function Test-IsAdmin { $identity = [Security.Principal.WindowsIdentity]::GetCurrent() $principal = New-Object Security.Principal.WindowsPrincipal($identity) return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } function Add-TrustedPath { param( [AllowNull()][string]$Existing, [Parameter(Mandatory = $true)][string]$PathToAdd ) $normalizedAdd = $PathToAdd.Trim().TrimEnd('\') $items = @() if ($Existing) { $items = $Existing.Split(';') | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" } } if (-not $items -or $items.Count -eq 0) { # 保留 AutoCAD 常见默认值,避免误删用户原本设置 $items = @("APP_PATH") } $exists = $false foreach ($it in $items) { if ($it.Trim().TrimEnd('\').Equals($normalizedAdd, [StringComparison]::OrdinalIgnoreCase)) { $exists = $true break } } if (-not $exists) { $items += $PathToAdd } # 去重(忽略大小写 + 末尾反斜杠) $seen = New-Object "System.Collections.Generic.HashSet[string]" ([StringComparer]::OrdinalIgnoreCase) $dedup = New-Object System.Collections.Generic.List[string] foreach ($it in $items) { $k = $it.Trim().TrimEnd('\') if ($seen.Add($k)) { $dedup.Add($it.Trim()) } } return ($dedup -join ";") } Write-Step "[0/3] 环境检查..." if (Get-Process acad -ErrorAction SilentlyContinue) { Write-Host "" Write-Host "注意:检测到 AutoCAD 正在运行 (acad.exe)。" -ForegroundColor Yellow Write-Host "为避免 DLL 被占用导致复制/删除失败,建议先完全关闭 AutoCAD 再安装。" -ForegroundColor Yellow Write-Host "" } if (Test-IsAdmin) { Write-Host "" Write-Host "注意:你正在以管理员身份运行。" -ForegroundColor Yellow Write-Host "本插件按“当前 Windows 用户”安装(%AppData% + HKCU 注册表)。" -ForegroundColor Yellow Write-Host "如果不是在设计师账号下运行,安装后可能在设计师账号下不生效。" -ForegroundColor Yellow Write-Host "" } $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $bundleSrc = Join-Path $scriptDir "FurnitureExporter.bundle" # 兼容“解压覆盖旧目录”导致的套娃结构:FurnitureExporter.bundle\FurnitureExporter.bundle\... if (-not (Test-Path (Join-Path $bundleSrc "PackageContents.xml"))) { $nested = Join-Path $bundleSrc "FurnitureExporter.bundle" if (Test-Path (Join-Path $nested "PackageContents.xml")) { $bundleSrc = $nested } } if (-not (Test-Path (Join-Path $bundleSrc "PackageContents.xml"))) { Write-Host "未找到 FurnitureExporter.bundle(请先解压安装包,再运行 安装.bat)。" -ForegroundColor Red exit 1 } $appPlugins = Join-Path $env:APPDATA "Autodesk\ApplicationPlugins" $bundleDst = Join-Path $appPlugins "FurnitureExporter.bundle" $dllPath = Join-Path $bundleDst "Contents\FurnitureExporter.dll" $trustedDir = Join-Path $bundleDst "Contents" Write-Step "[1/3] 复制插件..." New-Item -ItemType Directory -Path $appPlugins -Force | Out-Null if (Test-Path $bundleDst) { try { Remove-Item -Path $bundleDst -Recurse -Force } catch { Write-Host "" Write-Host "删除旧版本失败:$bundleDst" -ForegroundColor Red Write-Host "常见原因:AutoCAD 未完全关闭/文件被占用/安全软件拦截。" -ForegroundColor Yellow Write-Host "请关闭 AutoCAD 后重试,或手动删除上述目录。" -ForegroundColor Yellow Write-Host "" throw } } Copy-Item -Path $bundleSrc -Destination $bundleDst -Recurse -Force if (-not (Test-Path $dllPath)) { Write-Host "安装失败:未找到 DLL:$dllPath" -ForegroundColor Red exit 1 } Write-Step "[2/3] 注册自启动/可信路径..." $acadRoot = "HKCU:\Software\Autodesk\AutoCAD\R24.3" $curVerObj = Get-ItemProperty -Path $acadRoot -Name CurVer -ErrorAction SilentlyContinue $acadCur = $curVerObj.CurVer if (-not $acadCur) { Write-Host "未检测到 AutoCAD 2024 注册表信息:$acadRoot\CurVer" -ForegroundColor Yellow Write-Host "已完成文件复制,但未写入自启动注册。请确认已至少启动过 AutoCAD 2024 一次。" -ForegroundColor Yellow } else { $acadBase = Join-Path $acadRoot $acadCur # 1) Applications 自启动(.NET 插件标准方式) $appKey = Join-Path $acadBase "Applications\FurnitureExporter" New-Item -Path $appKey -Force | Out-Null New-ItemProperty -Path $appKey -Name "DESCRIPTION" -PropertyType String -Value "家具导出插件 v$Version" -Force | Out-Null New-ItemProperty -Path $appKey -Name "LOADCTRLS" -PropertyType DWord -Value 2 -Force | Out-Null New-ItemProperty -Path $appKey -Name "MANAGED" -PropertyType DWord -Value 1 -Force | Out-Null New-ItemProperty -Path $appKey -Name "LOADER" -PropertyType String -Value $dllPath -Force | Out-Null # 2) 可信路径:只加入本插件 Contents,避免把整个 ApplicationPlugins 目录设为可信 $profilesRoot = Join-Path $acadBase "Profiles" $profiles = Get-ChildItem -Path $profilesRoot -ErrorAction SilentlyContinue foreach ($p in $profiles) { $varsKey = Join-Path $p.PSPath "Variables" if (-not (Test-Path $varsKey)) { continue } $vars = Get-ItemProperty -Path $varsKey -ErrorAction SilentlyContinue $existingTrusted = $null if ($vars.PSObject.Properties.Name -contains "TRUSTEDPATHS") { $existingTrusted = [string]$vars.TRUSTEDPATHS } $newTrusted = Add-TrustedPath -Existing $existingTrusted -PathToAdd $trustedDir if ($existingTrusted -ne $newTrusted) { New-ItemProperty -Path $varsKey -Name "TRUSTEDPATHS" -PropertyType String -Value $newTrusted -Force | Out-Null } # 如果 SECURELOAD=2,会导致即使加入可信路径也不加载(尽量只从 2 降到 1) if ($vars.PSObject.Properties.Name -contains "SECURELOAD") { if ([string]$vars.SECURELOAD -eq "2") { New-ItemProperty -Path $varsKey -Name "SECURELOAD" -PropertyType String -Value "1" -Force | Out-Null } } } } Write-Step "[3/3] 完成" Write-Host "" Write-Host "安装完成。请完全关闭 AutoCAD 后重新启动。" -ForegroundColor Green Write-Host "启动后命令行输入:FEXPORT(单资产)或 FEXPORTPRODUCT(整套兼容)" -ForegroundColor Green Write-Host "" '@ Write-TextFileUtf8Bom -Path (Join-Path $InstallerDir "install.ps1") -Content $InstallPs1 $UninstallPs1 = @' # 家具导出插件 - 一键卸载脚本(PowerShell) # 目标:删除当前用户 ApplicationPlugins 下的 bundle,并清理自启动注册/可信路径。 $ErrorActionPreference = "Stop" function Write-Step { param([string]$Text) Write-Host $Text -ForegroundColor Cyan } function Remove-TrustedPath { param( [AllowNull()][string]$Existing, [Parameter(Mandatory = $true)][string]$PathToRemove ) if (-not $Existing) { return $Existing } $normalizedRemove = $PathToRemove.Trim().TrimEnd('\') $items = $Existing.Split(';') | ForEach-Object { $_.Trim() } | Where-Object { $_ -ne "" } $kept = New-Object System.Collections.Generic.List[string] foreach ($it in $items) { $k = $it.Trim().TrimEnd('\') if (-not $k.Equals($normalizedRemove, [StringComparison]::OrdinalIgnoreCase)) { $kept.Add($it.Trim()) } } return ($kept -join ";") } Write-Step "[0/3] 环境检查..." if (Get-Process acad -ErrorAction SilentlyContinue) { Write-Host "" Write-Host "注意:检测到 AutoCAD 正在运行 (acad.exe)。" -ForegroundColor Yellow Write-Host "如卸载失败,请先完全关闭 AutoCAD 后再重试。" -ForegroundColor Yellow Write-Host "" } $appPlugins = Join-Path $env:APPDATA "Autodesk\ApplicationPlugins" $bundleDst = Join-Path $appPlugins "FurnitureExporter.bundle" $trustedDir = Join-Path $bundleDst "Contents" Write-Step "[1/3] 删除插件文件..." if (Test-Path $bundleDst) { try { Remove-Item -Path $bundleDst -Recurse -Force } catch { Write-Host "" Write-Host "删除失败:$bundleDst" -ForegroundColor Red Write-Host "常见原因:AutoCAD 未完全关闭/文件被占用/安全软件拦截。" -ForegroundColor Yellow Write-Host "你也可以手动删除该目录后,再运行 卸载.bat 清理注册表。" -ForegroundColor Yellow Write-Host "" throw } } Write-Step "[2/3] 清理注册表..." $acadRoot = "HKCU:\Software\Autodesk\AutoCAD\R24.3" $curVerObj = Get-ItemProperty -Path $acadRoot -Name CurVer -ErrorAction SilentlyContinue $acadCur = $curVerObj.CurVer if ($acadCur) { $acadBase = Join-Path $acadRoot $acadCur # Applications 自启动项 $appKey = Join-Path $acadBase "Applications\FurnitureExporter" if (Test-Path $appKey) { Remove-Item -Path $appKey -Recurse -Force -ErrorAction SilentlyContinue } # TRUSTEDPATHS 移除插件 Contents(只移除我们加的这一项,避免影响其他插件) $profilesRoot = Join-Path $acadBase "Profiles" $profiles = Get-ChildItem -Path $profilesRoot -ErrorAction SilentlyContinue foreach ($p in $profiles) { $varsKey = Join-Path $p.PSPath "Variables" if (-not (Test-Path $varsKey)) { continue } $vars = Get-ItemProperty -Path $varsKey -ErrorAction SilentlyContinue if (-not ($vars.PSObject.Properties.Name -contains "TRUSTEDPATHS")) { continue } $existingTrusted = [string]$vars.TRUSTEDPATHS $newTrusted = Remove-TrustedPath -Existing $existingTrusted -PathToRemove $trustedDir if ($existingTrusted -ne $newTrusted) { if ([string]::IsNullOrWhiteSpace($newTrusted)) { Remove-ItemProperty -Path $varsKey -Name "TRUSTEDPATHS" -ErrorAction SilentlyContinue } else { New-ItemProperty -Path $varsKey -Name "TRUSTEDPATHS" -PropertyType String -Value $newTrusted -Force | Out-Null } } } } else { Write-Host "未检测到 AutoCAD 2024 注册表信息,跳过注册表清理。" -ForegroundColor Yellow } Write-Step "[3/3] 完成" Write-Host "" Write-Host "卸载完成。请完全关闭 AutoCAD 后重新启动。" -ForegroundColor Green Write-Host "" '@ Write-TextFileUtf8Bom -Path (Join-Path $InstallerDir "uninstall.ps1") -Content $UninstallPs1 # 5) bat 入口(只负责启动 PowerShell 脚本) $InstallBat = @' @echo off setlocal EnableExtensions title 家具导出插件 安装程序 set "SCRIPT_DIR=%~dp0" if not exist "%SCRIPT_DIR%install.ps1" ( echo 未找到 install.ps1 echo 请先解压安装包到当前目录后再运行。 echo. pause exit /b 1 ) powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%install.ps1" echo. pause endlocal '@ Write-TextFileGbk -Path (Join-Path $InstallerDir "安装.bat") -Content $InstallBat $UninstallBat = @' @echo off setlocal EnableExtensions title 家具导出插件 卸载程序 set "SCRIPT_DIR=%~dp0" if not exist "%SCRIPT_DIR%uninstall.ps1" ( echo 未找到 uninstall.ps1 echo 请先解压安装包到当前目录后再运行。 echo. pause exit /b 1 ) powershell.exe -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%uninstall.ps1" echo. pause endlocal '@ Write-TextFileGbk -Path (Join-Path $InstallerDir "卸载.bat") -Content $UninstallBat # 6) 使用说明 $Readme = @" ═══════════════════════════════════════════════════════════════════════ 家具导出插件 v$Version - 安装指南 ═══════════════════════════════════════════════════════════════════════ 安装步骤(只需30秒) ─────────────────────────────────────────────────────────────────────── 1) 解压安装包 2) 双击运行:安装.bat 3) 完全关闭 AutoCAD 2024 后重新启动 卸载步骤(只需10秒) ─────────────────────────────────────────────────────────────────────── 1) 双击运行:卸载.bat 2) 完全关闭 AutoCAD 2024 后重新启动 使用方法 ─────────────────────────────────────────────────────────────────────── 打开 AutoCAD 后,命令行输入: FEXPORT -> 单资产导出到资产中心(板材 / 组件 / 五金) FEXPORTASSET -> 单资产导出别名 FEXPORTPRODUCT -> 整套家具兼容导出 FEXPORTBUNDLE -> 整套家具兼容导出别名 FINFO -> 查看实体信息 安装脚本做了什么(便于 IT/排查) ─────────────────────────────────────────────────────────────────────── 1) 复制到: %AppData%\Autodesk\ApplicationPlugins\FurnitureExporter.bundle 2) 注册自启动(HKCU): HKCU\Software\Autodesk\AutoCAD\R24.3\\Applications\FurnitureExporter 3) 追加可信路径(HKCU / 每个 Profile): TRUSTEDPATHS += ...\FurnitureExporter.bundle\Contents 常见问题 ─────────────────────────────────────────────────────────────────────── Q: 提示“未知命令 FEXPORT / FEXPORTPRODUCT” A: 1) 确认已重启 AutoCAD(关闭再打开) 2) 确认插件目录存在: %AppData%\Autodesk\ApplicationPlugins\FurnitureExporter.bundle\Contents\FurnitureExporter.dll 3) 如果 SECURELOAD=2,脚本会将其降到 1;如仍无效请截图安装日志给开发排查 版本: v$Version "@ Write-TextFileUtf8NoBom -Path (Join-Path $InstallerDir "使用说明.txt") -Content $Readme # 7) 打包为 ZIP(并同时输出一个可直接双击测试的目录) Write-Host "📦 正在生成安装包..." -ForegroundColor Yellow New-Item -ItemType Directory -Path $OutputDir -Force | Out-Null $ZipPath = Join-Path $OutputDir "$OutputName.zip" Compress-Archive -Path (Join-Path $InstallerDir "*") -DestinationPath $ZipPath -Force $StagingDir = Join-Path $OutputDir $OutputName if (Test-Path $StagingDir) { Remove-Item -Path $StagingDir -Recurse -Force } New-Item -ItemType Directory -Path $StagingDir -Force | Out-Null Copy-Item -Path (Join-Path $InstallerDir "*") -Destination $StagingDir -Recurse -Force Write-Host "" Write-Host "========================================" -ForegroundColor Green Write-Host " ✅ 安装包生成成功!" -ForegroundColor Green Write-Host "========================================" -ForegroundColor Green Write-Host "" Write-Host "输出文件:" -ForegroundColor Cyan Write-Host " - $ZipPath" Write-Host " - $StagingDir" Write-Host ""