Hi,
Let me add a first few comments, then I'll try to answer your
questions.
Firstly - what you are trying to do is clearly pushing against the
functional way that spreadsheet calculations are designed, and so it's
not surprising that you bump into funny problems. However, trying to
update the sheet in various ways from a function is quite a common
request, and I think add-ins like that from Bloomberg do this too. A
similar situation is if you are returning data from a query, where the
number of rows is unknown when the query is run. I think there have
been some attempts to support this in recent versions of Excel with
features like data tables, though I have no experience with this.
Secondly - only do the scheduled macro call if the size of the calling
range is smaller than the size of the results you want to send back,
and then in the macro call do not do xlSet with the values, but rather
extend the array formula to the enlarged range (which also forces a re-
calc, no returning to the right size range). You'd still have to go
through all the hoops to get into a context where you can set the
formula in the range, but future recalculations would not have to re-
do this. Your function would return an array of the right size and
need no funny work in most cases. This aligns much better with Excel's
recalculation, and limits the number of macro run/set events going on.
Thirdly - thank you very much for the detailed write-up of what you
are finding. I'm sure many others will find it useful to see what you
are trying. Keep it coming...
Now for more details on your questions:
1. You syntax for xlcOnTime is correct, and indeed it seems to fail
when called from a UDF as you have found. You can check that you are
making the call correctly, by putting the call into a macro that you
run from a menu - in this case your scheduled call-back should work
fine. I think there are some examples of where I tried this in the
MoreSamples.dna file.
2. Marking a function with IsMacroType=true changes the registration
string that ExcelDna sends to Excel - it adds a # to the string
denoting a "macro sheet equivalent function". Such functions are in
Class 2 and can call various Excel information functions "that return
a value but perform no action that affects the workspace or any open
workbook". (some details here:
http://msdn.microsoft.com/en-us/library/bb687835(v=office.12).aspx
and if you search for xlfRegister). IsMacroType=true functions still
cannot call xlSet, or change other parts of the sheet, as you have
discovered.
3. I don't know if it is safe to call Application.OnTime from a UDF
context, but my guess is that you are OK. I'd certainly like to know
if you find that this causes any crashes. Certainly trying to be more
ambitious in your calls to the automation interface during UDF calls
can be dangerous - it just seems like Application.OnTime would not do
work that interferes with the calculation path.
As for the RTD calls, I think a call to XlCall.Excel(XlCall.xlfRtd...)
from inside your UDF will essentially make your function get
recomputed like an RTD function - so you get to expose a nice name
without the topic stuff to your users and get async updates from those
cells through the RTD mechanism. I think you can also use the hidden
names feature to set up behind-the-scenes names that get updated from
RTD formulae - this might be useful for setting up RTD calls that do
not affect your sheet.
4. Other ways to do this, and avoiding Application.OnTime? The old-
fashioned way to do this is explained well in Steve Dalton's book on
building XLL's, called "Financial Applications Using Excel Add-in
Development in C/C++". His plan is to set up a polling mechanism using
xlcOnTime, which checks periodically whether any async work needs to
be done. Your UDF would then set some flags or data for processing on
the next OnTime macro call, and the OnTime macro would also re-
schedule itself to run again later. As you have noticed, the xlcOnTime
calls are more reliable in happening even if Excel is in a funny state
than Application.OnTime, something he also mentions.
There are some problems with this - when the Application.OnTime or
xlcOnTime macro fires, some of the Excel state is reset, for example
the Cut/Copy selection is lost. This can be quite irritating to users,
so it is suggested that you add a menu option to disable the polling.
If you find Application.OnTime safe (Excel doesn't crash) but
unreliable (the call is sometimes forgotten) you might set a Windows
timer to call Application.OnTime periodically, until the work is done.
You might like to look around the Excel SDK newsgroup
(microsoft.public.excel.sdk -
http://groups.google.com/group/microsoft.public.excel.sdk)
archives for some more ideas, or even re-post your question there. It
has not been very active lately, but in the past there have been some
really detailed discussions on this kind of thing, from people who
have good experience pushing the limits of Excel.
If you get a nice plan that works, please let us know. Maybe we could
wrap it into an ExcelDnaUtil.CallMacro(...) helper that sets
everything up and gets called in a macro context, or even an
ExtendRange=true flag on the function attribute to do this.
Please let us know what you find.
Regards,
Govert