Wait for a function to finish and then continue - excel

I have a macro called Setup and inside it I have a function called Game. The macro start executing when a button is pressed, and then he calls Game, now, the macro continues to run without waiting for Game to finish, and then the calculations I do after Game are invalid because the game haven't finished yet.
I want my macro to wait for Game to finish and then calculate the score based on the game.
Sub Setup()
Game
CalculateHighestScore
End Sub
As you can see by the code above, CalculateHighestScore start without waiting for the game to finish and then the stats are just wrong

You need to make sure the variable/data required by CalculateHighestScore are GLOBAL, so that after Game function is executed the data is still valid and accessible to CalculateHighestScore function
if the function is extracting data from excel range, try adding
Sub Setup()
Game
Application.calculate
CalculateHighestScore
End Sub
and check if it works, this will recalculate all excel formulas before processing CalculateHighestScore

Related

Reset VBA variable when code starts / stops

Building an automated logging process where the log calls are automatically inserted into any applications VBA code. For example, log the name of each sub or function when it is called. If sub SUB_A calls function FUNCTION_Q then the log file will read:
SUB_A
FUNCTION_Q
I need to add in log points to seperate processes so that I can see the start and end. An example output could be:
New Process started
SUB_A
FUNCTION_Q
New Process started
SUB_C
FUNCTION_D
FUNCTION_T
Issue is how to determine the start / end of a process. The Excel application may have multiple processes triggered by the user without closing Excel to reset anything. I'd thought of declaring a boolean variable (which initialises as False) that gets set to True if it is False but the value of True persists after the code has ended.
Any ideas?

HypRetrieve not retrieving correct status code

Background
I have two databases that I need to connect to. One is in Hyperion and the other one is in ESS. I have imported the smartview.bas as stated by the documentation and I am attempting to use the functions within it. I have dummy sheets (SavedLogHyperion and SavedLogESS) for each enviroment to make sure the users logs in before running all the code. I want to retrieve the proper error code if the user closes the window without logging or other things that may prevent the successful login.
Problem
The HypRetrieve only acknowledge for the first result: if the user was able to log to Hyperion environment, but if ESS login window is cancelled or provided with non-valid credentials and then closed, it detects the code as 0 ("Ok"), thus detecting a successful login for the second environment when it was not.
Code
I wrote a function to retrieve the number, I thought that it could be a time thing and that is why I made it (so for the main code could resolve on time), but it seems like it is not.
Function Return_NumCodeSVHypRetrieve(VarTxtSheetToLogin As Variant) As Long
Dim NumCodeHypRetrieve As Long
NumCodeHypRetrieve = HypRetrieve(VarTxtSheetToLogin)
Return_NumCodeSVHypRetrieve = NumCodeHypRetrieve
End Function
This function is called in my main sub
Sub Main()
Dim NumCodeConnectionSheet1 As Long
Dim NumCodeConnectionSheet2 As Long
NumCodeConnectionSheet1 = Return_NumCodeSVHypRetrieve("SavedLogHyperion")
NumCodeConnectionSheet2 = Return_NumCodeSVHypRetrieve("SavedLogESS") 'If I log in "SavedLogHyperion", this variable becomes 0 too, or any other error code that variable had
End Sub
Question
How can I make the correct code according to the sheet attempted to log be correctly saved? I am clueless on what may be the approach
Solution:
The problem seems to be on how the function works; I noticed that when the function is applied, it activates the sheet, which lead me to believe that there was a problem on timing events, I came with the following solution, which has been basically to provide the scenario that I saw the function is expecting to, also I noticed that if I set the NumCode to retrieve as long as the direct result, it does not behave as expected, my approach was to declare it a variant and then cast it to a long instead.
Function Return_NumCodeSVHypRetrieve(VarTxtSheetToLogin As Variant) As Long
Dim VarNumCode As Variant
'It seems the function relies on the sheet being activated and if the Retrives does it, it takes miliseconds to do, which are not sync with excel life cycle, thus causing missreadings
Sheets(VarTxtSheetToLogin).Visible = True: Sheets(VarTxtSheetToLogin).Select: DoEvents
VarNumCode = HypRetrieve(VarTxtSheetToLogin)
Sheets(VarTxtSheetToLogin).Visible = False
Return_NumCodeSVHypRetrieve = CLng(VarNumCode)
End Function

Changing what is in the love.load function

My friend and I have been working on a game in love2d recently, but in the early stages of developing my computer hard drive stopped working meaning only my friend could work on it. Now I have a computer and I want to make a main menu in Love2d but There is alot of code in the love.load function (generation of the world and such). My question is can I change what is in love.load when the game is running? e.g The main menu loads up, then the generation of the world loads up when the start button is pressed.
The love.load function runs only once. As you mention, it's generally used to set up data structures and pre-load other resources. You can use it to handle both the world pre-load and the menu pre-load. Then, control what is active via some sort of state. A simplified example:
local state = "menu"
function love.load()
preLoadMenu()
preLoadWorld()
end
function love.update(dt)
if state == "menu" then
updateMenu()
else
updateWorld()
end
end
function love.draw()
if state == "menu" then
drawMenu()
else
drawWorld()
end
end
function love.mousepressed(x, y, button)
if startClicked(x,y,button) then
state = "world"
end
end
It's conceivable that you won't want to pre-load absolutely everything for your game on load. Maybe your game is just too big. If that's the case, consider working with an active scene. A scene might be the menu or maybe it's a game level. Again, a simplified example:
local currentScene
function love.load()
currentScene = loadMenu()
end
function love.update(dt)
currentScene.update(dt)
end
function love.draw()
currentScene.draw()
end
function love.mousepressed(x, y, button)
if startClicked(x,y,button) then
currentScene = loadWorld()
end
end
This second option is much more flexible in the long run. It can handle any number and type of scenes without conditional logic for each. It will require a little "object" thinking. All scenes need a consistent way to update and draw.
If your world takes a while to load, you may want to display a temporary "world is loading" scene so your users don't get impatient.

Excel Application.Ready=False When Receiving Data via COM dll

I am delivering external data using the dispatch interface via a COM dll to an Excel plugin. In the VBA sink method, I check that Application.Ready = True before trying to write the data to the appropriate cell like this,
If Application.Ready = True Then
With Workbooks(bookName).Sheets(sheetName).Range(rangeName)
.Value = thisData
End With
Else
Debug.Print "Dropped Payload"
End If
I don't want to just drop the data, so I've tried to do a few things to get it right.
Call Application.OnTime instead of setting the value. Unfortunately, Excel would not let me call Application.OnTime when Application.Ready=False.
Use the technique here to start a Windows timer that then calls OnTime. The problem here was that multiple events were coming in on top of each other and they were all trying to access the AfterUDFRoutine1 simultaneously, causing a crash.
So my latest thought is to put the data into a queue and then drain the queue when Application.Ready=True. To implement this, I need an event to fire when Application.Ready changes state, but I can't find the event in the list. Does anybody know if this exists and how to expose it? Alternatively, if somebody has a better idea of how to make this work I'm very open to it. The only suggestion I can't do is to use RTD instead of the dispatch I have already - I can't change that part.

How can I execute a long running process in VBA without making pc crawl?

I have a VBA application that creates an instance of a COM object and then continuously polls the objects DataReady property to see if there is new data. When new data is available it sticks the data into a spread sheet. The problem is this macro (vba sub routine) continually runs and this slows down excel to a crawl and makes the computer somewhat unusable while the process is running. Is there a way that I can start this process on a separate thread or do something like a .NET background worker?
My two attempts were to use a while loop like this..
While(True)
If(myObject.DataReady)
Do some stuff here
End If
WEnd
and then this
Sub GrabNewPoint()
If (myModule.NewDataReady_Receiver = True) Then
Do some stuff here...
End If
If (StopTest = False) Then
NextTime = Now() + TimeValue("00:00:20")
Application.OnTime NextTime, "GrabNewPoint"
End If
The second attempt definitly works better but it still slows things down considerably. Is there a better solution?
My COM object is a class library that I wrote in C#. I can add events that fire in the Class Library when data is ready but how do I listen for those events in the VBA program?
Have you tried using DoEvents?
While(True)
If(myObject.DataReady)
'your code here
End If
DoEvents
WEnd
I would think the best solution is to have the COM object raise a VBA event whenever data is ready.
If that is not an option (no control over COM object, etc.) then you will HAVE to spin the CPU. All you can do is increase the time interval between checking the DataReady property, which you already discovered in your second option. I would figure out just how fat you can increase the interval without losing functionality and leave it there.
Try this, see if things improve.
Just pause, let the CPU fly... key is to trap it here so it releases as long as you like
Public Sub App_Hard_Wait_DoEvents(dblSeconds As Double)
If dblSeconds = 0 Then Exit Sub
Dim varStart As Variant
varStart = Timer
Do While Timer < (varStart + dblSeconds)
DoEvents
Loop
End Sub
DO
Call App_Hard_Wair_DoEvents(10)
loop until (myObject.DataReady)

Resources