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:
- AccessibleObjectFromWindow Documentation
- Using Visual C++ to Automate Office
- EnumChildWindows Documentation
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.
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.
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.
@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.
@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.
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.
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.
Same issue with Excel 2007 : returns E_FAIL
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
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.
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.
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.
Can’t seem to actually use the pApp pointer in your example to do anything like say pApp->Quit.
It just throws errors.