NavisworksTransport/deploy-body.ps1
tian ead8a35372 refactor: 部署脚本先普通权限部署,失败才提权,避免无谓UAC
deploy-plugin.bat 原为:非管理员直接 Start-Process RunAs 提权弹UAC。
但文件可写且 Roamer 可被当前用户关闭时根本不需要管理员。

重构:抽取部署主体到 deploy-body.ps1(关Roamer+等解锁+复制+校验+设写权限)。
deploy-plugin.bat 先以当前用户运行 deploy-body.ps1,成功则免UAC;
失败(如文件被管理员所有)才提权重试。
bat 用纯ASCII注释避免中文编码乱码问题。
2026-07-31 16:52:48 +08:00

130 lines
5.7 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 插件部署主体(供 deploy-plugin.bat 调用)
# 关闭 Navisworks(Roamer)、等待文件解锁、复制 DLL 与资源、校验并设置可写权限。
$ErrorActionPreference = 'Stop'
$targetDir = [System.IO.Path]::GetFullPath('C:\ProgramData\Autodesk\Navisworks Manage 2026\plugins\TransportPlugin')
$buildDir = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot 'bin\x64\Release'))
$deployDllNames = @(
'TransportPlugin.dll',
'geometry4Sharp.dll',
'Newtonsoft.Json.dll',
'Roy-T.AStar.dll',
'SQLite.Interop.dll',
'System.Data.SQLite.dll',
'Tomlyn.dll'
)
$deployRootFiles = @(
'TransportRibbon.xaml',
'TransportRibbon_16.png',
'TransportRibbon_32.png'
)
$stalePluginFiles = @(
'Autodesk.Navisworks.Api.dll',
'NavisworksTransport.UnitTests.dll',
'Microsoft.VisualStudio.TestPlatform.TestFramework.dll',
'Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll'
)
function Test-FileUnlocked([string]$path) {
if (-not (Test-Path $path)) { return $true }
try {
$stream = [System.IO.File]::Open($path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
$stream.Close()
return $true
} catch {
return $false
}
}
function Wait-FileUnlocked([string]$path, [datetime]$deadline) {
while (-not (Test-FileUnlocked $path)) {
if ((Get-Date) -ge $deadline) { throw ('Target file still locked: ' + $path) }
Start-Sleep -Milliseconds 500
}
}
# 关闭 Navisworks 进程以解锁 DLL
Get-Process Roamer -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
$deadline = (Get-Date).AddSeconds(15)
while (Get-Process Roamer -ErrorAction SilentlyContinue) {
if ((Get-Date) -ge $deadline) { throw 'Roamer.exe still running after 15 seconds.' }
Start-Sleep -Milliseconds 500
}
$keyTargetFiles = @(
(Join-Path $targetDir 'TransportPlugin.dll'),
(Join-Path $targetDir 'geometry4Sharp.dll'),
(Join-Path (Join-Path $targetDir 'resources') 'unit_cube.nwc'),
(Join-Path (Join-Path $targetDir 'resources') 'unit_cylinder.nwc')
)
foreach ($keyFile in $keyTargetFiles) { Wait-FileUnlocked $keyFile $deadline }
New-Item -ItemType Directory -Force -Path $targetDir | Out-Null
foreach ($staleFileName in $stalePluginFiles) {
$stalePath = Join-Path $targetDir $staleFileName
if (Test-Path $stalePath) { Remove-Item $stalePath -Force }
}
foreach ($dllName in $deployDllNames) {
$sourceDll = Join-Path $buildDir $dllName
if (-not (Test-Path $sourceDll)) { throw ('Deployment source file missing: ' + $sourceDll) }
Copy-Item $sourceDll $targetDir -Force
}
foreach ($rootFileName in $deployRootFiles) {
$sourceRootFile = Join-Path $buildDir $rootFileName
if (Test-Path $sourceRootFile) {
Copy-Item $sourceRootFile $targetDir -Force
foreach ($locale in @('en-US','zh-CN')) {
$localeDir = Join-Path $targetDir $locale
New-Item -ItemType Directory -Force -Path $localeDir | Out-Null
Copy-Item $sourceRootFile $localeDir -Force
}
}
}
if (Test-Path (Join-Path $buildDir 'resources')) {
New-Item -ItemType Directory -Force -Path (Join-Path $targetDir 'resources') | Out-Null
Copy-Item (Join-Path $buildDir 'resources\*') (Join-Path $targetDir 'resources') -Recurse -Force
}
# 校验复制结果
$sourceFiles = @()
foreach ($dllName in $deployDllNames) { $sourceFiles += Get-Item (Join-Path $buildDir $dllName) }
foreach ($rootFileName in $deployRootFiles) {
$sourceRootFile = Join-Path $buildDir $rootFileName
if (Test-Path $sourceRootFile) { $sourceFiles += Get-Item $sourceRootFile }
}
if (Test-Path (Join-Path $buildDir 'resources')) {
$sourceFiles += Get-ChildItem (Join-Path $buildDir 'resources') -File
}
foreach ($sourceFile in $sourceFiles) {
$targetFile = if ($sourceFile.DirectoryName -eq $buildDir) { Join-Path $targetDir $sourceFile.Name } else { Join-Path (Join-Path $targetDir 'resources') $sourceFile.Name }
if (-not (Test-Path $targetFile)) { throw ('Deployment verification failed: missing target file ' + $targetFile) }
$targetInfo = Get-Item $targetFile
if ($sourceFile.Length -ne $targetInfo.Length -or $sourceFile.LastWriteTime -ne $targetInfo.LastWriteTime) {
throw ('Deployment verification failed for ' + $sourceFile.Name + ': source=' + $sourceFile.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss.fff') + ' (' + $sourceFile.Length + '), target=' + $targetInfo.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss.fff') + ' (' + $targetInfo.Length + ')')
}
Write-Host ('Verified ' + $sourceFile.Name + ': ' + $targetInfo.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss.fff'))
if ($deployRootFiles -contains $sourceFile.Name) {
foreach ($locale in @('en-US','zh-CN')) {
$localeTargetFile = Join-Path (Join-Path $targetDir $locale) $sourceFile.Name
if (-not (Test-Path $localeTargetFile)) { throw ('Deployment verification failed: missing locale target file ' + $localeTargetFile) }
$localeTargetInfo = Get-Item $localeTargetFile
if ($sourceFile.Length -ne $localeTargetInfo.Length -or $sourceFile.LastWriteTime -ne $localeTargetInfo.LastWriteTime) {
throw ('Deployment verification failed for locale copy ' + $localeTargetFile)
}
Write-Host ('Verified ' + $locale + '\' + $sourceFile.Name + ': ' + $localeTargetInfo.LastWriteTime.ToString('yyyy-MM-dd HH:mm:ss.fff'))
}
}
}
# 设置目标文件可写权限,避免后续部署需要管理员提权(一次性管理员部署后免 UAC
foreach ($f in Get-ChildItem $targetDir -File -Recurse -ErrorAction SilentlyContinue) {
try {
$acl = Get-Acl $f.FullName
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule('BUILTIN\Users','FullControl','Allow')
$acl.SetAccessRule($rule)
Set-Acl $f.FullName $acl
} catch { }
}
Write-Host 'Plugin deployed successfully!'