Excel中的自定义函数:第四部分 - XLLs





5.00/5 (1投票)
XLLs 速度显著更快,并允许开发人员定义函数的名称/参数。
引言
在之前的文章中,我们已经了解了 VBA、自动化加载项和 RTDs。 XLLs 速度显著更快,并允许开发人员定义函数的名称/参数。 它们实现起来也不难,尽管设置函数的定义可能有点棘手。 与其他 C++ 加载项不同,XLL 不是 COM 服务器,它使用 Excel C API。
必备组件
您需要 Excel 2010 XLL SDK,如果您有 Excel 2007,则使用“以前的版本”。
入门
XLL 只是一个 DLL,它导出一组 Excel 将调用的标准函数。 最初,我打算从头开始为这篇文章创建一些东西,但 SDK 附带的示例已经可以工作,因此我将重点介绍可以增强它的几种方法。 我们将改进注册函数的过程,以便代码更易于编写,并且更易于记录函数/参数,以便用户知道如何使用它。
框架
在使用示例之前,我们需要构建 samples 目录中包含的 Framework 项目。 打开“Excel2010XLLSDK\SAMPLES\FRAMWRK\Framework.sln”。 如果需要转换,转换后直接构建即可。
XLL 示例
打开示例“Excel2010XLLSDK\SAMPLES\EXAMPLE\Example.sln”,如果您使用的是较新版本的 Visual Studio,它将自动转换它。 如果你打开 example.c,你会注意到缺少一些包含文件。
要修复此问题,只需在解决方案资源管理器中单击“Example”项目,然后在“属性页”中,转到“配置属性”和“VC++ 目录”,然后添加 *include* 目录和 *library* 目录。 将“<BASE_DIR>\Excel2010XLLSDK\INCLUDE”和“<BASE_DIR>\Excel2010XLLSDK\SAMPLES\FRAMWRK”添加到 *Include* 目录。 将“<BASE_DIR>\Excel2010XLLSDK\LIB”和“<BASE_DIR>\Excel2010XLLSDK\SAMPLES\FRAMWRK\Debug”添加到 *Library* 目录。 在我的例子中,我用“C:\2010 Office System Developer Resource”替换了 BASE_DIR,但您的可能不同。
现在您应该可以构建该项目了。
简单的函数注册
现在,函数的注册看起来像这样
static LPWSTR rgFuncs[rgFuncsRows][7] = {
{L"CallerExample", L"I", L"CallerExample"},
{L"debugPrintfExample", L"I", L"debugPrintfExample"},
{L"EvaluateExample", L"I", L"EvaluateExample"},
{L"Excel12fExample", L"I", L"Excel12fExample"},
{L"Excel12Example", L"I", L"Excel12Example"},
// Then later on...
for (i=0;i < rgFuncsRows;i++)
{
Excel12f(xlfRegister, 0, 4,
(LPXLOPER12)&xDLL,
(LPXLOPER12)TempStr12(rgFuncs[i][0]),
(LPXLOPER12)TempStr12(rgFuncs[i][1]),
(LPXLOPER12)TempStr12(rgFuncs[i][2]));
}
借助辅助函数,我们将能够注册具有可变数量参数的函数,并且我们可以定义与参数关联的帮助,这些帮助将显示在函数向导中。 XLLRegisterInfo
结构也可以动态定义,因此我们可以使用它来为描述/帮助提供本地化的 string
。
VOID RegisterHelper(struct XLLRegisterInfo registryInfo)
{
static XLOPER12 xDLL, xRegId;
static LPXLOPER12 args[260];
int count;
int i;
count = 10 + registryInfo.argumentCount;
/*
Get the name of the DLL.
*/
Excel12f(xlGetName, &xDLL, 0);
if(registryInfo.argumentCount == 0)
{
Excel12f(xlfRegister, 0, count,
(LPXLOPER12)&xDLL, /* moduleText */
(LPXLOPER12)TempStr12(registryInfo.procedure),
(LPXLOPER12)TempStr12(registryInfo.typeText),
(LPXLOPER12)TempStr12(registryInfo.functionText),
(LPXLOPER12)TempStr12(registryInfo.argumentText),
(LPXLOPER12)TempInt12
(registryInfo.macroType), /* function listed in function wizard */
(LPXLOPER12)TempStr12(registryInfo.categoryName),
(LPXLOPER12)TempStr12(L""), /* shortcut text */
(LPXLOPER12)TempStr12(registryInfo.helpTopic),
(LPXLOPER12)TempStr12(registryInfo.functionHelp));
}
else
{
count = 0;
args[count++] = (LPXLOPER12)&xDLL; /* moduleText */
args[count++] = (LPXLOPER12)TempStr12(registryInfo.procedure);
args[count++] = (LPXLOPER12)TempStr12(registryInfo.typeText);
args[count++] = (LPXLOPER12)TempStr12(registryInfo.functionText);
args[count++] = (LPXLOPER12)TempStr12(registryInfo.argumentText);
args[count++] =
(LPXLOPER12)TempInt12(registryInfo.macroType); /* function listed in function wizard */
args[count++] = (LPXLOPER12)TempStr12(registryInfo.categoryName);
args[count++] = (LPXLOPER12)TempStr12(L""); /* shortcut text */
args[count++] = (LPXLOPER12)TempStr12(registryInfo.helpTopic);
args[count++] = (LPXLOPER12)TempStr12(registryInfo.functionHelp);
for(i = 0; i < registryInfo.argumentCount; i++)
{
args[count++] = (LPXLOPER12)TempStr12(registryInfo.argumentHelp[i]);
}
Excel12v(xlfRegister, 0, count,
args);
}
}
当然,您可能想要 XLLRegisterInfo
的定义,所以这是它
struct XLLRegisterInfo
{
/**
Name of the procedure to be registered.
*/
LPWSTR procedure;
/**
The type text see pxTypeText here: http://msdn.microsoft.com/en-us/library/bb687900.aspx
*/
LPWSTR typeText;
/**
Name of the function in Excel.
*/
LPWSTR functionText;
/**
String of Letters that represent arguments/types. See pxArgumentText
here: http://msdn.microsoft.com/en-us/library/bb687900.aspx
*/
LPWSTR argumentText;
/**
Type of function. 0 - Macro sheet equivalent, 1 - Worksheet Functions, 2 - Commands.
When in doubt use 1;
*/
int macroType;
/**
Name of the category to add the function to.
*/
LPWSTR categoryName;
/**
Commands only.
*/
LPWSTR shortcutText;
/**
Reference to a help file (.chm or .hlp).
Use form: filepath!HelpContextID or http://address/path_to_file_in_site!0
*/
LPWSTR helpTopic;
/**
String that describes the function in the function wizard.
*/
LPWSTR functionHelp;
/**
* Number of descriptions in argumentHelp.
*/
int argumentCount;
/**
Array of Argument help strings.
*/
LPWSTR argumentHelp[15];
};
在 2007 中,argumentHelp
字段可以是最多 245 个参数,在 2003 中是 20 个。 为您的加载项选择合适的东西。
定义完之后,可以用这些结构的数组替换 rgFuncs
。
struct XLLRegisterInfo rgFuncs[] =
{
{
L"CalcCircum" /* procedure */, L"BB" /*typeText*/,
/* functionText */ L"CalcCircum",
/*argumentText*/ L"Radius", 1, L"Examples",
L"" /*shortcutText*/, L"" /*helpTopic*/,
L"Calculates the circumferance of a circle."
/* functionHelp */, 1 /*argCount */,
{L"Radius of the circle. "
/*argumentHelp1 pad with space, Excel bug*/}
},
{ NULL }
};
我在末尾添加了 NULL
来标记数组的结尾。 这样,我不需要一个 rgFuncsCount
常量,每次添加/删除函数时我都必须更新它。 还要记得在最后一个 argumentHelp
中添加一个空格。 原因是 Excel 中存在一个错误,会删除最后一个 argumentHelp
的最后一个字符。 不要指望这个错误会被修复,但以防万一我使用空格,它看起来比笑脸更专业。
for(i=0;rgFuncs[i].procedure != NULL;i++)
RegisterHelper(rgFuncs[i]);
现在,我可以定义具有多个参数的函数,并为每个参数提供适当的帮助,并且所有额外的复杂性都隐藏在 RegisterHelper
中。 xlAutoOpen
的代码现在也更具可读性,并且它可以处理具有不同数量参数的不同函数。