Cómo automatizar Excel y saber que el usuario lo ha cerrado

Resumen

En este artículo se muestra cómo
  • Automatizar Microsoft Excel utilizando Microsoft Visual C++ y Microsoft Foundation Classes (MFC).
  • Esperar que el usuario final cierre esa instancia de Excel.
  • Hacer que la controladora VC++ sepa que Excel está cerrado.

Más información

Microsoft Excel no provoca ningún evento Quit que pueda ser observado por otros procesos. Cuando una aplicación fuera de proceso que controla una instancia de Excel libera todas las referencias de objeto a la instancia y le indica que salga, esa controladora sabe que Excel ha finalizado. Pero cuando la controladora crea y hace que una instancia de Excel sea visible, no sabe cuándo el usuario final ha cerrado Excel. Siempre y cuando la aplicación que controla no haya liberado referencias de objeto a la instancia, Excel permanece cargado y registrado en la Tabla de objetos en ejecución.

En este artículo se utilizan los conceptos del artículo de Microsoft Knowledge Base:

178749 CÓMO: Crear un proyecto de automatización mediante MFC y una biblioteca de tipos
y modifica el paso 14 de dicho artículo para incluir código de ejemplo que ilustra un método para saber que el usuario final ha cerrado realmente la instancia de Excel. Se trata de llamar a WaitForSingleObject() para determinar que el proceso de Excel concreto ha finalizado.

Notas para automatizar Microsoft Excel 2000

Algunos métodos y propiedades han cambiado en Microsoft Excel 2000. Para obtener información adicional acerca de cómo utilizar el código de ejemplo descrito en este artículo con la biblioteca de tipos de Microsoft Excel 2000, consulte el artículo siguiente en Microsoft Knowledge Base:

224925 Las bibliotecas de tipos para Office pueden cambiar con las nuevas versiones

Crear un proyecto de Automatización

  1. Con Microsoft Developer Studio, inicie un nuevo proyecto "MFC AppWizard (exe)" denominado AutoProject.
  2. En el paso 1 de MFC AppWizard, elija Basado en cuadros de diálogo como tipo de aplicación y haga clic en Finalizar. Aparecerá el cuadro de diálogo New Project Information, donde se indica que las clases que se van a crear incluyen:

    Application: CAutoProjectApp en AutoProject.h y AutoProject.cpp
    Dialog: CAutoProjectDlg en AutoProject.h y AutoProjectDlg.cpp

    Haga clic en Aceptar para crear el proyecto.
  3. En el área de trabajo del proyecto, haga clic en la ficha "Vista de recursos". Haga doble clic en Recursos de AutoProject para expandir el árbol de recursos. Haga doble clic en Diálogo en el árbol Recurso y haga doble clic para seleccionar el recurso de cuadro de diálogo IDD_AUTOPROJECT_DIALOG.
  4. Quite el control de etiqueta (IDC_STATIC).
  5. Cambie el nombre del botón Aceptar a IDSEEXLQUIT y el título a "Ver si se cierra Excel". Cierre el formulario de diseño del cuadro de diálogo AutoProject.rc.
  6. Haga clic en ClassWizard en el menú Ver (o presione CTRL+W).
  7. Seleccione la ficha Mapas de mensajes. Seleccione IDSEEXLQUIT en el cuadro de lista de identificadores de objetos y seleccione BN_CLICKED en el cuadro de lista Mensajes. Haga clic en Agregar función y acepte el nombre de función OnSeexlquit. Haga clic en Aceptar para cerrar ClassWizard.

    Nota: este paso agrega una declaración para el miembro de función OnSeexlquit(); al archivo de encabezado denominado AutoProjectDLG.h. Este paso también agrega una función de controlador de mensajes esqueleto vacía denominada CAutoProjectDlg::OnSeexlquit() al archivo denominado AutoProjectDLG.cpp.
  8. Haga clic en ClassWizard en el menú Ver (o presione CTRL+W).
  9. Seleccione la ficha Automatización. Haga clic en Agregar clase y elija "Desde una biblioteca de tipos". Desplácese para seleccionar la Biblioteca de objetos de Microsoft Excel 8.0 (la ubicación predeterminada es C:\Archivos de programa\Microsoft Office\Office\Excel8.olb) y haga clic en Abrir. Seleccione todas las clases de la lista Confirmar clases y haga clic en Aceptar. En Excel 2000, seleccione la Biblioteca de objetos de Microsoft Excel 9.0; la ubicación predeterminada es C:\Archivos de programa\Microsoft Office\Office\Excel9.olb. En Excel 2002, la ruta de acceso completa es C:\Archivos de programa\Microsoft Office\Office10\excel.exe. .

    Nota: el cuadro de lista del cuadro de diálogo Confirmar clases contiene todas las interfaces IDispatch de la biblioteca de tipos de Microsoft Excel. En la mitad inferior del cuadro de diálogo verá que un archivo de implementación denominado Excel8.cpp incluye contenedores de clase generados derivados de ColeDispatchDriver() y que el archivo de encabezado de declaración adecuado se denomina Excel8.h. En Excel 2000, el archivo de implementación es Excel9.cpp y el archivo de encabezado de declaración es Excel9.h. En Excel 2002, el archivo de implementación es Excel.cpp y el archivo de encabezado de declaración es Excel.h.
  10. Haga clic en Aceptar para cerrar el cuadro de diálogo MFC ClassWizard.
  11. Agregue el código siguiente a la función CAutoProjectApp::InitInstance(), que carga y habilita la biblioteca de servicios COM:
          BOOL CAutoProjectApp::InitInstance()
    {
    if(!AfxOleInit()) // Your addition starts here.
    {

    AfxMessageBox("Could not initialize COM dll");
    return FALSE;
    } // End of your addition.

    AfxEnableControlContainer();
    .
    .
    .

    }
  12. Agregue la línea siguiente a las instrucciones #include que aparecen en la parte superior del archivo de programa AutoProject.cpp:
          #include <afxdisp.h>
  13. Agregue la instrucción de inclusión de Excel8.h después de la instrucción de inclusión de Stdafx.h, en la parte superior del archivo de programa AutoProjectDlg.cpp:
          #include "stdafx.h"
    #include "excel8.h"
    // for Excel 2000, use #include "excel9.h"
    // for Excel 2002<?xm-insertion_mark_start author="v-thomr" time="20070327T063222-0600"?> or later versions of Excel<?xm-insertion_mark_end?>, use #include "excel.h"

  14. Agregue código de automatización a CAutoProjectDlg::OnSeexlquit(), de forma que aparezca como se muestra a continuación:
          void CAutoProjectDlg::OnSeexlquit() // Message handler function.

    {
    char buf[1024]; // General purpose message buffer.
    _Application oExcel; // oExcel is an _Application object.
    Workbooks oBooks;
    LPDISPATCH lpDisp;

    // Common OLE-variants... Easy variants to use for calling arguments.

    COleVariant
    covTrue((short)TRUE),
    covFalse((short)FALSE),
    covOptional((long)DISP_E_PARAMNOTFOUND, VT_ERROR);

    // Start Excel and get Application object.
    if(!oExcel.CreateDispatch("Excel.Application"))
    {
    AfxMessageBox("Couldn't CreateDispatch on Excel");
    return;
    }

    // Set visible.
    oExcel.SetVisible(TRUE);
    oExcel.SetUserControl(TRUE); // This is a property of the
    // _Application object. Set it so you
    // can Release the oExcel and
    // oBooks objects without killing
    // Excel.

    // Get Workbooks collection...
    lpDisp = oExcel.GetWorkbooks(); // Get an IDispatch pointer
    ASSERT(lpDisp); // or fail.
    oBooks.AttachDispatch( lpDisp ); // Attach IDispatch pointer to
    // oBooks object.

    // Open a workbook.<?xm-deletion_mark author="v-thomr" time="20070327T063248-0600" data=".."?><?xm-insertion_mark_start author="v-thomr" time="20070327T063324-0600"?> If this code is run in Microsoft Office Excel 2007,
    // change the file name to MybookTest.xlsx.<?xm-insertion_mark_end?>
    lpDisp = oBooks.Open("C:\\MybookTest.xls", // Change for your .xls.
    covOptional, covOptional, covOptional, covOptional,
    covOptional, covOptional, covOptional, covOptional,
    covOptional, covOptional, covOptional, covOptional // Excel 2000
    );
    /*
    // <?xm-insertion_mark_start author="v-thomr" time="20070327T063406-0600"?>If you want to <?xm-insertion_mark_end?><?xm-deletion_mark author="v-thomr" time="20070327T063415-0600" data="O"?><?xm-insertion_mark_start author="v-thomr" time="20070327T063415-0600"?>o<?xm-insertion_mark_end?>pen an Excel 2002 <?xm-insertion_mark_start author="v-thomr" time="20070327T063420-0600"?>or an Excel 2003 <?xm-insertion_mark_end?>workbook<?xm-insertion_mark_start author="v-thomr" time="20070327T063429-0600"?>,
    // use this code<?xm-insertion_mark_end?>.
    lpDisp = books.Open("C:\\Test",
    covOptional, covOptional, covOptional, covOptional,
    covOptional, covOptional, covOptional, covOptional,
    covOptional, covOptional, covOptional, covOptional,
    covOptional, covOptional, covOptional ); // Excel 2000 requires
    */ // only 13 arguments<?xm-insertion_mark_start author="v-thomr" time="20070327T063451-0600"?>

    // If you want to open an Excel 2007 workbook, use this code.
    lpDisp = oBooks.Open("C:\\Test.xlsx",
    covOptional, covOptional, covOptional, covOptional,
    covOptional, covOptional, covOptional, covOptional,
    covOptional, covOptional, covOptional, covOptional,
    covOptional, covOptional);<?xm-insertion_mark_end?>

    ASSERT(lpDisp); // It better have worked?

    HWND hWnd;
    hWnd = ::FindWindow("XLMain", // Pointer to class name.
    NULL // Pointer to window name option.
    );
    if(NULL==hWnd)
    {
    long lErr = GetLastError();
    sprintf(buf, "FindWindow error code = %d", lErr);
    AfxMessageBox(buf);
    }

    DWORD pid; // Variable to hold the process ID.
    DWORD dThread; // Variable to hold (unused) thread ID.
    dThread = GetWindowThreadProcessId(hWnd, // Handle to window.
    &amp;pid // Address of variable
    // for process identifier.
    );
    HANDLE hProcess; // Handle to existing process

    hProcess = OpenProcess(SYNCHRONIZE | PROCESS_ALL_ACCESS, // access
    // flag
    TRUE, // handle inheritance flag
    pid // process identifier
    );

    oBooks.ReleaseDispatch(); // Release the object-IDispatch binding.
    oExcel.ReleaseDispatch();
    oBooks = NULL; // Destroy the object references.
    oExcel = NULL;

    DWORD dwReason; // Variable to receive signal.

    dwReason = WaitForSingleObject(hProcess, // Handle to object to
    // wait for its end.
    INFINITE // Time-out interval in
    // milliseconds.
    );

    sprintf(buf, "Reason for Wait to terminate is %d", dwReason);
    // Zero is good.
    AfxMessageBox(buf);
    }
  15. Genere y ejecute el proyecto. Cuando aparezca el cuadro de diálogo, minimice Visual Studio, saliendo del cuadro de diálogo de su proyecto. Haga clic en el botón "Ver si se cierra Excel". Espere a que Excel aparezca y sea visible. Cuando sea visible, haga clic en el botón de control situado en el extremo derecho de la barra de título (la "X") para cerrar Excel.

Referencias

Este artículo presenta un enfoque concreto para saber que el usuario final ha finalizado una instancia de Excel. Si desea más información acerca de cómo generar un proyecto de cuadro de diálogo para otros servidores de Automatización, consulte el artículo siguiente en Microsoft Knowledge Base.

178749 CÓMO: Crear un proyecto de automatización mediante MFC y una biblioteca de tipos

Para obtener más información acerca de cómo interceptar eventos de aplicación, consulte el artículo siguiente en Microsoft Knowledge Base:

183599 Cómo interceptar eventos de Microsoft Word 97 mediante Visual C++
Propiedades

Id. de artículo: 192348 - Última revisión: 12/27/2007 - Revisión: 1

Comentarios