Convert Image from VBA Using ImageMagick - vba

I would like to convert images downloaded from the internet[1] to JPGs with ImageMagick in VBA. So far, I've attempted two methods that have both failed.
First, I tried using the ImageMagickObject 1.0 Type Library:
Private Sub CommandButtonOkay_Click()
Dim sURL As String, sNetFile As String, sLocalFile As String, _
cmd As String, RetVal As Integer, img As Object
Set img = New ImageMagickObject.MagickImage
sURL = UserForm1.TextBoxImgURL
sLocalFile = "C:\temp\" & UserForm1.TextBoxName
DownloadFile sURL, sLocalFile ' Function to download image from a URL and save it to a local directory
RetVal = img.Convert(sLocalFile, sLocalFile & ".jpg") '<-- This line produces the error
UserForm1.Hide
End Sub
This ends up giving me the following error:
The source file ("C:\temp\image") exists, but the file that was to be created ("C\temp\image.jpg") does not. This is very similar to the question posted here, but I have not been able to find a solution to that so far.
Second, I tried just calling ImageMagick using the Shell command:
Private Sub CommandButtonOkay_Click()
Dim sURL As String, sNetFile As String, sLocalFile As String, _
cmd As String, RetVal As Integer
sURL = UserForm1.TextBoxImgURL
sLocalFile = "C:\temp\" & UserForm1.TextBoxName
DownloadFile sURL, sLocalFile ' Function to download image from a URL and save it to a local directory
RetVal = Shell("convert.exe """ & sLocalFile & """ """ & sLocalFile & ".jpg""")
UserForm1.Hide
End Sub
When I run this, the image gets downloaded just fine, but the image isn't converted and no error is thrown. Furthermore, when I execute the command that the Shell command executes in a separate command window, the conversion happens exactly as I would expect.
So the question then seems to be why is the ImageMagick command working beautifully when it is operating in its own command prompt, but not working at all when operating from within VBA?
[1] I don't know if this is useful information or not, but I'm downloading the images from the internet programmatically, so I have no means of knowing what format I'm getting; however, the image I've been using to test this with is a PNG.

The problem is the Shell is really only for opening programs. Therefore, it is necessary to actually tell it to open a command prompt and run the appropriate command. This can be done by changing the line with the Shell command to the following:
RetVal = Shell("cmd.exe /c convert.exe """ & sLocalFile & """ """ & sLocalFile & ".jpg""")

Related

manual entry in cmd window works, VBA executing CMD works, but not VBA when I use run (so I can hide the window)

SECOND EDIT/UPDATE: tried the path change recommendations, did not see any changes to the command string, still does not work. I re-wrote the code to use a fixed text file instead of a random temp file so I could monitor the contents of the file during execution. Able to conclusively show it is the
oShellObject.Run sCommandStringToExecute & " > " & sShellRndTmpFile, 0, True
code line that doesn't behave as expected. Still works with the w32tm command line, but not with the ntpq command line. With ntpq command, no changes made to the file, no error flags. I also tried out (again) the exec version of this problem where the window is supposed to flash a bit before it gets hidden programmatically. I get the expected reslut using exactly the same command string, cut and pasted into the other code. So the same command line works with manual entry into CMD, into PowerShell, and in the .exec code version, not the .run code version.
End of second edit. -------------------
EDIT: more debugging... ntpq -p works if I do .exec instead of .run, but then of course can't hid the cmd window. Extra test code at the end.
This Works: If I run these two commands in manually opened cmd window, or PowerShell window, both give the expected results.
w32tm /stripchart /computer:time.nist.gov /dataonly /samples:3 /rdtsc /period:1
ntpq -p
The second, ntpq -p, is bundled with NTP windows software from the home of the Network Time Protocol project that gives similar information to windows' w32tm when NTP is set up to look at the same time service computer as in the w32tm command.
This Doesn't work:
When I try to use these two command string when running CMD functions hidden using the classic "write to file" method shown in SO here and other places, the w32tm version gives the same results as the manual version, but the ntpq version just returns "error".
I read every single one of the recommended links for this question as well as searching OS and Google, and have not found an answer.
I am stuck on next step to troubleshoot the problem...only thing I could think of was to run the commands manually to confirm they work there. I can't imagine it being a administrator privileges issue since I can run them both in CMD line or PowerShell windows opened at normal rights level.
What should I look at next?
Here is the test code.
Option Explicit
Sub TestShellRun()
Dim sCmd As String, sReturnNTP As String
sCmd = "w32tm /stripchart /computer:time.nist.gov /dataonly /samples:3 /rdtsc /period:1 " ' /packetinfo"
sCmd = "%ComSpec% /C %SystemRoot%\system32\" & sCmd
sReturnNTP = fShellRun(sCmd) 'good return value, same as manual cmd line
Debug.Print sReturnNTP
sCmd = "ntpq -p"
sCmd = "%ComSpec% /C %SystemRoot%\system32\" & sCmd
sReturnNTP = fShellRun(sCmd) 'ERROR return value, even though manual cmd line has good values
Debug.Print sReturnNTP
End Sub
Public Function fShellRun(sCommandStringToExecute) As String
' This function will accept a string as a DOS command to execute.
' It will then execute the command in a shell, and capture the output into a file.
' That file is then read in and its contents are returned as the value the function returns.
' "myIP" is a user-selected global variable
Dim oShellObject, oFileSystemObject, sShellRndTmpFile
Dim oShellOutputFileToRead
Dim iErr As Long
Set oShellObject = CreateObject("Wscript.Shell")
Set oFileSystemObject = CreateObject("Scripting.FileSystemObject")
sShellRndTmpFile = oShellObject.ExpandEnvironmentStrings("%temp%") & oFileSystemObject.GetTempName
On Error Resume Next
oShellObject.Run sCommandStringToExecute & " > " & sShellRndTmpFile, 0, True
iErr = Err.Number
On Error GoTo 0
If iErr <> 0 Then
fShellRun = "error"
Exit Function
End If
On Error GoTo err_skip
fShellRun = oFileSystemObject.OpenTextFile(sShellRndTmpFile, 1).ReadAll
oFileSystemObject.DeleteFile sShellRndTmpFile, True
Exit Function
err_skip:
fShellRun = "error"
oFileSystemObject.DeleteFile sShellRndTmpFile, True
End Function
sCommand = "ntpq.exe -p"
Set WshShell = CreateObject("WScript.Shell")
Set WshShellExec = WshShell.Exec(sCommand)
strOutput = WshShellExec.StdOut.ReadAll
Debug.Print strOutput
Your fShellRun function didn't work due to error in temporary file path. Here is fixed version.
Function fShellRun(sCommandStringToExecute) As String
...
'invalid file path without path separator between directory path and filename!
sShellRndTmpFile = oShellObject.ExpandEnvironmentStrings("%temp%") & _
oFileSystemObject.GetTempName
'valid path with path separator between directory path and filename
sShellRndTmpFile = oFileSystemObject.BuildPath( _
Environ("temp"), oFileSystemObject.GetTempName)
...
End Function

File Download via shdocvw.dll with custom headers

I need to download a really large file in msaccess via a vba application.
Using the objects MSXML2.ServerXMLHTTP.6.0 and WinHttp.WinHttpRequest.5.1 result in an error stating that there is not enough storage available to complete this operation. Therefore i resorted in using the DoFileDownload method from shdocvw.dll.
What i want to do is pass an extra header (an API key) to the request sent by the function.
Here is roughly what i want to do.
Private Declare Function DoFileDownload Lib "shdocvw.dll" _
(ByVal lpszFile As String) As Long
Public Sub Download()
sDownloadFile = StrConv(<link_to_download>, vbUnicode)
'set a header before calling DoFileDownload
Call DoFileDownload(sDownloadFile)
End Sub
How do i approach this problem?
A WebRequest downloading a whole file at once stores the whole data in response.
Although there are options to chunk response, using Wget is less coding, but more options.
Private Sub DownloadFileWget()
Const PathToWget As String = "" 'if wget is not in path use "Path\To\Wget"
Dim LinkToFile As String
Dim SavePath As String
With CreateObject("WScript.Shell")
LinkToFile = "http://download.windowsupdate.com/microsoftupdate/v6/wsusscan/wsusscn2.cab" 'huge file > 500MB
SavePath = "C:\doc" 'folder to save download
.CurrentDirectory = SavePath
.Run Chr(34) & PathToWget & "wget.exe" & Chr(34) & " --header='name: value' " & Chr(34) & LinkToFile & Chr(34) & " -N", 1, True
' -N: Continue download only if the local version is outdated.
End With
End Sub

How to copy a Folder Iterative in vb.net

i created a program that copies folders + content into a different location, but this doesn't work with Folders in sizes i work with (50GB). I currently have a recursive function but it seems that this exceeds the memory limits. The only solution I could think of by now was a CMD call like.
process.start("cmd", "/C xcopy /E /V /I /Y """ & srcfld & """ """ & targfld & """")
Please excuse my variable-names, I'm lazy when it comes to typing names.
EDIT: The requested function:
Public Sub ordkop(ByVal srcfld As String, ByVal targfld As String)
Directory.CreateDirectory(targfld)
Dim files() As String
files = Directory.GetFileSystemEntries(srcfld)
For Each element As String In files
If Directory.Exists(element) Then
CopyDir(element, Path.Combine(targfld, Path.GetFileName(element)))
Else
File.Copy(element, Path.Combine(targfld, Path.GetFileName(element)), True)
End If
Next
End Sub

Strange symbols stopping my batch file running in VB.net

I am trying to create and run a batch file from VB.net, then get the output and print it out. But when it runs it is appended by these symbols '´╗┐. Causing this error '´╗┐cd' is not recognized as an internal or external command, operable program or batch file. When I look at the batch file in notepad++ there is no symbol there! What is happening! Thanks James.
Code:
Dim path As String = Directory.GetCurrentDirectory()
Dim command As String = "cd " & path & " & " & argument
MsgBox(command)
Dim file As System.IO.StreamWriter
file = My.Computer.FileSystem.OpenTextFileWriter(tempFile, False)
file.WriteLine("#ECHO OFF")
file.WriteLine(command)
file.Close()
Dim objProcess As New Process()
Dim SROutput As System.IO.StreamReader
With objProcess.StartInfo
.FileName = tempFile
.RedirectStandardOutput = True
.UseShellExecute = False
.Arguments = ""
End With
objProcess.Start()
SROutput = objProcess.StandardOutput
Do While SROutput.Peek <> -1
'MessageBox.Show(SROutput.ReadLine)
rtbOutput.Text = rtbOutput.Text & SROutput.ReadLine & vbNewLine
Loop
objProcess.Dispose()
'Process.Start(tempFile)
rtbOutput.Text = rtbOutput.Text & message & vbNewLine
That's a Byte Order Mark.
It means the OpenTextFileWriter() method is using a different encoding than you expect. You can fix the problem by using OpenTextFileWriter() overload that allows you pick an encoding like ASCII with no byte order mark or use the encoding with the byte order mark that matches what the DOS subsystem is expecting.
Solved, Im not entirely sure what was happening when it was writing the file, but I have changed it to this
Using writer As StreamWriter = New StreamWriter(tempFile)
writer.Write(command)
End Using
and its now running fine!. Thanks for any time spent on this and feel free to post an explination as to why this was happening.

VBA Shell command always returns "File Not Found"

I'm using VBA for MS Access in order to link a small C# app to my database as a helper tool. I have tried a couple of different ideas from stackoverflow itself, including the ShellAndWait utility and another on that page.
I have a button on a form. When you click this button, it should run another application that I am currently storing in %APPDATA%/program/
This is the code that is currently active:
Private Sub BtnImport_Click()
Dim file As String
Dim hProcess as Long
file = Environ("APPDATA") & "\program\component_import.exe"
'This is the standard version, which apparently does nothing at this time.
hProcess = Shell(file, vbNormalFocus)
'This is the RunApplication version I got from here earlier. It ends
'with "Successfully returned -532462766
import_funcs.RunApplication(file)
'This is the ShellAndWait version, which gives me a "File not Found" error
import_funcs.ShellAndWait(file, 0, vbNormalFocus, AbandonWait)
End Sub
I had changed the original shell out for both the ShellAndWait module and another similar module. Neither of those options work any differently in terms of my application not starting.
I have double-checked that "file" is correct (It points to C:\Users\Me\AppData\Roaming\program\component_import.exe). I have double-checked to make sure that my app is in the correct location.
It runs fine if I double-click from file explorer. It says Run-time error '53': File not found. whenever I attempt to run it from MS Access.
Any suggestions?
Edit: As an aside, the path itself does not contain any spaces.
Edit: Added some additional code.
Link to first pastebin: RunApplication pastebin
Link to second pastebin: ShellAndWait pastebin
I found sometimes folder names with spaces throws error when using shell command.
eg: C:\My Folder\appl.exe
make it:
C:\MyFolder\appl.exe
Also can check for a valid path:
The following code checks the folder where chrome.exe residing and calling www.google.com from there by passing url as argument:
Public Sub Display_Google()
Dim chromePath As String
chromePath = "C:\Program Files\Google\Chrome\Application\chrome.exe"
If FileExists(chromePath) Then
Shell (chromePath & " -url" & " " & "www.google.com"), vbMaximizedFocus
Else
chromePath = "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
Shell (chromePath & " -url" & " " & "www.google.com"), vbMaximizedFocus
End If
End Sub
Public Function FileExists(ByVal FileName As String) As Boolean
On Error Resume Next
FileExists = Not CBool(GetAttr(FileName) And (vbDirectory Or vbVolume))
On Error GoTo 0
End Function