http://www.microsoft.com/downloads/details.aspx?displaylang=es&FamilyID=7554f536-8c28-4598-9b72-ef94e038c891

Usar ODBC para acceder a Excel Con VBScript

Introduccion

El uso de VBA en Excel nos permite realizar muchas tareas automatizadas, tanto de formatos como de datos en sí. La forma "tradicional" de trabajar datos es recorrer las líneas, trabajar las celdas como rangos, etc. Esto es muy útil, pero es trabajoso y lento. Si miramos en las herramientas administrativas los orígenes de datos ODBC, observamos que existe un driver ODBC para Excel ¿Nos permitirá Excel usar SQL para realizar tareas y, por tanto tener una mayor potencia a la hora de realizar búsquedas, modificaciones e inserciones? La respuesta es ¡¡Sí!! No solo eso, nos permite algo que es muy interesante, y es el poder trabajar con libros de Excel en equipos ¡que no tienen Excel instalado! Esto último es interesantísimo en servidores, para poder, por ejemplo, en una tarea programada que lance un script VBScript, que se lean datos o se guarden datos en un libro de Excel, lo que abre posibilidades maravillosas.

Imaginemos que tenemos un script VBScript que ponemos como tarea programada para que comprima y envíe ficheros por correo a determinados destinatarios, que pueda incluir varios destinatarios, tanto en el campo Para, CC como en el CCO y que, además, cada correo pueda incluir más de un archivo comprimido y cada archivo comprimido pueda incluir más de un fichero. Si queremos que el script no lleve las tareas en sí dentro del código (mala cosa a la hora de reutilizarle con distintos datos, pues supone retocar el código), si no que lea lo que tiene que hacer de un fichero, el fichero resultante sería muy complejo, con arrays dentro de arrays y diferentes separadores para cada uno de ellos, siendo la tarea de establecer y recuperar la información muy dificultosa. Sin embargo, en una estructura de bases de datos, sería muy fácil leer esto, pues tendríamos una tabla de tareas, otra con los archivos comprimidos con una clave externa a la de tareas, otra de ficheros con una clave externa a la de archivos, y otra de destinatarios y tipo de destinatario (Para, CC o CCO) con una clave externa a la tabla de tareas. Bastaría realizar las consultas pertinentes, con sus JOINS si fueran necesarios, para recuperar la información de una forma muy sencilla. Este es un ejemplo en el que una base de datos con Excel nos puede ayudar (sería mejor con Access y se podría tambien usar desde VBScript, pero tiene el inconveniente de que para preparar los datos se necesita Access, y eso sale más caro que Excel, al venir este último en los paquetes más básicos de Office).

En este artículo, veremos cómo se trabaja con un libro Excel desde VBScript usando ADODB.

Como Abrir Una Conexión ADO A Un Libro Excel

Para conectarse con ADO a un libro Excel se utiliza un objeto ADODB.Connection. La conexión puede ser realizada por medio de dos proveedores:
  1. Proveedor OLE DB de Microsoft Jet.
  2. Proveedor OLE DB de Microsoft para controladores ODBC.

Consideraciones De Los Proveedores

Existen diferencias entre ambos proveedores, que se deben tener en cuenta a la hora de elegir uno u otro para establece la conexión:

Como se puede ver en lo expuesto anteriormente, es mejor usar el proveedor Jet que el ODBC, siendo preferible reservar el uso de éste cuando se tiene que acceder por fuerza vía ODBC con un DSN ya creado.

Tipos De Datos De Las Columnas

Respecto a los tipos de datos de las columnas, como hemos visto antes, ADO lo decide en función del contenido de éstas. Esta decisión se hace en función de cuál es el tipo que más ocurrencias tiene, si el numérico o el de cadena, sustituyendo los valores restantes por nulos. En caso de empate, decide que es numérico, dejando como nulos las cadenas que se presenten. Por ello, cuando es necesario que los datos sean mixtos, debemos almacenarlos como texto, de esa manera no perderemos las cadenas o los números.

Conexión Con El Proveedor OLE DB De Microsoft Jet

La información que se pasa al proveedor para la conexión es la ruta + nombre del libro y la versión. Como versión de proveedor se debe usar la 4.0, pues la 3.51 no admite controladores Jet ISAM. Como versión de Excel se pasa:

  1. Excel 5.0: para Excel 95
  2. Excel 8.0: para Excel 2000/XP/2003/2007
  3. Excel 12.0: para Excel 2007

Veamos dos ejemplos de conexión con el proveedor OLE DB de Microsoft Jet. Los ejemplos consisten en dos funciones que reciben la ruta más nombre de un libro de Excel y devuelven un objeto ADODB.Connection conectado al libro recibido como parámetro. La diferencia entre ambas estriba en la manera en la que se especifica el proveedor OLE DB de Microsoft Jet, asignándolo a la propiedad Provider del objeto ADODB.Connection o estableciéndolo en la propia cadena de conexión. Este es el ejemplo estableciéndolo con la propiedad Provider:

Function f_ConectarALibroExcel(str_Libro) 'As ADODB.Connection
	
    Dim str_Conexion 'As String
    Dim ado_Conexion 'As ADODB.Connection
    
    'Establecemos la cadena de conexión
    str_Conexion = _
        "Data Source=" & str_Libro & ";" & _
        "Extended Properties=Excel 8.0"
    
    'Creamos el objeto conexión
    Set ado_Conexion = CreateObject("ADODB.Connection")
    
    'Establecemos el proveedor
	ado_Conexion.Provider = "Microsoft.Jet.OLEDB.4.0"
	
	'Conectamos el objeto al libro Excel
    ado_Conexion.Open str_Conexion
    
    'La función devuelve el objeto conexión
    Set f_ConectarALibroExcel = ado_Conexion
    
    'Vaciamos el objeto
Set ado_Conexion = Nothing End Function 'f_ConectarALibroExcel

Este otro ejemplo especifica el proveedor en la propia cadena de conexión:

Function f_ConectarALibroExcel(str_Libro) 'As ADODB.Connection
	
    Dim str_Conexion 'As String
    Dim ado_Conexion 'As ADODB.Connection
    
    'Establecemos la cadena de conexión
    str_Conexion = _
        "Provider=Microsoft.Jet.OLEDB.4.0;" & _
        "Data Source=" & str_Libro & ";" & _
        "Extended Properties=Excel 8.0"
    
    'Creamos el objeto conexión
    Set ado_Conexion = CreateObject("ADODB.Connection")
    
    'Conectamos el objeto al libro Excel
    ado_Conexion.Open str_Conexion
    
    'La función devuelve el objeto conexión
    Set f_ConectarALibroExcel = ado_Conexion
    
    'Vaciamos el objeto
Set ado_Conexion = Nothing End Function 'f_ConectarALibroExcel

Cuando queremos abrir un libro de Excel 2007, es necesario que establezcamos como proveedor Microsoft.ACE.OLEDB.12.0. Por tanto, las dos funciones anteriores, para que contemplaran la posibilidad de estar abriendo un libro de Excel 2007, podrían recibir una booleana que lo especificase y quedarían así:

'Esta es la versión que lo especifica con la propiedad Provider
Function f_ConectarALibroExcel(str_Libro, bol_2007) 'As ADODB.Connection
	
    Dim str_Conexion 'As String
    Dim ado_Conexion 'As ADODB.Connection
    
    'Establecemos la cadena de conexión
    str_Conexion = _
        "Data Source=" & str_Libro & ";"
    
    'Creamos el objeto conexión
    Set ado_Conexion = CreateObject("ADODB.Connection")
    
    'Establecemos el proveedor y la versión
    If bol_2007 Then

    	 ado_Conexion.Provider = "Microsoft.ACE.OLEDB.12.0"
        str_Conexion = str_Conexion & "Extended Properties=Excel 12.0"

    Else

        ado_Conexion.Provider = "Microsoft.Jet.OLEDB.4.0"
        str_Conexion = str_Conexion & "Extended Properties=Excel 8.0"

    End If
	
	 'Conectamos el objeto al libro Excel
    ado_Conexion.Open str_Conexion
    
    'La función devuelve el objeto conexión
    Set f_ConectarALibroExcel = ado_Conexion
    
    'Vaciamos el objeto
Set ado_Conexion = Nothing End Function 'f_ConectarALibroExcel 'Esta es la versión que pasa el proveedor en la propia cadena de conexión Function f_ConectarALibroExcel(str_Libro, bol_2007) 'As ADODB.Connection Dim str_Conexion 'As String Dim ado_Conexion 'As ADODB.Connection 'Establecemos el proveedor y la versión If bol_2007 Then str_Conexion = "Provider=Microsoft.ACE.OLEDB.12.0;" & _ "Extended Properties=Excel 12.0;" Else str_Conexion = "Provider=Microsoft.Jet.OLEDB.4.0;" & _ "Extended Properties=Excel 8.0;" End If 'Establecemos la cadena de conexión str_Conexion = str_Conexion & _ "Data Source=" & str_Libro 'Creamos el objeto conexión Set ado_Conexion = CreateObject("ADODB.Connection") 'Conectamos el objeto al libro Excel ado_Conexion.Open str_Conexion 'La función devuelve el objeto conexión Set f_ConectarALibroExcel = ado_Conexion 'Vaciamos el objeto
Set ado_Conexion = Nothing End Function 'f_ConectarALibroExcel

 

Conexión Con El Proveedor OLE DB de Microsoft para controladores ODBC

Para establecer la conexión es necesario pasar el dato de Driver, siendo el de Excel ({Microsoft Excel Driver (*.xls)}) el que se especifica y la ruta más nombre del libro. Se puede especificar el proveedor, si bien no es imprescindible, pues al recibir el dato de driver el objeto ya asume que se trata del proveedor OLE DB de Microsoft para controladores ODBC. Otro dato que se debe pasar es el identificador de driver, DriverId, cuyo valor será (Accessing Data with ADO):

  1. Excel 95/97: 790
  2. Versiones posteriores: 278

Como se trata de ODBC, se puede conectar usando un DSN (Data Source Name) definido o sin usar un DSN. Al tratarse de libros Excel, y debido a que vamos orientados a abrir cualquier libro, no me pararé en las conexiones con DSN definido, sólo veremos las conexiones sin DSN. La siguiente función recibe una ruta más nombre de libro y una booleana que cuando es verdadera implica que el libro es de la versión Excel 97:

Function f_ConectarALibroExcel(str_Libro, bol_97) 'As ADODB.Connection

	Dim str_Conexion 'As String
	Dim ado_Conexion 'As ADODB.Connection
	Dim str_Version 'As String
	
	If bol_97 Then
	
		str_Version = "790"
		
	Else
	
		str_Version = "278"
		
	End If
	
	'Establecemos la cadena de conexión
	str_Conexion = _
	    "Driver={Microsoft Excel Driver (*.xls)};" & _
		"DriverId=" & str_Version & ";" & _
		"Dbq=" & str_Libro
		
    'Creamos el objeto conexión
	Set ado_Conexion = CreateObject("ADODB.Connection")
    
	'Conectamos el objeto al libro Excel
	ado_Conexion.Open str_Conexion
	
	'La función devuelve el objeto conexión
	Set f_ConectarALibroExcel = ado_Conexion
	
	'Vaciamos el objeto
	Set ado_Conexion = Nothing
	
End Function 'f_ConectarALibroExcel

Si queremos conectar a un libro Excel 2007, es necesario que establezcamos, en la cadena de conexión, como proveedor {Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)}, así pues, podemos retocar la anterior función para que reciba una booleana que especifique si es un libro de Excel 2007 o no:

Function f_ConectarALibroExcel(str_Libro, bol_97, bol_2007) 'As ADODB.Connection

	Dim str_Conexion 'As String
	Dim ado_Conexion 'As ADODB.Connection
	Dim str_Version 'As String
	
	If bol_97 Then
	
		str_Version = "790"
		
	Else
	
		str_Version = "278"
		
	End If
	
	'Establecemos la cadena de conexión
	If bol_2007 Then
	
	    str_Conexion = _
	        "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};"
			
	Else
	
	    str_Conexion = _
	        "Driver={Microsoft Excel Driver (*.xls)};"
			
	End If

    str_Conexion = str_Conexion & _
	        "DriverId=" & str_Version & ";" & _
            "Dbq=" & str_Libro
		
    'Creamos el objeto conexión
	Set ado_Conexion = CreateObject("ADODB.Connection")
    
	'Conectamos el objeto al libro Excel
	ado_Conexion.Open str_Conexion
	
	'La función devuelve el objeto conexión
	Set f_ConectarALibroExcel = ado_Conexion
	
	'Vaciamos el objeto
	Set ado_Conexion = Nothing
	
End Function 'f_ConectarALibroExcel

Conexión A Libros De Excel 2007

Cuando ser trata de conectar a libros de Excel 2007, es necesario instalar en el equipo los Componentes de conectividad de datos de Office 2007. Una vez conectados, la forma de establecer la conexión depende de si usamos OLE DB de Microsoft Jet o OLE DB de Microsoft para controladores ODBC:

Uniéndolo Todo

A continuación, una función como las tres anteriores, que nos permite elegir qué proveedor se utilizará por medio de la booleana bol_ODBC (OLE DB de Microsoft para controladores ODBC en el caso de ser verdadera y OLE DB de Microsoft Jet en el caso de ser falsa), la versión del libro Excel por medio de la booleana bol_97 (Excel 95/97 si es verdadera, resto si es falsa) y el libro con el que se conectará, por medio de la cadena str_Libro:

Function f_ConectarALibroExcel(str_Libro, _
                               bol_ODBC, _
                               bol_97) 'As ADODB.Connection

    Dim str_Conexion 'As String
    Dim ado_Conexion 'As ADODB.Connection
    Dim str_Version 'As String
	
    'Establecemos la versión según proveedor
    'Primero la versión 97 con ODBC
    If bol_ODBC _
    And bol_97 Then

        str_Version = "790"

    'Resto de versiones con ODBC
    ElseIf bol_ODBC Then

        str_Version = "278"

    'Versión 97 con Jet
    ElseIf bol_97 Then

        str_Version = "Excel 5.0"

    'Resto de versiones con Jet
    ElseIf bol_2007 Then

        str_Version = "Excel 12.0"

    Else

        str_Version = "Excel 8.0"

    End If
		
    'Creamos el objeto conexión
    Set ado_Conexion = CreateObject("ADODB.Connection")
	
    'Vamos crear la cadena de conexión en función del proveedor;
    'Primero con ODBC
    If bol_ODBC Then
	
        If bol_2007 Then

            str_Conexion = _
	            "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};" & _
		        "DriverId=" & str_Version & ";" & _
		        "Dbq=" & str_Libro

        Else

            str_Conexion = _
	            "Driver={Microsoft Excel Driver (*.xls)};" & _
		        "DriverId=" & str_Version & ";" & _
		        "Dbq=" & str_Libro

        End If

    'Ahora con Jet
    ElseIf bol_2007 Then

        'Establecemos la cadena de conexión
        str_Conexion = _
            "Provider=Microsoft.ACE.OLEDB.12.0;" & _
            "Data Source=" & str_Libro & ";" & _
            "Extended Properties=" & str_Version

    Else
	
        'Establecemos la cadena de conexión
        str_Conexion = _
            "Provider=Microsoft.Jet.OLEDB.4.0;" & _
            "Data Source=" & str_Libro & ";" & _
            "Extended Properties=" & str_Version

    End If
			
    'Conectamos el objeto al libro Excel
    ado_Conexion.Open str_Conexion
	
    'La función devuelve el objeto conexión
    Set f_ConectarALibroExcel = ado_Conexion
	
    'Vaciamos el objeto
    Set ado_Conexion = Nothing
	
End Function 'f_ConectarALibroExcel
Para llamar a esta función es tan simple como hacer así:
'Conectamos por Jet a un libro Excel 97
Set con_Pescados = f_ConectarALibroExcel("c:\Listados\Pescados.xls", False, True)

'Conectamos por ODBC a un libro de otra versión de Excel
Set con_Aves = f_ConectarALibroExcel("c:\Listados\Aves.xls", True, False)
Con eso habremos, en una sola línea, conectado al libro Excel que deseemos, con tan solo pasar su ruta y nombre, si queremos conectar por ODBC o por Jet y su versión de Excel ¿Cómodo, eh?

Los Datos

Como vimos antes, es importante que tengamos en cuenta que ADO considera la primera línea siempre como nombres de campo (siempre que no establezcamos lo contrario con HDR en el proveedor Jet; como vimos con el proveedor ODBC, la primera línea siempre será considerada de encabezados). Esto quiere decir que los datos los deberíamos tener siempre con encabezados, pues si estos no existen, la primera línea de datos la perderemos y se convertirá en los nombres de los campos (lo cual puede dar errores debido a caracteres prohibidos, etc.). También vimos que la estructura de los datos debe ser coherente, pues ADO lee las primeras ocho líneas de datos para decidir qué tipo de dato tienen los campos.

Con ADO podemos leer hojas, rangos con nombre y rangos sin nombre.

Leer Hojas

Las hojas son lo que el ADO considera tablas de tipo tabla de sistema. A la hora de hacer referencia a una hoja en una SQL, se debe añadir a su final un caracter de dolar ($) y al conjunto encerrarlo entre corchetes([]); esto es, la hoja Tunidos, debe ponerse en la SELECT como [Tunidos$]. La siguiente función nos devuelve un objeto ADODB.Recordset con los datos de la hoja recibida como parámetro. Esto lo hace con la conexión que también recibe como parametro:

Function f_RsHojaExcel(ado_Conexion, str_Hoja) 'As ADODB.Recordset

    Dim str_Consulta 'As String
    Dim rs_Consulta 'As ADODB.Recordset

    'Montamos la consulta, agregando un dolar al final del nombre
    'de la hoja y encerrando el resultado entre corchetes
    str_Consulta = _
        "SELECT " & vbCrLf & _
        "       * " & vbCrLf & _
        "FROM " & vbCrLf & _
        "       [" & str_Hoja & "$]"

    'Creamos el Recordset
    Set rs_Consulta = CreateObject("ADODB.Recordset")

    'Ejecutamos la consulta
    rs_Consulta.Open str_Consulta, ado_Conexion

    'La función devuelve el Recordset
    Set f_RsHojaExcel = rs_Consulta

    'Vaciamos el objeto
    Set rs_Consulta = Nothing

End Function 'f_RsHojaExcel
Para obtener un Recordset  con los datos de la tabla, es tan simple como esto:

Set rs_Tunidos = f_RsHojaExcel(con_Pescados, "Tunidos")
Señalar que es también posible encerrar el nombre de la hoja entre acentos graves, en lugar de corchetes; es decir, es lo mismo [Tunidos$] que `Tunidos$`

Leer Rangos Con Nombre

Los rangos con nombre son lo que el driver considera tablas de tipo tabla. Se consultan igual que las hojas, pero poniendo el nombre sin más, no es necesario encerrar entre corchetes o acentos graves, ni poner un dolar al final del nombre de la tabla; para tener esto en cuenta, podríamos cambiar la anterior función para indicarle, por medio de una booleana, si se trata de un rango con nombre o de una hoja:

Function f_RsTablaExcel(ado_Conexion, _
                        str_Tabla, _
                        bol_Hoja) 'As ADODB.Recordset

    Dim str_Consulta 'As String
    Dim str_Origen 'As String
    Dim rs_Consulta 'As ADODB.Recordset

    'Guardamos el nombre de tabla en la variable str_Origen
    str_Origen = str_Tabla

    'Si es una hoja...
    If bol_Hoja Then

        'Ponemos el dolar final al nombre y encerramos todo
        'entre corchetes
        str_Origen = "[" & str_Origen & "$]"

    End If

    'Montamos la consulta
    str_Consulta = _
        "SELECT " & vbCrLf & _
        "       * " & vbCrLf & _
        "FROM " & vbCrLf & _
        "       " & str_Origen

 'Creamos el Recordset
 Set rs_Consulta = CreateObject("ADODB.Recordset")

 'Ejecutamos la consulta
 rs_Consulta.Open str_Consulta, ado_Conexion

 'La función devuelve el Recordset
 Set f_RsTablaExcel = rs_Consulta

 'Vaciamos el objeto
 Set rs_Consulta = Nothing

End Function 'f_RsTablaExcel
Para usar esta función:

'En este caso se trata de una hoja y ponemos la booleana a True
'En la SELECT se pondrá como tabla [Tunidos$]
Set rs_Tunidos = f_RsHojaExcel(con_Pescados, "Tunidos", True)

'En este otro es un rango con nombre y la ponemos a False
'En la SELECT se pondrá como tabla Serranidos
Set rs_Serranidos = f_RsTablaExcel(con_Pescados, "Serranidos", False)

Leer Rangos Sin Nombre

También es posible leer directamente el rango que queramos sin que éste tenga nombre. Para ello, especificamos la hoja y las coordenadas de ese rango. La sintaxis es:

[nombre de hoja$coordenada 1:coordenada 2]

Por ejemplo, para referirnos al rango A1:F24 de la hoja Tunidos, lo haríamos como [Tunidos$A1:F24]. Podríamos retocar la función anterior para que nos permita especificar un rango dentro de una hoja. Será necesario que la booleana la pasemos como True:

Function f_RsTablaExcel(ado_Conexion, _
                        str_Tabla, _
						  str_Rango, _
                        bol_Hoja) 'As ADODB.Recordset

    Dim str_Consulta 'As String
    Dim str_Origen 'As String
    Dim rs_Consulta 'As ADODB.Recordset

    'Guardamos el nombre de tabla en la variable str_Origen
    str_Origen = str_Tabla

    'Si es una hoja...
    If bol_Hoja Then

        'Ponemos el corchete inicial, el dolar al 
        'final del nombre, concatenamos el rango y
        'ponemos el corchete final
        str_Origen = "[" & str_Origen & "$" & str_Rango & "]"

    End If

    'Montamos la consulta
    str_Consulta = _
        "SELECT " & vbCrLf & _
        "     * " & vbCrLf & _
        "FROM " & vbCrLf & _
        "     " & str_Origen

    'Creamos el Recordset
    Set rs_Consulta = CreateObject("ADODB.Recordset")

    'Ejecutamos la consulta
    rs_Consulta.Open str_Consulta, ado_Conexion

    'La función devuelve el Recordset
    Set f_RsTablaExcel = rs_Consulta

    'Vaciamos el objeto
    Set rs_Consulta = Nothing

End Function 'f_RsHojaExcel
Para usar este función:

'En este caso se trata de una hoja y ponemos la booleana a True
'La tabla a la que hará referencia la SELECT será [Tunidos$]
Set rs_Tunidos = f_RsHojaExcel(con_Pescados, "Tunidos", "", True)

'En este otro es un rango con nombre y la ponemos a False
'La tabla a la que hará referencia la SELECT será Serranidos
Set rs_Serranidos = f_RsTablaExcel(con_Pescados, "Serranidos", "", False)

'Por último, ésta sería la forma de obtener un rango sin nombre
'La tabla a la que hará referencia la SELECT será [Tunidos$A1:F11]
Set rs_10Tunidos = f_RsHojaExcel(con_Pescados, "Tunidos", "A1:F11", True)

Listar Las Tablas De Un Libro

Podemos listar las tablas de un libro usando el método OpenSchema del objeto ADODB.Connection que devuelve un objeto ADODB.Recordset. A este método le debemos pasar el parámetro QueryType, que es un parámetro de tipoSchemaEnum, establecido a adSchemaTables (que tiene un valor entero decimal de 20). Este objeto ADODB.Recordset nos devolverá los nombres de las tablas y rangos con nombre en nueve campos, de los cuales sólo rellena cuatro, en el caso de Jet y tres en el de ODBC. Estos son los de Jet:

No obstante, tanto date_created como date_modified muestran siempre la misma fecha, que es la de última modificación; así pues, realmente, sólo tres de los cuatro campos son fiables: table_name, table_type y date_modified.

Respecto a ODBC, los campos que usa son estos tres:

Esta es la teoría, la práctica nos demuestra otra cosa: table_type es siempre de tipo TABLE,se trate de hojas o de rangos con nombre. Así pues, la única manera que tenemos de averiguar si se trata de uno u otro tipo es si aparece el caracter de dolar ($) al final del nombre; si es así, se trata de una hoja, si no, de un rango.

El siguiente método recibe la ruta más nombre de un libro de Excel y muestra por pantalla los nombres de sus tablas y rangos con nombre:

Sub s_TablasEnLibro (str_Libro)

    Const adSchemaTables = 20

    Dim con_Libro 'As ADODB.Connection
    Dim rs_Tablas 'As ADODB.Recordset
    Dim str_Devolucion 'As String
    Dim str_Linea 'As String
    Dim bol_Hoja 'As Boolean
    Dim str_Tabla 'As String

    'Usamos la función de conexión a un libro de
    'Excel para crear el objeto Conexión
    Set con_Libro = f_ConectarAExcel (str_Libro)

    'Creamos el Recordset con la información de
    'las tablas del libro
    Set rs_Tablas = con_Libro.OpenSchema (adSchemaTables)

    'Recorremos los registros de la consulta
    While Not rs_Tablas.EOF

        'Obtenemos si se trata de un rango o una hoja
        bol_Hoja = (Right(rs_Tablas.Fields("table_name").Value, 1) = "$")
        
        'Guardamos el nombre de la tabla
        str_Tabla = rs_Tablas.Fields("table_name").Value

        'Ahora agregaremos si se trata de una hoja o de un
        'rango con nombre y quitamos el dolar final si se trata de una
        'hoja
        If bol_Hoja Then

            str_Tabla = Left(str_Tabla, Len(str_Tabla) - 1 )
            str_Linea = "Nombre de tabla: " & str_Tabla & vbCrLf & _
                        "Tipo de Tabla: Hoja"

        Else

            str_Linea = "Nombre de tabla: " & str_Tabla & vbCrLf & _
                        "Tipo de tabla: Rango con nombre"

        End If

        WScript.Echo str_Linea

    Wend

    'Cerramos el Recordset y la conexion
    rs_Tablas.Close
    con_Libro.Close

    'Limpieza de culete :-)
    Set rs_Tablas = Nothing
    Set con_Libro = Nothing

End Sub 's_TablasEnLibro

Volcar Los Datos De Las Consultas

Para volcar los datos de una consulta, podemos usar un método como el que sigue. El método recibe un objeto ADODB.Recordset y muestra por pantalla los datos que contiene. Este es un método típico a la hora de recorrer los registros de un Recordset cualquiera, da igual que sea ADO, DAO, etc.
Sub s_VolcarDatosRS (rs_Tabla)

    Dim int_Campo 'As String
    Dim str_Linea 'As String
    Dim str_Subrayado 'As String

    'Mostramos los encabezados, para ello, recorremos los
    'campos en un bucle For y almacenamos los nombres,
    'separados por tabuladores
    For int_Campo = 0 To rs_Tabla.Fields.Count - 1

        'Almacenamos el nombre
        str_Linea = str_Linea & _
                    rs_Tabla.Fields(int_Campo).Name & _
                    vbTab
                    
        str_Subrayado = str_Subrayado & _
                        String( _
                              Len(rs_Tabla.Fields(int_Campo).Name), _
                               "=") & _
                        vbTab

    Next 'int_Campo

    'Quitamos el tabulador final
    If Len(str_Linea) > 0 Then
    
        str_Linea = Left(str_Linea,Len(str_Linea) - 1)
        str_Subrayado = Left(str_Subrayado,Len(str_Subrayado) - 1)
        
    End If    

    'Mostramos los encabezados
    WScript.Echo str_Linea

    'Los subrayamos
    WScript.Echo String(Len(str_Linea), "=")

    'Vaciamos la variable con la línea
    str_Linea = ""

    'Recorremos ahora la consulta
    While Not rs_Tabla.EOF

        For int_Campo = 0 To rs_Tabla.Fields.Count - 1

            'Almacenamos el nombre
            str_Linea = str_Linea & _
                        rs_Tabla.Fields(int_Campo).Value & _
                        vbTab

        Next 'int_Campo

        'Quitamos el tabulador final
        If Len(str_Linea) > 0 Then _
            str_Linea = Left(str_Linea,Len(str_Linea) - 1)

        'Mostramos los datos
        WScript.Echo str_Linea

        'Iniciamos la variable de la línea
        str_Linea = ""
        
        'Vamos al siguiente registro
        rs_Tabla.MoveNext
        
    Wend

    'Cerramos y vaciamos el Recordset
    rs_Tabla.Close
    
Set rs_Tabla = Nothing

End Sub 's_VolcarDatosRS

Nota: El ADODB.Recordset creado cuando no se especifica el tipo de cursor es de tipo adOpenForwardOnly, que es el predeterminado. Se puede indicar el tipo de cursor de esta manera:

objetoRS.Open consulta, objeto_conexion, tipo

Siendo el tipo uno de los de la enumeración CursorTypeEnum. Por ejemplo, para abrir un cursor dinámico:

'Definimos la constante de cursor dinámico
Const adOpenDynamic = 2

'Creamos el Recordset
Set rs_Tabla = CreateObject("ADODB.Recordset")

'Abrimos el Recordset como dinámico
rs_Tabla.Open str_Consulta, ado_Conexion, adOpenDynamic

'Mostramos por pantalla el tipo de Recordset que se ha abierto
WScript.Echo "Tipo de Recordset: " & rs_Tabla.CursorType
¡Perfecto, ya podemos movernos adelante y atrás con este cursor, agregar, modificar y borrar registros! Va ser que no... La última línea del código nos mostrará el tipo de cursor abierto por pantalla, que debería haber sido 2, correspondiente a adOpenDynamic, sin embargo mostrará un 3, el correspondiente a adOpenStatic, que nos permite movernos adelante y atrás en el Recordset, pero no permite actualizaciones, inserciones ni borrados. Esto es debido a que el 

Creación De Un Libro Excel Desde ODBC

Sí, has leído bien, podemos crear un libro Excel desde VBScript usando ADO, sin necesidad de tener Excel instalado. Es, realmente, muy sencillo utilizando DDL (Data Definition Language). Digamos que la forma más "normal" de crear un libro Excel desde VBScript, usando objetos de acceso a datos es utilizar ADOX. Para que un libro sea creado con ADOX es necesario:

  1. Crear un objeto ADODB.Connection.
  2. Conectarlo a un libro Excel inexistente (ruta y nombre del libro que queremos crear).
  3. Crear un objeto ADOX.Catalog.
  4. Establecer la conexión creada como conexión activa del objeto ADOX.Catalog.
  5. Crear un objeto ADOX.Table.
  6. Crear, al menos, un objeto ADOX.Column y agregarlo a la tabla.
  7. Agregar la tabla al catálogo.
Veamos un ejemplo de creación de un libro con ADOX

'Constantes para el tipo de datos de los campos
Const adBoolean = 11
Const adCurrency = 6
Const adDate = 7
Const adDouble = 5
Const adVarWChar = 202

'Variables de objetos de datos
Dim ado_Conexion 'As ADODB.Connection
Dim ado_Catalogo 'As ADOX.Catalog
Dim ado_Tabla 'As ADOX.Table
Dim ado_Campo 'As ADOX.Column

'Creamos un objeto y lo conectamos a un libro inexistente
'de ruta más nombre igual al del libro que queremos crear
Set ado_Conexion = CreateObject("ADODB.Connection")
ado_Conexion.Open _
        "Driver={Microsoft Excel Driver (*.xls)};" & _
        "DriverId=790;" & _
        "Dbq=c:\Listados\LibroNuevo.xls"

'Creamos un objeto catálogo y establecemos
'la conexión como su conexión activa
Set ado_Catalogo = CreateObject("ADOX.Catalog")
ado_Catalogo.ActiveConnection = ado_Conexion

'Creamos el objeto tabla
Set ado_Tabla = CreateObject("ADOX.Table")

'Establecemos su nombre
ado_Tabla.Name = "MiHoja"

'Creamos el campo adBoolean
Set ado_Campo = CreateObject("ADOX.Column")
With ado_Campo
    .Name = "Booleano"
    .Type = adBoolean
End With

'Lo agregamos a la tabla
ado_Tabla.Columns.Append(ado_Campo)

'Vaciamos el objeto campo
Set ado_Campo = Nothing

'Creamos el adCurrency
Set ado_Campo = CreateObject("ADOX.Column")
With ado_Campo
    .Name = "Moneda"
    .Type = adCurrency
End With

'Lo agregamos a la tabla
ado_Tabla.Columns.Append(ado_Campo)

'Vaciamos el objeto campo
Set ado_Campo = Nothing

'Creamos el adDate
Set ado_Campo = CreateObject("ADOX.Column")
With ado_Campo
    .Name = "Fecha"
    .Type = adDate
End With

'Lo agregamos a la tabla
ado_Tabla.Columns.Append(ado_Campo)

'Vaciamos el objeto campo
Set ado_Campo = Nothing

'Creamos el adDouble
Set ado_Campo = CreateObject("ADOX.Column")
With ado_Campo
    .Name = "Numerico"
    .Type = adDouble
End With

'Lo agregamos a la tabla
ado_Tabla.Columns.Append(ado_Campo)

'Vaciamos el objeto campo
Set ado_Campo = Nothing

'Creamos el adVarWChar
Set ado_Campo = CreateObject("ADOX.Column")
With ado_Campo
    .Name = "Texto"
    .Type = adVarWChar
End With

'Lo agregamos a la tabla
ado_Tabla.Columns.Append(ado_Campo)

'Vaciamos el objeto campo
Set ado_Campo = Nothing

'Agregamos la tabla al catálogo
ado_Catalogo.Tables.Append(ado_Tabla)

'Cerramos y vaciamos objetos
Set ado_Tabla = Nothing
Set ado_Catalog = Nothing
ado_Conexion.Close
Set ado_Conexion = Nothing
Como podemos ver, es bastante trabajoso, de ahí el que yo prefiera usar la potencia de SQL, en concreto la parte DDL. Al usar DDL los pasos a dar son:
  1. Crear un objeto ADODB.Connection.
  2. Conectarlo a un libro Excel inexistente (ruta y nombre del libro que queremos crear).
  3. Ejecutar la instrucción DDL con el método Execute del objeto ADODB.Connection.
Veamos ahora la creación del mismo libro de antes usando DDL. La instrucción DDL para la creación de tablas es CREATE TABLE, cuya sintaxis es:

CREATE TABLE Tabla (campo1 tipo1[, campo2 tipo2][,...][, campoN tipoN])

Los tipos posibles para Excel desde ADO son:
  1. Booleano: bit.
  2. Fecha: datetime.
  3. Moneda: money.
  4. Numérico: numeric.
  5. Texto: varchar.
La máxima precisión es de 15 decimales, que es la máxima precisión de Excel. Aunque el máximo tamaño de contenido de una celda sea de 32 Kbytes, el driver ODBC de Excel sólo permite texto de 255 caracteres, a no ser que sea Excel 2000 o posterior y retoquemos el registro (Knowledge Base 189897); este problema no lo tiene Jet.
'Definición del objeto de conexión
Dim ado_Conexion 'As ADODB.Connection

'Variable para la instrucción DDL
Dim str_Consulta

'Creamos un objeto y lo conectamos a un libro inexistente
'de ruta más nombre igual al del libro que queremos crear
Set ado_Conexion = CreateObject("ADODB.Connection")
ado_Conexion.Open _
        "Driver={Microsoft Excel Driver (*.xls)};" & _
        "DriverId=790;" & _
        "Dbq=c:\Listados\LibroNuevo.xls"

'Definimos la instrucción DDL
str_Consulta = _
        "CREATE TABLE " & vbCrLf & _
        "           MiHoja " & vbCrLf & _
        "           (" & vbCrLf & _
        "               Booleano bit," & vbCrLf & _
        "               Moneda money," & vbCrLf & _
        "               Fecha datetime," & vbCrLf & _
        "               Numerico numeric," & vbCrLf & _
        "               Texto varchar" & vbCrLf & _
        "            )"

'Ejecutamos la instrucción DDL
ado_Conexion.Execute str_Consulta

'Cerramos la conexión y vaciamos el objeto
ado_Conexion.Close
Set ado_Conexion = Nothing

Bastante más simple que con ADOX ¿verdad? Si se tratara de crear una base de datos Access, sería preferible ADOX, pues nos permite definir muchas más propiedades de los campos, tipos de datos, precisiones de los mismos, etc., pero teniendo en cuenta las limitaciones de ADO al manejar Excel, que estamos viendo a lo largo de este artículo, es más que suficiente el uso de sentencias DDL.

Inserción De Datos En Una Hoja O Rango

Podemos insertar datos en una hoja o rango utilizando la parte DML (Data Manipulation Language) de SQL, en concreto la instrucción INSERT INTO. Su sintaxis es:

INSERT INTO Tabla [(campo1, campo2, ..., campoN)] VALUES (valor1, valor2, ..., valorN)

Como tabla tendremos que poner el nombre de la hoja o rango con nombre. Si insertamos en una hoja, lo hará en la línea inmediatamente por debajo de la última escrita, si se trata de un intervalo con nombre, lo hará en la primera línea a continuación de la última del rango, es decir, como si aumentara el rango una línea más, pero ojo, si la línea está ya escrita, se producirá el error -2147467259: No se puede expandir el intervalo con nombre. La sentencia de inserción será evaluada, esperando encontrar los tipos de datos adecuados. Cada tipo tiene sus normas:

Cosas a tener en cuenta:

Nota: La sintaxis de sentencia INSERT que he puesto más arriba no es la única posible, solo se trataba de explicar el ejemplo expuesto. Es posible insertar de golpe muchos registros basado en una consulta. Para una descripción más profunda de INSERT INTO:

INSERT INTO Statement (Microsoft Access SQL)


Veamos un ejemplo de inserción de un registro en la tabla anteriormente creada:

'Definición del objeto de conexión
Dim ado_Conexion 'As ADODB.Connection

'Variable para la instrucción INSERT
Dim str_Consulta

'Creamos un objeto y lo conectamos al libro
Set ado_Conexion = CreateObject("ADODB.Connection")
ado_Conexion.Open _
        "Driver={Microsoft Excel Driver (*.xls)};" & _
        "DriverId=790;" & _
        "Dbq=c:\Listados\LibroNuevo.xls"

'Definimos la instrucción INSERT
str_Consulta = _
        "INSERT INTO " & vbCrLf & _
        "         [MiHoja$] " & vbCrLf & _
        "           (" & vbCrLf & _
        "               Booleano," & vbCrLf & _
        "               Moneda," & vbCrLf & _
        "               Fecha," & vbCrLf & _
        "               Numerico," & vbCrLf & _
        "               Texto" & vbCrLf & _
        "            )" & vbCrLf & _
        "     VALUES " & vbCrLf
        "           (" & vbCrLf & _
        "               False," & vbCrLf & _
        "               1025.34," & vbCrLf & _
        "               #9/4#2008#," & vbCrLf & _
        "               25436.3456," & vbCrLf & _
        "               'Este es el texto'" & vbCrLf & _
        "            )"

'Ejecutamos la instrucción INSERT
ado_Conexion.Execute str_Consulta

'Cerramos la conexión y vaciamos el objeto
ado_Conexion.Close
Set ado_Conexion = Nothing

Modificación De Datos

Podemos modificar datos por medio de la instrucción UPDATE de SQL, cuya sintaxis es:

UPDATE Tabla SET campo1 = valor1[, campo2 = valor2][,...][, campoN = valorN] [WHERE condiciones]

Podemos cambiar uno o varios campos de uno o varios registros. La parte SET se encarga de establecer qué campos se cambian y qué valores se les ponen y la parte WHERE se encarga de especificar qué registros son los afectados; si no se especifica parte WHERE, todos los registros son modificados.

'Definición del objeto de conexión
Dim ado_Conexion 'As ADODB.Connection

'Variable para la instrucción UPDATE
Dim str_Consulta

'Creamos un objeto y lo conectamos al libro
Set ado_Conexion = CreateObject("ADODB.Connection")
ado_Conexion.Open _
        "Driver={Microsoft Excel Driver (*.xls)};" & _
        "DriverId=790;" & _
        "Dbq=c:\Listados\LibroNuevo.xls"

'Definimos la instrucción UPDATE
str_Consulta = _
        "UPDATE " & vbCrLf & _
        "         [MiHoja$] " & vbCrLf & _
        "SET " & vbCrLf & _
        "         Booleano = True, " & vbCrLf & _
        "         Moneda = 3452.98, " & vbCrLf & _
        "         Numerico = 32.67538," & vbCrLf & _
        "         Texto = 'Registro modificado' " & vbCrLf & _
        "WHERE " & vbCrLf
        "       Fecha = #9/6#2008#"

'Ejecutamos la instrucción UPDATE
ado_Conexion.Execute str_Consulta

'Cerramos la conexión y vaciamos el objeto
ado_Conexion.Close
Set ado_Conexion = Nothing
No se pueden modificar fórmulas (el contenido de la celda empieza por el caracter igual =), pues las fórmulas son de sólo lectura.

Borrado De Datos

Esto va ser muy rápido: no se pueden borrar registros, pues se produce el error Este ISAM no admite la eliminación de datos de tablas vinculadas. Sólo se puede borrar campo por campo, es decir, vaciar de contenido el registro, aunque, realmente, éste no desaparece, al no haberse eliminado la línea. Tampoco se pueden borrar celdas que contengan fórmulas de Excel, produciendose el error Operación no permitida en este contexto.

Ejemplos

A continuaciòn pongo dos ejemplos de uso de ADO con libros de Excel. Si los quieres probar, hazlo desde un equipo que no tenga Excel instalado, será mas chulo -(|:o)).

Ejemplo 1

Este script permite leer el contenido del libro de Excel que recibe como parámetro sin nombre requerido. El script volcará en pantalla el contenido de cada hoja. Su sintaxis es:

cscript [//nologo] leer-xls-con-ado.vbs [/S] [/?] libro

Siendo

Ejemplo: Se leerán los datos del libro "e:\datos chorizos\gang-chicharron.xls":

cscript //nologo leer-xls-con-ado.vbs "e:\datos chorizos\gang-chicharron.xls"

Este es el código del script:

'*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
'*°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°*
'* leer-xls-con-ado.vbs                                                *
'*                                                                     *
'* Este script permite leer el contenido del libro de Excel que recibe *
'* como parámetro sin nombre requerido. El script volcará el contenido *
'* de cada hoja. Para leer el libro Excel utiliza ADO, lo que permite  *
'* que se puedan leer sus datos en un equipo que no tenga Excel        *
'* instalado (por ejemplo un servidor), y sí tenga el driver ODBC para *
'* Excel.                                                              *
'*                                                                     *
'* Sintaxis                                                            *
'*                                                                     *
'* cscript [//nologo] leer-xls-con-ado.vbs [/S] [/?] libro             *
'*                                                                     *
'* Siendo                                                              *
'*                                                                     *
'* - libro (Requerido):                                                *
'*         Ruta y nombre del libro de Excel (XLS) que se ha de leer.   *
'*                                                                     *
'* - /S: encabezados (Opcional):                                       *
'*         Si se pasa este modificador, se considerará que las hojas   *
'*         no contienen encabezados, y por tanto la primera fila       *
'*         contiene datos; si no se pasa, la primera fila se           *
'*         considerará como nombre de los campos de la tabla.          *
'*                                                                     *
'* - /?: ayuda (Opcional):                                             *
'*         Muestra la ayuda en línea                                   *
'*                                                                     *
'*                                                                     *
'* Ejemplos:                                                           *
'*                                                                     *
'* - Se leerán los datos del libro "e:\datos                           *
'* chorizos\gang-chicharron.xls":                                      *
'*                                                                     *
'* cscript //nologo leer-xls-con-ado.vbs "e:\datos                     *
'* chorizos\gang-chicharron.xls"                                       *
'*                                                                     *
'*                                                                     *
'*                                                                     *
'*                                                                     *
'* © Fernando Reyes                                                    *
'* Marzo De 2008                                                       *
'*°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°*
'*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*

'Exigimos la declaración de variables
Option Explicit

Const adSchemaTables = 20

Dim str_Error 'As String
Dim int_Error 'As String
Dim str_Libro 'As String
Dim bol_SinEncabezados 'As Boolean
Dim ado_Conexion
Dim ado_Recordset
Dim int_Campo
Dim str_Linea
Dim str_Hoja
Dim str_Consulta

bol_SinEncabezados = False


'Validando los argumentos y almacenando
'sus valores
If f_RevisarArgumentos( _
                       str_Error, _
                       int_Error) Then

    Call s_Ayuda(str_Error)
    WScript.Quit int_Error

End If

Set ado_Conexion = f_ConectarAExcel(str_Libro, True)

str_Consulta = "SELECT " & vbCrLf & _
               "        * " & vbCrLf & _
               "FROM " & vbCrLf & _
               "        [Informes$]"

Set ado_Recordset = f_RecordsetExcel(ado_Conexion, "",str_Consulta)

Call s_Volcar(ado_Recordset)

ado_Recordset.Close
ado_Conexion.Close
Set ado_Recordset = Nothing
Set ado_Conexion = Nothing


Sub s_VolcarDatosTabla(str_Hoja)

    Dim ado_Recordset
    
    WScript.Echo "Datos en hoja: " & str_Hoja

    Set ado_Recordset = f_RecordsetExcel(ado_Conexion,str_Hoja,"")
    
    For int_Campo = 0 To ado_Recordset.Fields.Count - 1
    
        str_Linea = str_Linea & ado_Recordset.Fields(int_Campo).Name & vbTab
    
    Next 'int_Campo 
    
    str_Linea = Left(str_Linea,Len(str_Linea)-1)   
    WScript.Echo str_linea
    
    Do While Not ado_Recordset.EOF
    
        For int_Campo = 0 To ado_Recordset.Fields.Count - 1
        
            str_Linea = str_Linea & ado_Recordset.Fields(int_Campo).Value & vbTab
        
        Next 'int_Campo    
        str_Linea = Left(str_Linea,Len(str_Linea)-1)   
        WScript.Echo str_linea
        str_Linea = ""
        
        ado_Recordset.MoveNext
        
    Loop
    
    ado_Recordset.Close
    Set ado_Recordset = Nothing


End Sub



Function f_RevisarArgumentos( _
                             str_Error, _
                             int_Error _
                             ) 'As Boolean
'***********************************************************************
'* Procedimiento: f_RevisarArgumentos                                  *
'* Tipo         : Función                                              *
'* Devolución   : Booleana                                             *
'* Fecha y Hora : 2008-03-07 17:10:13                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función revisa los argumentos recibidos,        *
'*                recogiendo los posibles fallos por falta de          *
'*                argumentos requeridos y almacenando en las           *
'*                variables correspondientes los argumentos            *
'*                recibidos. recibe dos parámetros cuyo fin es ser de  *
'*                salida: una cadena que almacenará los errores        *
'*                detectados y un entero que almacenará el código de   *
'*                los errores detectados. Hay tres tipos de error;     *
'*                error 1 para los argumentos sin nombre requeridos y  *
'*                no encontrados, error 2 para los argumentos con      *
'*                nombre requeridos y no encontrados, por último,      *
'*                error 4 para los combos de argumentos opcionales     *
'*                (un combo de argumentos opcionales es aquel          *
'*                conjunto de argumentos opcionales que es requerido   *
'*                que se pase al menos uno de ellos y que si se pasa   *
'*                más de uno se ignorarán aquellos que estén detrás    *
'*                en la prioridad entre ellos; una característica      *
'*                clara de lo que es un combo de argumentos es cuando  *
'*                dos omás argumentos almacenan su valor en la misma   *
'*                variable). En el caso de producirse más de un tipo   *
'*                de error, el número de error será la suma de ambos   *
'*                de los errores recibidos, es decir 3, 5 o 6          *
'***********************************************************************

    Dim bol_Devolucion 'As Boolean
    Dim bol_Error1 'As Boolean
    Dim bol_Error2 'As Boolean
    Dim bol_Error4 'As Boolean

    'Iniciamos los indicadores
    bol_Devolucion = False
    bol_Error1 = False
    bol_Error2 = False
    bol_Error4 = False


    'Si hay que mostrar la ayuda, se muestra y
    'termina el script
    If WScript.Arguments.Named.Exists("?") Then

        Call s_Ayuda("******************" & vbCrLf & _
                     "*     AYUDA      *" & vbCrLf & _
                     "******************")

        WScript.Quit 0

    End If

    'Revisamos si están todos los argumentos
    'sin nombre requeridos
    If WScript.Arguments.Unnamed.Count < 1 Then

        str_Error = "Error 1, falta/n argumento/s sin " & _
                    "nombre requerido/s"
        bol_Error1 = True

    Else

    'Guardamos los argumentos en las variables
    'correspondientes
        If _
          WScript.Arguments.Unnamed.Count - 1 _
          >= 0 Then _
               str_Libro = _
                       WScript.Arguments.Unnamed(0)


    End If

    'Preparamos las variables de devolucion:
    'el entero como suma de los posibles errores 1, 2 y 4
    int_Error = Abs(bol_Error1) + _
                (2 * Abs(bol_Error2)) + _
                (4 * Abs(bol_Error4))
    'La devolucion de la función será True en caso de
    'haber alguno de los errores
    bol_Devolucion = (bol_Error1 Or bol_Error2 Or bol_Error4)

    'Hacemos la devolución de la función
    f_RevisarArgumentos = bol_Devolucion

End Function 'f_RevisarArgumentos

Sub s_Ayuda(str_Error)
'***********************************************************************
'* Procedimiento: s_Ayuda                                              *
'* Tipo         : Sub                                                  *
'* Devolución   :                                                      *
'* Fecha y Hora : 2008-03-07 17:10:14                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Este procedimiento muestra la ayuda en línea.        *
'*                Recibe un parámetro de tipo cadena que si viene      *
'*                será mostrado antes de la línea; pensado para que    *
'*                se muestre un error que se haya detectado.           *
'***********************************************************************

    'Si hay que mostrar algún texto previo a la ayuda, lo hacemos
    If Len(str_Error) > 0 Then

        WScript.Echo str_Error & vbCrLf & vbCrLf

    End If

    'A continuación, mostramos la ayuda por pantalla
    WScript.Echo "Este script permite leer el contenido del libro d" & _
                 "e Excel que recibe como"
    WScript.Echo "parámetro sin nombre requerido. El script volcará" & _
                 " el contenido de cada hoja."
    WScript.Echo "Para leer el libro Excel utiliza ADO, lo que perm" & _
                 "ite que se puedan leer sus"
    WScript.Echo "datos en un equipo que no tenga Excel instalado (" & _
                 "por ejemplo un servidor), y sí"
    WScript.Echo "tenga el driver ODBC para Excel."
    WScript.Echo ""
    WScript.Echo "Sintaxis"
    WScript.Echo ""
    WScript.Echo "cscript [//nologo] leer-xls-con-ado.vbs [/S] [/?]" & _
                 " libro"
    WScript.Echo ""
    WScript.Echo "Siendo"
    WScript.Echo ""
    WScript.Echo "- libro (Requerido):"
    WScript.Echo "Ruta y nombre del libro de Excel (XLS) que se ha " & _
                 "de leer."
    WScript.Echo ""
    WScript.Echo "- /S: encabezados (Opcional):"
    WScript.Echo "Si se pasa este modificador, se considerará que l" & _
                 "as hojas no"
    WScript.Echo "contienen encabezados, y por tanto la primera fil" & _
                 "a contiene"
    WScript.Echo "datos; si no se pasa, la primera fila se consider" & _
                 "ará como"
    WScript.Echo "nombre de los campos de la tabla."
    WScript.Echo ""
    WScript.Echo "- /?: ayuda (Opcional):"
    WScript.Echo "Muestra la ayuda en línea"
    WScript.Echo ""
    WScript.Echo ""
    WScript.Echo "Ejemplos:"
    WScript.Echo ""
    WScript.Echo "- Se leerán los datos del libro ""e:\datos choriz" & _
                 "os\gang-chicharron.xls"" y se"
    WScript.Echo "considerará que las primeras líneas contienen los" & _
                 " nombres de los campos:"
    WScript.Echo ""
    WScript.Echo "cscript //nologo leer-xls-con-ado.vbs ""e:\datos " & _
                 "chorizos\gang-chicharron.xls"""
    WScript.Echo ""
    WScript.Echo "- Se leeran los datos  del libro ""e:\datos chori" & _
                 "zos\gang-chicharron.xls"" y se"
    WScript.Echo "considerará que las primeras líneas contienen dat" & _
                 "os, no los nombres de los"
    WScript.Echo "campos:"
    WScript.Echo ""
    WScript.Echo "cscript //nologo leer-xls-con-ado.vbs /S ""e:\dat" & _
                 "os chorizos\gang-chicharron.xls"""
    WScript.Echo ""
    WScript.Echo ""
    WScript.Echo ""

End Sub 's_Ayuda

Function f_ConectarAExcel(str_XLS, bol_Jet)
'***********************************************************************
'* Procedimiento: f_ConectarAExcel                                     *
'* Tipo         : Función                                              *
'* Devolución   : Objeto ADODB.Connection                              *
'* Fecha y Hora : 2008-02-29 17:37:13                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función recibe la ruta más nombre de libro de   *
'*                Excel y devuelve un objeto Conexión de ADODB         *
'*                conectado a dicho libro de Excel                     *
'***********************************************************************

    Dim str_Conexion 'As String
    Dim ado_Conexion 'As ADODB.Connection
    
    'Establecemos la cadena de conexión con el proveedor
    'OLE DB de Microsoft Jet si bol_Jet es verdadero
    If bol_Jet Then
    
        str_Conexion = "Provider=Microsoft.Jet.OLEDB.4.0;" & _
                       "Data Source=" & str_XLS & "; " & _
                       "Extended Properties=Excel 8.0"
    
    'Como bol_Jet es falso, establecemos la cadena de conexión
    'con el proveedor OLE DB de Microsoft para controladores
    'ODBC                 
    Else
    
        str_Conexion = "Driver={Microsoft Excel Driver (*.xls)};" & _
                       "DriverId=790;" & _
                       "Dbq=" & str_XLS & ";"
                       
    End If
                    
    Set ado_Conexion = CreateObject("ADODB.Connection")
    ado_Conexion.Open str_Conexion

    Set f_ConectarAExcel = ado_Conexion
    WScript.Echo "Funcion de conexión conectada a " & f_ConectarAExcel.ConnectionString
    
'    ado_Conexion.Close
    Set ado_Conexion = Nothing

End Function 'f_ConectarAExcel

Function f_RecordsetExcel(ado_Conexion, str_Hoja, str_Consulta)
'***********************************************************************
'* Procedimiento: f_RecordsetExcel                                     *
'* Tipo         : Función                                              *
'* Devolución   : ADODB.Recordset                                      *
'* Fecha y Hora : 2008-02-29 17:47:10                                  *
'* Autor        : Fernando Reyes                                       *
'*¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*
'* Propósito    : Esta función recibe una conexión Excel y un nombre   *
'*                de hoja Excel y devuelve un ADODB.Recordset con los  *
'*                datos de la tabla contenida en la hoja cuyo nombre   *
'*                es el recibido, hoja del libro al cual está          *
'*                conectado la conexión ADODB.Connection. Si se pasa   *
'*                vacío el parámetro hoja, se creará la consulta que   *
'*                se reciba con el parámetro str_Consulta              *
'***********************************************************************

    Dim ado_Recordset 'As ADODB.Recordset

    If Len(Trim(str_Hoja)) > 0 Then
    
        str_Consulta = "SELECT " & _
                       "        * " & _
                       "FROM " & _
                       "       " & str_Hoja
    
    End If                    

    Set ado_Recordset = CreateObject("ADODB.Recordset")
    
'    WScript.Echo str_Consulta
    
    ado_Recordset.Open str_Consulta, ado_Conexion
    
    Set f_RecordsetExcel = ado_Recordset
    
'    ado_Recordset.Close
    Set ado_Recordset = Nothing

End Function 'f_RecordsetExcel

Sub s_Volcar(rs_Consulta)

    Dim int_Campo 'As Integer
    Dim str_Linea 'As String
    Dim arr_Adjuntos 'As String
    Dim str_Adjunto 'As String
    
    For int_Campo = 0 To rs_Consulta.Fields.Count - 1
    
        str_Linea = str_Linea & _
                    rs_Consulta.Fields(int_Campo).Name & _
                    vbTab
    
    Next 'int_Campo
    
    str_Linea = str_Linea & _
                "Para" & vbTab & _
                "CC" & vbTab & _
                "CCO" & vbTab
    
    str_Linea = Left(str_Linea,Len(str_Linea) - 1)
    
    WScript.Echo str_Linea
    
    str_Linea = ""
    
    While Not rs_Consulta.EOF
    
        For int_Campo = 0 To rs_Consulta.Fields.Count - 1
        
            str_Linea = str_Linea & _
                        rs_Consulta.Fields(int_Campo).Value & _
                        vbTab
        
        Next 'int_Campo

        str_Linea = str_Linea & _
                f_Cuentas(rs_Consulta.Fields("id_informe").Value,"Para") & vbTab & _
                f_Cuentas(rs_Consulta.Fields("id_informe").Value,"CC") & vbTab & _
                f_Cuentas(rs_Consulta.Fields("id_informe").Value,"CCO") & vbTab 

        arr_Adjuntos = f_Comprimir(rs_Consulta.Fields("id_informe").Value)
        
        For Each str_Adjunto In arr_Adjuntos
        
            MsgBox str_Adjunto
        
        Next
        
        str_Linea = Left(str_Linea,Len(str_Linea) - 1)
        
        WScript.Echo str_Linea
        
        str_Linea = ""
        
        rs_Consulta.MoveNext
    
    Wend

End Sub

Function f_Cuentas(id_Informe,str_Tipo) 'As String

    Dim str_Consulta 'As String
    Dim rs_Consulta 'As ado_Recordset
    Dim str_Devolucion 'As String
    
    str_Consulta = "SELECT " & vbCrLf & _
                   "        destinatario, " & vbCrLf & _
                   "        email " & vbCrLf & _
                   "FROM " & vbCrLf & _
                   "        [" & str_Tipo & "$] " & vbCrLf & _
                   "WHERE " & vbCrLf & _
                   "        id_informe = " & id_Informe
                   
    Set rs_Consulta = f_RecordsetExcel(ado_Conexion, "" ,str_Consulta)
    
    If Not rs_Consulta.EOF Then
    
        While Not rs_Consulta.EOF
        
            str_Devolucion = str_Devolucion & _
                             rs_Consulta.Fields("Destinatario").Value & _
                             "<" & rs_Consulta.Fields("email").Value & ">;"
                             
            rs_Consulta.MoveNext
        
        Wend
        
        str_Devolucion = Left(str_Devolucion,Len(str_Devolucion) - 1)
        
    End If
    
    rs_Consulta.Close
    Set rs_Consulta = Nothing
    
    f_Cuentas = str_Devolucion

End Function

Function f_Comprimir(id_Informe)

    Dim str_Consulta 'As String
    Dim rs_Consulta 'As ADODB.Recordset
    Dim str_Comando 'As String
    Dim str_Adjuntos 'As String
    
    str_Consulta = "SELECT " & vbCrLf & _
                   "       nombre," & vbCrLf & _
                   "       archivo " & vbCrLf & _
                   "FROM " & vbCrLf & _
                   "       [Ficheros$] " & vbCrLf & _
                   "WHERE " & vbCrLf & _
                   "       id_informe = " & id_Informe
                   
    Set rs_Consulta = f_RecordsetExcel(ado_Conexion,"",str_Consulta)
    
    If Not rs_Consulta.EOF Then
    
        While Not rs_Consulta.EOF
        
            str_Comando = "7za a " & rs_Consulta.Fields("archivo").Value & _
                          " " & rs_Consulta.Fields("nombre").Value
                          
            MsgBox str_Comando
            
            rs_Consulta.MoveNext
        
        Wend
    
    End If
                   
    rs_Consulta.Close
    
    str_Consulta = "SELECT " & vbCrLf & _
                   "       DISTINCT archivo " & vbCrLf & _
                   "FROM " & vbCrLf & _
                   "       [Ficheros$] " & vbCrLf & _
                   "WHERE " & vbCrLf & _
                   "       id_informe = " & id_Informe
    
    Set rs_Consulta = f_RecordsetExcel(ado_Conexion,"",str_Consulta)
    
    If Not rs_Consulta.EOF Then
    
        While Not rs_Consulta.EOF
        
            str_Adjuntos = str_Adjuntos & _
                           rs_Consulta.Fields("archivo").Value & _
                           vbTab
                                      
            rs_Consulta.MoveNext
        
        Wend
        
        str_Adjuntos = Left(str_Adjuntos, Len(str_Adjuntos) - 1)
    
    End If
    
    rs_Consulta.Close
    
    Set rs_Consulta = Nothing               
    
    f_Comprimir = Split(str_Adjuntos, vbTab)

End Function

Ejemplo 2

Por último, presento un ejemplo de uso de un libro de Excel desde VBScript sin necesidad de tener instalada la aplicación en el equipo. En concreto desarrollé esto para enviar determinados informes a determinadas personas de forma programada. Basta con cambiar los registros del libro Excel para que los envíos cambien de destinatario, asunto, adjuntos, etc.

En el ejemplo hay dos ficheros:

La estructura del libro consta de las siguientes hojas y campos en ellas:

En el libro he dejado datos de ejemplo que implican que se envíen tres informes: