I have worksheet which gets data from RTD server through the following formula:
=RTD("tos.rtd", , "ASK", ".SPX150220C750")
I would like to save the worksheet with above formula every 1 minute or so. The challenge is to pause VBA code and also make sure that before we save, the value in the cell is updated. I have tried the following code.
Sub Archiving()
For i = 0 To 4
Worksheets("Test").Activate
Application.Sheets("Test").Copy
Application.DisplayAlerts = False
ActiveWorkbook.SaveAs Filename:="D:\Save " & i & ".csv", FileFormat:=xlCSV
ActiveWorkbook.Save
ActiveWindow.Close
Windows("Real time data.xlsm").Activate
Application.DisplayAlerts = True
Application.Wait (Now + TimeValue("0:00:05"))
ActiveWorkbook.RefreshAll
DoEvents
Next i
End Sub
The code does not work, simply because DoEvents waits until RTD is done with updates, which is never. I also have seen example where connection to DB is paused explicitly, but I don't know how to adapt it RTD server case. I tried to run RTD server from C#, but failed miserably. RTD in C# for dummies
Any suggestions?
The challenge is to pause VBA code and also make sure that before we save, the value in the cell is updated.
THE PROBLEM with your previous implementation is that by doing it inside a loop, since VBA doesn't support multi-threading, the application was "busy" and unable to receive new data from RTD server.
This is based mostly on what I've gathered from Microsoft's documentation/knowledge-base, emphasis added:
The RTD function retrieves data from an RTD server for use in the
workbook. The function result is updated whenever new data becomes
available from the server and the workbook can accept it. The server
waits until Excel is idle before updating. This relieves the developer
of having to determine whether Excel is available to accept updates.
The RTD function differs from other functions in this regard because
other functions are updated only when the worksheet is recalculated.
And further suggests that toggling the application's .CalculationState etc. will have no effect on the RTD server:
Because RTD updates data when Excel is idle, it continues to receive
information if Excel is in manual calculation mode. In that event, the
new data is cached and the current values are used when a manual
calculation is performed.
So the data will be updated when it becomes available from the server (presumably not a problem) but what is a problem in your implementation is that the workbook can't accept it because it's running the VBA thread and an RTD formula is not a "normal" external link.
Although the RTD function provides a link to data on a server, it is
not the same type of link as references to cells in other worksheets
or workbooks. For example, if you use the RTD function in a workbook,
you do not receive the Links startup message when you open the
workbook, nor can you manage the status of an RTD function through the
Edit Links dialog box.
I suspect that another dissimilarity is that the RefreshAll method has no effect on this function, you can't force it to get external data because it's already doing so when the workbook can accept it.
POTENTIAL SOLUTION
By using the Application.OnTime event to schedule the save interval, I think you should be able to avoid the problem of the workbook being unable to receive data.
if you want to save the data at a regular interval, this function will call itself recursively, subject to the limitations of the Appliction.OnTime method:
Private Sub CreateArchive()
'Saves a copy of sheet "Test" and sets OnTime to save again in 60 seconds
Dim saveTime as String
saveTime = Format(Now(), "YYYY-MM-DD-hh-nn")
Worksheets("Test").Copy
Application.DisplayAlerts = False
ActiveWorkbook.SaveAs Filename:="D:\Save " & saveTime & ".csv", FileFormat:=xlCSV
ActiveWorkbook.Close
Windows("Real time data.xlsm").Activate
Application.DisplayAlerts = True
'Call on this function again in 60 seconds:
Application.OnTime Now + TimeValue("00:00:60"), CreateArchive
End Sub
NOTE: I can't replicate on my end because I don't have your COM object /etc. that is being called from the RTD function. So, take this with a grain of salt and understand that I am very limited in how much further assistance I can offer you.
I had a similar issue. I added the following command in my VBA to trigger a RTD data refresh. I did this command before I used the data in my VBA macro. Hope this helps.
Excel.Application.RTD.RefreshData
Related
I want to automatically download financial data by using Excel and save that data. The download of the data is done by an Excel add-in. ISINs are used as the data identifier. The process looks as follows:
Open Workbook
Replace the existing ISIN with a new one
Download data
Export data
Close file
The data download is done by a custom function which is defined inside the Excel Add-in (and which I cannot access as it is password protected): INFGETHIST(), one function parameter is an ISIN. Unfortunately, the function does not load the data until the VBA macro I am using to update the ISIN and close the workbook afterwards is fully executed - which means, the workbook gets closed before the data is updated.
I've tried the following
Application.CalculateFullRebuild
Calculate and
Sub WaitUntilFinished()
Application.Calculate
If Not Application.CalculationState = xlDone Then
DoEvents
End If
End Sub
but to no avail.
I'm searching for a way to trigger the download of the data before my code executes completely. Any ideas much appreciated.
I am a VBA beginner trying to determine the "normal" behavior of excel during a timed call to a macro using Application.OnTime. In my code (below) I am calling ActiveWorkbook.RefreshAll but I think this question may be relevant to the timed call to any macro. As a newbie, I want to understand the behavior and limits of VBA so that I can determine if VBA is the right tool or if I should use another solution in certain cases (perhaps python / openpyxl).
My set up: I am using Microsoft Excel 365 (annual subscription) and the Account page says I am using Version 2006 (Build 13001.20384 Click-to-Run). I have a single workbook containing a single Sheet1 which contains the formulas to import stock data using the built in feature under the Excel Data tab. I have several rows (one stock per row) of columnar data corresponding to the stock fields such as ticker symbol, price, etc.
To automatically refresh the data, I borrowed & modified code to refresh the data every minute. The code works fine. The "issue" I have is that between refreshes, if I start editing any cell on the sheet and continue to do so during the time when the next automatic call to the refresh should occur, it indefinitely delays the execution of said call until the moment I complete (or cancel) the cell edit. When it does the refresh upon my edit completion that refresh does seem to work correctly.
If I start a cell edit and do not complete it for several minutes then only a single refresh occurs when I complete (or cancel) the edit, even though several intervals may have passed (it does not makeup/catchup the missed/delayed calls).
This behavior is OK with me in this application, but I would like to understand if this is considered "normal" behavior, and if I should expect it to always behave this way. Perhaps this question extends beyond a refresh call but also to any timed call to any macro that may be delayed as a result of a cell edit in progress.
Here is my code.
This is in a file called VBA-Test-02-StockData.xlsm under ThisWorkbook (Code):
Private Sub Workbook_Activate()
'ActiveWindow.WindowState = xlMaximized
Application.Speech.Speak ("Activating stock workbook")
Call AutoRefresh
End Sub
This is in the same xlsm file under Module1 (Code):
Sub RefreshStockData()
Application.Speech.Speak ("Refreshing stock data")
ActiveWorkbook.RefreshAll
End Sub
Sub AutoRefresh()
Call RefreshStockData
Application.OnTime Now + TimeValue("00:01:00"), "AutoRefresh"
End Sub
Sorry about the title.
I have an excel workbook that I want to update daily so I created the following code. I also created a task so that it will update off peak hours which is great. The workbook will open, refresh, save, and close without having me to do anything.
The issue I can see with this is when I want to manually open the workbook, it's going to run this code when I don't want it to. I only want this to run with the scheduled task and now when it's manually opened. I thought about putting in a message box but then the task scheduler won't get past it.
How can I make sure the workbook is updated automatically with task scheduler, but not updated when I open the workbook manually?
Sub RefreshAndClose_Open()
ThisWorkbook.RefreshAll
DoEvents
ThisWorkbook.Save
DoEvents
Application.Quit
End Sub
I would change the scheduled batch job to create a file in the same directory as the workbook named "runmacro.txt", open the workbook, then delete the file. Then in your macro:
If Len(Dir(ThisWorkbook.Path & Application.PathSeparator & "runmacro.txt")) > 0 Then
'Your code
End if
The file won't be there except when opened by the scheduled task.
Per #urdearboy I went with a simple time if... then statement. One hour blocked off isn't a lot since it's hardly opened. I'll look into #chrisneilsen suggestion of a countdown timer form.
Sub RefreshAndClose()
If Hour(Now) >= 12 And Hour(Now) <= 13 Then
ThisWorkbook.RefreshAll
DoEvents
ThisWorkbook.Save
DoEvents
Application.Quit
End If
End Sub
When you have more than one Excel file open, and your VBA/VSTO code calls the Calculate function, or turns on Automatic Calculation, Excel will painfully recalculate all open Workbooks, not just the active Workbook.
This is a well-known and well-reported problem, which has been around for years, but Microsoft doesn't seem interested in fixing it.
Calculate only the active workbook before saving
Microsoft Excel wishlist: Workbook level calculation
Ridiculously, in both VBA and VSTO, Microsoft gives us the ability to:
recalculate a particular Worksheeet
recalculate all open Workbooks
...but there's no option to just recalculate one particular Workbook.
In the financial company I work for, this is a huge issue. Our actuaries have big, bulky Excel files, full of formulae, and when they click on Calculate on one Workbook or we perform a calculate before saving a file, they then have to wait several minutes for all other open Excel files to also get calculated.
There are two ways around this.
You could run some VBA like this:
Application.Calculation = xlManual
For Each sh In ActiveWorkbook.Worksheets
sh.Calculate
Next sh
..but this isn't guaranteed to work. Your "Sheet1" might contain formulae pointing to cells in "Sheet2", but other cells in "Sheet2" might have dependencies back on "Sheet1". So, calculating each Worksheet once might not be enough to perform a full calculation on your Workbook.
Alternatively, you could open each Excel file in a separate instance (by holding down ALT as you open the Excel icon). But then, you lose the full Excel cut'n'pasting functionality, as described here:
Can't fully cut'n'paste between Excel instances
So, my question is... has anyone found a workaround for this issue ?
I just want to recalculate the cells in the Active Excel Workbook.
I wondered if I could add some VBA or VSTO which sets all non-Active Workbooks to "read-only" before I kick off a Calculation on the Active Workbook, thus preventing other Workbooks from being able to be recalculated. But this isn't possible. The "Workbook.ReadOnly" can only be read, not programmatically set.
Or perhaps adding a handler to the Worksheet_Calculate event, which checks if that VBA code which is being run belongs to the Active Workbook, and if not, it aborts attempting to calculate...? But this event actually gets kicked off after that Worksheet has been calculated, so it's too late.
Our company can't be the only one suffering from this issue...
Any advice (other than upgrading to Lotus 1-2-3) ?
This method uses another instance of Excel to avoid multiple workbook calculations. A few lines of the code for using a new instance were taken from this SO question, which deals with a similar topic and may be of interest to you.
You will have to test this for speed in your specific case, since the closing/opening time might not out-weigh the avoided calculations!
Macro steps
Set calculation to manual
Save and exit the desired workbook
Open it in a new instance of Excel
Recalculate
Save, close and re-open in original instance of Excel.
Key point for running this script:
The macro cannot live within the workbook to be recalculated, since it gets closed (twice) during the process. It should be placed in some other "utility" workbook.
Code - see comments for details
Sub CalculateWorkbook(WB As Workbook)
' Store path of given workbook for opening and closing
Dim filepath As String
filepath = WB.FullName
' Turn off calculation before saving
Dim currentCalcBeforeSave As Boolean
currentCalcBeforeSave = Application.CalculateBeforeSave
Application.CalculateBeforeSave = False
' Store current calculation mode / screen update and then set it to manual
Dim currentCalcMode As Integer, currentScreenUpdate As Integer
currentCalcMode = Application.Calculation
currentScreenUpdate = Application.ScreenUpdating
Application.Calculation = xlCalculationManual
Application.ScreenUpdating = False
' Close and save the given workbook
WB.Close savechanges:=True
' Open a new INSTANCE of Excel - meaning seperate calculation calls
Dim newExcel As Excel.Application
Set newExcel = CreateObject("Excel.Application")
' Could make it visible so that any problems don't leave it hidden in the background
' newExcel.Visible = False
' Set the calculation mode to manual in the new instance sothat the workbook isn't calculated on opening.
' This can't be done without an existing workbook object.
Dim tempWB As Workbook
Set tempWB = newExcel.Workbooks.Add
newExcel.Calculation = xlCalculationManual
newExcel.CalculateBeforeSave = False
' Open the workbook in the new instance of Excel
Dim newWB As Workbook
Set newWB = newExcel.Workbooks.Open(filepath)
' Calculate workbook once
newExcel.Calculate
' Close and save the workbook, tempworkbook and quit new instance
newWB.Close savechanges:=True
tempWB.Close savechanges:=False
newExcel.Quit
' Re-open in the active instance of Excel
Application.Workbooks.Open filepath
' Reset the application parameters
Application.CalculateBeforeSave = currentCalcBeforeSave
Application.ScreenUpdating = currentScreenUpdate
Application.Calculation = currentCalcMode
End Sub
Call the above sub by passing it the workbook object you wish to recalculate (this could be done from a button etc).
This has been tested on a very simple example workbook, and the concept works. However, please test on a copy of your workbook first, since it has not been fully robustness tested, and has no error handling.
The method I use for this in my FastExcel product involves setting WorkSheet.EnableCalculation to false for all the worksheets in all non-activeworkbooks and to True for all the worksheets in the active workbook.
This works but has the disadvantage of making the next calculation a Full calculation of the new active workbook when you change the active workbook: so its a tradeoff.
You can try this out in your situation by downloading the trial version of FastExcel from Download the 15-day full-featured trial of FastExcel V3 build 231.655.789.380
Then use FastExcel Calculation Options and check the Active Workbook checkbox in the Current Calculation Mode settings;
Disclaimer: I own, develop and market the FastExcel product. The FastExcel component that contains the Active Workbook Calculation code is FastExcel V3 Calc
So I am currently studying SQL Server but right now I am just working a standard office admin job while I'm studying.
I never really made macro's before and little knowledge on VB but decided to design a macro for work to make things a bit easier for my team.
The macro just very simply allows the user to enter data, stats etc and gives the percentage or average statistic resulting in a total letting the user know if the statistics have been hit that day, week, month etc.
It works well but I would like to add a "SUBMIT" button that when a user clicked it would send the data they have entered in specified cells to myself. I am not sure how to go about it, If needed I don't have access to systems like SQL, Visual Studio etc in work as said just basic admin job at the moment.
Would It need to be submitted as a CSV? or could it be submitted from the user's sheet straight onto another macro I have designed giving the results for the whole team? As said I am totally new to this idea.
Cheers Guys.
Awright, according to what you may need in a very simple approach, the first thing you need to do it's to know the cells where they're going to enter info (care with ranges ), let's assume for this example that whe only had one data entered in the first cell of the team worksheet. So, create a button called 'button1' or as you wish and on the click event use this code :
Private Sub button1_click()
Teamsheet.Cells(row,column) = Yoursheet.Cells(destinyrow,destinycolumn)
End Sub
That would copy the value from one sheet to another, now, if you had you sheet locked via password, you must unlock it before doing that,then lock it again so code would be like this :
Private Sub button1_click()
On Error Resume Next
yoursheet.unprotect password:="yourpassword"
Teamsheet.Cells(row,column) = Yoursheet.Cells(destinyrow,destinycolumn)
On Error Resume Next
yoursheet.PROTECT password:="yourpassword"
End Sub
I clarify that this is a very simple approach, so, if you're using specific cells you can copy one by one and this would do (so you can make anny calculation son your admin sheet), but when you're copying ranges should be like this :
Teamsheet.Range("A1:D3").Value = yoursheet.Range("A1:D3").Value
Also, always consider how they enter this data you need.
UPDATE :
Let's say you have a team workbook and yours is admin_workbook, concept it's similar. This code will do what you need but both workbooks should be at the same folder or path :
Private Sub button1_click()
Var_data = Teamsheet.Cells(row,column)
Application.ScreenUpdating = False
Workbooks.Open Filename:=ThisWorkbook.Path & "\admin_workbook.xls"
ThisWorkbook.Activate
Admin_sheet.Cells(destinyrow,destinycolumn) = var_data
Workbooks("admin_workbook.xls").Close SaveChanges:=True
Application.ScreenUpdating = True
End Sub
First you capture data on a var, then you open your admin book, put the data on the cell you want and close that workbook saving changes (you decide if you keep this line or mantain the workbook open and save manually). Also, Application.screenupdating it's a line that helps your screen doesn't flick when changing between workbooks.
Hope it helps friend !