Excel PasteSpecial Values 快捷按鈕導致 QuantLib 崩潰
當我使用 Excel 右鍵點擊工具欄上的快速訪問 PasteOptions/Values 按鈕時,我遇到了 QuantLibXL 的一個奇怪問題(為圖片道歉:擷取右鍵點擊菜單的螢幕抓取非常困難!)。
有沒有其他人經歷過這個?
如果我使用此按鈕粘貼特殊/值,而不是“粘貼特殊…”子菜單,則 QuantLib 似乎在內部崩潰並且對庫的所有呼叫都返回 #NUM!。唯一的解決方案是關閉並重新啟動 Excel。
這是一個重現問題的簡單表格(NB Excel 計算是自動的)。
用公式:
因此 qlCalendarAdvance() 函式採用參數 (B2),該參數本身就是函式 (=B1) 的結果。
重現錯誤的步驟:
- 選擇單元格 B1
- 右鍵點擊單元格 B2,然後選擇粘貼選項/值快捷按鈕(如上圖所示)。
- 單元格 B3 變為 #NUM!
筆記:
- 如果 B2 已經被硬編碼,則不會發生錯誤。
- 錯誤不會 100% 發生,儘管發生的頻率要高得多,這表明重新計算時存在某種時間問題。
- 顯然,解決方法是避免使用該按鈕(!)。
Excel:Microsoft 365 64 位。2009 版(內部版本 13231.20390)
QL 外掛:QuantLibXL-vc141-x64-mt-s-1_16_0.xll
在深入探勘之後,我在 QuantLibXL 基礎程式碼中發現了一個問題:建構子沒有處理異常。
在 QL 用於包裝 Excel4 C 樣式介面 (functioncall.cpp) 的基本程式碼中,有以下程式碼,其中該類設置了一個靜態指針以確保僅存在一個實例:
FunctionCall *FunctionCall::instance_ = 0; FunctionCall::FunctionCall(const std::string functionName) : functionName_(functionName), callerDimensions_(CallerDimensions::Uninitialized), error_(false) { OH_REQUIRE(!instance_, "Multiple attempts to initialize global FunctionCall object"); instance_ = this; //The issue is here Excel(xlfCaller, &xCaller_, 0); if (xCaller_->xltype == xltypeRef || xCaller_->xltype == xltypeSRef) { Excel(xlfReftext, &xReftext_, 1, &xCaller_); refStr_ = ConvertOper(xReftext_()); //THIS CAN FAIL callerType_ = CallerType::Cell; } else if (xCaller_->xltype & xltypeErr) { callerType_ = CallerType::VBA; } else if (xCaller_->xltype == xltypeMulti) { callerType_ = CallerType::Menu; } else { callerType_ = CallerType::Unknown; } }
問題似乎是,如果您使用的是右鍵點擊按鈕,Excel 界面將無法解析“呼叫者”(它告訴 UDF 如何呼叫它)。對 ConvertOper(xRefText…) 的呼叫會引發異常(因為它無法強制執行 Error OPER)。這不在建構子中處理,但在死前建構子程式碼已設置
instance_ = this;
由於解構子(將 instance_ 設置回 0)永遠不會被呼叫,以後對建構子的呼叫總是認為已經有一個實例,並且 OH_REQUIRE 斷言失敗。
畢竟,解決方案非常簡單:只需將設置實例的行移到可能引發異常的程式碼之後:
//instance_ = this; MOVE FROM HERE Excel(xlfCaller, &xCaller_, 0); if (xCaller_->xltype == xltypeRef || xCaller_->xltype == xltypeSRef) { Excel(xlfReftext, &xReftext_, 1, &xCaller_); refStr_ = ConvertOper(xReftext_()); //THIS CAN FAIL callerType_ = CallerType::Cell; } else if (xCaller_->xltype & xltypeErr) { callerType_ = CallerType::VBA; } else if (xCaller_->xltype == xltypeMulti) { callerType_ = CallerType::Menu; } else { callerType_ = CallerType::Unknown; } instance_ = this; //TO HERE }
重新編譯,一切正常。