Llamar a Excel desde el DLL o XLL
Hace referencia a: Excel 2013 | Office 2013 | Visual Studio
Microsoft Excel permite que el DLL tenga acceso a comandos de Excel integrados, funciones de hoja de cálculo y funciones de hoja de macros. Están disponibles en los comandos de DLL y las funciones que se llaman desde Visual Basic para Aplicaciones (VBA), así como en las funciones y los comandos XLL registrados que Excel llama directamente.
Funciones de Excel4, Excel4v, Excel12 y Excel12v
Excel permite que el DLL tenga acceso a los comandos y las funciones a través de las funciones de devolución de llamada Excel4, Excel4v, Excel12 y Excel12v.
Las funciones de Excel4 y Excel4v se introdujeron en la versión 4 de Excel. Trabajar con la estructura de datos XLOPER. Excel 2007 introdujo dos nuevas funciones de devolución de llamada, Excel12 y Excel12v, que trabajan con a estructura de datos XLOPER12. Las funciones de Excel4 y Excel4v se exportan mediante la biblioteca Xlcall32.lib, que debe incluirse en el proyecto DLL o XLL. Excel12 y Excel12v se incluyen en el archivo de origen del SDK para C++ Xlcall.cpp, el cual debe incluirse en el proyecto si desea obtener acceso a las funciones de Excel mediante estructuras XLOPER12.
El siguiente código muestra los prototipos de función para estas cuatro funciones. Los primeros tres argumentos son los mismos, excepto que el segundo argumento es un puntero a una estructura XLOPER en el primer par y un puntero a una estructura XLOPER12 en el segundo par. La convención de llamada es _cdecl en Excel4 y Excel12 para permitir las listas de argumentos variables. Los puntos suspensivos representan punteros a valores de XLOPER para Excel4 y valores de XLOPER12 para Excel12. El número de punteros es igual al valor del parámetro de recuento.
Todas las versiones de Excel
int _cdecl Excel4(int xlfn, LPXLOPER operRes, int count,... );
int pascal Excel4v(int xlfn, LPXLOPER operRes, int count, LPXLOPER opers[]);
A partir de Excel 2007
int _cdecl Excel12(int xlfn, LPXLOPER12 operRes, int count,... );
int pascal Excel12v(int xlfn, LPXLOPER12 operRes, int count, LPXLOPER12 opers[]);
Para que el DLL pueda llamar a Excel4, Excel4v, Excel12 o Excel12v, Excel debe pasar el control al DLL. Esto significa que es posible llamar a estas devoluciones de llamada de API C únicamente en las siguientes situaciones:
Desde un comando XLL al que Excel llamó directamente o a través de VBA.
Desde una hoja de cálculo XLL o una función de hoja de macros que Excel llamó directamente o a través de VBA.
No puede llamar a la API C de Excel en las siguientes situaciones:
Desde un evento de sistema operativo (por ejemplo, desde la función DllMain).
Desde un proceso en segundo plano que haya creado el DLL.
Valores devueltos
Las cuatro funciones devuelven un valor entero que informa al autor de llamada si fue posible llamar correctamente a la función o al comando. Los valores devueltos pueden ser uno de los siguientes:
Valor devuelto | Definido en Xlcall.h como | Descripción |
---|---|---|
0 | xlretSuccess | La función o el comando se ejecutaron correctamente. Esto no significa que la ejecución haya estado exenta de errores. Por ejemplo, Excel4 podría devolver xlretSuccess al llamar a la función FIND, aunque se evaluara como #VALUE! porque no se encontró el texto de búsqueda. Es necesario inspeccionar el tipo y el valor devuelto XLOPER o XLOPER12 cuando es posible. |
1 | xlretAbort | El usuario detuvo una macro de comando al hacer clic en el botón CANCELAR o presionar la tecla ESC. |
2 | xlretInvXlfn | El código de comando o la función especificada no es válida. Este error puede ocurrir cuando la función de llamada no tiene permiso para llamar a la función o el comando. Por ejemplo, una función de hoja de cálculo no puede llamar a una función de comando o una función de información de hoja de macros. |
4 | xlretInvCount | El número de argumentos proporcionados en la llamada no es correcto. |
8 | xlretInvXloper | Uno o más de los valores del argumento XLOPER o XLOPER12 no están formados o rellenados correctamente. |
16 | xlretStackOvfl | Excel detectó un riesgo en la operación de desbordamiento de su pila y, por lo tanto, no llamó a la función. |
32 | xlretFailed | Se produjo un error en el comando o la función por un motivo que no se describe en uno de los demás valores devueltos. Una operación que necesita una gran cantidad de memoria, por ejemplo, podría generar este error. Esto puede ocurrir durante un intento de convertir una enorme referencia en una matriz xltypeMulti mediante la función xlCoerce. |
64 | xlretUncalced | La operación intentó recuperar el valor de una celda no actualizada. Para mantener la integridad de actualización de Excel, no se permite que las funciones de la hoja de cálculo lo hagan. Sin embargo, las funciones y los comandos XLL registrados como funciones de hoja de macros puedan obtener acceso a los valores de celdas no actualizadas. |
128 | xlretNotThreadSafe | (A partir de Excel 2007) Una función de hoja de cálculo XLL registrada como segura para subprocesos intentó llamar a una función de la API de C que no es segura para subprocesos. Por ejemplo, una función segura para subprocesos no puede llamar a la función XLM xlfGetCell. |
256 | xlRetInvAsynchronousContext | (A partir de Excel 2010) El controlador de la función asincrónica no es válido. |
512 | xlretNotClusterSafe | (A partir de Excel 2010) No se admite la llamada en clústeres. |
Si la función devuelve uno de los valores de error en la tabla (es decir, no devuelve xlretSuccess), el valor devuelto XLOPER o XLOPER12 también se establecerá en #VALUE!. En determinadas circunstancias, comprobarlo puede ser una prueba suficiente de éxito, pero tenga en cuenta que una llamada puede devolver tanto xlretSuccess como #VALUE!.
Si una llamada a la API C da como resultado xlretUncalced o xlretAbort, el código de DLL o XLL debe devolver control a Excel antes de realizar cualquier otra llamada a la API C (además de las llamadas a la función xlfree para liberar recursos de memoria asignados a Excel en los valores XLOPER y XLOPER12).
Argumento de enumeración de funciones o comandos: xlfn
El argumento xlfn es el primer argumento de las funciones de devolución de llamada y es un entero con signo de 32 bits. El valor debe ser una de las enumeraciones de comandos o funciones definidas en el archivo de encabezado del SDK Xlcall.h, tal como se muestra en el siguiente ejemplo.
// Excel function numbers.
#define xlfCount 0
#define xlfIsna 2
#define xlfIserror 3
#define xlfSum 4
#define xlfAverage 5
#define xlfMin 6
#define xlfMax 7
#define xlfRow 8
#define xlfColumn 9
#define xlfNa 10
...
// Excel command numbers.
#define xlcBeep (0 | xlCommand)
#define xlcOpen (1 | xlCommand)
#define xlcOpenLinks (2 | xlCommand)
#define xlcCloseAll (3 | xlCommand)
#define xlcSave (4 | xlCommand)
#define xlcSaveAs (5 | xlCommand)
#define xlcFileDelete (6 | xlCommand)
#define xlcPageSetup (7 | xlCommand)
#define xlcPrint (8 | xlCommand)
#define xlcPrinterSetup (9 | xlCommand)
...
Todas las funciones de la hoja de macros y la hoja de cálculo están comprendidas entre 0 (xlfCount) y 0x0fff hexadecimal, aunque el mayor número asignado en Excel 2013 es 547 decimal, 0x0223 hexadecimal (xlfFloor_precise).
Todas las funciones de comandos están comprendidas entre 0x8000 hexadecimal (xlcBeep) y 0x8fff hexadecimal, aunque el mayor número asignado en Excel 2013 es 0x8328 hexadecimal (xlcHideallInkannots). Se definen en el archivo de encabezado como (n | xlCommand)
, donde n
es un número decimal mayor o igual que 0 y xlCommand se define como 0x8000 hexadecimal.
Invocar comandos de Excel que usan cuadros de diálogo
Algunos de los códigos de comando corresponden a las acciones en Excel que usan cuadros de diálogo. Por ejemplo, xlcFileDelete toma un solo argumento: un nombre de archivo o una máscara. Esto se puede invocar con el cuadro de diálogo para que el usuario tenga la oportunidad de cancelar o modificar la operación de eliminación. También se puede llamar sin el cuadro de diálogo, en cuyo caso el archivo o los archivos se eliminan sin mayor interacción, suponiendo que existen y que al autor de llamada tiene permiso. Para llamar a estos comandos en su forma de cuadro de diálogo, la enumeración de comandos debe combinarse mediante la operación bit a bit OR con 0x1000 (xlPrompt).
En el ejemplo de código siguiente se eliminan los archivos del directorio actual que coinciden con la máscara my_data*.bak, mostrando un cuadro de diálogo solo si el argumento es true.
bool delete_my_backup_files(bool show_dialog)
{
XLOPER12 xResult, xFilter;
xFilter.xltype = xltypeStr;
xFilter.val.str = L"\014my_data*.bak"; // String length: 14 octal
int cmd;
if(show_dialog)
cmd = xlcFileDelete | xlPrompt;
else
cmd = xlcFileDelete;
// xResult should be Boolean TRUE if successful, in which
// case return true; otherwise, false.
return (Excel12(cmd, &xResult, 1, &xFilter) == xlretSuccess
&& xResult.xltype == xltypeBool
&& xResult.val.xbool == 1);
}
Llamar a funciones y comandos en versiones internacionales
Puede configurar Excel para mostrar los nombres de comandos XLM y funciones en varios idiomas. Algunas funciones y comandos de la API de C operan en cadenas que se interpretan como nombres de comandos o funciones. Por ejemplo, xlcFormula acepta un argumento de cadena que está pensado para colocarse en una celda especificada. Para que el complemento funcione con todas las opciones de idioma, puede proporcionar los nombres de la cadena en inglés y establecer el bit 0x2000 (xlIntl) en la enumeración de comandos o funciones.
En el siguiente ejemplo de código se coloca el equivalente a =SUM(X1:X100)
en la celda A2 de la hoja activa. Tenga en cuenta que se usa la función Framework, TempActiveRef, para crear una referencia externa temporal XLOPER. La fórmula aparecerá en A2 en el idioma correcto determinado por la configuración regional (por ejemplo, =SOMME(X1:X100)
si el idioma es francés).
int WINAPI InternationlExample(void)
{
XLOPER12 xSum, xResult;
xSum.xltype = xltypeStr;
xSum.val.str = L"\015=SUM(X1:X100)";
Excel12(xlcFormula | xlIntl, &xResult, 2,
&xSum, TempActiveRef(2,2,1,1));
return 1;
}
Nota:
Como el resultado de la llamada a Excel12 no es necesario, podría pasarse cero (NULL) como segundo argumento en lugar de la dirección de xResult. Esto se describe con más detalle en la siguiente sección.
Comandos y funciones solo de DLL
Excel admite un número reducido de funciones que solo son accesibles desde un DLL o XLL. Se definen en el archivo de encabezado como (n | xlSpecial)
, donde n
es un número decimal mayor o igual que 0 y xlSpecial
se define como 0x4000 hexadecimal. Estas funciones se enumeran en la siguiente tabla y se documentan en la Referencia de la función de API.
Función | n | xlSpecial | Descripción |
---|---|---|---|
xlFree | 0 | xlSpecial | Libera los recursos de memoria asignados por Excel. |
xlStack | 1 | xlSpecial | Devuelve el espacio disponible en la pila de Excel. |
xlCoerce | 2 | xlSpecial | Convierte entre tipos de XLOPER y XLOPER12 |
xlSet | 3 | xlSpecial | Proporciona un método rápido para establecer los valores de celda. |
xlSheetId | 4 | xlSpecial | Obtiene un nombre de hoja de cálculo a partir de su identificador interno. |
xlSheetNm | 5 | xlSpecial | Obtiene un identificador interno de hoja de cálculo a partir de su nombre. |
xlAbort | 6 | xlSpecial | Comprueba si el usuario hizo clic en el botón CANCELAR o presionó la tecla ESC. |
xlGetInst | 7 | xlSpecial | Obtiene el identificador de instancia de Excel. |
xlGetHwnd | 8 | xlSpecial | Obtiene el identificador de ventana principal de Excel. |
xlGetName | 9 | xlSpecial | Obtiene la ruta de acceso y el nombre de archivo del DLL. |
xlEnableXLMsgs | 10 | xlSpecial | Esta función está en desuso y ya no es necesario llamarla. |
xlDisableXLMsgs | 11 | xlSpecial | Esta función está en desuso y ya no es necesario llamarla. |
xlDefineBinaryName | 12 | xlSpecial | Define un nombre de almacenamiento binario permanente. |
xlGetBinaryName | 13 | xlSpecial | Obtiene datos de un nombre de almacenamiento binario permanente. |
Devuelve el valor XLOPER o XLOPER12: operRes
El argumento operRes es el segundo argumento para las devoluciones de llamadas y es un puntero a un XLOPER (Excel4 y Excel4v) o XLOPER12 (Excel12 y Excel12v). Después de una llamada correcta, contiene el valor devuelto de la función o el comando. operRes puede establecerse en cero (puntero nulo) si no se requiere ningún valor devuelto. El contenido anterior de operRes se sobrescribe para poder liberar antes toda la memoria a la que se apuntó previamente a fin de evitar pérdidas de memoria en la llamada.
Si no es posible llamar a la función o el comando (por ejemplo, si los argumentos son incorrectos), operRes se establece en el error #VALUE!. Un comando siempre devuelve true booleano si es correcto, o FALSE si se produjo un error o el usuario lo canceló.
Número de argumentos siguientes: count
El argumento count es el tercer argumento de las devoluciones de llamada y es un entero con signo de 32 bits. Se debe establecer en el número de argumentos siguientes, a partir de 1. Si un comando o una función no recibe argumentos, debe establecerse en cero. En Microsoft Office Excel 2003, el número máximo de argumentos que puede recibir cualquier función es 30, aunque la mayoría reciben menos. A partir de Excel 2007, se aumenta el número máximo de argumentos que puede recibir cualquier función a 255.
Con Excel4 y Excel12, count es el número de punteros a los valores XLOPER o XLOPER12 que se pasan. Debe tener cuidado de no pasar menos argumentos que el valor en el que se establece count. Esto provocaría que Excel siga leyendo en la pila e intente procesar valores XLOPER o XLOPER12 no válidos, lo que podría bloquear la aplicación.
Con Excel4v y Excel12v, count es el tamaño de la matriz de punteros a los valores XLOPER o XLOPER12 que se pasan como el argumento siguiente y final. De nuevo, debe tener mucho cuidado de no pasar una matriz menor que count elementos en tamaño, ya que esto provocará la saturación de los límites de la matriz.
Pasar argumentos a las funciones de la API de C
Tanto Excel4 como Excel12 toman listas de argumentos de longitud variable, después de count, que se interpretan como punteros a los valores XLOPER y XLOPER12, respectivamente. Excel4v y Excel12v reciben un solo argumento, después de count, que es un puntero a una matriz de punteros a los valores XLOPER en el caso de Excel4v y a los valores XLOPER12 en el caso de Excel12v.
Los formularios de matriz, Excel4v y Excel12v, le permiten programar una llamada a la API de C de forma nítida cuando el número de argumentos es variable. El siguiente ejemplo muestra una función que toma una matriz de números de tamaño variable y usa las funciones de hoja de cálculo de Excel, mediante la API de C, para calcular la suma, el promedio, el mínimo y el máximo.
void Excel12v_example(double *dbl_array, int size, double &sum, double &average, double &min, double &max)
{
// 30 is the limit in Excel 2003. 255 is the limit in Excel 2007.
// Use the lower limit to be safe, although it is better to make
// the function version-aware and use the correct limit.
if(size < 1 || size > 30)
return;
// Create an array of XLOPER12 values.
XLOPER12 *xOpArray = (XLOPER12 *)malloc(size * sizeof(XLOPER12));
// Create an array of pointers to XLOPER12 values.
LPXLOPER12 *xPtrArray =
(LPXLOPER12 *)malloc(size * sizeof(LPXLOPER12));
// Initialize and populate the array of XLOPER12 values
// and set up the pointers in the pointer array.
for(int i = 0; i < size; i++)
{
xOpArray[i].xltype = xltypeNum;
xOpArray[i].val.num = dbl_array[i];
xPtrArray[i] = xOpArray + i;
}
XLOPER12 xResult;
int retval;
int fn[4] = {xlfSum, xlfAverage, xlfMin, xlfMax};
double *result_ptr[4] = {&sum, &average, &min, &max};
for(i = 0; i < 4; i++)
{
retval = Excel12v(fn[i], &xResult, size, xPtrArray);
if(retval == xlretSuccess && xResult.xltype == xltypeNum)
*result_ptr[i] = xResult.val.num;
}
free(xPtrArray);
free(xOpArray);
}
Reemplazar las referencias a los valores XLOPER12 con XLOPER y Excel12v con Excel4v, en el código anterior tendría como resultado una función compatible con todas las versiones de Excel. Esta operación de las funciones de Excel SUMA, PROMEDIO, MIN y MÁX es bastante sencilla por lo que sería más eficaz programarlas en C y evitar la sobrecarga de preparar los argumentos y llamar a Excel. Sin embargo, muchas de las funciones que contiene Excel son más complejas, por lo que este método es útil en algunos casos.
El tema sobre xlfRegister proporciona otro ejemplo de trabajo con Excel4v y Excel12v. Cuando se registra una función de hoja de cálculo XLL, puede proporcionar una cadena descriptiva para cada argumento que se usa en el cuadro de diálogo Pegar función. Por lo tanto, el número de argumentos totales que se proporciona a xlfRegister depende del número de argumentos que acepta la función XLL y puede variar de una función a otra.
Si siempre llama a una función de la API de C o un comando con el mismo número de argumentos, querrá evitar el paso adicional de la creación de una matriz de punteros para esos argumentos. En esos casos, es más sencillo y más claro usar Excel4 y Excel12. Por ejemplo, al registrar los comandos y las funciones XLL, debe proporcionar la ruta de acceso completa y el nombre de archivo del DLL o XLL. Puede obtener el nombre de archivo en una llamada a xlfGetName y después liberarlo con una llamada a xlFree, tal como se muestra en el ejemplo siguiente para Excel4 y Excel12.
XLOPER xDllName;
if(Excel4(xlfGetName, &xDllName, 0) == xlretSuccess)
{
// Use the name, and
// then free the memory that Excel allocated for the string.
Excel4(xlFree, 0, 1, &xDllName);
}
XLOPER12 xDllName;
if(Excel12(xlfGetName, &xDllName, 0) == xlretSuccess)
{
// Use the name, and
// then free the memory that Excel allocated for the string.
Excel12(xlFree, 0, 1, &xDllName);
}
En la práctica, la función , Excel12v_example, podría codificarse de forma más eficaz mediante la creación de un único argumento xltypeMultiXLOPER12 y la llamada a la API de C mediante Excel12, como se muestra en el ejemplo siguiente.
void Excel12_example(double *dbl_array, int size, double &sum, double &average, double &min, double &max)
{
// In this implementation, the upper limit is the largest
// single column array (equals 2^20, or 1048576, rows in Excel 2007).
if(size < 1 || size > 1048576)
return;
// Create an array of XLOPER12 values.
XLOPER12 *xOpArray = (XLOPER12 *)malloc(size * sizeof(XLOPER12));
// Create and initialize an xltypeMulti array
// that represents a one-column array.
XLOPER12 xOpMulti;
xOpMulti.xltype = xltypeMulti;
xOpMulti.val.array.lparray = xOpArray;
xOpMulti.val.array.columns = 1;
xOpMulti.val.array.rows = size;
// Initialize and populate the array of XLOPER12 values.
for(int i = 0; i < size; i++)
{
xOpArray[i].xltype = xltypeNum;
xOpArray[i].val.num = dbl_array[i];
}
XLOPER12 xResult;
int fn[4] = {xlfSum, xlfAverage, xlfMin, xlfMax};
double *result_ptr[4] = {&sum, &average, &min, &max};
for(i = 0; i < 4; i++)
{
Excel12(fn[i], &xResult, 1, &xOpMulti);
if(xResult.xltype == xltypeNum)
*result_ptr[i] = xResult.val.num;
}
free(xOpArray);
}
Nota:
En este caso, el valor devuelto de Excel12 se pasa por alto. En su lugar, el código comprueba que el valor XLOPER12 devuelto sea xltypeNum para determinar si la llamada se realizó correctamente.
XLCallVer
Además de las devoluciones de llamada a Excel4, Excel4v, Excel12 y Excel12v, Excel exporta una función XLCallVer, que devuelve la versión de la API de C actualmente en ejecución.
El prototipo de la función es el siguiente:
int pascal XLCallVer(void);
Puede llamar a esta función, que es segura para subprocesos, desde cualquier función o comando XLL.
En Excel 97 a Excel 2003, XLCallVer devuelve 1280 = 0x0500 hex = 5 x 256, que indica la versión 5 de Excel. A partir de Excel 2007, devuelve 3072 = 0x0c00 hex = 12 x 256, que asimismo indica la versión 12.
Aunque puede usar esto para determinar si la nueva API de C está disponible en tiempo de ejecución, tal vez prefiera detectar la versión en ejecución de Excel mediante Excel4(xlfGetWorkspace, &version, 1, &arg)
, donde arg
es un valor XLOPER numérico que se establece en 2. La función devuelve una cadena XLOPER, versión, que después puede convertirse en un entero. Las razones para depender de la versión de Excel en lugar de la versión de la API de C es que hay diferencias entre Excel 2000, Excel 2002 y Excel 2003 que tal vez el complemento necesite detectar también. Por ejemplo, los cambios realizados en la precisión de algunas de las funciones estadísticas.