Using SSIS Script Task to download files from FTP - vb.net

I am trying to download files from FTP using SSIS script task, I have written some code to download files from FTP using the ftpWebRequest class which is working. But I also need to take in to account that we might be asked to download it from a sFTP. Since many are suggesting that WinSCP assemblies are the best way to download sFTP files, can some provide me with some examples to download files using a SSIS script with the WinSCP classes.
PS: I am new to Vb.net

Yes, as people have said the only option is to use a library like WinSCP.
Take a look at the examples here on the WinSCP website - http://winscp.net/eng/docs/script_vbnet_robust_example
Here is the source code from the example
Imports System
Imports System.IO
Imports System.Diagnostics
Imports System.Xml
Imports System.Xml.XPath
Imports System.Configuration.ConfigurationManager
Public Class SFTP
' SFTP support, built on WinSCP
Public Shared Function PutSFTP(ByRef filename As String, ByRef remotehost As String, ByRef username As String, ByRef password As String, _
Optional ByVal outfilename As String = Nothing, Optional ByVal output As String = Nothing,
Optional ByRef errmsg As String = Nothing) As Boolean
' Run hidden WinSCP process
Dim winscp As Process = New Process()
Dim logname As String = Path.ChangeExtension(Path.GetTempFileName, "xml")
With winscp.StartInfo
' SFTPExecutable needs to be defined in app.config to point to winscp.com
Try
.FileName = AppSettings("SFTPExecutable")
If .FileName Is Nothing OrElse .FileName.Length = 0 Then Throw (New Exception("from PutSFTP: SFTPExecutable not set in config file."))
Catch ex As Exception
errmsg = ex.Message
Return False
End Try
.Arguments = "/xmllog=" + logname
.UseShellExecute = False
.RedirectStandardInput = True
.RedirectStandardOutput = True
.CreateNoWindow = True
End With
Try
winscp.Start()
Catch ex As Exception
errmsg = "from PutSFTP: Could not run the WinSCP executable " & winscp.StartInfo.FileName & Environment.NewLine & ex.Message
Return False
End Try
' Feed in the scripting commands
With winscp.StandardInput
.WriteLine("option batch abort")
.WriteLine("option confirm off")
.WriteLine("open sftp://" & username & ":" & password & "#" & remotehost & "/")
If outfilename Is Nothing Then .WriteLine("put " & filename) Else .WriteLine("put " & filename & " """ & outfilename & """")
.Close()
End With
If output IsNot Nothing Then output = winscp.StandardOutput.ReadToEnd()
' Wait until WinSCP finishes
winscp.WaitForExit()
' Parse and interpret the XML log
' (Note that in case of fatal failure the log file may not exist at all)
If Not File.Exists(logname) Then
errmsg = "from PutSFTP: The WinSCP executable appears to have crashed."
Return False
End If
Dim log As XPathDocument = New XPathDocument(logname)
Dim ns As XmlNamespaceManager = New XmlNamespaceManager(New NameTable())
ns.AddNamespace("w", "http://winscp.net/schema/session/1.0")
Dim nav As XPathNavigator = log.CreateNavigator()
' Success (0) or error?
Dim status As Boolean = (winscp.ExitCode = 0)
If Not status Then
errmsg = "from PutSFTP: There was an error transferring " & filename & "."
' See if there are any messages associated with the error
For Each message As XPathNavigator In nav.Select("//w:message", ns)
errmsg &= Environment.NewLine & message.Value
Next message
End If
Try
My.Computer.FileSystem.DeleteFile(logname)
Catch ex As Exception
' at least we tried to clean up
End Try
Return status
End Function
End Class

Related

How Do I Automatically Update the ChromeDriver or EdgeDriver in VBA?

I have searched the web for a solution to "auto-update" my Selenium EdgeDriver automatically. For those who use SeleniumBasic, you know it can be a hassle to manually download the driver from the respective webpage every so often when your main Host browser gets a major update.
In my web searching, I have found support for other languages to "auto-update" their versions of these drivers, but VBA, as usual, was lacking support.
Now I don't claim this to be the perfect solution, but it at least works. The problem I can see in the future is that layouts of each respective webpage may change, so I do welcome updates if this is the case and I will try to update as well. But for the most part, it should just work.
While this is a Self-Answered question, I absolutely would love to see other methods posted here for myself and other users to try out. SeleniumBasic is a good tool for certain applications, but often lacks community support as VBA is not as widely used in the community as other languages - at least not on a more sophisticated level.
I should start out by saying that I am only supporting Chrome and Edge drivers for the time being. But if you can follow along, you might be able to add your own support for any of the other SeleniumBasic-supported WebDrivers.
Before we get started, it is important to enable the following References by going to Tools > References within the VBE:
Next, you need to create a Class Module named SeleniumWebDriver.
I decided to make this a Class Object because I intend to build a little on it in the future. You may add your own Properties and functions as you wish, but the code being provided will only allow updating the WebDrivers, at least for now.
Here is the complete Class Module Code:
Option Explicit
Rem Did Chrome change their file url and break your code?
' Check for an update: https://stackoverflow.com/a/67996166/5781745
Private ChromeDriver As Selenium.ChromeDriver
Private EdgeDriver As Selenium.EdgeDriver
Private SeleniumFolder As String
Private TempZipFile As String
Private ChromeInit As Boolean, EdgeInit As Boolean
Public Enum dType
Chrome
Edge
End Enum
Public Property Get SeleniumFolderPath() As String
SeleniumFolderPath = SeleniumFolder
End Property
Public Property Let SeleniumFolderPath(ByVal FolderPath As String)
SeleniumFolder = FolderPath
End Property
Public Sub UpdateDriver(ByVal DriverType As dType)
'URLs to the drivers' home pages to which we can grab the curr versions
Dim URLPath As String
Select Case DriverType
Case dType.Chrome
URLPath = "https://chromedriver.chromium.org/home"
Case dType.Edge
URLPath = "https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/"
End Select
'Grab the current Version # from the driver's webpage
Dim Doc As New HTMLDocument, DriverVer As String
With New MSXML2.XMLHTTP60
.Open "GET", URLPath
.send
Doc.body.innerHTML = .responseText
End With
DriverVer = getCurrentVersion(Doc, DriverType)
DownloadUpdatedDriver DriverVer, DriverType
ExtractZipAndCopy DriverType
End Sub
' For use in a later project. Not needed at this time
Private Sub InitializeDriver(ByVal DriverType As dType)
Select Case DriverType
Case dType.Chrome
Set ChromeDriver = New Selenium.ChromeDriver
ChromeDriver.Start
ChromeInit = True
Case dType.Edge
Set EdgeDriver = New Selenium.EdgeDriver
EdgeDriver.Start
EdgeInit = True
End Select
End Sub
Private Function getCurrentVersion(Doc As HTMLDocument, DriverType As dType) As String
Dim div As HTMLDivElement
Select Case DriverType
Case dType.Chrome
For Each div In Doc.getElementsByTagName("p")
If div.innerText Like "Latest stable release*" Then
With New VBScript_RegExp_55.RegExp
.Pattern = "ChromeDriver\s([\d\.]+)\b"
getCurrentVersion = .Execute(div.innerText)(0).SubMatches(0)
Exit Function
End With
End If
Next
Case dType.Edge
With New VBScript_RegExp_55.RegExp
.Pattern = "Version:\s([\d\.]+)"
For Each div In Doc.getElementsByClassName("module")(0).getElementsByTagName("p")
If .test(div.innerText) Then
getCurrentVersion = .Execute(div.innerText)(0).SubMatches(0)
Exit Function
End If
Next
End With
End Select
End Function
Private Sub DownloadUpdatedDriver(ByVal CurrVersion As String, DriverType As dType)
Dim URLPath As String
Select Case DriverType
Case dType.Chrome
URLPath = "https://chromedriver.storage.googleapis.com/" & CurrVersion & "/chromedriver_win32.zip"
Case dType.Edge
Kill Environ$("LocalAppData") & "\SeleniumBasic\Driver_Notes\*.*"
URLPath = "https://msedgedriver.azureedge.net/" & CurrVersion & "/edgedriver_win64.zip"
End Select
Dim FileStream As New ADODB.Stream
With New MSXML2.XMLHTTP60
.Open "GET", URLPath
.send
FileStream.Open
FileStream.Type = adTypeBinary
FileStream.Write .responseBody
FileStream.SaveToFile TempZipFile, adSaveCreateOverWrite
FileStream.Close
End With
End Sub
Private Sub ExtractZipAndCopy(ByVal DriverType As dType)
Dim FileName As String
Select Case DriverType
Case dType.Chrome: FileName = "\chromedriver.exe"
Case dType.Edge: FileName = "\edgedriver.exe"
End Select
'Delete the old WebDriver
Kill SeleniumFolder & FileName
'Copy the new driver from .zip file to SeleniumBasic folder
Dim oShell As New shell
oShell.Namespace(SeleniumFolder).CopyHere oShell.Namespace(TempZipFile).Items
'Selenium VBA expects 'edgedriver' for edge, but new drivers are named 'msedgedriver'.
'If we are updating Edge, we need to rename the file
If DriverType = dType.Edge Then
Name SeleniumFolder & "msedgedriver.exe" As SeleniumFolder & "edgedriver.exe"
End If
'Delete the temporary zip file
Kill TempZipFile
End Sub
Private Sub Class_Initialize()
' Set the default file path. Can be modified later using ChromeDriverPath property
SeleniumFolder = Environ$("LocalAppData") & "\SeleniumBasic\"
TempZipFile = Environ$("LocalAppData") & "\Temp\WebDriver.zip"
End Sub
Now that you've created your Selenium Class, you can now use it in a standard module such as:
Important Tip: I am not sure if there is a delay between when you update your web browser and when the drivers are officially released. Therefore before updating your driver, I would put some error handling to see if Selenium throws an error first. If the driver does not match the browser version, Selenium will throw error # 33. If you check for this error, you should be safe to go ahead and update the WebDriver at this point. What we want to prevent is that you update your driver before your browser is automatically updated, causing mismatching versions.
It is also possible that your browser may update and the Selenium driver hasn't been released yet - but unfortunately that is not something that we can control.
The remainder of this answer will just go into some detail as to what it's doing. If you don't care, you may leave now.
First, as with any other object, we have to initialize it. In the above example, we do that with the With New SeleniumWebDriver statement. This fires the Class_Initialize() event here:
Private Sub Class_Initialize()
' Set the default file path. Can be modified later using ChromeDriverPath property
SeleniumFolder = Environ$("LocalAppData") & "\SeleniumBasic\"
TempZipFile = Environ$("LocalAppData") & "\Temp\WebDriver.zip"
End Sub
The purpose of this is to set the default file paths for the SeleniumBasic folder and temp file. However, if your folder is somewhere else, this class has a property to which you can change the folder manually. Just use the ClassObj.SeleniumFolderPath() property to establish your new path.
The TempZipFile is a class-scoped variable that will store the .zip file you download from the respective websites.
Upon calling the UpdateDriver method, the class will place a GET request to the respective driver's webpage, then grab the current version # from the page. It will then pass this driver version to the DownloadUpdatedDriver routine, which stores the download links for each respective driver. For Chrome, the link is: https://chromedriver.storage.googleapis.com/<Version#>/chromedriver_win32.zip, and for Edge it's: https://msedgedriver.azureedge.net/<Version#>/edgedriver_win64.zip. It's important to realize that if you happen to be using the 32 bit version of Edge, you will need to change the URL to edgedriver_win32.zip. This routine downloads the .zip file to your local AppData's Temp Folder.
After the file has been downloaded, we then proceed to call the ExtractZipAndCopy routine. This simply extracts the .exe files to the Selenium Folder, first deleting the old file. Edge does a little extra maintenance work, but you're now essentially updated!
I hope this helps someone out there who is annoyed as I am having to periodically update these drivers and was wanting an automated solution. Please feel free to edit this answer if minor changes are needing to be made, such as if a URL is broken.
There is now an API to check Latest Release of ChromeDriver. So the code can become shorter. Also, adding a functionality to run the update only if the current chrome version does not match installed chromedriver version. And to copy using administrator rights.
I am not using Edge so that code functionality is not there in my code.
Function chkchromever()
Tempfolder = "D:\"
TempZipFile = Tempfolder & "Chromedriver.zip"
SeleniumFolder = Environ$("ProgramW6432") & "\SeleniumBasic\"
TempDrvFile = Tempfolder & "Chromedriver.exe"
'Delete chromedriver.exe from temporary folder if it already exists
If Dir(TempDrvFile) <> "" Then
Kill (TempDrvFile)
End If
Dim oShell As New WshShell
Dim objHttp As Object
Set objHttp = CreateObject("MSXML2.ServerXMLHTTP.6.0")
'Get chrome version
chrversion = CreateObject("WScript.Shell").RegRead("HKEY_CURRENT_USER\Software\Google\Chrome\BLBeacon\version")
dotsarr = Split(chrversion, ".")
leftchrver = dotsarr(0) & dotsarr(1)
'Get chromedriver version
gspath = Chr(34) & SeleniumFolder & "chromedriver.exe" & Chr(34)
torun = gspath & " --version"
errcode = oShell.Exec(torun).StdOut.ReadAll
verarr = Split(errcode, " ")
chrdrv = verarr(1)
dotsarr2 = Split(chrdrv, ".")
leftchrdrv = dotsarr(0) & dotsarr(1)
'If major version mismatch (first two numbers) then ask if update required
If leftchrver <> leftchrdrv Then
myyn = MsgBox("Wrong version of chromedriver. " & vbCrLf & "Chrome version is " & chrversion & vbCrLf & "Chrome driver version is " & chrdrv, vbYesNo, "Do you want to update Chromedriver ?")
If myyn = vbNo Then Exit Function
'Get latest release version of chromedriver which matches installed version of Chrome
url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_" & dotsarr(0)
Call objHttp.Open("GET", url, False)
Call objHttp.send("")
version_number = objHttp.responseText
dotsarr3 = Split(version_number, ".")
leftversion_no = dotsarr3(0) & dotsarr3(1)
If leftchrver = leftversion_no Then
'If chromedriver found then download it
download_url = "https://chromedriver.storage.googleapis.com/" + version_number + "/chromedriver_win32.zip"
Call objHttp.Open("GET", download_url, False)
Call objHttp.send("")
Set fileStream = New ADODB.Stream
With fileStream
.Open
.Type = adTypeBinary
.Write objHttp.responseBody
.Position = 0
.SaveToFile TempZipFile, adSaveCreateOverWrite
.Close
End With
'Copy the new driver from .zip file to SeleniumBasic folder
Set oApp = CreateObject("Shell.Application")
oApp.Namespace(Tempfolder).CopyHere oApp.Namespace(TempZipFile).Items
'Create batch file to copy chromedriver.exe to Seleniumbasic folder and run it using Administrator rights
tmpbatpath = Tempfolder & "copychdrv.bat"
'Check if Chromedriver downloaded successfully
If Dir(TempDrvFile) <> "" Then
Open tmpbatpath For Output As #1
'Enable these if required to copy chromedriver.exe
' Print #1, "taskkill /f /im GoogleCrashHandler.exe"
' Print #1, "taskkill /f /im GoogleCrashHandler64.exe"
' Print #1, "taskkill /f /im Chrome.exe"
' Print #1, "taskkill /f /im Googleupdate.exe"
If IsProcessRunning("Chromedriver.exe") Then
Print #1, "taskkill /f /im Chromedriver.exe"
End If
Print #1, "copy " & Chr(34) & TempDrvFile & Chr(34) & " " & Chr(34) & SeleniumFolder & "Chromedriver.exe" & Chr(34) & "/y"
Close #1
'copy it now by running batch file
success = ShellExecute(0, "runas", tmpbatpath, aPic, vbNullString, SW_SHOWNORMAL)
End If
'Cleanup
If Dir(TempZipFile) <> "" Then
Kill (TempZipFile)
End If
If Dir(tmpbatpath) <> "" Then
Kill (tmpbatpath)
End If
End If
End If
End Function
As i am on a company PC, I updated the code to work on a locked PC
Option Explicit
'**********************************************************
' PUBLIC FUNCTION
'/
'**********************************************************
' #Fn chkchromever
'
' #brief Check if Selenium Crome Driver is up to date or update it
'
' #param checks Crome version and Installed and availible Driver for Selenium
'
' #SUBs IsProcessRunning, ShellExecute
'
' Librarys Selenium Type library
' Windows Script Host Object Model - Dim oShell As New WshShell ,
' Microsoft ActiveX Data Objects 6.1 Library - Set fileStream = New ADODB.Stream
'
' #return new driver
'/
Public Declare Function ShellExecute _
Lib "shell32.dll" _
Alias "ShellExecuteA" ( _
ByVal Hwnd As Long, _
ByVal lpOperation As String, _
ByVal lpFile As String, _
ByVal lpParameters As String, _
ByVal lpDirectory As String, _
ByVal nShowCmd As Long) _
As Long
Function chkchromever()
Dim tempfolder As Variant, TempZipFile As Variant
Dim SeleniumFolder As String, TempDrvFile As String
Dim chrversion As String, leftchrver As String
Dim dotsarr() As String
Dim gspath As String, torun As String, errcode As String
Dim verarr() As String, chrdrv As String, leftchrdrv As String
Dim dotsarr2() As String
Dim myyn As Integer
Dim Url As String, version_number As String, leftversion_no As String
Dim dotsarr3() As String
Dim download_url As String
tempfolder = "C:\Temp\"
TempZipFile = tempfolder & "Chromedriver.zip"
'SeleniumFolder = Environ$("ProgramW6432") & "\SeleniumBasic\"
SeleniumFolder = Environ$("LOCALAPPDATA") & "\SeleniumBasic\"
TempDrvFile = tempfolder & "Chromedriver.exe"
'Delete chromedriver.exe from temporary folder if it already exists
If Dir(TempDrvFile) <> "" Then
Kill (TempDrvFile)
End If
Dim oShell As New WshShell
Dim objHttp As Object
Set objHttp = CreateObject("MSXML2.ServerXMLHTTP.6.0")
'Get chrome version
chrversion = CreateObject("WScript.Shell").RegRead("HKEY_CURRENT_USER\Software\Google\Chrome\BLBeacon\version")
dotsarr = Split(chrversion, ".")
leftchrver = dotsarr(0) & dotsarr(1)
'Get chromedriver version
gspath = Chr(34) & SeleniumFolder & "chromedriver.exe" & Chr(34) ' chr34 er gåseøjne
torun = gspath & " --version"
errcode = oShell.Exec(torun).StdOut.ReadAll
verarr = Split(errcode, " ")
chrdrv = verarr(1)
dotsarr2 = Split(chrdrv, ".")
leftchrdrv = dotsarr2(0) & dotsarr2(1)
'If major version mismatch (first two numbers) then ask if update required
If leftchrver <> leftchrdrv Then
myyn = MsgBox("Wrong version of chromedriver. " & vbCrLf & "Chrome version is " & chrversion & vbCrLf & "Chrome driver version is " & chrdrv, vbYesNo, "Do you want to update Chromedriver ?")
If myyn = vbNo Then Exit Function
'Get latest release version of chromedriver which matches installed version of Chrome
Url = "https://chromedriver.storage.googleapis.com/LATEST_RELEASE_" & dotsarr(0)
Call objHttp.Open("GET", Url, False)
Call objHttp.send("")
version_number = objHttp.responseText
dotsarr3 = Split(version_number, ".")
leftversion_no = dotsarr3(0) & dotsarr3(1)
If leftchrver = leftversion_no Then
'If chromedriver found then download it
download_url = "https://chromedriver.storage.googleapis.com/" + version_number + "/chromedriver_win32.zip"
Call objHttp.Open("GET", download_url, False)
Call objHttp.send("")
Dim fileStream As Object
Set fileStream = New ADODB.Stream
With fileStream
.Open
.Type = adTypeBinary
.Write objHttp.responseBody
.Position = 0
.SaveToFile TempZipFile, adSaveCreateOverWrite
.Close
End With
'unzip file
Dim oApp As Object
Set oApp = CreateObject("Shell.Application")
oApp.Namespace(tempfolder).CopyHere oApp.Namespace(TempZipFile).Items
'Check if Chromedriver downloaded successfully
If Dir(TempDrvFile) <> "" Then
If IsProcessRunning("Chromedriver.exe") Then
Shell "cmd /c""" & "taskkill /f /im Chromedriver.exe"
End If
'Copy / Overwrite file in SeleniumBasic folder
FileCopy TempDrvFile, SeleniumFolder & "Chromedriver.exe"
End If
'Cleanup
If Dir(TempZipFile) <> "" Then
Kill (TempZipFile)
End If
If Dir(TempDrvFile) <> "" Then
Kill (TempDrvFile)
End If
End If
End If
End Function
Function IsProcessRunning(process As String)
Dim objList As Object
Set objList = GetObject("winmgmts:") _
.ExecQuery("select * from win32_process where name='" & process & "'")
IsProcessRunning = objList.Count > 0
End Function
I had to modify the code posted above by Jesper Martin Schumacher as our IT department has updated all Office Applications to 64-bit.
That code was failing on instances of "New" keyword, so I researched and found:
compile error User-defined type not defined at "oShell As WshShell"
The suggestion there was to use "Late Binding" CreateObject method instead, as below.
Set oShell = CreateObject("WScript.Shell") 'Had to edit this line. It WAS "Set oShell = New WshShell"
Set fileStream = CreateObject("ADODB.Stream") 'Had to edit this line. It WAS "Set fileStream = New ADODB.Stream"

Vb.net problems download

How can I do a check that first download something and only after that it starts up the program.exe?
I already tried to do something like this but it excutes the file when the download is not finished and it throws some errors.
my Code:
Dim client As WebClient = New WebClient
Dim SourcePath As String = "C:\ProgramData\KDetector\UserAssistView.exe"
Dim SaveDirectory As String = "C:\ProgramData\KDetector"
Dim FileName As String = System.IO.Path.GetFileName(SourcePath)
Dim SavePath As String = System.IO.Path.Combine(SaveDirectory, FileName)
If System.IO.File.Exists(SavePath) Then
Process.Start("C:\ProgramData\KDetector\UserAssistView.exe")
Else
client.DownloadFileAsync(New Uri("http://ge.tt/70n8YPr2"), "C:\ProgramData\KDetector\")
Process.Start("C:\ProgramData\KDetector\UserAssistView.exe")
End If
You are calling the asynchronous DownloadFile method. Asynchronous method will not block the calling thread.
In order to avoid the problem, your code must be like this:
Dim downloadLink As String = "http://www.nirsoft.net/utils/userassistview.zip"
Dim saveFilePath As String = "C:\ProgramData\KDetector\userassistvkew.zip"
Dim fileName As String = Path.GetFileNameWithoutExtension(saveFilePath)
Dim client As WebClient = New WebClient
Try
CheckExsist(saveFilePath, fileName)
'Before was client.DownloadFileAsync(New Uri("http://ge.tt/70n8YPr2"))
client.DownloadFile(downloadLink, saveFilePath)
MsgBox("Download file completed. File saved in: " & saveFilePath)
'Zip extraction stuff
Catch ex As Exception
MsgBox(ex.Message)
End Try
This is the CheckExsist sub:
Private Sub CheckExsist(ByRef sourcePath As String, ByVal fileName As String, Optional ByVal counter As Integer = 1)
If System.IO.File.Exists(sourcePath) Then
sourcePath = sourcePath.Replace(Path.GetFileName(sourcePath), "") & fileName & "(" & counter & ")" & Path.GetExtension(sourcePath)
counter += 1
CheckExsist(sourcePath, fileName, counter)
End If
End Sub
I manage to download the software (from the official website) and save it as a .zip. Code the last few rows to programmatically extract the .zip, if you ecounter any problem or bug feel free to ask with another question. Anyways there are alot of post on SO regarding zip extraction on vb.net

Executing msg.exe from Visual Basic application

I am trying to take text fields for old hostname, new hostname, username, and password and remotely change computer names. That part is working fantastic. It was all great until my manager saw it in action, since we have a policy against downloading and using freeware.
It's not freeware if I made it. Unfortunately, he sent it to my director, and know my director knows I know a little bit about Visual Basic, so he wants to loop the names from a CSV file, change the name, and send a message to the end user instructing them to save their files and reboot.
Unfortunately, net send has gone the way of XP since Vista. However, from Vista - Win8.1, there's a utility called msg.exe in C:\Windows\System32. In order to use it, the target computer has to have the registry value AllowRemoteRPC in HKLM\SYSTEM\CurrentControlSet\Control\Terminal Services set to 1.
So here's what the app does:
Reads the DWORD key AllowRemoteRPC and stores it to a variable (MyVal), changes the key to 1, attempts to send the message alerting the user they need to restart, changes the key back to MyVal, and then executes netdom renamecomputer and renames the PC. Everything works perfectly EXCEPT sending the message. I can open up a command prompt and type:
msg /server:hostname * /v /time:3600 "my message here
And it works perfectly (after manually editing the registry key to the needed value).
However, running it from VB doesn't work. Here's what I've tried:
"msg /server:" & hostname & " * /v /time:3600 ""my message here"""
"cmd.exe /D /c msg /server:" & hostname & " * /v /time:3600 ""my message here"""
Neither seems to work. I know the registry value is being changed. I put message boxes after each step in my and refreshed the regedit to actually see the value of the DWORD key, and it is changing. Everything APPEARS to be going smoothly, the message is just not getting sent.
I do have these commands running as arguments to a function I created in order to create a process so I could output the streamreader to a listbox.
Here's my code. Please keep in mind, I'm barely over 2 months into learning visual basic, so it's probably not the prettiest code out there:
Imports System
Imports System.IO
Imports System.Diagnostics
Imports System.Security.Permissions
Imports Microsoft.Win32
Public Class applicationMain
Private Sub btnExecute_Click(sender As Object, e As EventArgs) Handles btnExecute.Click
Dim oldPC As String = txtOldPC.Text
Dim newPC As String = txtNewPC.Text
Dim username As String = txtUsername.Text
Dim password As String = txtPassword.Text
If oldPC <> "" And newPC <> "" And username <> "" And password <> "" Then
Dim MyReg As Microsoft.Win32.RegistryKey = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(Microsoft.Win32.RegistryHive.LocalMachine, oldPC)
Dim MyRegKey As Microsoft.Win32.RegistryKey
Dim MyVal As String
lbOutput.Items.Clear()
MyRegKey = MyReg.OpenSubKey("System\CurrentControlSet\Control\Terminal Server")
MyVal = MyRegKey.GetValue("AllowRemoteRPC", RegistryValueKind.DWord)
MyRegKey.Close()
lbOutput.Items.Add("Processing registry changes...")
Try
MyRegKey = MyReg.OpenSubKey("System\CurrentControlSet\Control\Terminal Server", True)
MyRegKey.SetValue("AllowRemoteRPC", &H1, RegistryValueKind.DWord)
Catch ex As Exception
MessageBox.Show("An Error Has Occured:" & vbCrLf & vbCrLf & ex.ToString())
lbOutput.Items.Add("")
lbOutput.Items.Add("ABORTED!")
Exit Sub
End Try
lbOutput.Items.Add("Success!")
lbOutput.Items.Add("Sending message to user:")
Try
ExecuteCommand("cmd.exe", "/D /c msg /SERVER:" & oldPC & ".na.int.grp * /v /TIME:3600 ""Changes have been made by IS to your computer that require a restart. Please save your files and restart your computer to avoid service interruption.""")
Catch ex As Exception
MessageBox.Show("An Error Has Occured:" & vbCrLf & vbCrLf & ex.ToString())
lbOutput.Items.Add("")
lbOutput.Items.Add("ABORTED!")
MyRegKey = MyReg.OpenSubKey("System\CurrentControlSet\Control\Terminal Server", True)
MyRegKey.SetValue("AllowRemoteRPC", MyVal, RegistryValueKind.DWord)
Exit Sub
End Try
lbOutput.Items.Add(" Message: ""Changes have been made by IS to your computer that require a restart. Please save your files and restart your computer to avoid service interruption."" ")
lbOutput.Items.Add("Reverting registry changes...")
Try
MyRegKey = MyReg.OpenSubKey("System\CurrentControlSet\Control\Terminal Server", True)
MyRegKey.SetValue("AllowRemoteRPC", MyVal, RegistryValueKind.DWord)
Catch ex As Exception
MessageBox.Show("An Error Has Occured:" & vbCrLf & vbCrLf & ex.ToString())
lbOutput.Items.Add("")
lbOutput.Items.Add("ABORTED!")
Exit Sub
End Try
Try
ExecuteCommand("netdom", "renamecomputer " & oldPC & " /newname:" & newPC & " /userD:na\" & username & " /passwordd:" & password & " /usero:na\" & username & " /passwordo:" & password & " /Force")
Catch ex As Exception
MessageBox.Show("An Error Has Occured:" & vbCrLf & vbCrLf & ex.ToString())
lbOutput.Items.Add("")
lbOutput.Items.Add("ABORTED!")
Exit Sub
End Try
lbOutput.Items.Add("Success!")
lbOutput.Items.Add("")
lbOutput.Items.Add("Rename successful for " & oldPC & "!")
lbOutput.Items.Add("******************************************************************")
lbOutput.Items.Add("")
End If
End Sub
Private Function ExecuteCommand(ByVal cmd As String, ByVal arguments As String)
Dim cmdProcess As New Process()
Dim cmdProcessStartInfo As New ProcessStartInfo()
Dim cmdStreamReader As IO.StreamReader
Dim output As String
cmdProcessStartInfo.UseShellExecute = False
cmdProcessStartInfo.CreateNoWindow = True
cmdProcessStartInfo.RedirectStandardOutput = True
cmdProcessStartInfo.FileName = cmd
cmdProcessStartInfo.Arguments = arguments
cmdProcess.StartInfo = cmdProcessStartInfo
cmdProcess.Start()
cmdStreamReader = cmdProcess.StandardOutput
Do While cmdStreamReader.EndOfStream = False
output = cmdStreamReader.ReadLine()
lbOutput.SelectedIndex = lbOutput.Items.Count - 1
lbOutput.Items.Add(output)
Loop
cmdProcess.WaitForExit()
cmdProcess.Close()
Return vbNull
End Function
End Class
What do you know. There's actually nothing wrong with my code at all. While trying to play around with the paths variable, I decided "Fuhgeddaboudit, I'll just add the executable to the project!". Right clicked the project, Add -> Existing Item. Selected Executable as the type, and went to C:\Windows\System32 and, get this now, msg.exe wasn't there. At all. Opened Explorer and went to System32, msg.exe was there. For whatever reason, Visual Studio cannot see the program at all. Which is in and of itself weird.
So I copied msg.exe to my desktop, added it to source, the program works like a charm now.

Uploading to Google drive using VBA?

I have an MS Access database which now requires me to 'attach' documents to it. My intention is to store the documents on Google Drive and have a link on the database for users to retrieve the documents.
As there are many users spread through different cities, it is not practical to require them to have synced Google Drive folders. All the users will need the ability to upload to the database/GD so my intention is to have a separate Google account for the database - with its own login details.
example:
User clicks button to upload file
Save as dialog box appears and user selects file
Database logs into its Google Drive and uploads selected file
Lots of problems with this though, the main one being that Google Drive does not support VBA.
If the user is logged into their own Gmail account, that will probably be another issue.
I came across this code for vb.net on another site.
Imports System
Imports System.Diagnostics
Imports DotNetOpenAuth.OAuth2
Imports Google.Apis.Authentication.OAuth2
Imports Google.Apis.Authentication.OAuth2.DotNetOpenAuth
Imports Google.Apis.Drive.v2
Imports Google.Apis.Drive.v2.Data
Imports Google.Apis.Util
Imports Google.Apis.Services
Namespace GoogleDriveSamples
Class DriveCommandLineSample
Shared Sub Main(ByVal args As String)
Dim CLIENT_ID As [String] = "YOUR_CLIENT_ID"
Dim CLIENT_SECRET As [String] = "YOUR_CLIENT_SECRET"
'' Register the authenticator and create the service
Dim provider = New NativeApplicationClient(GoogleAuthenticationServer.Description, CLIENT_ID, CLIENT_SECRET)
Dim auth = New OAuth2Authenticator(Of NativeApplicationClient)(provider, GetAuthorization)
Dim service = New DriveService(New BaseClientService.Initializer() With { _
.Authenticator = auth _
})
Dim body As New File()
body.Title = "My document"
body.Description = "A test document"
body.MimeType = "text/plain"
Dim byteArray As Byte() = System.IO.File.ReadAllBytes("document.txt")
Dim stream As New System.IO.MemoryStream(byteArray)
Dim request As FilesResource.InsertMediaUpload = service.Files.Insert(body, stream, "text/plain")
request.Upload()
Dim file As File = request.ResponseBody
Console.WriteLine("File id: " + file.Id)
Console.WriteLine("Press Enter to end this process.")
Console.ReadLine()
End Sub
Private Shared Function GetAuthorization(ByVal arg As NativeApplicationClient) As IAuthorizationState
' Get the auth URL:
Dim state As IAuthorizationState = New AuthorizationState( New () {DriveService.Scopes.Drive.GetStringValue()})
state.Callback = New Uri(NativeApplicationClient.OutOfBandCallbackUrl)
Dim authUri As Uri = arg.RequestUserAuthorization(state)
' Request authorization from the user (by opening a browser window):
Process.Start(authUri.ToString())
Console.Write(" Authorization Code: ")
Dim authCode As String = Console.ReadLine()
Console.WriteLine()
' Retrieve the access token by using the authorization code:
Return arg.ProcessUserAuthorization(authCode, state)
End Function
End Class
End Namespace
It was suggested that the IE library could be utilised to log into the Google Drive and the API calls made from the above to upload. I don't know how to do this. Somewhere else it was mentioned that a 'COM wrapper' may be suitable. I don't have experience with any coding other than VBA (self taught) so am struggling to understand what the next step should be.
If anyone has done something similar or can offer any advice, I would be grateful to hear from you.
This thread might be dead now but if you are working with forms in your database and the user needs to be attaching the files to a particular record displayed in a form with a unique identification number then this is definitely possible but you would have to do it in an external application written in .NET I can provide you with the necessary code to get you started, vb.net is very similar to VBA.
What you would need to do is create a windows form project and add references to Microsoft access core dll and download the nugget package for google drive api from nugget.
Imports Google
Imports Google.Apis.Services
Imports Google.Apis.Drive.v2
Imports Google.Apis.Auth.OAuth2
Imports Google.Apis.Drive.v2.Data
Imports System.Threading
Public Class GoogleDriveAuth
Public Shared Function GetAuthentication() As DriveService
Dim ClientIDString As String = "Your Client ID"
Dim ClientSecretString As String = "Your Client Secret"
Dim ApplicationNameString As String = "Your Application Name"
Dim secrets = New ClientSecrets()
secrets.ClientId = ClientIDString
secrets.ClientSecret = ClientSecretString
Dim scope = New List(Of String)
scope.Add(DriveService.Scope.Drive)
Dim credential = GoogleWebAuthorizationBroker.AuthorizeAsync(secrets, scope, "user", CancellationToken.None).Result()
Dim initializer = New BaseClientService.Initializer
initializer.HttpClientInitializer = credential
initializer.ApplicationName = ApplicationNameString
Dim Service = New DriveService(initializer)
Return Service
End Function
End Class
This code will authorise your drive service then you create a Public Shared Service As DriveService under your imports that can be used from any sub or function then call this function on your form load event like
Service = GoogleDriveAuth.GetAuthentication
Add a reference to your project to Microsoft Access 12.0 Object Library or whatever version you have
Then this piece of code will look at the form you want to get the value of the record no from and upload a file to your choice of folder
Private Sub UploadAttachments()
Dim NumberExtracted As String
Dim oAccess As Microsoft.Office.Interop.Access.Application = Nothing
Dim connectedToAccess As Boolean = False
Dim SelectedFolderIdent As String = "Your Upload Folder ID"
Dim CreatedFolderIdent As String
Dim tryToConnect As Boolean = True
Dim oForm As Microsoft.Office.Interop.Access.Form
Dim oCtls As Microsoft.Office.Interop.Access.Controls
Dim oCtl As Microsoft.Office.Interop.Access.Control
Dim sForm As String 'name of form to show
sForm = "Your Form Name"
Try
While tryToConnect
Try
' See if can connect to a running Access instance
oAccess = CType(Marshal.GetActiveObject("Access.Application"), Microsoft.Office.Interop.Access.Application)
connectedToAccess = True
Catch ex As Exception
Try
' If couldn't connect to running instance of Access try to start a running Access instance And get an updated version of the database
oAccess = CType(CreateObject("Access.Application"), Microsoft.Office.Interop.Access.Application)
oAccess.Visible = True
oAccess.OpenCurrentDatabase("Your Database Path", False)
connectedToAccess = True
Catch ex2 As Exception
Dim res As DialogResult = MessageBox.Show("COULD NOT CONNECT TO OR START THE DATABASE" & vbNewLine & ex2.Message, "Warning", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Warning)
If res = System.Windows.Forms.DialogResult.Abort Then
Exit Sub
End If
If res = System.Windows.Forms.DialogResult.Ignore Then
tryToConnect = False
End If
End Try
End Try
' We have connected successfully; stop trying
tryToConnect = False
End While
' Start a new instance of Access for Automation:
' Make sure Access is visible:
If Not oAccess.Visible Then oAccess.Visible = True
' For Each oForm In oAccess.Forms
' oAccess.DoCmd.Close(ObjectType:=Microsoft.Office.Interop.Access.AcObjectType.acForm, ObjectName:=oForm.Name, Save:=Microsoft.Office.Interop.Access.AcCloseSave.acSaveNo)
' Next
' If Not oForm Is Nothing Then
' System.Runtime.InteropServices.Marshal.ReleaseComObject(oForm)
' End If
' oForm = Nothing
' Select the form name in the database window and give focus
' to the database window:
' oAccess.DoCmd.SelectObject(ObjectType:=Microsoft.Office.Interop.Access.AcObjectType.acForm, ObjectName:=sForm, InDatabaseWindow:=True)
' Show the form:
' oAccess.DoCmd.OpenForm(FormName:=sForm, View:=Microsoft.Office.Interop.Access.AcFormView.acNormal)
' Use Controls collection to edit the form:
oForm = oAccess.Forms(sForm)
oCtls = oForm.Controls
oCtl = oCtls.Item("The Name Of The Control Where The Id Number Is On The Form")
oCtl.Enabled = True
' oCtl.SetFocus()
NumberExtracted = oCtl.Value
System.Runtime.InteropServices.Marshal.ReleaseComObject(oCtl)
oCtl = Nothing
' Hide the Database Window:
' oAccess.DoCmd.SelectObject(ObjectType:=Microsoft.Office.Interop.Access.AcObjectType.acForm, ObjectName:=sForm, InDatabaseWindow:=True)
' oAccess.RunCommand(Command:=Microsoft.Office.Interop.Access.AcCommand.acCmdWindowHide)
' Set focus back to the form:
' oForm.SetFocus()
' Release Controls and Form objects:
System.Runtime.InteropServices.Marshal.ReleaseComObject(oCtls)
oCtls = Nothing
System.Runtime.InteropServices.Marshal.ReleaseComObject(oForm)
oForm = Nothing
' Release Application object and allow Access to be closed by user:
If Not oAccess.UserControl Then oAccess.UserControl = True
System.Runtime.InteropServices.Marshal.ReleaseComObject(oAccess)
oAccess = Nothing
If NumberExtracted = Nothing Then
MsgBox("The Number Could Not Be Obtained From The Form" & vbNewLine & vbNewLine & "Please Ensure You Have The Form Open Before Trying To Upload")
Exit Sub
End If
If CheckForDuplicateFolder(SelectedFolderIdent, NumberExtracted + " - ATC") = True Then
CreatedFolderIdent = GetCreatedFolderID(NumberExtracted + " - ATC", SelectedFolderIdent)
DriveFilePickerUploader(CreatedFolderIdent)
Else
CreateNewDriveFolder(NumberExtracted + " - ATC", SelectedFolderIdent)
CreatedFolderIdent = GetCreatedFolderID(NumberExtracted + " - ATC", SelectedFolderIdent)
DriveFilePickerUploader(CreatedFolderIdent)
End If
Catch EX As Exception
MsgBox("The Number Could Not Be Obtained From The Form" & vbNewLine & vbNewLine & "Please Ensure You Have The Form Open Before Trying To Upload" & vbNewLine & vbNewLine & EX.Message)
Exit Sub
Finally
If Not oCtls Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(oCtls)
oCtls = Nothing
End If
If Not oForm Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(oForm)
oForm = Nothing
End If
If Not oAccess Is Nothing Then
System.Runtime.InteropServices.Marshal.ReleaseComObject(oAccess)
oAccess = Nothing
End If
End Try
End
End Sub
Check For Duplicate Folders In The Destination Upload Folder
Public Function CheckForDuplicateFolder(ByVal FolderID As String, ByVal NewFolderNameToCheck As String) As Boolean
Dim ResultToReturn As Boolean = False
Try
Dim request = Service.Files.List()
Dim requeststring As String = ("'" & FolderID & "' in parents And mimeType='application/vnd.google-apps.folder' And trashed=false")
request.Q = requeststring
Dim FileList = request.Execute()
For Each File In FileList.Items
If File.Title = NewFolderNameToCheck Then
ResultToReturn = True
End If
Next
Catch EX As Exception
MsgBox("THERE HAS BEEN AN ERROR" & EX.Message)
End Try
Return ResultToReturn
End Function
Create New Drive Folder
Public Sub CreateNewDriveFolder(ByVal DirectoryName As String, ByVal ParentFolder As String)
Try
Dim body1 = New Google.Apis.Drive.v2.Data.File
body1.Title = DirectoryName
body1.Description = "Created By Automation"
body1.MimeType = "application/vnd.google-apps.folder"
body1.Parents = New List(Of ParentReference)() From {New ParentReference() With {.Id = ParentFolder}}
Dim file1 As Google.Apis.Drive.v2.Data.File = Service.Files.Insert(body1).Execute()
Catch EX As Exception
MsgBox("THERE HAS BEEN AN ERROR" & EX.Message)
End Try
End Sub
Get The Created Folder ID
Public Function GetCreatedFolderID(ByVal FolderName As String, ByVal FolderID As String) As String
Dim ParentFolder As String
Try
Dim request = Service.Files.List()
Dim requeststring As String = ("'" & FolderID & "' in parents And mimeType='application/vnd.google-apps.folder' And title='" & FolderName & "' And trashed=false")
request.Q = requeststring
Dim Parent = request.Execute()
ParentFolder = (Parent.Items(0).Id)
Catch EX As Exception
MsgBox("THERE HAS BEEN AN ERROR" & EX.Message)
End Try
Return ParentFolder
End Function
Drive File Picker Uploader To Upload Files Selected From A File Dialog Box To The Newly Created Folder
Public Sub DriveFilePickerUploader(ByVal ParentFolderID As String)
Try
ProgressBar1.Value = 0
Dim MimeTypeToUse As String
Dim dr As DialogResult = Me.OpenFileDialog1.ShowDialog()
If (dr = System.Windows.Forms.DialogResult.OK) Then
Dim file As String
Else : Exit Sub
End If
Dim i As Integer = 0
For Each file In OpenFileDialog1.FileNames
MimeTypeToUse = GetMimeType(file)
Dim filetitle As String = (OpenFileDialog1.SafeFileNames(i))
Dim body2 = New Google.Apis.Drive.v2.Data.File
body2.Title = filetitle
body2.Description = "J-T Auto File Uploader"
body2.MimeType = MimeTypeToUse
body2.Parents = New List(Of ParentReference)() From {New ParentReference() With {.Id = ParentFolderID}}
Dim byteArray = System.IO.File.ReadAllBytes(file)
Dim stream = New System.IO.MemoryStream(byteArray)
Dim request2 = Service.Files.Insert(body2, stream, MimeTypeToUse)
request2.Upload()
Next
Catch EX As Exception
MsgBox("THERE HAS BEEN AN ERROR" & EX.Message)
End Try
End Sub
Get The Mime Type Of The Files Being Uploaded
Public Shared Function GetMimeType(ByVal file As String) As String
Dim mime As String = Nothing
Dim MaxContent As Integer = CInt(New FileInfo(file).Length)
If MaxContent > 4096 Then
MaxContent = 4096
End If
Dim fs As New FileStream(file, FileMode.Open)
Dim buf(MaxContent) As Byte
fs.Read(buf, 0, MaxContent)
fs.Close()
Dim result As Integer = FindMimeFromData(IntPtr.Zero, file, buf, MaxContent, Nothing, 0, mime, 0)
Return mime
End Function
<DllImport("urlmon.dll", CharSet:=CharSet.Auto)> _
Private Shared Function FindMimeFromData( _
ByVal pBC As IntPtr, _
<MarshalAs(UnmanagedType.LPWStr)> _
ByVal pwzUrl As String, _
<MarshalAs(UnmanagedType.LPArray, ArraySubType:=UnmanagedType.I1, SizeParamIndex:=3)> ByVal _
pBuffer As Byte(), _
ByVal cbSize As Integer, _
<MarshalAs(UnmanagedType.LPWStr)> _
ByVal pwzMimeProposed As String, _
ByVal dwMimeFlags As Integer, _
<MarshalAs(UnmanagedType.LPWStr)> _
ByRef ppwzMimeOut As String, _
ByVal dwReserved As Integer) As Integer
End Function
Hopefully this helps you make a start I am 100% convinced this is achievable as I have already done this for my manager.
This reply might be late but just wanna share one of the approach!
I have done this successfully with VBA and the demo link is here
http://www.sfdp.net/thuthuataccess/demo/democAuth.rar?attredirects=0&d=1
With this, you can upload, download or delete a file with your GoogleDrive in Access..
Just Wininet + WinHTTP enough
Dang Dinh ngoc
Vietnam

Using VB, is there a way to capture the FTP results from a CMD window?

I'm using Visual studio 2012 and writing in VB. I want to find a way to launch a batch file for an FTP download or upload, have the progress show in the CMD window and record the results of the transfer times to a textbox.
Any help is appreciated.
FTP batch Scripts are as follows:
Filename: FTP_1M_Download.bat:
#Echo off
cd (Folder name here)
ftp -s:FTP_1M_Download_s.bat
Filename: FTP_1M_Download_s.bat:
open (FTP Address here)
username
password
hash
bin
be
get down_1M.zip
Here is some un-edited utility code I use to run RoboCopy using ProcessStartInfo. It captures the output stream (the code doesn't do anything with it in this case.)
I use the Kellerman SFTP library - works great, not expensive.
Sub RunRobo(ByVal SD As String, ByVal DD As String, ByVal Job As String, ByRef Out As String, ByRef Err As String)
' runs RoboCopy and returns log text
' typical job: "*.* /V /NDL /S /E /COPY:DAT /PURGE /NP /R:0 /W:0 "
Try
Application.DoEvents()
Dim sParams As String = SD & " " & DD & " " & Job
'If Not ShowHeadFoot Then sParams &= " /NJH /NJS"
' Set start information.
Dim start_info As New ProcessStartInfo("RoboCopy", sParams)
start_info.UseShellExecute = False
start_info.CreateNoWindow = True
start_info.RedirectStandardOutput = True
start_info.RedirectStandardError = True
' Make the process and set its start information.
Dim proc As New Process()
proc.StartInfo = start_info
' Start the process.
proc.Start()
' Attach to stdout and stderr.
Dim std_out As System.IO.StreamReader = proc.StandardOutput()
Dim std_err As System.IO.StreamReader = proc.StandardError()
' Display the results.
Out = std_out.ReadToEnd()
Err = std_err.ReadToEnd()
'Dim aLines() As String = sOut.Split(vbCrLf) ' if no header/footer creates array of file info
' more generic would be to return via params
'txtStreetsErr.Text = sErr
'txtStreetsOut.Text = sOut
Try
std_out.Close()
std_err.Close()
Catch ex As Exception
End Try
Try
proc.Close()
Catch ex As Exception
End Try
Catch ex As Exception
MsgBox(ex.Message, , "Error during RoboCopy")
Finally
'Cursor = Cursors.Default
End Try
End Sub