Execute shell is not opening up file provided as string - excel

I am trying to solve an error in code written by someone else. The shell function running in an MS Excel VBA environment is as follows:
Public Function ExecCmd(cmdline$)
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
Dim ret&
' Initialize the STARTUPINFO structure:
start.cb = Len(start)
' Start the shelled application:
ret& = CreateProcessA(vbNullString, cmdline$, 0&, 0&, 1&, _
NORMAL_PRIORITY_CLASS, 0&, vbNullString, start, proc)
' Wait for the shelled application to finish:
ret& = WaitForSingleObject(proc.hProcess, INFINITE)
Call GetExitCodeProcess(proc.hProcess, ret&)
Call CloseHandle(proc.hThread)
Call CloseHandle(proc.hProcess)
ExecCmd = ret&
End Function
The cmdline$ input is: "excel.exe W:\L\BDTP\Products\Mandate FS\OfferToolUpdate.xlsm"
When I run the code it opens another excel instance and attempts to open a file under "W:\L\BDTP\Products\Mandate.xlsx"... after two more error messages it tells me that it can also not find "FS\OfferToolUpdate.xlsm"
Clearly this error is produced some how due to the space in the naming convention of the Folder the file resides in.
How can I open the file without changing the folder name?

I believe you used example as shown at: https://support.microsoft.com/en-us/kb/129796 ...
Just update execution line to wrap out the file:
ExecCmd "excel.exe ""U:\ADMINISTRATION\Expenses\Some File.xlsx"""

I have found a viable workaround for the time being, though I'm sure there are more professional ways of going about it. I have changed the input command line to be in extra quotes:
"""" & "excel.exe W:\L\BDTP\Products\Mandate FS\OfferToolUpdate.xlsm" & """"

Related

How to check file lock on .txt file before opening

Long story short... I am using a pc to open a .txt file located on a server... but sometimes the .txt file is not finished (there is still data stored in a buffer of the source computer).
FilePath = "D:\test.txt"
Workbooks.Open(Filename:=FilePath, ReadOnly:=True, IgnoreReadOnlyRecommended:=True)
Someone smarter than I am has identified that the .txt file is "locked" by the operating system until it is finished but I am still able to open it. I would like to wait for the .txt file to be "not locked" before opening it. How do I do this?
Note: The "smarter" person than me explained... the .txt file can be opened by a "dumb" program like "notepad" but if you try to use "Microsoft Word" to open it... you get a message telling you that it is locked...
import time
is_open = False
while not(is_open):
try:
f = open('test.txt','w')
is_open=True
except:
time.sleep(1)
I don't know how well the code below will work for your scenario.
You could change the constants into parameters (if you think their values need to change/be determined dynamically).
You could also change the GetFileOrWait's implementation such that it allows an infinite loop (I chose to avoid this, but maybe you want this).
All in all, the below is basically a function which tries to return the workbook in 120 seconds (or times out with an error) -- which you can hopefully use in a parent subroutine/procedure.
You may be able to specify finer frequencies (seconds may be too coarse) by using Timer instead or other lower level APIs.
Option Explicit
Private Function GetFileOrNothing() As Workbook
Const FILE_PATH As String = "D:\test.txt" ' Could pass this in as argument (if needed).
On Error Resume Next
Set GetFileOrNothing = Workbooks.Open(Filename:=FILE_PATH, ReadOnly:=True, IgnoreReadOnlyRecommended:=True)
On Error GoTo 0
End Function
Private Function GetFileOrWait() As Workbook
' Attempts to open a file. If access fails, waits n seconds before trying again.
' This function raises an error (times out to prevent infinite loop) after N seconds.
Const MAXIMUM_WAIT_IN_SECONDS As Long = 10
Const INTERVAL_WAIT_IN_SECONDS As Long = 1
Dim timeToStopAt As Date
timeToStopAt = DateAdd("s", MAXIMUM_WAIT_IN_SECONDS, Now)
Do While Now < timeToStopAt
Dim outputWorkbook As Workbook
Set outputWorkbook = GetFileOrNothing()
If Not (outputWorkbook Is Nothing) Then Exit Do
Application.Wait DateAdd("s", INTERVAL_WAIT_IN_SECONDS, Now)
DoEvents
Loop
If outputWorkbook Is Nothing Then
Err.Raise vbObjectError + 5, , "Failed to access file within the specified time frame."
End If
Set GetFileOrWait = outputWorkbook
End Function

How can I use installscript to detect Excel.exe running?

Ive been trying to detect the excel process in my installshield installer.
I have a custom action that runs after appsearch and pops a window if it finds the process and displays a warning to the user.
I have tried using some old examples I found on installsite.org and using the findWindow() call. Neither seems to find excel.exe in the process list.
Here is a snippet of code I was using when trying the findwindow
export prototype MyTestFunction(HWND);
function MyTestFunction(hMSI)
HWND nHwnd;
begin
nHwnd = FindWindow("EXCEL", "");
if (nHwnd != 0) then
MessageBox("found excel", WARNING);
SendMessage(nHwnd, WM_CLOSE, 0, 0);
else
MessageBox("cant find excel", WARNING);
endif;
end;
Note that only the else block ever seems to fire regardless of the application being open or closed.
I have tried several different variants of this mostly just replacing the "excel" with different capitalization, extensions and versions. Nothing seems to detect the window. I used Spy++ and it reported that the window is named after the name of the currently opened notebook which complicates things since I have no way of knowing what a user could have opened.
I am open to suggestions here. The only requirement for this solution is that it has to be able to run as either a custom action or part of an install condition from within Installshield.
You could use a vbscript Custom Action.
You can run this CA at the begining of UISequence or ExecuteSequence (or both) If you want it as a part of the Install condition.
Add the code in a vbscript function and configure "Return Processing" Option for the Custom Action to "Synchonous (Check exit code)" if you want to stop the installation process.
Here is my script:
Public Function StopProcess
Dim objWMIService, objProcess, colProcess
Dim strComputer, executableFileName
Const IDABORT = 3
strComputer = "."
executableFileName = "excel.exe"
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2")
Set colProcess = objWMIService.ExecQuery("Select * from Win32_Process Where Name = '" & executableFileName & "'")
For Each objProcess in colProcess
objProcess.Terminate()
' OR
StopProcess = IDABORT
Exit for
Next
End function
Obviously trying to figure out if a process is running by finding the associated window has its pitfalls.
My suggestion is to detect if the process for Excel.exe is running. It would involve enumerating the processes on the system. Modify your code accordingly. Its easier to do it using C++ but there are numerous examples available which show you how to achieve what i have just stated.
https://community.flexerasoftware.com/archive/index.php?t-162141.html
https://community.flexerasoftware.com/archive/index.php?t-188807.html
Take
We can write a InstallScript code as well to achieve this. Please refer the code below :
function CheckRunningProcessAndTerminate(hMSI)
// To Do: Declare local variables.
OBJECT wmi,objProc;
STRING szProcessName;
begin
// To Do: Write script that will be executed when MyFunction is called.
szProcessName = "Excel.exe";
set wmi = CoGetObject("winmgmts://./root/cimv2", "");
set objProc = wmi.ExecQuery("Select * from Win32_Process where Name = '" + szProcessName + "'");
if (objProc.count > 0) then
MessageBox("Process is running.", INFORMATION);
//kill proces
TerminateProcesses(szProcessName);
//objProc.Terminate(); //I tried this, but it didn't worked.
else
MessageBox("Process is not running.", INFORMATION);
endif;
end;

How do you run a .exe with parameters using vba's shell()?

I have a target file path that is structured like example below.
C:\Program Files\Test\foobar.exe /G
What I need to do is be able to execute this file using VBA's shell() command.
How do I have to format the file path to tell Shell() that there is an argument that it needs to call along with running the .exe
What I've read/tried (with no avail) is below with the results to the right.
file = """C:\Program Files\Test\foobar.exe"" /G" <---Bad file name or number (Error 52)
shell(file)
file2 = "C:\Program Files\Test\foobar.exe /G" <---file never found
shell(file2)
I've succeeded with running other .exe's using shell() so I know it's not a problem with VBA or the function.
Example:
works = "C:\Program Files\Test\test.exe"
shell(works)
I'm not particularly familiar with the process involved with executing files that require additional parameters so if I misspeak or you need more information, please let me know.
This works for me (Excel 2013):
Public Sub StartExeWithArgument()
Dim strProgramName As String
Dim strArgument As String
strProgramName = "C:\Program Files\Test\foobar.exe"
strArgument = "/G"
Call Shell("""" & strProgramName & """ """ & strArgument & """", vbNormalFocus)
End Sub
With inspiration from here https://stackoverflow.com/a/3448682.
Here are some examples of how to use Shell in VBA.
Open stackoverflow in Chrome.
Call Shell("C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" & _
" -url" & " " & "www.stackoverflow.com",vbMaximizedFocus)
Open some text file.
Call Shell ("notepad C:\Users\user\Desktop\temp\TEST.txt")
Open some application.
Call Shell("C:\Temp\TestApplication.exe",vbNormalFocus)
Hope this helps!
The below code will help you to auto open the .exe file from excel...
Sub Auto_Open()
Dim x As Variant
Dim Path As String
' Set the Path variable equal to the path of your program's installation
Path = "C:\Program Files\GameTop.com\Alien Shooter\game.exe"
x = Shell(Path, vbNormalFocus)
End Sub
sTempBAT = "d:\tempLog.txt"
Set shellwindows = GetObject("new:9ba05972-f6a8-11cf-a442-00a0c90a8f39")
Set itemobj = shellwindows.Item()
itemobj.document.Application.ShellExecute sTempBAT, "", "", "open", 0
An alternative way to call shell function
https://blog.sevagas.com/IMG/pdf/bypass_windows_defender_attack_surface_reduction.pdf

access command line data in Excel VBA fails

I need to access the command line parameters in an Excel VBA macro, and found many variations but only one that worked with Excel-2010, seems that the API has changed over time. I tried this code I found "out there":
Dim pCmdLine As Long ' Pointer to the Comand-line string
Dim strCmdLine As String ' Command line string
Dim CmdLine As String ' Command line string
' Get the pointer to the command line string
pCmdLine = GetCommandLineA
' Fill the string with zeros
' (300 characters for command line seems to be enough)
strCmdLine = String$(300, vbNullChar)
' Copy from the pointer to VBA-style string
lstrcpynA strCmdLine, pCmdLine, Len(strCmdLine)
' At this point we got the string,
' now skip the rest of it filled with 0 characters.
CmdLine = Left(strCmdLine, InStr(1, strCmdLine, vbNullChar) - 1)
MsgBox "Length of the command line = " & Len(CmdLine) '' Debug
MsgBox "Command Line:: " & CmdLine '' Debug
Which I put into the Auto_open macro of a spreadsheet. If I try this call:
start excel TestMacro.xlsm /e/abcd/xyz
It seems to generally work and the macro reports:
Command line = " C:/.../excel.exe TestMacro.xlsm"
So I get the invocation part, but the parameters are lost.
Partial Solution:
I find that if I change the invocation to:
start excel /e/abcd/xyz TestMacro.xlsm
It works, except the parsing code has to be changed to ignore the file name which is not at the end, and also this form doesn't seem to allow any blanks in any parameter, even if quoted. The system seems to interpret them as file names for a target excel file and give an error. For example:
start excel /e/abc/"my sheet"/ TestMacro.xlsm
gives the error:
file 'sheet"/.xlsx' not found
Although after the spurious startup error, the intended sheet does open and get teh entire line to work with.

(VB6) Reading text files line by line looking for specific words

Good afternoon StackOverflow,
I've just signed up here - I've been using this site for ages, and it seems to always be the site to supply the answer so I decided to be part of things.
Without further ado, here is my question -
I am writing an API for LAN parties that me and a group have monthly to help sort things out on the scorekeeping side. My friend is writing the backend for it, and I'm writing the VB6 frontend. It's been a while since I wrote VB6, and I never wrote it as intensively as the grade of frontend I'm aiming for here.
The premise of the program is this - The backend will write events from the game we're playing to a text file in realtime - Which the frontend reads from in realtime. The part I'd like to enquire about at the moment is this -
I know you can read text files line-by-line in VB6. I want the program to 'listen' (so to speak) for certain buzzwords and use their defined 'Values' to affect variables. Here is a mock example of the kind of file it'll be reading -
******************
LANrealm Match Log
******************
Game: Call of Duty 4
Game Type: Team Deathmatch
Date: 01-Jan-2013
Time: 19:00:00
Players: Tramp
Roper
d00b
Pleb
Score Limit: 150
Event: Game Start
Event: Roper killed Pleb (M4A1) shots=5 Feet=2 Body=2 Head=1
Event: Tramp committed suicide (Fall damage)
Event: Tramp killed d00b (Grenade)
Event: Pleb said "I'm saying something"
Event: Pleb teamkilled d00b (G3) shots=3 Feet=0 Body=2 Head=1
Event: Game Finished
Winner: Roper
Roper: Kills=1,Deaths=0,Suicides=0,Teamkills=0
Tramp: Kills=1,Deaths=0,Suicides=1,Teamkills=0
Pleb: Kills=0,Deaths=0,Suicides=0,Teamkills=1
d00b: Kills=0,Deaths=0,Suicides=0,Teamkills=0
Well, I think just by looking at this you can tell what I want the program to pick out of that. It would be a lot easier if I just made it fully comma delimited, but I want to maintain readability of the raw text file. But yeah, just in case you didn't get it, I'd want the program to recognise that 'Roper' had 1 'Kill' and so on and so forth. An example code snippet would be great!
Thanks in advance, guys.
Here's a function you could use to load the contents of a file:
Public Function LoadFile(dFile As String) As String
Dim ff As Integer
On Error Resume Next
ff = FreeFile
Open dFile For Binary As #ff
LoadFile = Space(LOF(ff))
Get #ff, , LoadFile
Close #ff
End Function
Next, you want to split the output of that file.
First, you will need to know what type of EOL termination character will be produced by the back-end. Assuming each line ends with a carriage return (13) and a line feed (10), you could use this code to store each line into a string array:
Dim lines() As String
lines = Split(LoadFile("LANrealm.log"), vbCrLf)
Finally, it's a matter of cycling through each line (using a For...Next loop) and look for whatever information you want to extract:
For i = 0 To Ubound(lines)
' Add here necessary logic to extract the information.
' Each line can be accessed by indexing the array as: lines(i)
Next
Hope this helps you get started...
To test the code:
Start VB6 and create a new project. VB6 will create an empty project with one form
Double click the form to view it
Right click the Toolbox and select "Components"
Locate the "Microsoft Common Dialog Control" and select it
Click OK
Now, drag the "CommonDialog" component from the Toolbox onto the form
Double click the form to view its source code
Paste the following code
NOTE: Make sure you overwrite any pre-existing code
Option Explicit
Private Sub Form_Load()
cDlg.DefaultExt = "txt"
cDlg.Filter = "Text Files|*.txt;*.log"
cDlg.ShowOpen
If cDlg.fileName <> "" Then AnalyzeFile .fileName
End Sub
Private Sub AnalyzeFile(fileName As String)
Dim fileContents As String
Dim lines() As String
Dim i As Integer
fileContents = LoadFile(fileName)
lines = Split(fileContents, vbCrLf)
For i = 0 To UBound(lines)
If InStr(1, lines(i), "event:", vbTextCompare) Then
MsgBox "Line #" & i & " contains the string 'event'" + vbCrLf + vbCrLf + lines(i)
End If
Next
End Sub
Private Function LoadFile(dFile As String) As String
Dim ff As Integer
On Error Resume Next
ff = FreeFile
Open dFile For Binary As #ff
LoadFile = Space(LOF(ff))
Get #ff, , LoadFile
Close #ff
End Function
Run the program and, when asked to supply a file, select one of the logs that will be generated by the back-end.
In this example, the program will tell you which lines contain "event information", such as "Event: Roper killed Pleb (M4A1) shots=5 Feet=2 Body=2 Head=1".
One problem I would see doing this in real-time, reading and writing is if two or more computers or apps try to open the same file. This could be a bit of a mess.
Ok, so if you REALLY want to read your file line by line, I would change your logfile a little different.
******************
LANrealm Match Log
******************
Game: Call of Duty 4
Game Type: Team Deathmatch
Date: 01-Jan-2013
Time:1 9:00:00
Players: Tramp, Roper, d00b, Pleb
Score Limit: 150
Event: Game Start
Event: Roper killed Pleb (M4A1) shots=5 Feet=2 Body=2 Head=1
Event: Tramp committed suicide (Fall damage)
Event: Tramp killed d00b (Grenade)
Event: Pleb said "I'm saying something"
Event: Pleb teamkilled d00b (G3) shots=3 Feet=0 Body=2 Head=1
Event: Game Finished
Winner: Roper
Stat: Roper Kills=1,Deaths=0,Suicides=0,Teamkills=0
Stat: Tramp Kills=1,Deaths=0,Suicides=1,Teamkills=0
Stat: Pleb Kills=0,Deaths=0,Suicides=0,Teamkills=1
Stat: d00b Kills=0,Deaths=0,Suicides=0,Teamkills=0
You can use this to read the file line by line.
Dim FileNo As Integer
Dim TempData As String
Dim TempStr As String
FileNo = FreeFile
Open "c:\game.log" For Input As FileNo
Do
Line Input #FileNo, TempStr
TempData = TempData & TempStr or do what ever you want it to do with that line.
DoEvents
Loop Until EOF(FileNo)
Close #FileNo
MsgBox TempData
However, I would suggest to read the whole file into a string, and then parse out info you want.
If you did, you can then pick out info you might want, like name of game etc.....
Try this.
Create a new module, and paste this in it.
Public Function ParseData(DataSTR As String, StartSTR As String, EndSTR As String) As String
Dim Split1
Dim Split2
Split1 = Split(DataSTR, StartSTR, , 1)
Split2 = Split(Split1(1), EndSTR, , 1)
SplitParse = Split2(0)
End Function
Then add this to a command button.
Private Sub Command2_Click()
Dim FileNo As Integer
Dim TempData As String
FileNo = FreeFile
Open "c:\game.log" For Input As FileNo
TempData = Input(LOF(FileNo), FileNo)
Close
MsgBox TempData
MsgBox Trim(ParseData(TempData, "Game:", Chr(10)))
End Sub
Now this is just a sample of what you can do.

Resources