Can't send ActiveCell value to an external program - excel

I made an executable based on a Python Script that I made from a Selenium Code where I put a number and then it returns a *.pdf file. Now I'm trying to create a VBA Macro in Excel to send the ActiveCell value through the Shell Command to my application:
Sub do_it()
Dim RetVal As Variant
RetVal = Shell("C:\Users\ghost\Desktop\assist_exe\dist\assist_inputc.exe " & ActiveCell.Value, 1)
End Sub
The assist_inputc.exe opens up but the ActiveCell's value is not being captured.

i tried this:
RetVal = Shell("cmd /k C:\Users\ghost\Desktop\assist_exe\dist\assist_inputc.exe ECHO " & ActiveCell.Value, 1)
and still can´t post the value from the ActiveCell into my aplication, but, if i execute the same code without the aplication path it works fine, maybe there´s a diferent way to paste the value?

Searching related post´s i find a solution that help me in this problem :
Dim RetVal As Variant
RetVal = Shell("cmd /k C:\Users\ghost\Desktop\temp\dist\assist_empty.exe ", 1)
Application.Wait Now + TimeValue("00:00:01")
SendKeys ActiveCell.Value
SendKeys "{ENTER}"
I hope it works for someone with the same or similar problem.

Related

URL search in same tab using google chorme with VBA

I want to screen a lot of websites quickly using VBA. I have to have a quick look at them myself, so I basically just need my code to search for a new URL every other second in the same tab.
The existing VBA code I use is:
Sub test
End = Range("A2:C2").End(xlDown).Row
Dim chromepath As String
chromepath = """chrome.exe"""
Dim Weburl As String
Dim I As Integer
For I = 2 To end
Application.Wait (Now + TimeValue("0:00:02"))
Weburl = Cells(I, 1).Value & """"
Shell (chromepath & " -url " & Cells(I, 1))
end sub
The issue I am having is that it opens a lot of tabs, instead of searching in the same tab every other second. Has anyone found a solution for this?

open .exe in VBA excel and write in the input window

I have to run an .exe in VBA Excel and write in the input window "in.txt" "out.txt" in order to make the process automatic inside a macro. I tried to use shell but it works asynchrounous and I also don't know how to tell her to write inside the .exe.
I've also tried with SendKeys but apperently it doesen't work.
How could I make the VBA calling my .exe, open it, write inside the command window of the .exe, wait for the output and use it to go on?
thank you in advance
here are two attempts (both failed):
Sub write()
prog = Shell("C:\Users\arancia\Pictures\Camera Roll\axtur\axtur\AXTUR_64.exe", 1)
Application.Run "'AXTUR&EXCEL.xlsm'!inserisci_dati_input"
SendKeys.send "in.txt~", True
SendKeys.send "out.txt~", True
SendKeys "%{F4}", True
End Sub
Sub StartExeWithArgument()
Dim strProgramName As String
Dim strArgument As String
strProgramName = "C:\Users\arancia\Pictures\Camera Roll\axtur\axtur\AXTUR_64.exe"
strArgument = "in.txt~out.txt~"
Call Shell("""" & strProgramName & """ """ & strArgument & """", vbNormalFocus)
End Sub
One solution would be to write a batch file that includes all the parameters and then run the batch.
I have used WshShell (Windows scripting host) to run batch files to do what you want in the past but WshShell does not work on our computers since the Nov 2020 updates. WshShell allows you to wait for the outcome of the external program.
One way I found to go around it is to write a simple text file at the end of the batch and wait for it to show up. This is crude but it works.
In the code below, I write a simple batch file in the folder of the Excel sheet. The last line of the batch writes the content of the folder in a text file. The Do Until loop waits for the text file to show up in 1 second increments. When the code resumes after the loop, the text file is deleted. If you write the command line you would type in cmd instead of "echo Hello World" this should work.
You need to reference the Microsoft Scripting Runtime (scrrun) to use the file system object.
Good Luck!
Public Sub RunBatch()
Dim i As Integer
Dim k As Integer
Dim xlWB As Workbook
Dim fso1 As New FileSystemObject
Dim BatFile As Object
Dim IsDone As Boolean
Dim OutFileName As String
Set xlWB = ThisWorkbook
OutFileName = xlWB.Path & "\" & "HW.bat"
Set BatFile = fso1.CreateTextFile(OutFileName)
BatFile.WriteLine "cd /d " & xlWB.Path
BatFile.WriteLine "echo Hello World"
BatFile.WriteLine "dir > Done.txt"
BatFile.Close
IsDone = False
Call Shell(OutFileName, vbNormalFocus)
Do Until IsDone
If fso1.FileExists(xlWB.Path & "\Done.txt") Then IsDone = True
Application.Wait (Now + TimeValue("00:00:01"))
Loop
fso1.DeleteFile (OutFileName)
End Sub

Copy MsgBox text to clipboard

How do I copy MsgBox text to the clipboard using VBA?
myValue = InputBox("Please enter user text")
MsgBox("This is my text " & myValue & ", my second text & Now & ".")
I don't want to use ctrl + c, I want to copy this automatically after the MsgBox appears.
Copy the message where?
If you want it to be pasted in a cell use something like this:
myValue = InputBox("Please enter user text")
MsgBox("This is my text " & myValue & "," & my second text & Now & ".")
myMsgBox = "This is my text " & myValue & "," & my second text & Now & "."
ThisWorkbook.Sheets("Sheet_to_be_pasted_in").Range("A2").Value = myMsgBox
If u want to use it in any other way, the value is stored in the variable myMsgBox.
Copy it to clipboard so it's available on next paste command:
Dim objMsgBox As New DataObject
myValue = InputBox("Please enter user text")
MsgBox("This is my text " & myValue & "," & my second text & Now & ".")
myMsgBox = "This is my text " & myValue & "," & my second text & Now & "."
objMsgBox.SetText myMsgBox
objMsgBox.PutInClipboard
There is a straightforward approach to how to copy all "MsgBox" text!
The easiest way to do so (even if you personally don't want to do it that way :-) definitely IS to use the Ctrl+C "Copy-Handler that's builtin in every MsgBox.
The only thing you have to do is to send the ctrl+c keystroke back to VBA itself before executing the MsgBox(...) command.
Don't get mislead by other folks who want to make you believe that using the Application.SendKeys() command is not reliable. That's not true! The only truth is, that the Application.SendKeys() command will stuff your keystrokes into the keyboard buffer of your application regardless of what's already in there. Exactly that is the reason many other VBA guys believe that the Application.SendKeys() command isn't reliable.
The only step you have to take before issuing your Ctrl+C SendKeys command is, you have to clear your host's keyboard buffer! That is a piece of cake: Just write "DoEvents" one line before you call the "Application.SendKeys()" method and you're done!
Now, to make the whole approach really bullet-proof, you only have to make sure that your MsgBox really is the current window receiving Windows' messages. Only the top-most (active) window receives messages that are sent without a special receiver address (including your own "Ctrl+c" message). To achieve that, simply flag your MsgBox "vbSystemModal + vbMsgBoxSetForeground", and you're good to go.
That said, it's a walk in the park to solve your "problem". Here is some code to play with.
Public Sub Test(Optional ByVal myValue As String = vbNullString, _
Optional ByVal ShowMsgBox As Boolean = False, _
Optional ByVal EchoOut As Boolean = True)
' Param: myValue => Text that gets decorated
' Param: ShowMsgBox => If true then messageBox gets displayed
' Param: EchoOut => If true echos Clipboard to immediate window
If Len(myValue) = 0 Then
' Get something from the user
myValue = InputBox("Please enter something")
End If
' Clear keyboard buffer
DoEvents
' Send "Ctrl+c" "Copy2Clip" to MSO Host
Application.SendKeys "^c"
' If you like, let's also press the OK button of the message box
If Not ShowMsgBox Then
Application.SendKeys "{ENTER}"
End If
' Display Msgbox
MsgBox "This is my text '" & myValue & _
"' my second text [" & Now & "].", _
vbSystemModal + vbMsgBoxSetForeground
If EchoOut Then
' This will clear VBA's Console.
DoEvents
Application.SendKeys "^g ^a {DEL}"
' Copy what's in the Clipboard into VBA's "Immediate" window
DoEvents
Application.SendKeys "^g ^v"
End If
' Do not call DoEvents here! This only works when debugging in
' single-step mode(!) At runtime, VBA's immediate window cannot
' get the focus (Ctrl+g) to receive the "Ctrl+v" windows message
' here(!) Thus, DoEvents would eat up your
' Application.SendKeys "^g ^v"
'
' DoEvents ' no, No, NO!
'
End Sub
Add the test routine to one of your public code modules. Then go to your immediate window and type Test "This is my text" then hit enter. Have fun!
PS: There is not only the Application.SendKeys() method (see here: https://learn.microsoft.com/de-de/office/vba/api/Excel.Application.SendKeys), but also the VBA "SendKeys()" statement (see here: https://learn.microsoft.com/de-de/office/vba/Language/Reference/User-Interface-Help/sendkeys-statement). The latter even lets you send keystrokes to a different application (like some external text editor). The only thing you have to do from inside your VBA code is, you have to open the other application and bring its main window to the front, before sending "Ctrl+v" to that window. Well, starting other Windows executables, waiting for them being ready, sending keystrokes (using VBA's SendKeys statement) definitely can be a little bit tricky and also error-prone. Thus, that approach should be your last resort. You can find examples of how to do that here on StackOverflow. If you have Microsoft Word installed, I strongly recommend using Word-Automation instead of playing Tic-Tac-Toe with NotePad.exe :-)

Real time console output from WScript.Shell Exec

I spent most of the day searching for a solution to this, I'm starting to think its maybe not possible for my requirements
My basic setup is to run a vbscript (.vbs) called from an excel vba code. The vba code has to continue on and leave the vbscript running, but will monitor it from time to time using Exec.Status
In the vbscript I'm using WScript.StdOut.WriteLine "whatever" to track/debug it's progress, but as it stands I can only read it's output after the excel vba code is finished what it needs to do.
What I want is to see a real time output to the console from the vbscript
Here's the vba code...
Dim WSH As IWshRuntimeLibrary.WshShell 'Windows Script Host Object Model
Dim Exec As WshExec
Set WSH = CreateObject("WScript.Shell")
Set Exec = WSH.Exec("%COMSPEC% /C CSCRIPT.EXE //nologo " _
& VbsFileDir _
& " " & Arg1 _
& " " & Arg2 _
& " " & Arg3 _
& " " & Arg4)
I have been able to get a real time output by converting from WSH.Exec to WSH.Run, but I do need the access to Exec.Status, which is not available under WSH.Run
UPDATE - 2015-02-06
To clarify further... Using the example '...B.vbs' code provided by #Ekkehard.Horner's answer... The following excel-vba code WILL display a real-time output to the console...
WSH.Run("cscript C:\28353522-B.vbs")
...but the following WILL NOT display anything to the console
WSH.Exec("cscript C:\28353522-B.vbs")
I can't use the .Run() because I use the .Status flag from .Exec()
Also I can't just move the vbscript into the VBA code because the VBA goes on to do other tasks in parallel with the vbscript.
P.s. If anyone can submit an answer explaining why it can't be done, then I will mark that as accepted.
Use .Stdout.ReadLine() until the process has finished and .Stdout.ReadAll() to slurp the rest of the output - as in
28353522-A.vbs
Option Explicit
Const WshFinished = 1
Dim oExc : Set oExc = CreateObject("WScript.Shell").Exec("cscript 28353522-B.vbs")
WScript.Echo "A", "start"
Do While True
If oExc.Status = WshFinished Then
WScript.Echo "A", "WshFinished"
Exit Do
End If
WScript.Sleep 500
If Not oExc.Stdout.AtEndOfStream Then WScript.Echo "A", oExc.Stdout.ReadLine()
Loop
If Not oExc.Stdout.AtEndOfStream Then WScript.Echo "A", oExc.Stdout.ReadAll()
28353522-B.vbs
Option Explicit
Dim i
For i = 1 To 10
WScript.Echo "B", i, "whatever"
WScript.Sleep 100
Next
output:
cscript 28353522-A.vbs
A start
A B 1 whatever
A B 2 whatever
A B 3 whatever
A WshFinished
A B 4 whatever
B 5 whatever
B 6 whatever
B 7 whatever
B 8 whatever
B 9 whatever
B 10 whatever
BTW - How did you get real-time output with .Run()?
Why are you running two files? There is no need.
VBA, being full basic, can write to it's own console.
So put your vbs into VBA (you can cut and paste VBS into VBA and it will work if you put sub/end sub around it). To have the VBS run in VBA put a timer that fires it.
Here's a class module for VBA to create/read/write consoles.
'User global var gconsole
Public Function WriteToConsoles(sOut As String)
If IsConsoleAvailable() = True Then
Dim Result As Long, cWritten As Long
Result = WriteConsole(hConsole, ByVal sOut, Len(sOut), cWritten, ByVal 0&)
End If
End Function
Public Sub ExecuteCommand(Cmd As String, ReturnToPrompt As Boolean)
If IsConsoleAvailable() = True Then
If Len(Cmd) <> 0 Then
If ReturnToPrompt = True Then
Shell Environ$("comspec") & " /k " & Cmd
Else
Shell Environ$("comspec") & " /c " & Cmd
End If
End If
End If
End Sub
Public Sub CreateConsole()
If IsConsoleAvailable() = False Then
If AllocConsole() Then
hConsole = GetStdHandle(STD_OUTPUT_HANDLE)
If hConsole = 0 Then MsgBox "Couldn't allocate STDOUT"
Else
MsgBox "Couldn't allocate console"
End If
End If
End Sub
Public Sub CloseConsole()
If IsConsoleAvailable() = True Then
CloseHandle hConsole
hConsole = 0
FreeConsole
End If
End Sub
Public Function IsConsoleAvailable() As Boolean
If hConsole <> 0 Then
IsConsoleAvailable = True
Else
IsConsoleAvailable = False
End If
End Function
I've come up with an answer to my own question. Though this isn't a preferred solution (as I will explain below), so I'll not be marking as correct, but maybe someone can fix the issues with solution? (if so, post an answer and I'll mark as correct)
First off, +1 to #Ekkehard.Horner's answer for inspiring this solution.
Create the file 'B.vbs' representing my main vbscript to be run.
Option Explicit
Dim i
For i = 1 to 10
Wscript.Echo "B", i, "whatever"
Wscript.Sleep 100
Next
Create the file 'A.vbs' to act as a middle man between the main vbscript and my Excel VBA code
Option Explicit
Dim WSH
Set WSH = CreateObject("WScript.Shell")
WSH.Run "cscript C:\B.vbs", , True
Set WSH = Nothing
Now the excel VBA code...
Option Explicit
Sub Test()
Dim WSH As IWshRuntimeLibrary.WshShell
Dim Exec As WshExec
Set WSH = CreateObject("WScript.Shell")
Set Exec = WSH.Exec("cscript C:\A.vbs")
'Showing that I can still access the Exec.Status
While Exec.Status = WshRunning
Debug.Print "Running"
Wend
'But downside is nothing is avaiable from Stdout
Debug.Print Exec.StdOut.ReadAll
Set Exec = Nothing
Set WSH = Nothing
End Sub
So the Excel VBA calls the 'A.vbs' still using WSH.Exec(), then that will call the 'B.vbs' using WSH.Run(), which opens a second console window which will display the real-time output
Advantages
Excel VBA can still monitor the Exec.Status accurately
Progress of 'B.vbs' can be viewed from real-time console output
Disadvantages (reasons I'm not marking as correct)
The Exec.Terminate() will only terminate the 'A.vbs' (first console window), the 'B.vbs' will remain running
The Exec.StdOut. cannot read the output from 'B.vbs'

VBA SendKeys to specific window

I have a spreadsheet that summarizes usage of a server. The spreadsheet references a text file that is updated when I have VBA send some commands to the Linux server through the Windows command prompt (and Cygwin). My problem is that the only way I have found to execute commands in Cygwin is through the SendKeys command. This results in two problems:
I have to trust that the user will not click somewhere until the script is done running.
I have to hardcode the amount of time to wait for the command to be executed before the next command can be sent.
I would (and did) try creating a bash file to run in Cygwin, but I don't know of a way to do this that also allows the user to enter their password to log into the Linux server. I am currently getting the user's password from a UserForm that is run before the main code is executed. This password is then saved to a "very hidden" worksheet that is deleted when the application is closed.
I am currently using the following script to run the commands:
Public Sub test()
' Declare Variables
Dim cmd As String
Dim ret As Double
Dim LastRow As Integer
Dim PssWrd As String
PssWrd = Worksheets("PssWrd").Range("A1").Value
' Run Linux Commands
cmd = "C:\cygwin\Cygwin.bat"
ret = Shell(cmd, vbMinimizedFocus)
wait 0.02
cmd = "ssh " & Worksheets("Settings").Range("LOGIN") & "~" ' Format: user#hostname
SendKeys cmd
cmd = PssWrd & "~"
SendKeys cmd
wait 2.78
cmd = "{(} date ; fs ; qstat ; date {)} > & zout &~"
SendKeys cmd
cmd = "exit~"
SendKeys cmd
wait 0.5
cmd = "exit~"
SendKeys cmd
wait 5
' Update PivotTable and other data
End Sub
Public Sub wait(PauseTime As Double)
Dim Start As Double
Dim Finish As Double
Dim TotalTime As Double
Start = Timer ' Set start time
Do While Timer < Start + PauseTime
DoEvents ' Yield to other processes
Loop
Finish = Timer ' Set end time
TotalTime = Finish - Start ' Calculate total time
End Sub
Is there any way to at least send the keystrokes to a specific window instead of just the active window? Any help would be greatly appreciated, and any additional necessary information will be readily provided.
Okay. I think I found the solution. Thanks for all the help (#anishsane) pointing me in the right direction! Here is my updated code:
Public Sub test()
' Declare Variables
Dim cmd As String
Dim ret As Double
Dim LastRow As Integer
Dim PssWrd As String
Dim WshShell As Object
Dim plink_object As Object
PssWrd = Worksheets("PssWrd").Range("A1").Value
' Run Linux Commands
Set WshShell = CreateObject("WScript.Shell")
On Error Resume Next
Set plink_object = WshShell.Run("""C:\Program Files (x86)\PuTTY\plink.exe"" -ssh " & Worksheets("Settings").Range("LOGIN") & " -pw " & PssWrd & " ""(date; fs; qstat; date) > &zout&""", 7, True)
On Error GoTo 0
' Update PivotTable and other data
End Sub
Evidently, in VBA the WScript is not needed. Also, wshshell.exec seems to fail at some point because the commands never get executed (though PuTTY/Plink is started). For some reason, the WshShell.Run command resulted in the "Run-time error '424': Object required" error, but the essential part of the commands still get executed properly, so I just have the error being ignored. I also figured out how to execute my command all in one line so I don't have to worry about the stdIn.Write command that wasn't working before. I could use the Shell() command, but I need to make sure that the command has finished executing (via the bWaitOnReturn option) to make sure I have the most up-to-date file when the update script runs. Having said that, I believe that since I am having Linux write out the output of the command to a file, the file hasn't finished updating when VBA says the command has finished executing. However, I believe I've figured out a means to check that the last line of text is formatted correctly in the file (since it should be the date), so that shouldn't be a problem.

Resources