You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

因进程占用导致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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 03:31:36