xlladdins / xll

Excel add-in library
MIT License
104 stars 23 forks source link

Minimal example of async function #25

Open cabo40 opened 1 year ago

cabo40 commented 1 year ago

Hi, this is a great project to abstract away the complex Excel SDK. It would be very good if an example could be added of how to use the .Asynchronous Args method. I've tried to use the example from xll_sqlite but my code freezes excel and unfreezes on any keypress failing with a "#CALC" error, this calls again the calc function and if one waits, the final result is displayed. Is there a way to make an async function similar to the async of Excel-DNA, which don't block the worksheet and keeps the calculation on the background even when moving around to other cells?

This is the code that I was testing, slightly modified from the xll_sqlite one.

void execa(OPER12 x, LPOPER12 h)
{
    std::this_thread::sleep_for(std::chrono::seconds(x.as_int()));
    x.val.num *= 2;
    int result;
    XLOPER12 ret;
    result = ::Excel12(xlAsyncReturn, &ret, 2, h, &x);
    result = result;
}

AddIn xai_execa(
    Function(XLL_VOID, "xll_execa", "EXECA")
    .Arguments({
        Arg(XLL_LPOPER12, "x", "is x"),
        })
        .Asynchronous()
        .ThreadSafe()
        .Volatile()
);

void WINAPI xll_execa(LPOPER12 px, LPOPER12 ph)
{
#pragma XLLEXPORT
    std::thread t(execa, *px, ph);
    t.detach();
}
keithalewis commented 1 year ago

On my todo list. Happy to look at your pull requests on how to get this working.

cabo40 commented 1 year ago

I thought on writing a wrapper that immediately returns a string (non async) and forks a thread that writes the value to the cells address through a Macro, but if I recall correctly, UDF (or XLLs for that matter) cannot call Macros on their own. Other idea is to use a flag, if it's the first time that the function is being called (with the same params), launch a thread, if not, wait for the flag to be set and read the answer from a shared variable. This doesn't solve the "frozen screen" problem, but when moving through cells, the previous work doesn't get lost.

keithalewis commented 1 year ago

You can use .Uncaled() to get limited access to some Excel functions. I use this for handles because they call xlfCaller and xlCoerce.