问题描述
Excel“日期”在幕后是双打,XLOPER 结构似乎没有任何日期概念,只有 xltypeNum(与 VBA 中的 Variants 不同)。
我有一个简单的 XLL,它的函数不接受任何参数,将今天的日期作为 LPXLOPER(类型“P”)返回。我有其他函数通常返回日期但也可能返回错误,这就是为什么我使用“P”作为返回值而不是“B”。
DLLEXPORT LPXLOPER WINAPI epToday()
{
static XLOPER xResult;// Return value
xResult.xltype = xltypeNum;
xResult.val.num = ExcelDateForToday(); //A function that returns a double for today's date
return &xResult;
}
返回值在调用电子表格中显示为数字(例如 40303)。我希望能够告诉 Excel 将我返回的双精度值视为日期:与内置函数 TODAY() 一样。有没有办法做到这一点?
我看到 =TODAY() 将单元格 NumberFormat 更改为“Date”,我应该使用回调来更改调用单元格的格式吗?这似乎是 Excel 正在做的:例如,如果我使用 =TODAY() 函数重新计算单元格,它会再次将 NumberFormat 设置为 Date。
解决方法
据我所知,遵循通过回调设置日期格式的 TODAY()
方法是向 Excel 发出信号表明您从 XLL 函数返回的值是日期的唯一方法。但是,它确实有一些缺点:
- 性能(对于大量调用,虽然我还没有检查过)
- 如果调用者不是工作表,则需要特殊处理
- 对
=TODAY() - AnotherDate
做错事,但 Excel 也做错了! - 将在每次重新计算时覆盖格式 - 如果首选另一种格式,可能会惹恼用户
Excel 能够通过检测它是否作为计算周期的一部分被调用来神奇地避免 (4) - 我不知道如何在没有看到 Excel 源代码的情况下实现这一点。您可以将您的功能包装在一些东西中以设置格式,该格式将为用户提供选择(或撤消您的格式更改,因为回调将排在第二位),例如=SetFormat(MyToday(),"yyyy-mm-dd")
xlOil 中的回调相当简单(免责声明:我写的):
XLO_FUNC_START(testToday())
{
CallerInfo caller;
if (!caller.fullSheetName().empty()) // Check caller is a worksheet
excelPost([=]
{
excelApp().Range[caller.writeAddress().c_str()]->NumberFormat = L"dd-mm-yyyy";
});
std::tm buf;
auto now = std::time(0);
localtime_s(&buf,&now); // Slightly labourious to get current date in C++
return returnValue(buf);
}
XLO_FUNC_END(testToday);