Excel中Application.Run()会压缩返回数组维度?如何避免?
Excel XLL数组返回维度问题解答
问题描述
我使用Excel C API编写XLL库提供函数,返回xloper12类型的xltypeMulti数组时出现以下现象:
- 当返回n×m(n、m均大于1)的二维数组时,工作表和VBA的
Application.Run()调用都能正确识别二维结构; - 当返回1×m的二维数组时,工作表显示正常,但
Application.Run()会自动将其缩减为一维数组,在VBA中表现为包含m个元素的一维结构。
我可以在VBA中处理这种转换,但希望找到一种返回方式,让工作表和VBA都能识别并保留数组的原始二维维度。
注册代码示例
Excel12f(xlfRegister, 0, 10, (LPXLOPER12)&p_xllName, (LPXLOPER12)TempStr12(L"returns1x2table"), (LPXLOPER12)TempStr12(L"Q"), (LPXLOPER12)TempStr12(L"returns1x2table"), (LPXLOPER12)TempStr12(L""), (LPXLOPER12)TempInt12(1), (LPXLOPER12)TempStr12(L"Test"), (LPXLOPER12)TempStr12(L""), (LPXLOPER12)TempStr12(L""), (LPXLOPER12)TempStr12(L"Returns a variant 1x2"));
函数实现代码
extern "C" __declspec(dllexport) LPXLOPER12 WINAPI returns1x2table() { xloper12* result=(xloper12*)malloc(sizeof(xloper12)); result->xltype = xltypeMulti | xlbitDLLFree; result->val.array.rows = 1; result->val.array.columns = 2; result->val.array.lparray = (xloper12*)malloc(sizeof(xloper12) * 2); result->val.array.lparray[0].xltype = xltypeNum; result->val.array.lparray[0].val.num = 3.1415; result->val.array.lparray[1].xltype = xltypeNum; result->val.array.lparray[1].val.num = 2.7182; return result; } extern "C" __declspec(dllexport) LPXLOPER12 WINAPI returns2x2table() { xloper12* result=(xloper12*)malloc(sizeof(xloper12)); result->xltype = xltypeMulti | xlbitDLLFree; result->val.array.rows = 2; result->val.array.columns = 2; result->val.array.lparray = (xloper12*)malloc(sizeof(xloper12) * 4); result->val.array.lparray[0].xltype = xltypeNum; result->val.array.lparray[0].val.num = 3.1415; result->val.array.lparray[1].xltype = xltypeNum; result->val.array.lparray[1].val.num = 2.7182; result->val.array.lparray[2].xltype = xltypeNum; result->val.array.lparray[2].val.num = 1.4142; result->val.array.lparray[3].xltype = xltypeNum; result->val.array.lparray[3].val.num = 1.7321; return result; }
测试结果
- 工作表中:
returns1x2table()返回:| 3.1415 | 2.7182 |returns2x2table()返回:| 3.1415 | 2.7182 | | 1.4142 | 1.7321 | - VBA调用时:
监视窗口显示sub a() dim v1 as variant dim v2 as variant v1 = application.Run("returns1x2table") v2 = application.Run("returns2x2table") End Subv1为Variant/Variant(1 to 2)(二维数组被缩减为一维),v2为Variant/Variant(1 to 2, 1 to 2)(保留二维结构)。
解答
1. 这是Excel的预期行为
Application.Run()在处理返回的数组时,会自动将单维的二维数组(1×m或n×1)缩减为一维数组,这是Excel对象模型的默认逻辑——它会采用存储数据所需的最小维度,减少冗余结构。
2. 保留维度的解决方法
要让1×m的数组在VBA中也保留二维结构,可采用以下两种可靠方式:
方式一:在XLL中返回xltypeArray类型
xltypeArray对应VBA中的严格数组变体,会完整保留原始维度信息,不会被自动缩减。修改函数实现如下:
extern "C" __declspec(dllexport) LPXLOPER12 WINAPI returns1x2table() { xloper12* result = (xloper12*)malloc(sizeof(xloper12)); result->xltype = xltypeArray | xlbitDLLFree; // 定义1行2列的数组维度(xltypeArray索引从0开始) result->val.array.rows = 1; result->val.array.columns = 2; result->val.array.lparray = (xloper12*)malloc(sizeof(xloper12) * 2); // 按列优先填充数据(与xltypeMulti逻辑一致) result->val.array.lparray[0].xltype = xltypeNum; result->val.array.lparray[0].val.num = 3.1415; result->val.array.lparray[1].xltype = xltypeNum; result->val.array.lparray[1].val.num = 2.7182; return result; }
注:注册函数的返回类型参数仍为L"Q",Excel会自动识别xltypeArray并保留维度。
方式二:在VBA中显式转换维度
如果不想修改XLL代码,可在VBA接收后手动将一维数组转回1×m的二维数组:
sub a() dim v1 as variant v1 = application.Run("returns1x2table") ' 判断是否为一维数组并转换 if isarray(v1) then on error resume next dim colCount as integer colCount = ubound(v1, 2) if err.number <> 0 then ' 转换为1×m的二维数组 v1 = application.Transpose(application.Transpose(v1)) end if on error goto 0 end if end sub
两次Transpose是因为单次转置会得到m×1的数组,两次操作后可得到1×m的二维结构。
内容的提问来源于stack exchange,提问作者Giancarlo Pfeifer
相关产品推荐
相关产品推荐

