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.
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(); }
The new logic should work as follows:
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.