I'm trying to clean up my code on save using Resharper's Cleanup Code function. I made a macro that handles DocumentSaved events. The important parts:
Private Sub DocumentEvents_DocumentSaved(ByVal document As EnvDTE.Document) _
Handles DocumentEvents.DocumentSaved
...
DTE.ExecuteCommand("ReSharper_SilentCleanupCode")
document.Save()
End Sub
Every time I save, I get an exception message that says Error HRESULT E_FAIL has been returned from a call to a COM component. Any ideas?
Note: I see How can I configure ReSharper's code cleanup on save? and it won't work in my situation because I need to respond to the save event. Mapping a macro to CTRL+S isn't enough.
I think this is because when you call document.Save() it is recursing and saving again and then it falls over. Try this:
Private Sub DocumentEvents_DocumentSaved(ByVal Document As EnvDTE.Document) Handles DocumentEvents.DocumentSaved
Static currentDocument As EnvDTE.Document
If Not currentDocument Is Document Then
currentDocument = Document
DTE.Windows.Item(Document.Name).Activate()
DTE.ExecuteCommand("ReSharper_SilentCleanupCode")
DTE.ActiveDocument.Save()
End If
End Sub
This worked for me
Related
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"
I would like to create/use toasters notifications on Excel. Because we already use MsgBox to notify the user that something happen.
But it make the script to stop (pause).
Have you any idea of how to do ?
On google there is "System Tray Notification" but it need a lot of code and this is a old method. Can't find if there is a new method.
For example, the plugin from SAP : "Analysis For Office" put notifications on Excel.
I looked into this once and almost gave up on it entirely until I found a rather 'cheaty' way of alerting users of info without suspending execution, that works on my users machines.
We run Windows 10 here and have SCCM installed for software distribution and updates. I've absolutely no idea if that's mandatory in Windows for updates or not, so I've no idea if this works for you.. but the following code works a treat here if you don't mind the notification resembling a Software Centre notification:
Sub Toastnote(ccmTitle, ccmText)
Shell "c:\windows\ccm\sctoastnotification.exe """ & ccmTitle & """ """ & ccmText & """ "
End Sub
You can call it with:
toastnote "title goes here","message goes here"
It creates a little pop-up that looks like this:
As I say, it's a bit of a cheat and might confuse users who regularly receive CCM notifications, but for my user-base that wasn't an issue.
Lastly, it's probably worth wrapping this in an IF statement that checks the .exe file exists - just a thought..
Using the Plugin "Analysis For Office" from SAP, you can define messages and add them to the standard SAP-AnalysisForOffice message dialog :
Dim lResult As Long
lResult= Application.Run("SAPAddMessage", "This is a new error message!", "ERROR")
The message 'This is a new error message' with severity Error is displayed in the message dialog.
It will do the same as the picture sent with my question.
Source
Details about SAPAddMessage
I had a similar requirement (mainly for debugging)
My solution was to pop up a small form with a single label control and unload it 4 seconds later. The form proeprties are set no not show modal etc.
in a VBA "Module"
Private mFrmToast As frmToast
Public Sub clearToast()
On Error Resume Next
If Not mFrmToast Is Nothing Then
mFrmToast.Hide
Unload mFrmToast
Set mFrmToast = Nothing
End If
End Sub
Public Sub showToast(message As String)
On Error GoTo er_clear_in_4
If mFrmToast Is Nothing Then
Set mFrmToast = New frmToast
End If
mFrmToast.message = message
If Not mFrmToast.Visible Then
Call mFrmToast.Show(False)
End If
er_clear_in_4:
Application.OnTime Now + TimeValue("00:00:04"), "clearToast"
End Sub
The form "code behind" module contained a sample write only property, "message".
Option Explicit
Public Property Let message(ByVal sMessage As String)
lblMessage.Caption = sMessage
End Property
The usage is simply
showToast("your message here")
The Message Box called here is not closing upon clicking "OK". Keeps popping up until I kill the task via Task Manager.
The runtime environment is WinCE 6.0.
Private Sub frmPAConsole_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Activated
strConfigFileName = "\Program Files\Alarm System\PASystem_AudioFilesAllocation.txt"
If Not IO.File.Exists(strConfigFileName) Then
MsgBox("Configuration file doesn't exist: \Program Files\Alarm System\PASystem_AudioFilesAllocation.txt")
Me.Close()
End If
Call LabelStopButton()
Call ReadConfigFile(strConfigFileName)
Call PopulateButtonsDescription()
End Sub
I tested a message box when just clicking a button - no problem. Also, the device Windows messages are closing normally.
Any ideas?
Thanks!
This seems like some sort of a bug.
Even when implementing the Error Message using a new dialog form - the stickiness of the message persisted ONLY for the IO.file.open error (regardless of whether it was raised by error handling or checked if file.exist).
The only workaround I managed to do is instead of having error messages pop up in new windows, I implemented a textbox on the main form itself, on the bottom, for error messages, and I control its text and visibility.
Thank you all for reading and thinking about this issue.
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.
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)