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

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.

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?

Excel's automating a TFS update using VBA won't work in Workbook_Open function

I am trying to automate a workbook so that when it is opened it will automatically update the TFS data and send an email. I can update the TFS data successfully using a macro (found code on this blog) however when I run the code from the Workbook_Open event it errors out with "Run-time error '-2147467259 (80004005)' Method 'execute' of object '_CommandBarButton' failed". I looked at How do I resolve a VBA 'Method Execute of a [TFS Excel Add-in CommandBarButton] failed' Run-time error, but that solution doesn't work for me. This actually makes sense to me because the workbook hasn't actually connected to TFS yet - that happens AFTER the Workbook_Open completes.
My question is, is there an event after the TFS Database connection is made that I can call my code from or is there a way to force the TFS connection first? I have tried doing Application.Wait and Sleep, but that just delays the TFS Database connection.
Thanks!
I have tried doing Application.Wait and Sleep, but that just delays the TFS Database connection.
That's because these methods essentially pause the current (one and only) thread; they won't let any other (non-VBA) code run in the meantime.
the workbook hasn't actually connected to TFS yet - that happens AFTER the Workbook_Open completes.
Sounds like you need to defer execution then. Move the CommandButton.Execute code to another procedure, and then use Application.OnTime to schedule the execution of that procedure, say, 5 seconds later - assuming you pulled the offending code to some UpdateTFS procedure:
Application.OnTime DateTime.DateAdd("s", 5, DateTime.Now), "UpdateTFS"

Force a COM server to remain open

I have a COM automation server hosted by a VB6 exe.
The COM API is used from Excel VBA:
Dim o as MyCOMAPI.MyCOMType
Set o = new MyCOMAPI.MyCOMType
o.DoSomething
When I create objects in VBA the exe is started along with COM automation and VBA can use the API.
But the exe is closed quickly and "randomly" by Excel, I guess when Excel decides it doesn't need the COM API anymore.
This behaviour is causing random errors.
The simple solution is to start the exe before running the VBA code ; in this case all is working fine as the exe won't stop running until closed by the user.
Have you some information/documentation about the way Excel VBA manages calls to hosted APIs?
Is there a way to avoid this behaviour and have the exe kept open until the VBA code decides to stop it?
This would be the default behavior for a COM automation server when the last object is dereferenced, meaning that the variable that points to the server is set to nothing.
Now, if your code looks something like this today:
Sub MyFunction()
...
Dim o as MyCOMAPI.MyCOMType
Set o = new MyCOMAPI.MyCOMType
o.DoSomething
End Sub
Then, the server life is connected to the life of the o variable. That variable gets set to nothing when the function is finished, and then the server will be shut down (unless there are other variables keeping it alive).
To make sure that your COM server is kept alive for a longer time, simply define the variable as a Public variable as in the sample below.
This sample will start and show Excel and keep it open until the ShutdownExcel function is called.
Public o As Excel.Application
Sub MakeSureExcelIsRunning()
If o Is Nothing Then
Set o = New Excel.Application
o.Visible = True
End If
End Sub
Sub ShutdownExcel()
Set o = Nothing
End Sub
From COM docs.
**Component Automation**
Shutting Down Objects
ActiveX objects must shut down in the following way:
If the object's application is visible, the object should shut down only in response to an explicit user command (for example, clicking Exit on the File menu) or the equivalent command from an ActiveX client.
If the object's application is not visible, the object should shut down only when the last external reference is gone.
If the object's application is visible and is controlled by an ActiveX client, it should become invisible when the user shuts it down (for example, clicking Exit on the File menu). This behavior allows the controller to continue to control the object. The controller should shut down only when the last external reference to the object has disappeared.
© Microsoft Corporation. All rights reserved.
When you write an COM server exe the first thing you do it take a reference to yourself when starting as a normal exe else the exe shuts down as soon as initialisation is over.

winsock asynchronous receive in Excel

currently wsock.dll is used in Excel 2007 to connect to a TCP server. It is fine that I can send something out. However, regarding receiving, is there any way to make it event driven as I don't know when there is a msg sending to the Excel? And recv call will be blocked until something is arrived.
If you can get your hands on the Winsock ActiveX control then you can used an event-based programming model. I've excerpted this example for Access:
Private Sub axWinsockServer_DataArrival(ByVal bytesTotal As Long)
Dim strClientMsg As String
' The DataArrival event fires on the server when the client sends
' information. Get the data and display it in a text box.
wsServer.GetData strClientMsg, vbString
Me!Text1.Value = strClientMsg
End Sub
Although consider why, exactly, you need a spreadsheet to do network communication.

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