从网站下载后关闭 MSI 文件句柄

Close a MSI file handle after being downloaded from a website

我有一个用于 outlook 的 vsto 插件。有一个代码,我可以从网站下载 MSI 文件:

Public Sub DownloadMsiFile()
    Try
        Dim url As String = "https://www.website.com/ol.msi"
        Dim wc As New WebClient()
        wc.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;")
        If File.Exists(My.Computer.FileSystem.SpecialDirectories.Temp & "\ol.msi") Then
            System.IO.File.Delete(My.Computer.FileSystem.SpecialDirectories.Temp & "\ol.msi")
        End If
        wc.DownloadFile(url, My.Computer.FileSystem.SpecialDirectories.Temp & "\ol.msi")
        wc.Dispose()
    Catch ex As Exception
        MessageBox.Show("File couldn't be downloaded: " & ex.Message)
    End Try
End Sub

然后我使用以下函数获取 MSI 版本:

Function GetMsiVersion() As String
    Try
        Dim oInstaller As WindowsInstaller.Installer
        Dim oDb As WindowsInstaller.Database
        Dim oView As WindowsInstaller.View
        Dim oRecord As WindowsInstaller.Record
        Dim sSQL As String
        oInstaller = CType(CreateObject("WindowsInstaller.Installer"), WindowsInstaller.Installer)
        DownloadMsiFile()
        If File.Exists(My.Computer.FileSystem.SpecialDirectories.Temp & "\ol.msi") Then
            oDb = oInstaller.OpenDatabase(My.Computer.FileSystem.SpecialDirectories.Temp & "\ol.msi", 0)
            sSQL = "SELECT `Value` FROM `Property` WHERE `Property`='ProductVersion'"
            oView = oDb.OpenView(sSQL)
            oView.Execute()
            oRecord = oView.Fetch
            Return oRecord.StringData(1).ToString()
        Else
            Return Nothing
        End If
    Catch ex As Exception
        MessageBox.Show("File couldn't be accessed: " & ex.Message)
    End Try
End Function

然后我和当前的dll版本做比较,看是否需要下载更新的版本:

Public Sub CheckOLUpdates()
    Dim remoteVersion As String = GetMsiVersion()
    Dim installedVersion As String = Assembly.GetExecutingAssembly().GetName().Version.ToString
    If Not String.IsNullOrEmpty(remoteVersion) Then
        Try
            If String.Compare(installedVersion, remoteVersion) < 0 Then
                Dim Result As DialogResult = MessageBox.Show("A newer version is available for download, do you want to download it now?", "OL", System.Windows.Forms.MessageBoxButtons.OKCancel, MessageBoxIcon.Question)
                If Result = 1 Then
                    System.Diagnostics.Process.Start("http://www.website.com/update")
                Else
                    Exit Sub
                End If
            Else
                MessageBox.Show("You have the latest version installed!", "OL", MessageBoxButtons.OK, MessageBoxIcon.Information)
            End If
        Catch ex As Exception

        End Try

    End If

End Sub

如果这个 运行 一次,效果很好。但是,如果我再次尝试检查更新,我会在尝试删除 DownloadMsiFile() 中的文件时收到以下错误:

The process cannot access the file %temp%\ol.msi because it is being used by another process

如果我使用 sysinternals handle.exe 实用程序检查此文件的句柄,我会得到 outlook 进程在此文件上有一个句柄锁:

handle.exe %temp%\ol.msi
Nthandle v4.30 - Handle viewer
Copyright (C) 1997-2021 Mark Russinovich
Sysinternals - www.sysinternals.com

OUTLOOK.EXE        pid: 25964  type: File          4FC8: %temp%\ol.msi

我想知道如何关闭句柄以避免出现此错误?非常感谢任何帮助

所以这是关闭句柄我必须做的。我在打开 MSI 文件后添加了以下行:

            Marshal.FinalReleaseComObject(oRecord)
            oView.Close()
            Marshal.FinalReleaseComObject(oView)
            Marshal.FinalReleaseComObject(oDb)
            oRecord = Nothing
            oView = Nothing
            oDb = Nothing

所以我的最终代码如下所示:

Function GetMsiVersion() As String
    Try
        Dim oInstaller As WindowsInstaller.Installer
        Dim oDb As WindowsInstaller.Database
        Dim oView As WindowsInstaller.View
        Dim oRecord As WindowsInstaller.Record
        Dim sSQL As String
        Dim Version As String
        oInstaller = CType(CreateObject("WindowsInstaller.Installer"), WindowsInstaller.Installer)
        DownloadMsiFile()
        If File.Exists(My.Computer.FileSystem.SpecialDirectories.Temp & "\ol.msi") Then
            oDb = oInstaller.OpenDatabase(My.Computer.FileSystem.SpecialDirectories.Temp & "\ol.msi", 0)
            sSQL = "SELECT `Value` FROM `Property` WHERE `Property`='ProductVersion'"
            oView = oDb.OpenView(sSQL)
            oView.Execute()
            oRecord = oView.Fetch
            Version = oRecord.StringData(1).ToString()
            Marshal.FinalReleaseComObject(oRecord)
            oView.Close()
            Marshal.FinalReleaseComObject(oView)
            Marshal.FinalReleaseComObject(oDb)
            oRecord = Nothing
            oView = Nothing
            oDb = Nothing
        Else
            Version = Nothing
        End If
        Return Version
    Catch ex As Exception
        MessageBox.Show("File couldn't be accessed: " & ex.Message)
    End Try
End Function