Excel XLL SDK:自定义函数调用xlfRegisterId失败的原因及解决
问题
参考Stack Overflow用户Malik的回答尝试获取自定义函数的注册ID时遇到问题:
- 在
xlAutoOpen函数中调用xlfRegisterId可以正常获取到xRegId中的ID - 但在电子表格单元格直接调用的自定义函数
exampleAddin内调用xlfRegisterId时,xRegId返回空值或错误状态
xlAutoOpen函数代码
extern "C" __declspec(dllexport) int xlAutoOpen(void) { XLOPER12 xDLL; Excel12f(xlGetName, &xDLL, 0); Excel12f(xlfRegister, 0, 11, (LPXLOPER12)&xDLL, (LPXLOPER12)TempStr12(L"exampleAddin"), (LPXLOPER12)TempStr12(L"QQQ"), (LPXLOPER12)TempStr12(L"exampleAddin"), (LPXLOPER12)TempStr12(L"v1,v2"), (LPXLOPER12)TempStr12(L"1"), (LPXLOPER12)TempStr12(L"myOwnCppFunctions"), (LPXLOPER12)TempStr12(L""), (LPXLOPER12)TempStr12(L""), (LPXLOPER12)TempStr12(L"An example"), (LPXLOPER12)TempStr12(L"")); XLOPER12 xRegId; Excel12(xlfRegisterId, &xRegId, 2, (LPXLOPER12)&xDLL, (LPXLOPER12)TempStr12("exampleAddin")); //xRegId will be XltypeNum /* Free the XLL filename */ Excel12f(xlFree, 0, 1, (LPXLOPER12)&xDLL); return 1; }
自定义函数代码
extern "C" __declspec(dllexport) LPXLOPER12 exampleAddin(LPXLOPER12 x1, LPXLOPER12 x2) { XLOPER12 xDLL, xRegId; Excel12(xlGetName, &xDLL, 0);// xDLL will be xltypeStr Excel12(xlfRegisterId, &xRegId, 2, (LPXLOPER12)&xDLL, (LPXLOPER12)TempStr12("exampleAddin")); // ... my user defined code is here }
原因分析
这是Excel XLL的权限限制导致的:
xlAutoOpen在XLL加载时由Excel的UI线程执行,此时处于非计算模式,允许调用所有Excel API,包括xlfRegisterId- 自定义函数
exampleAddin在Excel的计算线程中执行,处于计算模式下,Excel会限制这类函数调用大部分交互式或注册相关的API,xlfRegisterId就属于被限制的范畴,因此调用后会返回空值或错误
解决方案
最直接的办法是在xlAutoOpen中提前获取注册ID并存储到全局变量,自定义函数直接读取该全局变量即可:
修改后的代码示例
// 全局变量存储注册ID,用atomic保证线程安全 #include <atomic> std::atomic<int> g_exampleAddinRegId = 0; extern "C" __declspec(dllexport) int xlAutoOpen(void) { XLOPER12 xDLL; Excel12f(xlGetName, &xDLL, 0); Excel12f(xlfRegister, 0, 11, (LPXLOPER12)&xDLL, (LPXLOPER12)TempStr12(L"exampleAddin"), (LPXLOPER12)TempStr12(L"QQQ"), (LPXLOPER12)TempStr12(L"exampleAddin"), (LPXLOPER12)TempStr12(L"v1,v2"), (LPXLOPER12)TempStr12(L"1"), (LPXLOPER12)TempStr12(L"myOwnCppFunctions"), (LPXLOPER12)TempStr12(L""), (LPXLOPER12)TempStr12(L""), (LPXLOPER12)TempStr12(L"An example"), (LPXLOPER12)TempStr12(L"")); XLOPER12 xRegId; Excel12(xlfRegisterId, &xRegId, 2, (LPXLOPER12)&xDLL, (LPXLOPER12)TempStr12("exampleAddin")); // 将注册ID存入全局变量 if (xRegId.xltype == xltypeNum) { g_exampleAddinRegId = static_cast<int>(xRegId.val.num); } /* Free the XLL filename */ Excel12f(xlFree, 0, 1, (LPXLOPER12)&xDLL); return 1; } extern "C" __declspec(dllexport) LPXLOPER12 exampleAddin(LPXLOPER12 x1, LPXLOPER12 x2) { // 直接使用全局变量中的注册ID int regId = g_exampleAddinRegId.load(); // ... 你的业务代码 }
注意事项
- 全局变量建议用
std::atomic类型,避免多线程计算时出现竞态问题 - 如果需要管理多个自定义函数的注册ID,可以用全局哈希表(如
std::unordered_map<std::wstring, std::atomic<int>>)存储,键为函数名,值为对应注册ID
内容的提问来源于stack exchange,提问作者Peter A
相关产品推荐
相关产品推荐

