How to edit cells in excel while vba script is running continously - excel

Option Explicit
Declare Sub Sleep Lib "kernel32" _
(ByVal dwMilliseconds As Long)
Dim status As String
Sub StartModule()
Dim index As Integer
Dim result As String
Dim o: Set o = CreateObject("NAddIn.Functions")
status = ""
Do Until status = "DADA"
result = o.getRandomNumber
Range("F2").Value = result
Sleep123
If status = "EXIT" Then Exit Do
Loop
End Sub
Sub StopModule()
status = "EXIT"
End Sub
Sub Sleep123()
Sleep 1000 'Implements a 1 second delay
End Sub
This vba script is calling called getRandomNumber() which is a user defined function in dll file. After running by clicking start button, I am able to run the function which continuously generates a random number and shows in a cell .
The problem is I'm unable to click stop button or edit any cell and even I cannot close the xl file.

Don't use the Sleep API. The Sleep function not only suspends the execution of the current thread for a specified interval but also will not let you do anything else. i.e it will freeze Excel. Use this custom function Wait that I created many years ago.
Option Explicit
Dim status As String
Sub StartModule()
Dim index As Integer
Dim result As String
Dim o: Set o = CreateObject("NAddIn.Functions")
status = ""
Do Until status = "DADA"
result = o.getRandomNumber
Range("F2").Value = result
Wait 1 '<~~ Wait for a second
If status = "EXIT" Then Exit Do
Loop
End Sub
Sub StopModule()
status = "EXIT"
End Sub
Private Sub Wait(ByVal nSec As Long)
nSec = nSec + Timer
While nSec > Timer
DoEvents
Wend
End Sub
BTW, it's not a good idea editing cells when you have set a timer to 1 second. When you are in the edit mode, you will get an error as Excel will not be able to write to cell F2. Consider increasing the timer in such a case :)

Related

Excel VBA - How do I Create a Button Depressed Effect for a Custom Macro Shape Button?

I have created a custom shape to use as a Macro button in Excel. Initially, I was using the default Excel macro button, but wanted to make the spreadsheet look more modern. I have achieved what I was seeking in that regard, but now the buttons do not provide any feedback when you click them- they just load the Macro. With the original buttons, pressing them would provide a depression effect. I would like to simulate this effect with the new shape.
After searching solutions on the internet, I found one that worked.. once. It simulated a button click for a fraction of a second and loaded the macro. After the first use, it stopped working all together. I tried creating a new subroutine, but it did not help. I also added a sleep step at the recommendation of the site I found it on, and it did not have any effect either. Here's the code I am using:
Sub SimulateButtonClick2()
Dim vTopType As Variant
Dim iTopInset As Integer
Dim iTopDepth As Integer
'Record original button properties
With ActiveSheet.Shapes(Application.Caller).ThreeD
vTopType = .BevelTopType
iTopInset = .BevelTopInset
iTopDepth = .BevelTopDepth
End With
'Button Down
With ActiveSheet.Shapes(Application.Caller).ThreeD
.BevelTopType = msoBevelSoftRound
.BevelTopInset = 24
.BevelTopDepth = 8
End With
Application.ScreenUpdating = True
Sleep 250
Application.ScreenUpdating = True
'Button Up - set back to original values
With ActiveSheet.Shapes(Application.Caller).ThreeD
.BevelTopType = vTopType
.BevelTopInset = iTopInset
.BevelTopDepth = iTopDepth
End With
Call checker
End Sub
I am assigning this macro to the button and calling the macro I need using "Call checker" at the end.
Thank you!
EDIT: updated to add alternative approach to pause until mouse button is released
This worked fine for me, using a loop with Timer and Doevents:
'add the 64-bit version if you need to support that...
Private Declare Function GetAsyncKeyState Lib "User32" (ByVal vKey As Long) As Long
Sub Tester()
SimulateButtonClick ActiveSheet.Shapes(Application.Caller)
Debug.Print "clicked!"
End Sub
'is the left mouse button down?
Function MouseIsDown() As Boolean
MouseIsDown = GetAsyncKeyState(&H1)
End Function
Sub SimulateButtonClick(shp As shape)
Dim vTopType As Variant, iTopInset As Integer, iTopDepth As Integer, t
With shp.ThreeD
vTopType = .BevelTopType 'Record original button properties
iTopInset = .BevelTopInset
iTopDepth = .BevelTopDepth
.BevelTopType = msoBevelSoftRound
.BevelTopInset = 8
.BevelTopDepth = 8
t = Timer
'#1: use this loop for a temporary "pressed" effect
Do While Timer - t < 0.1
DoEvents
Loop
'OR
'#2: use this loop to wait until mouse button is let up
Do While MouseIsDown
DoEvents
If Timer > (t + 10) Then Exit Do 'in case of glitches...
Loop
.BevelTopType = vTopType
.BevelTopInset = iTopInset
.BevelTopDepth = iTopDepth
DoEvents
End With
End Sub

Loop until element equals to specific text in drop down

I have the following piece of code
Do
On Error Resume Next
.FindElementById("ContentPlaceHolder1_DropDownListl2").AsSelect.SelectByText ("txt")
On Error GoTo 0
Loop Until .FindElementById("ContentPlaceHolder1_DropDownListl2").AsSelect.SelectedOption.Text = "txt"
I have a lot of drop down lists that I deal with them with the same approach and although I used On Error Resume Next, I got errors sometimes and I have to wait a little and click Resume to resume the code execution
Can I make this as public procedure as I will use such lines a lot with other elements?
And how I can avoid the errors? and of course at the same time get my target for selecting the desired text in the drop down
Here's a snapshot of one of the errors
Based on #QHarr reply I tried to make a public procedure like that
Sub WaitElement(driver As Selenium.WebDriver, sElement As SelectElement, txt As String)
Dim t As Date
Const MAX_SEC As Long = 30
With driver
On Error Resume Next
t = Timer
Do
DoEvents
sElement.AsSelect.SelectByText txt
If Timer - t > MAX_SEC Then Exit Do
Loop Until sElement.AsSelect.SelectedOption.Text = txt
On Error GoTo 0
End With
End Sub
But when trying to use it in that way
WaitElement bot, .FindElementById("ContentPlaceHolder1_DropDownListnat"), ws.Range("B11").Value
I got 'Run-time error 13' (Type mismatch)
After applying the UDF named 'TextIsSet' I got this error
and the same problem.. if I click on Debug then Resume then wait a little, the code resumes its work
I have used such lines too but doesn't help
Do
Loop While .FindElementsById("ContentPlaceHolder1_Dschool").Count = 0
I got the same last error of not founding such an element
This can happen when an action causes a change to the DOM. The lazy way is to add a timed loop to try for that element until that error goes away or time out reached. You could also try shifting the On Error to surround the loop instead of inside the loop and then add in a time out. This is a little brutal but without a webpage to test with.
As a function call (this feels ugly and you may find webElements don't like being passed around):
Option Explicit
Public Sub test()
Const MAX_WAIT_SEC As Long = 30
'other code
If TextIsSet(dropdown, expectedText, MAX_WAIT_SEC) Then
End If
End Sub
Public Function TextIsSet(ByRef dropdown As Object, ByVal expectedText As String, ByVal MAX_WAIT_SEC As Long) As Boolean
Dim t As Date
On Error Resume Next
t = Timer
Do
DoEvents
dropdown.AsSelect.SelectByText expectedText
If Timer - t > MAX_WAIT_SEC Then Exit Do
Loop Until dropdown.AsSelect.SelectedOption.Text = expectedText
If dropdown.AsSelect.SelectedOption.Text = expectedText Then
TextIsSet = True
Else
TextIsSet = False
End If
On Error GoTo 0
End Function
I don't have a stale element test case so I just used a drop down test case:
Option Explicit
Public Sub test()
Const MAX_WAIT_SEC As Long = 30
Dim d As WebDriver, expectedText As String, dropdown As Object
'expectedText = "AL - Alabama" ''Pass Case
expectedText = "Bananaman" 'Fail Case
Set d = New ChromeDriver
With d
.get "https://tools.usps.com/zip-code-lookup.htm?byaddress"
Set dropdown = .FindElementById("tState")
'other code
If TextIsSet(dropdown, expectedText, MAX_WAIT_SEC) Then
Debug.Print "Tada"
Else
Debug.Print "Sigh"
End If
.Quit
End With
End Sub
Public Function TextIsSet(ByRef dropdown As Object, ByVal expectedText As String, ByVal MAX_WAIT_SEC As Long) As Boolean
Dim t As Date
On Error Resume Next
t = Timer
Do
DoEvents
dropdown.AsSelect.SelectByText expectedText
If Timer - t > MAX_WAIT_SEC Then Exit Do
Loop Until dropdown.AsSelect.SelectedOption.Text = expectedText
If dropdown.AsSelect.SelectedOption.Text = expectedText Then
TextIsSet = True
Else
TextIsSet = False
End If
On Error GoTo 0
End Function

Yes/NO Message Box - Automatically press No If there is no response for 5 seconds [duplicate]

I am trying to generate a popup that closes after a given WaitTime in seconds.
I consulted this link and this link.
I tried to apply the method from "VBA Excel macro message box auto close"; my code is the following:
Sub TestSubroutine()
Dim TemporalBox As Integer
Dim WaitTime As Integer
Dim WScriptShell As Object
Set WScriptShell = CreateObject("WScript.Shell")
WaitTime = 1
TemporalBox = WScriptShell.Popup("The message box will close in 1 second.", _
WaitTime, "File processed")
End Sub
The popup is displayed but it never closes after one second.
Edit #1
Based on #Skip Intro comment, I have updated the code:
Sub TestSubroutine()
Dim WaitTime As Integer
WaitTime = 1
CreateObject("WScript.Shell").Popup "The message box will close in 1 second.", _
WaitTime, "File processed"
End Sub
However this does not solve the original issue, the popup does not close after 1 second.
Edit #2
This is the code suggested by #Glitch_Doctor, however it still doesn't work:
Sub TestSubroutine()
Dim TemporalBox As Integer
Dim WaitTime As Integer
Dim WScriptShell As Object
Dim test
Set WScriptShell = CreateObject("WScript.Shell")
WaitTime = 1
Select Case TemporalBox = WScriptShell.Popup("The message box will close in 1 second.", _
WaitTime, "File processed")
Case 1, -1
End Select
End Sub
I finally found a very simple solution - credits to #Orphid, see his answer in the following thread.
I did not solve the specific issue related to my original code, but I managed to create a PopUp that closes after a specified period of time. The code is the following:
Sub subClosingPopUp(PauseTime As Integer, Message As String, Title As String)
Dim WScriptShell As Object
Dim ConfigString As String
Set WScriptShell = CreateObject("WScript.Shell")
ConfigString = "mshta.exe vbscript:close(CreateObject(""WScript.Shell"")." & _
"Popup(""" & Message & """," & PauseTime & ",""" & Title & """))"
WScriptShell.Run ConfigString
End Sub
This works just fine.
Another approach (if your would not work at all).
Create a new userform named frm_Popup and add a label there named lbl_Message. Add the following void to userform code:
Public Sub StartProcess(iTime As Integer)
Me.lbl_Message.Caption = "The message box will close in " & iTime & " second(s)."
End Sub
then in your module:
Sub ShowMessage()
Dim iTimeToWait As Integer
iTimeToWait = 2
With frm_Popup
.Show False
Call .StartProcess(iTimeToWait)
End With
Application.OnTime Now + TimeValue("00:00:" & iTimeToWait), "HidePopup"
End Sub
Private Sub HidePopup()
Unload frm_Popup
End Sub
You're just missing the Select Case:
WaitTime = 1
Select Case TemporalBox = WScriptShell.Popup("The message box will close in 1 second.", _
WaitTime, "File processed")
Case 1, -1
End Select
I tested and it works...
Below code work for me, I added a 2-sec delay before the popup message appears. After 4-sec it auto disappear. I learn it from Mr. Dinesh Kumar Takyar. He added a 5-sec delay b4 popup appears. His youtube link
https://www.youtube.com/watch?v=x1nmqVRrq-Q&list=PLwC8syx0i_6nHjAogOm9m4oGBq40YHkXV&index=4
I think the key issue is you need a delay for the popup timer to work. Maybe the Excel application needs to run for a while b4 the popup appears.
Option Explicit
Const PopUpTime As Integer = 4
Sub ShellMessageBox()
Dim MsgBoxWithTimer As Integer
MsgBoxWithTimer=CreateObject("WScript.Shell").Popup("Put your message here", PopUpTime, _
"Notice!", 0)
End Sub
Sub startTimer()
Application.OnTime Now + TimeValue("00:00:02"), "ShellMessageBox"
End Sub
Private Sub Workbook_Open()
startTimer
End Sub
The following code works for me:
Sub TimeBasedPopUp()
Dim WaitTime As Integer
WaitTime = 1
Select Case CreateObject("WScript.Shell").Popup("The message box will close in 1 second.",_
WaitTime, "MS Excel")
Case 1, -1
End Select
End Sub

Excel VBA Wait For Shell to Finish before continuing with script

Right now I have this VBA script:
Sub executeFTPBatch(ftpfileName)
Call Shell("FTP -i -s:C:\Temp\" & ftpfileName & ".txt")
On Error Resume Next
Kill (C:\temp\" & ftpfileName & ".txt")
End Sub
The problem is that it kills the text file before the FTP script has even begun. I saw some wsh codes, but I wasn't sure of the syntax on how to use it with respect to calling the shell FTP. If you can help me with the correct syntax I would really appreciate it!
Use WScript's Shell instead, then you can check the status of the command
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Public Function RunCMD(ByVal strCMD As String) As String
'Runs the provided command
Dim wsh As New wshShell
Dim cmd As WshExec
Dim x As Integer
On Error GoTo wshError
x = 0
RunCMD = "Error"
Set cmd = wsh.Exec(strCMD)
Do While cmd.Status = WshRunning
Sleep 100 'for 1/10th of a second
x = x + 1
If x > 1200 Then 'We've waited 2 minutes so kill it
cmd.Terminate
MsgBox "Error: Timed Out", vbCritical, "Timed Out"
End If
Loop
RunCMD = cmd.StdOut.ReadAll & cmd.StdErr.ReadAll
Exit Function
wshError:
RunCMD = cmd.StdErr.ReadAll
End Function
This is a function I use, and it will return the status of the command including any errors.
(Almost forgot the Sleep declaration!)
(Edit 2: You will also want to include a reference to the Windows Script Host Object Model (wshom.ocx) so you can use the Intellisense features)
I prefered omegastripe's suggest:
Public Sub RunCMD(ByVal strCMD As String)
'Runs the provided command
Dim wsh As Object
Set wsh = CreateObject("WScript.Shell")
Call wsh.Run(strCMD, 2, True)
End Sub
With 2 as second param to avoid windows to popup

Run-time error 9: Subscript out of range while working with string arrays

I'm new to vba script. I am trying to write a function below but couldn't make it out successfully. I really appreciate any help I can get on this.
The code is:
Option Explicit
Dim status As String
Sub StartModule()
Dim index As Integer
Dim result As String
Dim a As Integer
Dim Name As Variant
Range("D4").Value = 1
Range("D5").Value = 5
Range("D6").Value = 9
Range("D7").Value = 2
Dim o: Set o = CreateObject("NAddIn.Functions")
status = ""
Do Until status = "DADA"
result = o.getRandomNumber
Name = Split(result, ",")
If Trim(Name(3)) = Trim(Range("D4").Value) Then
Range("C4").Value = "one"
End If
If Trim(Name(3)) = Trim(Range("D5").Value) Then
Range("C5").Value = "five"
End If
If Trim(Name(3)) = Trim(Range("D6").Value) Then
Range("C4").Value = "nine"
End If
If Trim(Name(3)) = Trim(Range("D7").Value) Then
Range("C7").Value = "two"
End If
Wait 1 '<~~ Wait for a second
If status = "EXIT" Then Exit Do
Loop
End Sub
Sub StopModule()
status = "EXIT"
End Sub
Private Sub Wait(ByVal nSec As Long)
nSec = nSec + Timer
While nSec > Timer
DoEvents
Wend
End Sub
This vba script is calling a getRandomNumber() which is a user defined function in dll file. It generates string of random numbers in the range(1,10); Then the thrid random number in the string is compared with cell values in excel to update cells in excel with some string values.
Bu,the problem is I am getting an error Run-time error 9: Subscript out of range at line If Trim(Name(3)) = Trim(Range("D4").Value) then.
Then the thrid random number in the string is compared with cell
values in excel to update cells in excel with some string values.
You're comparing fourth random number. Your 'Name' elements are (0),(1),(2),(3),(4),...
I suppose there is no element Name(3) as you're getting 'out of range' error.

Resources