I want to save an excel at every 2 seconds. Data is updated in this excel through DDE and want to read this data every 2 seconds. Unfortunately this data is not saved on hard disk.
I am aware of macro which can be used to save file after specified point of time but do not want to use macro.
Since data is updated frequently in this sheet through DDE (at every 100 MS) so sheet change event triggers too often.
Below is the code which i am trying but not getting success.
Dim ctme, ptme As Integer
Private Sub Workbook_Open()
ctme = Second(Now)
ptme = ctme - 2
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
ctme = Second(Now)
If (ctme - ptme) = 2 Then
Me.Save
ptme = ctme
End If
End Sub
Please help
Nidhi, people here are trying to help you and you need to understand that no one has the access to your brain to understand what you actually meant to ask. So it is quite natural to ask questions to understand the issue clearly before suggesting any answer. The people here, get equally frustrated when they are unable to understand a simple question, the time they spend could have been easily saved, had the person spent a little extra time in explaining the things better. So giving credit to those who are trying to help you, will not harm at all.
Ok, coming back to your question. I may be wrong, but I think that SheetChange event is not fired on DDE update. Please correct me if I am wrong.
The next option can be Application.OnTime functionality.
Write the following code in Workbook Open Method:
Private Sub Workbook_Open()
Dim currentTime As Date
currentTime = DateAdd("s", 2, Now)
Application.OnTime currentTime, "SaveFile"
End Sub
Then Add a new Module and add the following Function there in new Module:
Public Sub SaveFile()
ThisWorkbook.Save
Dim currentTime As Date
currentTime = DateAdd("s", 2, Now)
Application.OnTime currentTime, "SaveFile"
End Sub
The above code will create a timer which would run every two seconds to save your file. There are pros and cons for this approach, but it's Excel's best possible Timer functionality. Let me know if you have any questions.
Thanks,
Vikas
(this is totally away from the OP tags but just thought I'd put forward a possible alternative)
Create a small .NET console application.
User one of the Timer objects available to create this timed loop you require.
Then using a reference to Excel Interop library on each sweep of the loop it looks like you might need to open this workbook, save it, and then close it again .....depending on the calculations within the book and the size of the Excel file is it physically possible on your machine to open/calculate/save within 2 seconds?
Related
I have been wracking my brain trying to work out how to write a small piece of code that will activate only when particular fields at a task level have been modified.
I tried to make this code work at the project change level with a for each loop and select cases but that lags the whole program and still doesn't give me the result I need. I also tried to make it work when run manually with a for each loop and select cases or a bunch of If statements, but again, it can't tell me which field changed, but it can highlight a discrepancy between two fields.
The goal is to have a change log field (Text10) that auto updates based on the field that is modified and the date of the change. I only care about 4 fields changing (Date1, Date2, Date3, Date4).
e.g. If [Date1] is modified, Text10 = "Date1 modified 10/11/21"
Note: If 2 fields are modified, I would be happy enough with just listing the last one.
I was hoping there was some sort of "On Change, If Target = xxx" but I have not been able to find anything like that.
I also tried implementing the code as defined here >> Microsoft Documents: Project.Change Event but I am unclear what this is supposed to do and couldn't actually see it doing anything / I never got the message box I believe was supposed to appear.
I am using Microsoft Project Standard 2019.
After much research and trial and error, I ended up solving this.
To get it working, I added a Class Module and ran a piece of code on open to initialize it. This essentially tells Project to start watching for events. I then use the "Field" variant to fill the field name amongst the text string and "NewVal" variant to fill the result. This was an easy solution in the end. The code I found that worked is below:
In Class Module "cm_Events"
Public WithEvents MyMSPApp As MSProject.Application
Private Sub Class_Initialize()
Set MyMSPApp = Application
End Sub
Private Sub MyMSPApp_ProjectBeforeTaskChange(ByVal tsk As Task, ByVal Field As PjField, ByVal NewVal As Variant, Cancel As Boolean)
'What you want the code to do
End Sub
In Module "m_Events"
Public oMSPEvents As New cm_Events
Sub StartEvents()
Set oMSPEvents.MyMSPApp = MSProject.Application
End Sub
In ThisProject code
Private Sub Project_Open(ByVal pj As Project)
Call m_Events.StartEvents
End Sub
I am working on a very large VBA project in Excel at my job. We are about 1500 lines of code for just one feature and have about a dozen more features to add. Because of this, I've been trying to break everything down so that I can keep code for each feature in separate places. OOP sucks in VBA... The problem being that these controls MUST have events fired. Of course, some events (like the TextBox_AfterUpdate event) are not available when you dynamically create controls. It's a bit convoluted because of everything that is going on, so I'll break it down the best I can:
I have a class module that represents a tab for a multipage control. When a user clicks on a tab, the Userform calls this class module and THERE I have the controls created dynamically. This way I can keep the code in that class module. I have a sub that I deemed as the "AfterUpdate" sub and put code that I needed to run there. Now the problem is to get that sub to be called at the appropriate time.
So what I did is to set up a Timer of sorts to check and see if the "ActiveControl" is said textbox. If it is not, we can assume that focus has left and we can raise that event. Here's the code I'm using:
An abbreviated version of the tab creation...
Private WithEvents cmbMarketplace As MSForms.ComboBox
Public Sub LoadTab(ByVal oPageTab As Object)
If TabLoaded Then Exit Sub
Set PageTab = oPageTab
Dim tmp As Object
Set tmp = PageTab.Add("Forms.Label.1")
tmp.Top = 6: tmp.Left = 6: tmp.Width = 48
tmp.Caption = "Marketplace:"
Set cmbMarketplace = PageTab.Add("Forms.ComboBox.1", "cmbMarketplace")
' LOAD OTHER CONTROLS '
TabLoaded = True
Start_Timer
End Sub
Then Start_Timer:
Public Sub Start_Timer()
TimerActive = True
Application.OnTime Now() + TimeValue("00:00:01"), "Timer"
End Sub
And the sub that is to be fired:
Public Sub Timer()
If TimerActive Then
' DO SOME RANDOM THINGS '
Application.OnTime Now() + TimeValue("00:00:01"), "Timer"
End If
End Sub
Does this seem like a reasonable approach to solving the problem I'm facing? I'm open to suggestions...
That's the first problem. This seems like a lot of work to accomplish this. (I'm working on getting visual studio, but I don't know if that's going to happen)
The above code will work but the "Timer" sub will not get raised at all. I get no errors if I just run the code. Everything is created, everything works as I would hope. However, if I step through the code, I eventually will get the following error:
Cannot run the macro "...xlsm!Timer". The macro may not be available in this workbook or all macros may be disabled.
Obviously neither of those suggestions are valid. Macros ARE enabled and the sub is in the same darn class module. I tried making it public, same problem. Tried "ClassModule1!Timer" to no avail. I'm at my wits end trying to figure this out. Thinking of having people write ALL this in the Userform or just giving up.
Does anybody have any suggestions on how to effectively break up large chunks of code? And does anybody have a clue why this sub will not run and seemingly cannot be found?
I understand that this is a confusing situation, so if you need more info or code examples or want to know why I have something set up the way I do, let me know.
Thanks!
Obviously neither of those suggestions are valid. Macros ARE enabled and the sub is in the same darn class module.
There's the problem: a macro cannot be in a class module. The message is entirely correct: VBA cannot see the Timer procedure, because it's not accessible.
A class module is a blueprint for an object, VBA (or any OOP language for that matter) can't do anything with a class module, without an instance of that class - i.e. an object.
Your timer callback needs to be a Public Sub in a standard module, so that it can be called directly as a macro. Public procedures of a class modules are methods, not macros.
Depending on what ' DO SOME RANDOM THINGS ' actually stands for, this may or may not require some restructuring.
1500-liner spaghetti code can be written in any language BTW.
What I thought would be quick excel vba, doesn't seem to be quick anymore. Basically, i want to loop through a set of questions within a given time. when the given time has expired or all questions completed, exit the loop (close the form and return). BTW, would like to show a timer countdown on the form as well. is it possible to achieve it?
I can get a form to show questions with answer options but not sure how to add the time criteria.
As far I know, this is not an ease task to achieve. Briefly speaking, one of the possible ways to do it is:
1. write your own class module for this program
2. create a form with questions, through the form constructor (Form_Initialize) create a new instance of your class (from point 1.) and link it to the form through variables, in addition your form needs to have variables to pass the information supplied by user at a later stage (your questions) to the object behind for storing
3. when your class is instantiated, the object will have a piece of code that contains a timer, and this timer will fire events, let's say, every second to refresh your form (to show how much time is left for answering questions), see UserForm.Repaint method. The questions that have been answered so far would be re-inserted into the form on repaint (info should be stored in your class object, then re-used to update the form through timer events).
Check this link as a starting point:
https://msdn.microsoft.com/en-us/library/office/gg264327.aspx
or search for: RaiseEvent Statement in Excel Help (in VB Editor).
To see how to create vba classes:
http://www.cimaware.com/resources/article_39.html
In general, you need to search for information on how to:
create class modules,
separate object model (instantiated class) from the form,
pass information between the form and the model,
make the model to repaint your form at regular intervals (firing events)
I think Application.OnTime method may can help.
Scheduling Events With OnTime And Windows Timers
---updated---
As requested in comment section, I include the essential part on how the function works
'variable to keep time interval the timer run again
Public RunWhen As Double
'main function that kick off the exam
Public Sub startExam()
'a label display how much time left, says start with 60 seconds
DisplayTime.Caption = "60"
'call a function that load and display question on screen
'assume call LoadQuestion again when user provided quiz answer, not implemented
LoadQuestion (1)
'start the
call Timer()
End Sub
'the timer function repeat itself
Sub Timer()
'set the next run time for Timer function
RunWhen = Now + TimeSerial(0, 0, 1)
'set the schedule
Application.OnTime EarliestTime:=RunWhen, Procedure:="Timer", Schedule:=True
'update the time left on screen
UserForm1.DisplayTime.Caption = UserForm1.DisplayTime.Caption - 1
'if the time deduced to 0, stop the schedule and alert user
If UserForm1.DisplayTime.Caption = "0" Then
Application.OnTime EarliestTime:=RunWhen, Procedure:="Timer", chedule:=False
MsgBox "TimesUp"
End If
End Sub
at the moment I have a small application and need to take information from an object and display it into an excel file, using the Microsoft.office.interop class I've been able to write to the file, and it shows one by one the records being added, however about once every 3 times I try it, the spreadsheet stops filling somewhere between the 300th and 600th record, I have 6,000 in total and it's not breaking every time, I put a check after it finishes to see whether the last record is filled in but the code never reaches that point and I'm unsure of what's happening
I also don't know how to debug the problem as it'd mean going through 6,000 loops to check for it stopping... which might not even happen?
a little section of the code is here
loadExcel(incidents, WorkSheetName)
If WorkSheetName.Cells(DBObject.HighestInci + 1, 6) Is Nothing Then
MessageBox.Show("Failed to fill spreadsheet, Retrying now.")
loadExcel(incidents, WorkSheetName)
End If
above is the code calling and checking the method below
Private Sub loadExcel(ByVal incidents As List(Of Incident), ByRef WorkSheetName As Excel.Worksheet)
Dim i = 2
For Each inc As Incident In incidents
WorkSheetName.Cells(i, 1) = inc.DateLogged
WorkSheetName.Cells(i, 2) = inc.DateClosed
WorkSheetName.Cells(i, 3) = Convert.ToString(inc.DateLogged).Substring(3, 2)
i += 1
Next
End Sub
Thanks in advance
EDIT
I'm thinking loading it to a buffer of some sort then writing once they have all been updated would be the way to go instead of it currently loading and writing each separately? however I have no idea where to start for that?
I've fixed my problem, with what I had above Excel was opened and it started printing into the spreadsheet line by line, the problem is that any interactions with excel would cause the process to freeze
By adding an
ExcelApp.visible = false
before carrying out the process and an
ExcelApp.visible = true
afterwards, it all works and then opens the file afterwards
I have some legacy code that uses VBA to parse a word document and build some XML output;
Needless to say it runs like a dog but I was interested in profiling it to see where it's breaking down and maybe if there are some options to make it faster.
I don't want to try anything until I can start measuring my results so profiling is a must - I've done a little searching around but can't find anything that would do this job easily. There was one tool by brentwood? that requires modifying your code but it didn't work and I ran outa time.
Anyone know anything simple that works?
Update: The code base is about 20 or so files, each with at least 100 methods - manually adding in start/end calls for each method just isn't appropriate - especially removing them all afterwards - I was actually thinking about doing some form of REGEX to solve this issue and another to remove them all after but its just a little too intrusive but may be the only solution. I've found some nice timing code on here earlier so the timing part of it isn't an issue.
Using a class and #if would make that "adding code to each method" a little easier...
Profiler Class Module::
#If PROFILE = 1 Then
Private m_locationName As String
Private Sub Class_Initialize()
m_locationName = "unknown"
End Sub
Public Sub Start(locationName As String)
m_locationName = locationName
MsgBox m_locationName
End Sub
Private Sub Class_Terminate()
MsgBox m_locationName & " end"
End Sub
#Else
Public Sub Start(locationName As String)
'no op
End Sub
#End If
some other code module:
' helper "factory" since VBA classes don't have ctor params (or do they?)
Private Function start_profile(location As String) As Profiler
Set start_profile = New Profiler
start_profile.Start location
End Function
Private Sub test()
Set p = start_profile("test")
MsgBox "do work"
subroutine
End Sub
Private Sub subroutine()
Set p = start_profile("subroutine")
End Sub
In Project Properties set Conditional Compilation Arguments to:
PROFILE = 1
Remove the line for normal, non-profiled versions.
Adding the lines is a pain, I don't know of any way to automatically get the current method name which would make adding the profiling line to each function easy. You could use the VBE object model to inject the code for you - but I wonder is doing this manually would be ultimately faster.
It may be possible to use a template to add a line to each procedure:
http://msdn.microsoft.com/en-us/library/aa191135(office.10).aspx
Error handler templates usually include an ExitHere label of some description.. The first line after the label could be the timer print.
It is also possible to modify code through code: "Example: Add some lines required for DAO" is an Access example, but something similar could be done with Word.
This would, hopefully, narrow down the area to search for problems. The line could then be commented out, or you could revert to back-ups.
Insert a bunch of
Debug.Print "before/after foo", Now
before and after snippets that you think might run for long terms, then just compare them and voila there you are.
My suggestion would be to divide and conquer, by inserting some timing lines in a few key places to try to isolate the problem, and then drill down on that area.
If the problem is more diffused and not obvious, I'd suggest simplifying by progressively disabling whole chunks of code one at a time, as far as is possible without breaking the process. This is the analogy of finding speed bumps in an Excel workbook by progressively hard coding sheets or parts of sheets until the speed problem disappears.
About that "Now" function (above, svinto) ...
I've used the "Timer" function (in Excel VBA), which returns a Single.
It seems to work just fine. Larry