Yes, 0x800AC472 is the VBA_E_IGNORE error that is returned whenever an object model call is invoked while the property browser is suspended. Or to put it another way, when Excel developers want to suspend the object mdoel, they suspend the property browser. There are quite a few places where they do this (handling certain UI gestures is just one of the reasons) and unfortunately, I'm not familiar enough with the Excel code base to be able to tell you what might be happening that is causing it to occur around your delete call. The real problem with this error (as I mentioned) is that Excel's IMessageFilter implementation doesn't seem to be aware of the suspended state when it is handling incoming calls. As a result, cross-apartment calls are allowed to enter & then fail outside of the IMessageFilter mechanism.
The only way around that is going to be to make your object model call from a loop. You will need to continue to retry the call until it succeeds--so you'll set a success bit right after the call and then have an exception handler that catches (and eats) the 0x800AC472 exception which allows the loopback to continue. If your process is modal, then presumably Excel's suspension of the object model is transient and will resolve on its own if you give it enough time. You will have to decide for yourself how long you are willing to wait and what action you will take if you reach your timeout.
It is fine to call into the Excel object model on a background thread--provided that you undertand that you aren't getting true concurrency and you recognize the issues involved with cross-apartment marshalling. In the thread you refer to, what I was objecting to was having multiple UI threads in the same process. In general this is a really bad idea--multi-threading is hard enough to get right without introducing UI re-entrancy into the mix. For simple, discrete UI elements that are modal, the multi-threaded approach sometimes makes sense, but actually allowing a user to interface with multiple threads should be avoided. In fact, there really isn't a good reason to attempt such an implementation since user input is effectively single threaded (all input is handled by the Raw Input thread which is joined to the UI thread of the focused window). This is what I was objecting to in the thread that I mentioned.
With any UI programming, the key to responsiveness is to not hog the cpu--which means pumping messages on a regular basis. Typically, time intensive tasks involve processing in a loop, so to get proper re-painting of a progress bar (or responsive handling of a cancel button) you would just need to pump on every loop iteration. Of course, pumping messages in a loop is just a different form of re-entrancy and your code still has to be able to handle it. However since your program controls both the timing on the re-entrancy and (more importantly) which messages actually get processed, it can make things much simpler. In your progress bar case, you would just need to handle WM_PAINT and WM_LBUTTONUP/DOWN messages (for the cancel button click) and you could ignore most everything else which greatly reduces the reentrancy surface.
I'm honestly not sure whether running on a background thread could be contributing to the VBA_E_IGNORE problem you are encountering, but it would be simple enough to test. If it does turn out to be the problem, you can rewrite your progress bar as I suggest above.
---
I have an interesting problem that I hope someone can give me an idea on how to handle:
If I try any set operations to for example an Excel.Range when I have start to mark a range of cells and havent finished by releaseing the mouse button, I get a COMException HRESULT 800AC472. It seems that when the mouse is used to select a range, the workbook is set to some particular un-editable state, allthough I can't find any indication of this in either the application, workbook, worksheet or range instance.
What I have done is to put a thread on a form which sleeps and does Control.Invoke on a method UpdateCell that will update a value in A1. Using Invoke rather than calling the method directly would ensure that it is actually the main thread that performs the task. I can fetch the range A1 fine, and I can read the value, it just won't allow me to set either Value2 or Formula.
To give you the background:
What I want to do is to figure out what a full product performing long running tasks outputting data to Excel would look like. I soon realized that if an operation induced by a mouse click takes one minute to finish, Excel would be locked down for one minute, which is not acceptable. Spawning a thread to perform the background work would be ideal, but now it turns out that I can't output data particularly when the user is selecting a range. It seems there are several more operations that I can't perform either, like Worksheet.Activate. Any "set" operation really.
----
Do you know if anything changed from .net 3.5 to .net 4 (full not client) that would impact the way an IMessageFilter is registered against the currently running thread?
Code from Andrew Whitechapel's post (http://blogs.msdn.com/b/andreww/archive/2008/11/19/implementing-imessagefilter-in-an-office-add-in.aspx) works fine in .Net 3.5 targeted Excel add-ins, but it appears that the CoRegisterMessageFilter call fails to register the MessageFilter in .net 4 targeted add-ins.
As an example, if you run the attached project from Andrew's link as a 3.5 targeted project (the default) it works fine. If you change it to a .net 4 project it fails to call the ReTryRejectedCall when the Excel UI thread is busy and you're back to getting the HRESULT: 0x800AC472 exception.
Comments
0 comments
Please sign in to leave a comment.