XLL+ Class Library

Step 4 - Modify add-in function

Modify add-in function using function wizard

The first change we need to make to the add-in function is to add a new argument. This will contain the sequence number discussed in Communication between worker thread and RTD. When a calculation completes, the sequence number changes and, since it is an argument to the function AvgOptValue, the function is refreshed by Excel.

Use the XLL+ Function Wizard to add a new argument to AvgOptValue:

The new argument is of type Long and is named RtdSeqNum.

Existing add-in function's logic

The next step is to change the add-in function’s body, so that calls are passed to a worker thread, instead of being immediately calculated.

The original function looked as shown below:

extern "C" __declspec( dllexport )
LPXLOPER AvgOptValue(BOOL Put, double Spot, double Vol, double
    Loan, double Discount, double DivYield, long ValueDate, long
    Maturity, const COper* AvgInDates, const COper* AvgOutDates,
    long Iterations)
{
    ...
//}}XLP_SRC

    // Call the library function
    double dResult = 0.0;
    char achErr[256];
    int calc_ok = eval_avg_opt(Put ? 1 : 0, Spot, Vol, Loan, Discount, DivYield,
        ValueDate, Maturity, vecAvgInDates.size(), &vecAvgInDates[0], 
        vecAvgOutDates.size(), &vecAvgOutDates[0], Iterations,
        &dResult, achErr, sizeof(achErr));
    
    // Put the result or an error string in xloResult
    if (calc_ok)
        xloResult = dResult;
    else
        xloResult.Format("#ERROR: %s", achErr);

    return xloResult.Ret();
}

New logic for add-in function

The new logic should work as follows:

  1. Construct a new AvgOptData object, populated with the function’s inputs.
  2. Look up the inputs in the cache.
  3. If the inputs are matched, extract a result from the cached data object. If the object is marked as done=true, then the outputs can be used. If the calculation is not complete, then the value "#WAIT!" is returned.
  4. If the inputs are not matched, add the data object to the cache, send the calculation to a worker thread, and return "#WAIT!".

Modify add-in function's code

This logic is implemented as shown below:

extern "C" __declspec( dllexport )
LPXLOPER AvgOptValue(BOOL Put, double Spot, double Vol, double
    Loan, double Discount, double DivYield, long ValueDate, long
    Maturity, const COper* AvgInDates, const COper* AvgOutDates,
    long Iterations, long RtdSeqNum)
{
    ...
//}}XLP_SRC

    AvgOptData* argsIn = new AvgOptData(
        Put, Spot, Vol, Loan, Discount, DivYield,
        ValueDate, Maturity, vecAvgInDates, 
        vecAvgOutDates, Iterations);

    AvgOptData* argsOld = ::XllGetTypedApp()->m_cacheAvgOpt.Find(argsIn);
    if (argsOld != 0)
    {
        delete argsIn;
        if (argsOld->done)
            xloResult = argsOld->result;
        else
            xloResult = "#WAIT!";
        return xloResult.Ret();
    }

    XllGetTypedApp()->m_cacheAvgOpt.Add(argsIn);

    if (!::XllGetTypedApp()->m_threadManager.StartThread(
            AvgOptValue_threadfn, argsIn))
        return CXlOper::RetError(xlerrNull);
    xloResult = "#WAIT!";

    return xloResult.Ret(); 
}

Note that the old call to eval_avg_opt() is replaced with a call to ThreadManager::StartThread(), passing the address of our worker thread function and the AvgOptData object that it will use.

Next: Step 5 - Modify "Clear cache" command >>