Access Microsoft Excel 2007 COM API through Microsoft Active Accessibility

July 15, 2009 by Jeff Gibeau Leave a reply »

A couple days ago I showed you how to access the Microsoft Word 2007 COM API through Microsoft Active Accessibility (MSAA). Today, I’m going to switch it up just a little bit, and use the same method to access the Microsoft Excel 2007 COM API. Again, we’ll be using AccesssibleObjectFromWindow to get at the main Excel::Window object. From that object, you can obtain the Excel Application, and Excel Workbook objects. Some documentation from Microsoft can be found here.

First we obtain the hwnd of the Microsoft Excel Process:

//The main window in Microsoft Excel has a class name of XLMAIN
HWND excelWindow = FindWindow(L"XLMAIN", NULL);

We can then traverse the child windows until we find the one with classname EXCEL7:

//Use the EnumChildWindows function to iterate through all child windows until we find EXCEL7
EnumChildWindows(excelWindow, (WNDENUMPROC) EnumChildProc, (LPARAM)1);
static BOOL EnumChildProc(HWND hwnd, LPARAM)
{
     WCHAR szClassName[64];
     if(GetClassNameW(hwnd, szClassName, 64))
     {
          if(_wcsicmp(szClassName, L"EXCEL7") == 0)
          {
               //Get AccessibleObject
               Excel::Window* pWindow = NULL;
               HRESULT hr = AccessibleObjectFromWindow(hwnd, OBJID_NATIVEOM, __uuidof(Excel::Window), (void**)&pWindow);
               if(hr == S_OK)
               {
                    //Excel object is now in pWindow pointer, from this you can obtain the document or application
                    Excel::_Application* pApp = NULL;
                    pWindow->get_Application(&pApp);
                    pWindow->Release();
               }
               return false;     // Stops enumerating through children
          }
     }
     return true;
}

Next we obtain our Excel::Window object through AccessibleObjectFromWindow:

//Get AccessibleObject
Excel::Window* pWindow = NULL;
HRESULT hr = AccessibleObjectFromWindow(hwnd, OBJID_NATIVEOM, __uuidof(Excel::Window), (void**)&pWindow);

That’s it! We now have our Excel::Window COM Object. From this object we can obtain the Excel::Application and Excel::Workbook objects, allowing us full interaction with the active document. So with just a few lines of code, we now have access to automate an Excel 2007 document.

Full source code:

#include <windows.h>
#include <oleacc.h>
#include "msexcel.h"

static BOOL EnumChildProc(HWND hwnd, LPARAM)
{
     WCHAR szClassName[64];
     if(GetClassNameW(hwnd, szClassName, 64))
     {
          if(_wcsicmp(szClassName, L"EXCEL7") == 0)
          {
               //Get AccessibleObject
               Excel::Window* pWindow = NULL;
               HRESULT hr = AccessibleObjectFromWindow(hwnd, OBJID_NATIVEOM, __uuidof(Excel::Window), (void**)&pWindow);
               if(hr == S_OK)
               {
                    //Excel object is now in pWindow pointer, from this you can obtain the document or application
                    Excel::_Application* pApp = NULL;
                    pWindow->get_Application(&pApp);
                    pWindow->Release();
               }
               return false;     // Stops enumerating through children
          }
     }
	 return true;
}
int main( int argc, CHAR* argv[])
{
     //The main window in Microsoft Excel has a class name of XLMAIN
     HWND excelWindow = FindWindow(L"XLMAIN", NULL);

     //Use the EnumChildWindows function to iterate through all child windows until we find _WwG
     EnumChildWindows(excelWindow, (WNDENUMPROC) EnumChildProc, (LPARAM)1);

     return 0;
}

Useful Links for this post:

Key phrases: Accessing Excel 2007 COM object using AccessibleObjectFromWindow, Automating Excel 2007 with Microsoft Active Accessibility MSAA
Update: I’ve updated the full source to utilize get_Application. get_Document was in place previously, which was leftover from my Word source code.

Advertisement

12 Responses

  1. NarVish says:

    Thank you for posting this useful article.
    I’m getting the below compilation error;
    “error C2039: ‘get_Document’ : is not a member of ‘Excel::Window’”
    any guidelines to fix this error is appreciated. Thanks in advance.

  2. Agnes says:

    Thanks for your post. I’m getting the below compiler error.
    fatal error C1083: Cannot open include file: ‘msexcel.h’: No such file or directory

    How to include the msexcel header file.
    Thanks alot in advance.

  3. Jeff Gibeau says:

    @Agnes
    See this post http://www.northatlantawebdesign.com/index.php/2009/07/21/automating-excel-2007-in-c-by-importing-the-excel-2007-type-library/
    I’ve taken the tlh file that is created from the Microsoft Excel #import and renamed it msexcel.h for simplicity and so that #import is not needed each time.

  4. Jeff Gibeau says:

    @NarVish
    Thanks for noting that error. I’ve updated the full source to reflect the snippet at the top. get_Document has been replaced in my code by get_Application. get_Document was leftover from my Word source code.

  5. VLSJ says:

    Great to see the useful article. Thanks for posting.
    I’m getting E_FAIL message in hr at below line.

    HRESULT hr = AccessibleObjectFromWindow(hwnd, OBJID_NATIVEOM, __uuidof(Excel::Window), (void**)&pWindow);

    Please let me know your suggestions. A million thanks in advance.

  6. NarVish says:

    I’m getting the same error message E_FAIL as it was posted by VLSJ. I’m using Excel 2007. Please help us to resolve this issue.
    Thak you.

  7. Eric says:

    Same issue with Excel 2007 : returns E_FAIL

  8. Eric says:

    Actually there is just no child window of class “EXCEL7″ on my version of Excel 2007.
    Here are the child windows of that I enumerated through EnumChildWindows():
    class “E” with hwnd=0x3a0934
    class “M” with hwnd=0x60c88
    class “M” with hwnd=0xc40668
    class “N” with hwnd=0x98012c
    class “N” with hwnd=0x330bd2
    class “E” with hwnd=0x2406a2
    class “M” with hwnd=0x5b074c
    class “M” with hwnd=0x600bac
    class “N” with hwnd=0x3c0120
    class “N” with hwnd=0×420798
    class “N” with hwnd=0xd0c74
    class “N” with hwnd=0x9f0498
    class “N” with hwnd=0x140bd8
    class “E” with hwnd=0×430582
    class “N” with hwnd=0×190990
    class “N” with hwnd=0x8a06c2
    class “E” with hwnd=0x70c82
    class “E” with hwnd=0x70c6c
    class “C” with hwnd=0xd0c7a
    class “E” with hwnd=0x150c72
    class “E” with hwnd=0×760852
    class “N” with hwnd=0x30d02
    class “N” with hwnd=0x470c4e
    class “E” with hwnd=0xf0c6e
    class “E” with hwnd=0×910550
    class “E” with hwnd=0x1f05d8
    class “E” with hwnd=0x2d0a46
    class “E” with hwnd=0x3909fe
    class “E” with hwnd=0×500526
    class “X” with hwnd=0x2f05b2
    class “E” with hwnd=0xc0c50
    class “N” with hwnd=0xb40c8a
    class “N” with hwnd=0×250834
    class “N” with hwnd=0x46071e
    class “N” with hwnd=0x2c072e
    class “X” with hwnd=0x1f070a
    class “X” with hwnd=0x2008a8
    class “X” with hwnd=0x170c5a
    class “S” with hwnd=0x3a0750
    class “E” with hwnd=0x56054e
    class “M” with hwnd=0x2e083e
    class “M” with hwnd=0x170bd6

  9. Jeff Gibeau says:

    To all having the E_FAIL return issue, you will have to be in process in order for this to work. Unfortunately I can’t go further into detail on how to get in process.
    As for the classnames you are iterating through with enumchildren, you are only looking at the first letter of the classname. Look at the windows with Spy++ to verify the EXCEL7 window exists.

  10. KTTransfer says:

    I am not getting E_FAIL. Everything is just working as expected. But one issue: when Excel is closed by user, Excel.exe is not getting killed from task manager. Say if we are accessing 7 different Excel.exe’s then those many will run in the taskmanager even if user closes Excel workbooks. I tried releasing the Worbook*, _ApplicationPtr but Excel.exe is still running. Implicitly it should be closed but its not.Want to try killing explicitly by checking the number of workbooks. if number of workbooks are 0 then i can kill explicitly, please guide me to solve this issue.

  11. Jeff Gibeau says:

    KTTransfer, it sounds like you have a reference that has not been released. This is pretty common with handling Microsoft Office through COM. It may take some time, but you will need to go over your code with a fine tooth comb.
    If you are just using the code above, it looks like in my original code I even forgot to release the pWindow object after obtaining the application. This in itself could cause the application to hang even after all windows are closed. I’ve now updated the code.

  12. Can’t seem to actually use the pApp pointer in your example to do anything like say pApp->Quit.
    It just throws errors.

Leave a Reply