Close a MSI file handle after being downloaded from a website - vb.net

I have a vsto add-in for outlook. There is a code where I download a MSI file from a website:
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
And then I get the MSI version using the following function:
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
And then I do the comparison with the current dll version to see if there is a need to download a newer version or not:
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
This works pretty well if this ran once. However if I try again to check for update, I would get the following error which happens while trying to delete the file in DownloadMsiFile() :
The process cannot access the file %temp%\ol.msi because it is being used by another process
If I use the sysinternals handle.exe utility to check the handles on this file I get the outlook process having a handle lock on this file:
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
I was wondering how can I close the handle to avoid this error? Any help is really appreciated

So here is what I have to do to close the handle. I have added the following lines after opening the MSI file:
Marshal.FinalReleaseComObject(oRecord)
oView.Close()
Marshal.FinalReleaseComObject(oView)
Marshal.FinalReleaseComObject(oDb)
oRecord = Nothing
oView = Nothing
oDb = Nothing
So my final code looked like the following:
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

Related

Needs to get default program association on remote computers

I am using the below code to get some information on remote computers. Basically, I need to get the OS version (which is working well) and the default program for .pdf files and the default browser. I can get the OS version but the code fails to get the default program association. The Remote Registry is enabled and started but even though I am getting an "access is denied" error message. Any clue guys? Hope you could help me. Thanks
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Const HKEY_LOCAL_MACHINE As String = "80000002"
Const HKEY_current_user1 As String = "80000001"
'Dim remoteRegistryController As New System.ServiceProcess.ServiceController("RemoteRegistry", computerNameTB.Text)
'remoteRegistryController.Start()
Dim osName, pdf, browser As String
Dim options As New ConnectionOptions
options.Impersonation = ImpersonationLevel.Impersonate
options.EnablePrivileges = True
options.Username = "hd_juann"
options.Password = "Diosteamo42="
Dim myScope As New ManagementScope("\\" & computerNameTB.Text & "\root\default", options)
Dim mypath As New ManagementPath("StdRegProv")
Dim mc As New ManagementClass(myScope, mypath, Nothing)
Try
Dim inParams As ManagementBaseObject = mc.GetMethodParameters("GetDWORDValue")
inParams("hDefKey") = UInt32.Parse(HKEY_LOCAL_MACHINE, System.Globalization.NumberStyles.HexNumber) 'RegistryHive.LocalMachine
inParams("sSubKeyName") = "Software\Microsoft\Windows NT\currentVersion"
inParams("sValueName") = "ProductName"
'Dim osName As String
Dim outParams As ManagementBaseObject = mc.InvokeMethod("GetStringValue", inParams, Nothing)
If (outParams("ReturnValue").ToString() = "0") Then
'MessageBox.Show(outParams("sValue").ToString())
osName = outParams("sValue").ToString()
Else
MessageBox.Show("Error retrieving value : " + outParams("ReturnValue").ToString())
End If
'get pdf
Dim inParamsPDF As ManagementBaseObject = mc.GetMethodParameters("GetDWORDValue")
inParamsPDF("hDefKey") = UInt32.Parse(HKEY_current_user1, System.Globalization.NumberStyles.HexNumber) 'RegistryHive.LocalMachine
inParamsPDF("sSubKeyName") = "Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.pdf\UserChoice"
inParamsPDF("sValueName") = "ProgId"
'Dim pdf As String
Dim outParamsPDF As ManagementBaseObject = mc.InvokeMethod("GetStringValue", inParamsPDF, Nothing)
If (outParamsPDF("ReturnValue").ToString() = "0") Then
pdf = outParamsPDF("sValue").ToString()
Else
pdf = "No value Set"
End If
'MessageBox.Show(pdf)
'remoteRegistryController.Stop()
'get browser
Dim inParamsBrowser As ManagementBaseObject = mc.GetMethodParameters("GetDWORDValue")
inParamsBrowser("hDefKey") = UInt32.Parse(HKEY_current_user1, System.Globalization.NumberStyles.HexNumber) 'RegistryHive.LocalMachine
inParamsBrowser("sSubKeyName") = "SOFTWARE\Microsoft\Windows\Shell\Associations\URLAssociations\https\UserChoice"
inParamsBrowser("sValueName") = "ProgId"
'Dim browser As String
Dim outParamsBrowser As ManagementBaseObject = mc.InvokeMethod("GetStringValue", inParamsBrowser, Nothing)
If (outParamsBrowser("ReturnValue").ToString() = "0") Then
browser = outParamsBrowser("sValue").ToString()
Else
browser = "No value Set"
End If
MsgBox(osName & " is installed on computer " & computerNameTB.Text & vbCrLf & "PDF Default App: " & pdf & vbCrLf & "Default Browser: " & browser,, "ComputerInfo W10 Upgrade Project")
Catch err As Exception
If osName <> "" Then
MsgBox(osName & " is installed on computer: " & computerNameTB.Text & ". No default program association information could be accessed " & err.Message,, "ComputerInfo W10 Upgrade Project")
Else
MsgBox("It was no possible to query computer: " & computerNameTB.Text,, "ComputerInfo W10 Upgrade Project")
End If
'MsgBox("It was no possible to query computer: " & computerNameTB.Text,, "ComputerInfo W10 Upgrade Project")
End Try
End Sub
eoor

change extension and move file, using vb.net

I have the following code, which changes the extension of a txt to doc, and then moves it (from d:\1 to d:\2). The extension changes successfully, but it does not move, and I get an error
Cannot create a file when that file already exists.
Please suggest.
For Each filePath In Directory.GetFiles("D:\1", "*.txt")
File.Move(filePath, Path.ChangeExtension(filePath, ".doc"))
Next
Dim filesToMove = From f In New DirectoryInfo("d:\1").EnumerateFiles("*.doc")
For Each f In filesToMove
f.MoveTo("d:\2")
Next
This will check for an existing file of the same name and delete it first (you may want to handle this differently). It will then move and rename in one call to File.Move
Dim directory1 = "D:\1"
Dim directory2 = "D:\2"
For Each oldFileName In Directory.GetFiles(directory1, "*.txt")
Dim newFileName = Path.ChangeExtension(oldFileName, ".doc").Replace(directory1, directory2)
If File.Exists(newFileName) Then File.Delete(newFileName)
File.Move(oldFileName, newFileName)
Next
ok, finally found the solution. not very professional, but works anyways: ( Many thanks to all members who have helped earlier)
Private Sub logchange(ByVal source As Object,
ByVal e As System.IO.FileSystemEventArgs)
If e.ChangeType = IO.WatcherChangeTypes.Changed Then
Dim sourceDirectory As String = "D:\1"
Dim archiveDirectory As String = "D:\2"
Try
Dim jpgFiles = Directory.EnumerateFiles(sourceDirectory, "*.wav")
For Each currentFile As String In jpgFiles
Dim fileName = Path.GetFileName(currentFile)
Directory.Move(currentFile, Path.Combine(archiveDirectory,
Path.GetFileNameWithoutExtension(fileName) & ".doc"))
Next
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End If
If e.ChangeType = IO.WatcherChangeTypes.Created Then
Dim sourceDirectory As String = "D:\1"
Dim archiveDirectory As String = "D:\2"
Try
Dim jpgFiles = Directory.EnumerateFiles(sourceDirectory, "*.wav")
For Each currentFile As String In jpgFiles
Dim fileName = Path.GetFileName(currentFile)
Directory.Move(currentFile, Path.Combine(archiveDirectory,
Path.GetFileNameWithoutExtension(fileName) & ".doc"))
Next
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End If
End Sub

VB Script to vb.net

I've acquires an old VBScript that was used to retrived test score that I'm trying to convert to a VB.net Form app.
I'm stuck with this function
Function getit()
Dim xmlhttp
Dim pageNum
Dim objStream
Dim objDebugStream
Set objStream = CreateObject("ADODB.Stream")
objStream.Type = 1 'adTypeBinary
pageNum = 1
Do While pageNum > 0
Set xmlhttp=CreateObject("MSXML2.ServerXMLHTTP")
'strURL = DownloadDest
Wscript.Echo "Download-URL: " & strURL & "&page_num=" & pageNum
'For basic auth, use the line below together with user+pass variables above
xmlhttp.Open "GET", strURL & "&page_num=" & pageNum, false
xmlhttp.Send
Wscript.Echo "Download-Status: " & xmlhttp.Status & " " & xmlhttp.statusText
If xmlhttp.Status = 200 Then
If Left(LCase(xmlhttp.responseText),16) <> "no records found" Then
If objStream.State = 0 Then
objStream.Open
End If
objStream.Write xmlhttp.responseBody
If debugEachPage Then
Set objDebugStream = CreateObject("ADODB.Stream")
objDebugStream.Type = 1 'adTypeBinary
objDebugStream.Open
objDebugStream.Write xmlhttp.responseBody
objDebugStream.SaveToFile ".\sortest_aleks_" & classCode & "_page_" & pageNum & ".csv"
objDebugStream.Close
Set objDebugStream = Nothing
End If
Else
If pageNum = 1 Then
WScript.Echo "No Records Found for " & classCode
End If
pageNum = 0 ' Have to set this to exit loop
End If
Else
WScript.Echo "Response Status of " & xmlhttp.Status & " for " & classCode
End If
If pageNum <> 0 Then
pageNum = pageNum + 1
End If
Set xmlhttp=Nothing
Loop
If objStream.State <> 0 Then
objStream.SaveToFile LocalFile
objStream.Close
End If
Set objStream = Nothing
End Function
What I wrote looks like this
Private Sub GetALEKSData(ByVal strURL As String)
REM ======================================================================================================
' This Module will access the ALEKS Web Site and access the CofC foreign language scores for the terms indicated days
' The Comma Seperated Values (CSV) as then stored in the main form Text Box
'=========================================================================================================
Dim ALEKStr As System.IO.Stream = Nothing
Dim srRead As System.IO.StreamReader = Nothing
Try
'Create a WebReq for the URL
Dim WebReq As System.Net.WebRequest = System.Net.HttpWebRequest.Create(strURL)
'If required by the server, set the credentials.
WebReq.Credentials = CredentialCache.DefaultNetworkCredentials
'Get the Respponse.
Dim WebResp As System.Net.WebResponse = WebReq.GetResponse
' Display the status.
' If required by the server, set the credentials.
ALEKStr = WebResp.GetResponseStream
srRead = New System.IO.StreamReader(ALEKStr)
' read all the text
TextBox1.Text = srRead.ReadToEnd
Catch ex As Exception
TextBox1.Text = QQ REM Wipe Text box to indicate No DATA to Process
Finally
' Close Stream and StreamReader when done
srRead.Close()
ALEKStr.Close()
End Try
Debug.Print(TextBox1.Text)
REM Remove NO Data message
If InStr(TextBox1.Text, "No records match criteria.") > 0 Then TextBox1.Text = QQ
DataFileHasData = Len(TextBox1.Text) > 0
End Sub
It is returning with :Access denied: wrong3 HTTP header from
Not sure what I'm missing
Try this:
Private Sub GetALEKSData(ByVal strURL As String)
REM ======================================================================================================
' This Module will access the ALEKS Web Site and access the CofC foreign language scores for the terms indicated days
' The Comma Seperated Values (CSV) as then stored in the main form Text Box
'=========================================================================================================
Using wc As New System.Net.WebClient()
Try
wc.Credentials = CredentialCache.DefaultNetworkCredentials
TextBox1.Text = wc.DownloadString(strURL)
Catch
TextBox1.Text = QQ
End Try
End Using
Debug.Print(TextBox1.Text)
If TextBox1.Text.Contains("No records match criteria.") Then TextBox1.Text = QQ
DataFileHasData = Not String.IsNullorWhiteSpace(TextBox1.Text)
End Sub
And if that doesn't work, the error message says, "Access Denied", so the problem is probably this line:
wc.Credentials = CredentialCache.DefaultNetworkCredentials
If that still doesn't help, install fiddler and compare the HTTP requests sent by the old vbscript to the new VB.Net code. You'll be able to see exactly what you're missing.
Setting the UserAgent fixed the issue
Private Sub GetWEBData(ByVal strURL As String)
REM ======================================================================================================
' This Module will access the WEB Web Site and access the CofC foreign language scores for the terms indicated days
' The Comma Seperated Values (CSV) as then stored in the main form Text Box
'=========================================================================================================
'Clear existing data
Try
'Create a WebReq for the URL
Dim WebReq As HttpWebRequest = CType(WebRequest.Create(strURL), HttpWebRequest)
'If required by the server, set the credentials.
WebReq.Credentials = CredentialCache.DefaultNetworkCredentials
WebReq.UserAgent = "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36"
'Get the Respponse.
'Dim WebResp As System.Net.WebResponse = WebReq.GetResponse
Dim WebResp As HttpWebResponse = CType(WebReq.GetResponse(), HttpWebResponse)
' Display the status.
' Console.WriteLine(WebResp.StatusDescription)
' Open the stream using a StreamReader for easy access.
Dim WEBtream As Stream = WebResp.GetResponseStream()
' Open the stream using a StreamReader for easy access.
Dim srRead As New StreamReader(WEBtream)
' Read the content.
Dim responseFromServer As String = srRead.ReadToEnd()
' Display the content.
TextBox1.Text = responseFromServer
TextBox1.Refresh()
'Console.WriteLine(responseFromServer)
' Cleanup the streams and the response.
srRead.Close()
WEBtream.Close()
WebResp.Close()
Catch ex As Exception
MsgBox("WEB DATA READ ERROR OCCURED", MsgBoxStyle.Critical, "Program Error")
End Try
End Sub

FTP Client uploads only 0KB files

I am having a small problem with my FTP client.
Choosing a file works, renaming that file with 4 variables works.
It's the upload that is causing me trouble.
Whenever a file is uploaded to the FTP server it says it is 0KB.
I am thinking of 2 possible problems:
Visual studio tells me that the variable file is used before it has been assigned a value, to make sure it isn't null i did the following.
Dim file As Byte()
If (Not file Is Nothing) Then
strz.Write(file, 0, file.Length)
strz.Close()
strz.Dispose()
FileSystem.Rename(Filename, originalFile)
End If
This Takes care of any possible Errors.
The second one is fName, same warning as with file, and I took care of it the same way.
another possibility is that my code just takes the 4 variables and makes that into a file and uploads it, hence the 0KB size....
Here's my code:
Dim Filename As String
Dim originalFile As String
Private Function enumerateCheckboxes(ByVal path As String)
originalFile = path
Dim fName As String
For Each Control In Me.Controls
If (TypeOf Control Is ComboBox AndAlso DirectCast(Control, ComboBox).SelectedIndex > -1) Then
fName += CStr(Control.SelectedItem.Key) + "_"
End If
Next
Try
fName = path + fName.Substring(0, fName.Length - 1) + ".jpg"
Catch ex As Exception
MsgBox(ex.Message)
MsgBox("Stack Trace: " & vbCrLf & ex.StackTrace)
End Try
Return fName
End Function
Public Function OpenDialog()
Dim FD As OpenFileDialog = New OpenFileDialog()
FD.Title = "Selecteer een bestand"
FD.InitialDirectory = "C:\"
FD.Filter = "All files (*.*)|*.*|All files (*.*)|*.*"
FD.FilterIndex = 2
FD.RestoreDirectory = True
If FD.ShowDialog() = DialogResult.OK Then
Dim Filename As String = FD.FileName
Filename = StrReverse(Filename)
Filename = Mid(Filename, InStr(Filename, "\"), Len(Filename))
Filename = StrReverse(Filename)
MsgBox(enumerateCheckboxes(Filename))
End If
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim request As System.Net.FtpWebRequest = DirectCast(System.Net.WebRequest.Create("ip" & enumerateCheckboxes(Filename)), System.Net.FtpWebRequest)
request.Credentials = New System.Net.NetworkCredential("username", "password")
request.Method = System.Net.WebRequestMethods.Ftp.UploadFile
Dim file() As Byte
Try
Filename = OpenDialog()
If (Not Filename Is Nothing) Then
System.IO.File.ReadAllBytes(Filename)
End If
Catch ex As Exception
MessageBox.Show(ex.Message)
MessageBox.Show("Stack Trace: " & vbCrLf & ex.StackTrace)
End Try
If (Not Filename Is Nothing) Then
FileSystem.Rename(originalFile, Filename)
End If
Dim strz As System.IO.Stream = request.GetRequestStream()
If (Not file Is Nothing) Then
strz.Write(file, 0, file.Length)
strz.Close()
strz.Dispose()
FileSystem.Rename(Filename, originalFile)
End If
End Sub
End Class
I have looked at multiple threads with the same problem as me.
Threads like this
But i dont believe this applies to my problem.
If you would be so kind to explain what i did wrong and how i can fix and avoid this in the future, my debugging is still a bit rough...
Thank you in advance!
Visual Studio is giving you that warning because you never assign anything to the file array. I think that on the line where you have:
System.IO.File.ReadAllBytes(Filename)
You really meant to have:
file = System.IO.File.ReadAllBytes(Filename)

VB won't show PDF Reader

I use the following Code to Open a PDF File:
Public Sub Execute_Doc(afilename As String, Optional style As ProcessWindowStyle = ProcessWindowStyle.Minimized)
Dim myProcess As New Process
Const ERROR_FILE_NOT_FOUND As Integer = 2
Const ERROR_ACCESS_DENIED As Integer = 5
Try
myProcess.StartInfo.FileName = afilename
myProcess.StartInfo.WindowStyle = style
myProcess.Start()
Catch e As System.ComponentModel.Win32Exception
If e.NativeErrorCode = ERROR_FILE_NOT_FOUND Then
Console.WriteLine(e.Message + ". Check the path.")
MsgBox("File<" + afilename + "> not found!")
Else
If e.NativeErrorCode = ERROR_ACCESS_DENIED Then
Console.WriteLine(e.Message + ". You do not have permission to print this file.")
MsgBox("File <" + afilename + "> couldn't be opened!")
End If
End If
MsgBox(e.ToString())
Catch ex As Exception
MsgBox(e.ToString())
Finally
myProcess.Kill()
myProcess.Dispose()
End Try
End Sub
I call Execute_Doc("C:\ProgrammName\Test.pdf", ProcessWindowStyle.Normal) but the Adobe Reader won't show up. I can see it in the Task Manager.
Well it works if I start Adobe Reader first without any files by clicking the default Icon on my Desktop. It also works with the integrated PDF Reader from Windows 8.1. I can't debug this isse on my Windows 7 / VS 2013 Computer. The problem only exists on ONE! client computer.
Any tipps how to solve this?
Public Sub Execute_Doc(afilename As String, Optional style As ProcessWindowStyle = ProcessWindowStyle.Minimized)
Dim myProcess As New Process
Const ERROR_FILE_NOT_FOUND As Integer = 2
Const ERROR_ACCESS_DENIED As Integer = 5
Try
myProcess.StartInfo.FileName = "AcroRd32.exe " & afilename
myProcess.StartInfo.WindowStyle = style
myProcess.Start()
Catch e As System.ComponentModel.Win32Exception
If e.NativeErrorCode = ERROR_FILE_NOT_FOUND Then
Console.WriteLine(e.Message + ". Check the path.")
MsgBox("File<" + afilename + "> not found!")
Else
If e.NativeErrorCode = ERROR_ACCESS_DENIED Then
Console.WriteLine(e.Message + ". You do not have permission to print this file.")
MsgBox("File <" + afilename + "> couldn't be opened!")
End If
End If
MsgBox(e.ToString())
Catch ex As Exception
MsgBox(e.ToString())
Finally
myProcess.Kill()
myProcess.Dispose()
End Try
End Sub
Just use Shell Execute it is provided by the Win API so you do not have to worry about what program is installed to handle the extension. Windows does the work for you.
Private Function ShellExecute(ByVal File As String) As Boolean
Dim myProcess As New Process
myProcess.StartInfo.FileName = File
myProcess.StartInfo.UseShellExecute = True
myProcess.StartInfo.RedirectStandardOutput = False
myProcess.Start()
myProcess.Dispose()
End Function