Excel XLL 如何判断是否正在从公式栏中调用 UDF?

问题描述

我正在寻找一种方法来确定我的 XLL 内部调用是来自重新计算过程还是来自为单元格输入的新公式。我已经看到 XLL 可以检测它是否被函数向导调用(通过检查窗口类等)的例子,但是如果它直接输入到单元格中呢?

enter image description here

我可以从 xlfCaller 函数获取单元格的引用:

   XLOPER xlRef;
   Excel(xlfCaller,&xlRef,0);

但这并没有让我走得很远。我试图模仿 Excel 对 TODAY() 函数的处理,如果直接在单元格中输入公式(而不是粘贴或从另一个函数调用),它会更改单元格编号格式。

解决方法

可以通过挂钩 SheetChange 事件来复制 TODAY() 行为的这一方面 - 这只会在输入公式时触发,而不会在重新计算时触发(即使使用 Ctrl-Alt-F9)。我不知道 TODAY() 是否使用这种方法,或者它可能使用了用户定义函数不可用的东西。此外,复制 TODAY() 需要解析公式并应用一些逻辑来确定它是否应用日期格式,这会有点棘手,但有一些 Excel 公式解析器。

xlOil 中(免责声明:我写的)它可以实现为:

  XLO_FUNC_START(testToday())
  {
    CallerInfo caller;
    if (!caller.fullSheetName().empty()) // Check caller is worksheet
    {
      // Add a SheetChange handler
      auto handle = xloil::Event::SheetChange().bind(
        [=](const wchar_t* wsName,const Range& target)
        {
          // Need to check range here as well to avoid being triggered by another sheet change
          if (wsName == caller.sheetName())
            excelApp().Range[caller.writeAddress().c_str()]->NumberFormat = L"dd-mm-yyyy";
        }
      );
      // Wait 0.5 sec then unbind the event handler by queuing a window message with Excel
      auto milliSecsDelay = 500;
      excelPost([=]() mutable
      {
        handle.reset(); // Removes the SheetChange handler
      },QueueType::WINDOW,milliSecsDelay);
    }

    // This is how to get the current date in C++
    std::tm buf; 
    auto now = std::time(0);
    localtime_s(&buf,&now);

    return returnValue(buf); // xlOil will convert the std::tm to an Excel date
  }
  XLO_FUNC_END(testToday);

这只是一个概念验证,可以做一些整理,例如应该检查范围,如果多次调用该函数,开销可能会很大。