因进程占用导致PowerShell中Move-Item无法移动MSI文件求助
解决MSI文件因WindowsInstaller COM对象占用无法移动的问题
问题根源在于使用WindowsInstaller.Installer COM对象打开MSI数据库后,未主动释放相关资源,导致PowerShell ISE进程持续锁定文件,无法执行移动操作。需在提取完元数据后,显式关闭数据库、视图并释放COM对象,触发垃圾回收彻底释放文件锁。
解决方案步骤
- 处理每个MSI文件后,关闭打开的数据库视图和数据库对象
- 使用
[System.Runtime.Interopservices.Marshal]::ReleaseComObject()释放所有COM对象实例 - 强制触发.NET垃圾回收,确保文件锁被释放
修改后的完整脚本
$folderPath = Read-Host "Please enter the path to the folder containing MSI files" $destination = "C:\test\" Get-ChildItem $folderPath -Filter *.msi | ForEach-Object { $msi = $_.FullName $windowsInstaller = New-Object -ComObject WindowsInstaller.Installer $database = $windowsInstaller.GetType().InvokeMember("OpenDatabase", 'InvokeMethod', $Null, $windowsInstaller, @($msi, 0)) # 提取Manufacturer并释放资源 $view = $database.GetType().InvokeMember("OpenView", 'InvokeMethod', $Null, $database, (@("SELECT Value FROM Property WHERE Property = 'Manufacturer'"))) $view.GetType().InvokeMember("Execute", 'InvokeMethod', $Null, $view, $Null) $record = $view.GetType().InvokeMember("Fetch", 'InvokeMethod', $Null, $view, $Null) $manufacturer = $record.GetType().InvokeMember("StringData", 'GetProperty', $Null, $record, 1) $view.GetType().InvokeMember("Close", 'InvokeMethod', $Null, $view, $Null) [System.Runtime.Interopservices.Marshal]::ReleaseComObject($record) | Out-Null [System.Runtime.Interopservices.Marshal]::ReleaseComObject($view) | Out-Null # 提取ProductName并释放资源 $view = $database.GetType().InvokeMember("OpenView", 'InvokeMethod', $Null, $database, (@("SELECT Value FROM Property WHERE Property = 'ProductName'"))) $view.GetType().InvokeMember("Execute", 'InvokeMethod', $Null, $view, $Null) $record = $view.GetType().InvokeMember("Fetch", 'InvokeMethod', $Null, $view, $Null) $productName = $record.GetType().InvokeMember("StringData", 'GetProperty', $Null, $record, 1) $view.GetType().InvokeMember("Close", 'InvokeMethod', $Null, $view, $Null) [System.Runtime.Interopservices.Marshal]::ReleaseComObject($record) | Out-Null [System.Runtime.Interopservices.Marshal]::ReleaseComObject($view) | Out-Null # 提取ProductVersion并释放资源 $view = $database.GetType().InvokeMember("OpenView", 'InvokeMethod', $Null, $database, (@("SELECT Value FROM Property WHERE Property = 'ProductVersion'"))) $view.GetType().InvokeMember("Execute", 'InvokeMethod', $Null, $view, $Null) $record = $view.GetType().InvokeMember("Fetch", 'InvokeMethod', $Null, $view, $Null) $productVersion = $record.GetType().InvokeMember("StringData", 'GetProperty', $Null, $record, 1) $view.GetType().InvokeMember("Close", 'InvokeMethod', $Null, $view, $Null) [System.Runtime.Interopservices.Marshal]::ReleaseComObject($record) | Out-Null [System.Runtime.Interopservices.Marshal]::ReleaseComObject($view) | Out-Null # 释放数据库和Installer COM对象 [System.Runtime.Interopservices.Marshal]::ReleaseComObject($database) | Out-Null [System.Runtime.Interopservices.Marshal]::ReleaseComObject($windowsInstaller) | Out-Null # 强制垃圾回收释放文件锁 [GC]::Collect() [GC]::WaitForPendingFinalizers() $destinationPath = Join-Path $destination "$manufacturer\$productName\$productVersion" if (!(Test-Path -Path $destinationPath)) { New-Item -ItemType Directory -Path $destinationPath | Out-Null } Move-Item $_.FullName -Destination $destinationPath }
额外修正说明
原脚本中Get-ChildItem $Path存在变量名错误,已修正为Get-ChildItem $folderPath,确保能正确读取目标文件夹下的MSI文件。
内容的提问来源于stack exchange,提问作者Larry Bambelle
相关产品推荐
相关产品推荐

