Excel DNA 垂直调整返回数组的大小
Posted
技术标签:
【中文标题】Excel DNA 垂直调整返回数组的大小【英文标题】:Excel DNA resize return array vertically 【发布时间】:2014-05-02 16:40:14 【问题描述】:我正在尝试在 Excel-DNA api 的帮助下返回动态数组。我能够产生所需的输出。但我想产生垂直输出。
这是我的 UDF 的 c# 代码。我已使用此链接制作我的样品。 http://excel-dna.net/2011/01/30/resizing-excel-udf-result-arrays/
下面的代码产生这个输出:
(0,0) (0,1) (0,2) (0,3)
我想要以这种格式输出:
(0,0)
(0,1)
(0,2)
(0,3)
代码如下:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading;
using CometCollaborator;
using ExcelDna.Integration;
namespace ExcelUDFs
public class UDFs
[ExcelFunction(Description = "Make Array")]
public static object MakeArrayAndResize(int columns)
object result = MakeArray(columns);
// Call Resize via Excel - so if the Resize add-in is not part of this code, it should still work.
var a = XlCall.Excel(XlCall.xlUDF, "Resize", result);
return a;
[ExcelFunction(Description = "Make Array")]
public static object MakeArray( int columns)
object[,] result = new string[1, columns];
for (int i = 0; i < 1; i++)
for (int j = 0; j < columns; j++)
result[i, j] = string.Format("(0,1)", i, j);
return result;
static Queue<ExcelReference> ResizeJobs = new Queue<ExcelReference>();
// This function will run in the UDF context.
// Needs extra protection to allow multithreaded use.
public static object Resize(object[,] array)
ExcelReference caller = XlCall.Excel(XlCall.xlfCaller) as ExcelReference;
if (caller == null)
return array;
int rows = array.GetLength(0);
int columns = array.GetLength(1);
if ((caller.RowLast - caller.RowFirst + 1 != rows) ||
(caller.ColumnLast - caller.ColumnFirst + 1 != columns))
// Size problem: enqueue job, call async update and return #N/A
// TODO: Add guard for ever-changing result?
EnqueueResize(caller, rows, columns);
AsyncRunMacro("DoResizing");
return ExcelError.ExcelErrorNA;
// Size is already OK - just return result
return array;
//object[,] retArray = new object[columns, rows];
//for(int i=0;i<array.Length;i++)
//
// retArray[i, 0] = array[0, i];
//
//return retArray;
static void EnqueueResize(ExcelReference caller, int rows, int columns)
ExcelReference target = new ExcelReference(caller.RowFirst, caller.RowFirst + rows - 1, caller.ColumnFirst, caller.ColumnFirst + columns - 1, caller.SheetId);
ResizeJobs.Enqueue(target);
public static void DoResizing()
while (ResizeJobs.Count > 0)
DoResize(ResizeJobs.Dequeue());
static void DoResize(ExcelReference target)
try
// Get the current state for reset later
XlCall.Excel(XlCall.xlcEcho, false);
// Get the formula in the first cell of the target
string formula = (string)XlCall.Excel(XlCall.xlfGetCell, 41, target);
ExcelReference firstCell = new ExcelReference(target.RowFirst, target.RowFirst, target.ColumnFirst, target.ColumnFirst, target.SheetId);
bool isFormulaArray = (bool)XlCall.Excel(XlCall.xlfGetCell, 49, target);
if (isFormulaArray)
object oldSelectionOnActiveSheet = XlCall.Excel(XlCall.xlfSelection);
object oldActiveCell = XlCall.Excel(XlCall.xlfActiveCell);
// Remember old selection and select the first cell of the target
string firstCellSheet = (string)XlCall.Excel(XlCall.xlSheetNm, firstCell);
XlCall.Excel(XlCall.xlcWorkbookSelect, new object[] firstCellSheet );
object oldSelectionOnArraySheet = XlCall.Excel(XlCall.xlfSelection);
XlCall.Excel(XlCall.xlcFormulaGoto, firstCell);
// Extend the selection to the whole array and clear
XlCall.Excel(XlCall.xlcSelectSpecial, 6);
ExcelReference oldArray = (ExcelReference)XlCall.Excel(XlCall.xlfSelection);
oldArray.SetValue(ExcelEmpty.Value);
XlCall.Excel(XlCall.xlcSelect, oldSelectionOnArraySheet);
XlCall.Excel(XlCall.xlcFormulaGoto, oldSelectionOnActiveSheet);
// Get the formula and convert to R1C1 mode
bool isR1C1Mode = (bool)XlCall.Excel(XlCall.xlfGetWorkspace, 4);
string formulaR1C1 = formula;
if (!isR1C1Mode)
// Set the formula into the whole target
formulaR1C1 = (string)XlCall.Excel(XlCall.xlfFormulaConvert, formula, true, false, ExcelMissing.Value, firstCell);
// Must be R1C1-style references
object ignoredResult;
XlCall.XlReturn retval = XlCall.TryExcel(XlCall.xlcFormulaArray, out ignoredResult, formulaR1C1, target);
if (retval != XlCall.XlReturn.XlReturnSuccess)
// TODO: Consider what to do now!?
// Might have failed due to array in the way.
firstCell.SetValue("'" + formula);
finally
XlCall.Excel(XlCall.xlcEcho, true);
// Most of this from the newsgroup: http://groups.google.com/group/exceldna/browse_thread/thread/a72c9b9f49523fc9/4577cd6840c7f195
private static readonly TimeSpan BackoffTime = TimeSpan.FromSeconds(1);
static void AsyncRunMacro(string macroName)
// Do this on a new thread....
Thread newThread = new Thread(delegate()
while (true)
try
RunMacro(macroName);
break;
catch (COMException cex)
if (IsRetry(cex))
Thread.Sleep(BackoffTime);
continue;
// TODO: Handle unexpected error
return;
catch (Exception ex)
// TODO: Handle unexpected error
return;
);
newThread.Start();
static void RunMacro(string macroName)
object xlApp=null;
try
xlApp = ExcelDnaUtil.Application;
xlApp.GetType().InvokeMember("Run", BindingFlags.InvokeMethod, null, xlApp, new object[] macroName );
catch (TargetInvocationException tie)
throw tie.InnerException;
finally
Marshal.ReleaseComObject(xlApp);
const uint RPC_E_SERVERCALL_RETRYLATER = 0x8001010A;
const uint VBA_E_IGNORE = 0x800AC472;
static bool IsRetry(COMException e)
uint errorCode = (uint)e.ErrorCode;
switch (errorCode)
case RPC_E_SERVERCALL_RETRYLATER:
case VBA_E_IGNORE:
return true;
default:
return false;
【问题讨论】:
你的数组函数的返回值可以是一个二维的object[,]数组,并且可以包含你想要的任何形状的数据。从你的问题中不清楚为什么你不能从你的函数中返回一个 4 x 1 的数组。 我刚刚将 1*4 数组转换为 4*1 并提供了所需的输出。谢谢。 【参考方案1】:将一维数组转换为二维数组并调用 resize 方法。
object[,] retArray = new object[a.Length, 1];
for (int i = 0; i < a.Length; i++)
retArray[i, 0] = a[i];
var ab = XlCall.Excel(XlCall.xlUDF, "Resize", retArray);
return ab;
【讨论】:
以上是关于Excel DNA 垂直调整返回数组的大小的主要内容,如果未能解决你的问题,请参考以下文章
Python使用numpy函数vsplit垂直(行角度)拆分numpy数组(返回拆分后的numpy数组列表)实战:垂直拆分二维numpy数组split函数垂直拆分二维numpy数组