Uso de un componente activex de VB para la automatización de Word desde Internet Explorer

Resumen

En este artículo se muestra cómo puede usar un componente ActiveX para la automatización de Word del lado cliente desde una página web que se representa en Internet Explorer. El uso de un componente ActiveX desde una página web en lugar de un script insertado en la propia página web tiene varias ventajas:

  • Si ya tiene código de Visual Basic que automatiza Microsoft Word, puede reutilizar el código en el explorador mediante la conversión del proyecto de Visual Basic a un archivo EXE activex o a un archivo DLL de ActiveX.
  • Word no está marcado como seguro para scripting. En función de la configuración de seguridad de Internet Explorer, es posible que el código de Word Automation en el script no se ejecute o que se le pida al usuario una advertencia de seguridad. Suponiendo que el componente ActiveX cumple ciertas directrices, es posible que se marque como seguro para el scripting para evitar estos problemas de seguridad.
  • Visual Basic tiene varias características que no se pueden usar con script en una página web. Por ejemplo, una característica que está disponible para Visual Basic pero no para el script de página web es la capacidad de llamar a la interfaz de programación de aplicaciones (API) de Windows.

Un escenario común para desarrolladores consiste en presentar a los usuarios una interfaz de página web para crear un documento de Word mediante datos de algún origen o lógica externos. Aunque puede usar La automatización de Word del lado servidor para generar el documento y transmitirlo al cliente, hay muchos inconvenientes en el uso de un enfoque del lado servidor que implica Word Automation. El principal inconveniente es la escalabilidad; Word es un servidor de Automatización que consume muchos recursos y no se recomienda para la generación de documentos en el servidor web.

Mediante el uso de un componente ActiveX para realizar la generación de documentos en el cliente, puede alejar la automatización de Word que consume muchos recursos del servidor web. Esta es la solución que presenta el componente ActiveX de ejemplo que se describe en este artículo. Aunque el ejemplo es específico de Word Automation, se pueden aplicar los mismos principios para automatizar otras aplicaciones de Microsoft Office, como Microsoft Excel.

Más información

Componente ActiveX de Visual Basic

El componente ActiveX de Visual Basic de este ejemplo interactúa con el script de página web para generar un documento de factura de pedido a petición del usuario. La aplicación web puede permitir que el componente ActiveX obtenga la información de pedido de un identificador de pedido determinado, o la aplicación web puede optar por empaquetar la información del pedido como XML y enviarla al componente ActiveX para su procesamiento. En cualquier caso, después de que el componente obtenga la información del pedido, puede automatizar Word para compilar y mostrar el documento de factura para el pedido.

El componente ActiveX (AutomateWord) contiene una única clase, la clase Invoice, que expone tres métodos:

  • El método GetData usa Objetos de datos ActiveX (ADO) para extraer información sobre un orden en la base de datos de ejemplo Northwind. La información del pedido se almacena en la variable de miembro privado m_Data. Se puede llamar al método GetData para permitir que la extracción de datos se produzca en el lado cliente.
  • El método SendData usa Microsoft XML (MSXML) para rellenar la variable de miembro privado m_Data con la información de pedido proporcionada por el autor de la llamada. SendData espera un parámetro que representa un objeto DOMDocument para la información del pedido. Se puede llamar al método SendData para enviar la información del pedido desde la página web al componente. Con este enfoque, puede usar ASP para extraer el servidor de datos y presentar al cliente una isla de datos XML que se pueda usar para la generación de documentos.
  • El método MakeInvoice usa Word Automation para compilar un documento que contiene la información del orden en la variable de miembro privado m_Data. Un documento almacenado en el servidor web se usa como punto de partida para la factura. El autor de la llamada puede elegir mostrar el documento de Word completado fuera del explorador o guardar el documento completado en un disco para su uso posterior.

Invoice.cls

Option Explicit

Private Type InvoiceData
    OrderID As String
    OrderDate As Date
    CustID As String
    CustInfo As String
    ProdInfo As Variant
End Type

Private m_Data As InvoiceData

Public Sub GetData(sOrderID As Variant, sConn As Variant)

Dim oConn As Object, oRS As Object

'Connect to the Northwind database.
    Set oConn = CreateObject("ADODB.Connection")
    oConn.Open sConn

'Obtain the Customer ID and Order Date.
    Set oRS = CreateObject("ADODB.Recordset")
    oRS.Open "Select [OrderDate], [CustomerID] from Orders where " & _
             "[OrderID]=" & sOrderID, oConn, 3 'adOpenStatic=3
    m_Data.OrderID = sOrderID
    m_Data.OrderDate = CDate(oRS.Fields("OrderDate").Value)
    m_Data.CustID = oRS.Fields("CustomerID").Value
    oRS.Close

'Obtain Customer information.
    Set oRS = CreateObject("ADODB.Recordset")
    oRS.Open "Select * from Customers Where CustomerID='" & _
             m_Data.CustID & "'", oConn, 3 'adOpenStatic=3
    m_Data.CustInfo = oRS.Fields("CompanyName").Value & vbCrLf & _
                      oRS.Fields("City") & " "
    If Not (IsNull(oRS.Fields("Region"))) Then
       m_Data.CustInfo = m_Data.CustInfo & oRS.Fields("Region").Value & " "
    End If
    m_Data.CustInfo = m_Data.CustInfo & oRS.Fields("PostalCode").Value & _
                      vbCrLf & oRS.Fields("Country").Value
    oRS.Close

'Obtain Product information.
    Set oRS = CreateObject("ADODB.Recordset")
    oRS.Open "Select ProductName, Quantity, [Order Details].UnitPrice," & _
             "Discount from Products Inner Join [Order Details] on " & _
             "Products.ProductID = [Order Details].ProductID " & _
             "Where OrderID = " & sOrderID, oConn, 3 'adOpenStatic=3
    m_Data.ProdInfo = oRS.GetRows
    oRS.Close

'Close the connection to the database.
    oConn.Close

End Sub

Public Sub SendData(oXML As Variant)

'Extract the information from the DOMDocument object oXML and store
    'it in the private member variable m_Data.

m_Data.OrderID = oXML.getElementsByTagName("OrderID").Item(0).Text
    m_Data.OrderDate = oXML.getElementsByTagName("OrderDate").Item(0).Text
    m_Data.CustID = oXML.getElementsByTagName("CustID").Item(0).Text
    m_Data.CustInfo = oXML.getElementsByTagName("CustInfo").Item(0).Text

Dim oItems As Object, oItem As Object
    Set oItems = oXML.getElementsByTagName("Items").Item(0)
    ReDim vArray(0 To 3, 0 To oItems.childNodes.Length - 1) As Variant
    Dim i As Integer
    For i = 0 To UBound(vArray, 2)
        Set oItem = oItems.childNodes(i)
        vArray(0, i) = oItem.getAttribute("Desc")
        vArray(1, i) = oItem.getAttribute("Qty")
        vArray(2, i) = oItem.getAttribute("Price")
        vArray(3, i) = oItem.getAttribute("Disc")
    Next
    m_Data.ProdInfo = vArray

End Sub

Public Sub MakeInvoice(sTemplate As Variant, Optional bSave As Variant)

Dim oWord As Object
    Dim oDoc As Object
    Dim oTable As Object

If IsMissing(bSave) Then bSave = False

'Open the document as read-only.
    Set oWord = CreateObject("Word.Application")
    Set oDoc = oWord.Documents.Open(sTemplate, , True)

'Fill in the bookmarks.
    oDoc.Bookmarks("Customer_Info").Range.Text = m_Data.CustInfo
    oDoc.Bookmarks("Customer_ID").Range.Text = m_Data.CustID
    oDoc.Bookmarks("Order_ID").Range.Text = m_Data.OrderID
    oDoc.Bookmarks("Order_Date").Range.Text = m_Data.OrderDate

'Fill in the table with the product information.
    '** Note that the table starts out with three rows -- the first row
    '   contains headers for the table, the second row is for
    '   the first set of product data, and the third row contains a total.
    '   New rows are added for additional products before the "total row".

Set oTable = oDoc.Tables(1)
    Dim r As Integer, c As Integer
    For r = 1 To UBound(m_Data.ProdInfo, 2) + 1
        If r > 1 Then oTable.Rows.Add (oTable.Rows(oTable.Rows.Count))
        For c = 1 To 4
            oTable.Cell(r + 1, c).Range.Text = _
               m_Data.ProdInfo(c - 1, r - 1)
        Next
        oTable.Cell(r + 1, 5).Formula _
            "=(B" & r + 1 & "*C" & r + 1 & ")*(1-D" & r + 1 & ")", _
            "#,##0.00"
    Next

'Update the field for the grand total and protect the document.
    oTable.Cell(oTable.Rows.Count, 5).Range.Fields.Update
    oDoc.Protect 1 'wdAllowOnlyComments=1

If bSave Then
        'Save the document as "c:\invoice.doc" and quit Word.
        Dim nResult As Long
        nResult = MsgBox("Are you sure you wish to create the document" & _
             " ""c:\invoice.doc""? If this document already exists, " & _
             "it will be replaced", vbYesNo, "AutomateWord")
        If nResult = vbYes Then oDoc.SaveAs "c:\invoice.doc"
        oDoc.Close False
        oWord.Quit
    Else
        'Make Word visible.
        oWord.Visible = True
    End If

End Sub

Uso del componente ActiveX desde una página web

Autoword1.htm muestra cómo puede usar el método GetData para permitir que el componente ActiveX recupere el cliente de datos del pedido y compile el documento.

Autoword1.htm

<HTML>
<HEAD>
   <OBJECT ID="AutoWord"
    CLASSID="CLSID:32646EBA-0919-4C2F-94D6-599F46DC34F2"
    CODEBASE="https://YourWebServer/invoice/package/AutomateWord.CAB#version=1,0,0,0">
   </OBJECT>
</HEAD>
<BODY>
Enter an order id between 10248 and 11077 and click the button to view the invoice for the order:
<P/><INPUT TYPE="text" VALUE="10500" ID="OrderID">
<P/><BUTTON ID="InvoiceButton">Create Invoice</BUTTON>
</BODY>

<SCRIPT Language="VBScript">

Function InvoiceButton_OnClick()
      Dim sConn
      sConn = "Provider=sqloledb;Data Source=YourSQLServer;Initial Catalog=Northwind;UID=sa;"
      AutoWord.GetData OrderID.Value, sConn
      AutoWord.MakeInvoice "https://YourWebServer/invoice/invoice.doc"
   End Function

</SCRIPT>
</HTML>

El script de Autoword1.htm usa el componente ActiveX para mostrar el documento completado fuera del explorador. También puede optar por guardar el documento completado y mostrarlo en el explorador; sin embargo, para ello es necesario guardar el documento de Word en un disco. El componente puede guardar el documento como C:\Invoice.doc en la unidad local del cliente. Dado que el componente ActiveX está marcado como seguro para el scripting, se pide al cliente que confirme el guardado.

Si desea mostrar el documento completado en el explorador, cambie la llamada a MakeInvoice en Autoword1.htm a lo siguiente:

      AutoWord.MakeInvoice "https://YourWebServer/invoice/invoice.doc", True
      window.navigate "c:\invoice.doc"

Autoword2.htm muestra cómo puede usar el método SendData para enviar los datos del pedido como un objeto DOMDocument al componente ActiveX para generar el documento completado. DOMDocument se crea a partir de una isla de datos XML que reside en la página web. Para que el componente ActiveX procese correctamente la información de pedido enviada por el autor de la llamada, el XML debe estar bien formado y estructurado para que el componente pueda interpretarla como información de pedido.

Autoword2.htm

<HTML>
<HEAD>
   <OBJECT ID="AutoWord"
    CLASSID="CLSID:32646EBA-0919-4C2F-94D6-599F46DC34F2"
    CODEBASE="https://YourWebServer/invoice/package/AutomateWord.CAB#version=1,0,0,0">
   </OBJECT>
</HEAD>
<BODY>
   <BUTTON ID="InvoiceButton">Create Invoice</BUTTON>
   <XML ID="DataXML">
     <Order>
        <OrderID>10700</OrderID>
        <OrderDate>10/10/2000</OrderDate>
        <CustID>SAVEA</CustID>
        <CustInfo>Save-a-lot
Markets Boise ID 83720
USA</CustInfo>
        <Items>
           <Product Desc="Chai" Qty="5" Price="18" Disc="0.2"/>
           <Product Desc="Sasquatch Ale" Qty="12" Price="14" Disc="0.2"/>
           <Product Desc="Scottish Longbreads" Qty="40" Price="12.5" Disc="0.2"/>
           <Product Desc="Flotemysost" Qty="60" Price="21.5" Disc="0.2"/>
        </Items>        
     </Order>
   </XML>
</BODY>

<SCRIPT Language="VBScript">

Function InvoiceButton_OnClick()
      AutoWord.SendData DataXML.XMLDocument
      AutoWord.MakeInvoice "https://YourWebServer/invoice/invoice.doc"
   End Function

</SCRIPT>
</HTML>

En Autoword1.htm y Autoword2.htm, debe tener en cuenta que se crea una instancia del componente ActiveX mediante una etiqueta en lugar de la función CreateObject. El propósito de usar la etiqueta es habilitar la descarga automática del componente ActiveX para los usuarios que aún no tienen instalado el componente. Si un usuario visita una de estas páginas y el componente no está instalado, el componente se descarga del archivo de archivador (CAB) en la dirección URL que se indica en el atributo CODEBASE. En función de la configuración de seguridad del usuario en Internet Explorer, es posible que primero reciba un mensaje para confirmar la descarga.

NOTA: El archivo CAB que se incluye en Autoword.exe se creó con el Asistente para paquete e implementación para Visual Basic. El componente ActiveX del paquete está marcado como seguro para scripting e inicialización, pero no está firmado digitalmente.

Para obtener más información sobre cómo crear descargas de componentes de Internet, firmar digitalmente y marcar componentes como seguros para scripting e inicialización, consulte los siguientes sitios web de Microsoft Developer Network (MSDN):

Firma y comprobación de código con Authenticode https://msdn.microsoft.com/en-us/library/ms537364.aspx

Inicialización segura y scripting para controles ActiveX https://msdn.microsoft.com/en-us/library/Aa751977.aspx

Referencias

Para obtener más información, haga clic en los números de artículo siguientes para ver los artículos de Microsoft Knowledge Base:

257757 INFO: La automatización de Office para la ejecución desatendida no se recomienda ni se admite

(c) Microsoft Corporation 2001, Todos los derechos reservados. Contribuciones de Lori B. Turner, Microsoft Corporation.