Is file path needed in (Shell Function) VBA? - shell

I have a Matlab-generated executable file, Myfile.exe to call from excel-vba. I learned (Shell Function) is what I need to use.
I don't want to include the whole file path as I do not want to restrict the user to a certain folder in a certain location on each computer.
I have the following code to call the executable, which works fine:
Sub MyExe()
On Error Resume Next
Shell ("C:\Users\elwany\Desktop\Myfolder\Myfile.exe")
If Err <> 0 Then
MsgBox "Can't start the application.", vbCritical, "Error"
End If
End Sub
My problem/question is
I put the executable + the Excel file with the VBA project in the same folder (Myfolder), and then I modify the code to:
Sub MyExe()
On Error Resume Next
Shell ("Myfile.exe")
If Err <> 0 Then
MsgBox "Can't start the application.", vbCritical, "Error"
End If
End Sub
Sometimes it works, sometimes it doesn't!
For example, yesterday I ran the VBA code, it worked. Today I opened the same Excel file, same folder, same everything, it gives "Can't Start Application" error msg!!
Is it not okay to remove the file path even if I have everything in one folder?
Why does it sometimes work, sometimes not?
Is adding the file path absolutely mandatory?

As you have enquire further about different directories note that you can either
Use ChDir as per my earlier comment to your question
Use Dir instead to validate that myfile.exe is where it needs to be. This method doesn't need error handling to handle the file being missing.
Sub TestB()
Dim strPath As String
strPath = Dir("c:\temp\myfile.exe")
If Len(strPath) > 0 Then
Shell strPath
Else
MsgBox "Path doesn't exist"
End If
End Sub
Sub TestA()
On Error Resume Next
'use the host workbook path
' ChDir ThisWorkbook.Path
'set path here
ChDir "C:\temp"
Shell ("Myfile.exe")
If Err <> 0 Then
MsgBox "Can't start the application.", vbCritical, "Error"
Else
MsgBox "sucess!", vbOKOnly
End If
End Sub

When you run a shell like this without a path specified it runs from the Active Directory. What the Active Directory is depends on the OS, not Excel/VBA (unless you explicitly set it)
Try this instead
Sub MyExe()
On Error Resume Next
Shell (ThisWorkbook.Path & "\Myfile.exe")
If Err <> 0 Then
MsgBox "Can't start the application.", vbCritical, "Error"
End If
End Sub

Related

VBScript While file exists

This thing is going to kill me, or I'll kill it. I can't tell what I'm doing but I keep getting a file not found after I delete the file in a while statement.
MsgBox FSO.FileExists(file.path)'Returns True as a test
While (FSO.FileExists(file.path))
If objZip.Items.Item(0).Name = FSO.getfilename(file.path) Then
FSO.DeleteFile (file.path)
MsgBox FSO.FileExists(file.path)'Returns False as a test
End If
WScript.Sleep 100
Wend
Can someone point me to what I'm doing wrong? I have similar code in another working script.
When you delete the disk file the reference to it that is stored in the file variable is no longer valid. Store the file.Path value in a variable and change your code to use it.
Dim filePath
filePath = file.Path
While FSO.FileExists( filePath )
FSO.DeleteFile filePath
Wend
Note - Previous code just exposes the approach about how to reference the file. If DeleteFile fails (we are looping to remove the file) both error handling and loop wait are needed.

How do I search for a file extension located in the desktop and change their file types?

User = CreateObject("WScript.Network").UserName ' gets username
Set objFSO = CreateObject("Scripting.FileSystemObject")
Recurse objFSO.GetFolder("C:\Users\" & User & "\Desktop\") ' searches for file extensions in the desktop
Sub Recurse(objFolder)
Dim objFile, objSubFolder
For Each objFile In objFolder.Files
If LCase(objFSO.GetExtensionName(objFile.Name)) = "mymom" Then ' if a file extension is mymom (just a test)
objFSO.MoveFile objFile.Name objFile.Name & ".ayy" ' changes the file extension to ayy (another test)
End If
Next
End Sub
When I do this, I get an error saying, "Expected end of statement." However, I do not know where to add the end statement. What I am trying to do is I am trying to let the script search the Desktop for all files with a specific file extension (in this case, I want to search for a file extension with .mymom) Then, I want to change the file extension with .ayy (This is the struggle part) I don't know if my code is wrong, or if it's just the end statement part.
You are getting the error, probably because you have missed a , between the source and destination file paths in the moveFile method
Use this code:
strFinalName = replace(objFile.name, "."&objFso.getExtensionname(objFile.name),".ayy")
objFSO.MoveFile objFile.Name,strFinalName

Why doesn't FileExists support wildcards?

Consider this example VBScript fragment:
Dim fs
Set fs = CreateObject("Scripting.FileSystemObject")
If fs.FileExists("D:\Folder\File*.ext") Then ' Finds nothing!
fs.CopyFile "D:\Folder\File*.ext", "D:\OtherFolder\"
fs.Deletefile "D:\Folder\File*.ext"
End If
The FileExists method turns out not to support wildcards (* and ?). Not does FolderExists. I expected wildards to just work because they work fine for all similar methods in the FileSystemObject: CopyFile, CopyFolder, MoveFile, MoveFolder, DeleteFile, DeleteFolder and the Get* filename handling methods like GetAbsolutePathName.
Of course there are ways to work around this, like GetFolder and iterating over its files. But FileExists would have been much more readable, convenient, natural and consistent.
The fs.FileExists inconsistency feels like an API design problem. What could be the reason? Is there some idea behind it?
Only someone from the team that designed the Microsoft Scripting Runtime API (scrrun.dll), which these functions are a part of, can answer this question for sure.
But my guess is that FileExists is nothing but a wrapper for the CreateFile Windows API function with the dwCreationDisposition parameter set to OPEN_EXISTING ("Opens a file or device only if it exists."). This Windows API function does not support wildcards, so FileExists can't, either.
When the file does not exist, the system will respond with error 2 ("The system cannot find the file specified.") and FileExists will return False.
The above is based on using Process Monitor to inspect the behavior of a FileExists call.
It would be moot to discuss whether this is an API design oversight and whether it should be any different.
That being said, there is no reason for an "exists" check in the code you show.
If you want to move files from location A to location B, just do that.
If there is something to move, it will be moved. If there is nothing to move, there will be an error you can inspect. The "exists" check provides no extra information whatsoever.
Dim fs, source
Set fs = CreateObject("Scripting.FileSystemObject")
On Error Resume Next
fs.MoveFile "File*.ext", "D:\OtherFolder\"
If Err.Number = 0 Then
MsgBox "Done"
ElseIf Err.Number = 53 Then ' File not found
MsgBox "Nothing to do"
ElseIf Err.Number = 76 Then ' Path not found
MsgBox "Target path not found"
Else
MsgBox "Unexpected Error " & Err.Number & " - " & Err.Description
End If
On Error Goto 0
For convenience I would wrap that in a Sub so that I can re-use it and the On Error Resume Next won't leak into the rest of my code.
It's also worth noting that within the same volume, MoveFile will be way faster than copy-and-delete.
Why don't run DIR thru WSShell.Exec and capture its output?
set ows=createobject("Wscript.shell")
path="C:\windows\system32\"
wild="*.exe"
recurse="/S" ' or ""
Set oExec=ows.Exec("%comspec% /c dir /b " & recurse &" "& chr(34) & path & wild & chr(34) )
s= oExec.StdOut.ReadAll()
'using the result
if s =vbnullstring then
Wscript.echo "No files found"
else
s=split(s,vbcrlf)
wscript.echo "Files found " & ubound(s)
for each i in s
wscript.echo i
next
wscript.echo "End of list"
end if

Delete If Present at Destination, Copy To Destination If Error Move to Next

I have VBScript that I wrote a long while back to identify PDF based on the file name. It then appended data to the file name and moved it to the proper directory. I did it as a Select Case in order for it to loop for many file names. I am now attempting to modify the script to check if the file with the new name is already at the destination directory, and if so, delete the old file, and copy the new one (also if the file is open and can't be overwritten, ignore and move to the next). I've been searching on many forums, and have been able to find pieces of what I am attempting, but have been unable to successfully integrate the processes into my script. Here is what I have for my select case, this section is what gets repeated with the "VariableAddedtoFileName" changed.
Select Case Pname
Case "FileName"
sDestinationFolder = "\\Server\FileDir\"
sDestinationName = "VariableAddedtoFileName"
Set oFSO = CreateObject("Scripting.FileSystemObject")
sSourceFile = objStartFolder & "\" & objFile.Name
sDestinationFile = sDestinationFolder & "\" & Pname & " " & _
sDestinationName & Right(objFile.Name, 4)
If oFSO.FileExists(sDestinationFile) Then
Set oFSO = Nothing
Else
oFSO.MoveFile sSourceFile, sDestinationFile
Set oFSO = Nothing
End If
Case "StatementScriptTest"
Case Else
End Select
So if I change theSet oFSO line in the If oFSO.FileExists group to oFSO.DeleteFile sDestinationFile It deletes the file, but won't copy the new one. If Rerun, it then copies the file, since it is no longer there. I have tried multiple combinations of attempting to manipulate the if statements and then with no luck. I also attempted to delete the file prior to the if section with no avail. Any assistance would be greatly appreciated.
If the full script is needed I can provide, I only listed this section as it is the part that gets rerun numerous times. Also I am aware that there are multiple posts similar to this, but I want to figure out how to update my code to work.
Update: I have fixed the overwriting by using CopyFile:
If oFSO.FileExists(sDestinationFile) Then
oFSO.CopyFile sSourceFile, sDestinationFile, True
Else
oFSO.CopyFile sSourceFile, sDestinationFile, True
Set oFSO = Nothing
End If
But I am still getting errors if the file is open when the attempt to overwrite is made.
First, you won't need the IF statement if you will have the same code in each branch. Just use the oFSO.CopyFile sSourceFile, sDestinationFile, True and it will do the work for you.
Second, in order to catch the error, you will have to use On Error Resume Next declaration before the copy command and check if some error triggered:
On Error Resume Next ' this tells VB to not throw errors and to populate the Err object when an error occurs
oFSO.CopyFile sSourceFile, sDestinationFile, True
IF Err.Number <> 0 Then
' do something when error occurs
' ...
Err.Clear ' clears the error so it will not trigger this on the loop if no more errors occur
End IF
' When you want to stop ignoring the errors
On Error GoTo 0

VBscript Unable to Run Shell Command

I have set up the following Sub to run shell commands quickly and easily.
This script works for the login scripts at my company without fail.
I am currently developing a script to add a large batch of users to our domain.
When I used this Sub in my new script I receive an error saying that the file cannot be found.
I have tried using the fix in this stackoverflow post, but I recieve the same error even with this code.
VBScript WScript.Shell Run() - The system cannot find the file specified
The part I find puzzling is that this sub works just fine when run from the netlogon folder of our domain controller.
What am I doing wrong?
Thanks,
Sub runcommand(strCommand)
Dim objWshShell, intRC
set objWshShell = WScript.CreateObject("WScript.Shell")
intRC = objWshShell.Run(strCommand, 0, TRUE)
call reportError(intRC,strCommand)
set objWshShell = nothing
end Sub
function reportError(intRC, command)
if intRC <> 0 then
WScript.Echo "Error Code: " & intRC
WScript.Echo "Command: " & command
end if
end function
The previous values for strCommand had no spaces and were very straightforward. Your new script is passing more complex variables to your Sub so you need additional conditional handling, as Alex K. pointed out in his Collusion (i.e., "Comment/Solution") above. Alex K.'s sample above is perfect, so, being a Point Pimp tonight, will post it as the solution:
objWshShell.Run("cmd /k echo Hello World", 1, TRUE)

Resources