Using MS Office in MFC application

Introduction

Once I was engaged in the project, whose main features were theh presence of a great amount of the typical forms of input and output generally associated with Office-type applications. The documents were to be filled from a data store, which the program would display via the use of views. It would be extremely desirable that the document template could understand these different data independently. Therefore, we made the decision to integrate Microsoft Office into our application in order to leverage the already built-in functionalty that we were seeking.

To that extent, I present to you this article detailing exactly what steps are necessary in order to integrate Microsoft Office into your Visual C++/MFC applications.

I should also note at this point that one constant problem that I have run into with regards to Office is the unstable nature of the product itself when using it in relation to an ActiveX Document. Therefore, as you look through this article (and the demo) you might see some less than efficient code. An example of a problem that I still have is that even after my application ends, Microsoft Office application remains in memory and can only be removed via the Task manager. If you do solve this problem, please let me know so that I can update this article as well as my own code.

Integrating MS Office

Let's begin.
  1. Via the Visual C++ AppWizard, generate a new MDI application called XOffice. On the third step, it will be necessary to select the Container radio button as well as the Active Document Container checkbox.
  2. In addition, this application should be an Automation server. I have taken advantage of a way offered by Nick Hodapp in his article, Using ATL to Automate a MFC Application, which can be found here at CodeGuru. Please familiarize yourself with this example and execute all steps of program transformation to the automation server.
  3. Now we shall engage in integrating MS Office. Include a file Office.h with the contents into the project:
    // office.h
    
    #define Uses_MSO2000
    
    // for MS Office 2000
    #ifdef Uses_MSO2000
    #import "C:\Program Files\Microsoft Office\Office\MSO9.DLL"
    #import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"
    #import "C:\Program Files\Microsoft Office\Office\MSWORD9.OLB" \
     rename("ExitWindows","_ExitWindows")
    #import "C:\Program Files\Microsoft Office\Office\EXCEL9.OLB" \
     rename("DialogBox","_DialogBox") \
     rename("RGB","_RGB") \
     exclude("IFont","IPicture")
    #import "C:\Program Files\Common Files\Microsoft Shared\DAO\DAO360.DLL" \
     rename("EOF","EndOfFile") rename("BOF","BegOfFile")
    #import "C:\Program Files\Microsoft Office\Office\MSACC9.OLB"
    
    #else // for MS Office 97
    #import "C:\Program Files\Microsoft Office\Office\MSO97.DLL"
    #import "C:\Program Files\Common Files\Microsoft Shared\VBA\VBEEXT1.OLB"
    #import "C:\Program Files\Microsoft Office\Office\MSWORD8.OLB" \
     rename("ExitWindows","_ExitWindows")
    #import "C:\Program Files\Microsoft Office\Office\EXCEL8.OLB" \
     rename("DialogBox","_DialogBox") \
     rename("RGB","_RGB") \
     exclude("IFont","IPicture")
    #import "C:\Program Files\Common Files\Microsoft Shared\DAO\DAO350.DLL" \
     rename("EOF","EndOfFile")
     rename("BOF","BegOfFile")
    #import "C:\Program Files\Microsoft Office\Office\MSACC8.OLB"
    
    #endif
    
  4. We shall create a new form, which we shall require further. Select the New Form option from the Insert menu.

    Enter CFormDemo into a field Name. Press the button New located near a Document field and then click the OK button, in the newly appeared form. And once again click the OK button.

    We shall place three Edit Boxes and two Buttons on the new form. In ClassWizard we shall bind Edit Boxes to variables (accordingly CString m_str, double m_double, long m_long).

  5. We shall create handler of the following kind for Buttons:
    // FormDemo.cpp
    
    void NewXOfficeDoc(LPCTSTR,LPCTSTR,double,long);
    
    void CFormDemo::OnButton1()
    {
     UpdateData();
     NewXOfficeDoc("XOffice.doc",m_str,m_double,m_long);
    }
    
    void CFormDemo::OnButton2()
    {
     UpdateData();
     NewXOfficeDoc("XOffice.xls",m_str,m_double,m_long);
    }
    
    We shall add the following code to the beginning of XOfficeDoc.cpp file:
    // XOfficeDoc.cpp
    
    static CString g_template;
    static CString g_str;
    static double  g_double;
    static long    g_long;
    
    void NewXOfficeDoc(LPCTSTR aTemplate,
                       LPCTSTR aStr,
                       double aDouble,
                       long aLong)
    {
     CString   str;
     POSITION  pos = AfxGetApp()->GetFirstDocTemplatePosition();
     while (pos != NULL) 
     {
      CDocTemplate *temp = AfxGetApp()->GetNextDocTemplate(pos);
      if (temp->GetDocString(str,CDocTemplate::docName) 
      {
       str == _T("XOffice"));
       g_template = aTemplate;
       g_str      = aStr;
       g_double   = aDouble;
       g_long     = aLong;
       temp->OpenDocumentFile(NULL);
       return;
      }
     }
    }
    

    Now we are able to create MDI documents by pressing the buttons on the form.

  6. The MFC class ColeDocObjectItem provides the support of ActiveX document. The class can do a lot already, but we also need to teach it to load the documents, which we set up.

    Make the following change into a class CXOfficeCntrItem:

    // CntrItem.h
    class CXOfficeCntrItem : public ColeDocObjectItem
    {
    //...
    public:
     CXOfficeCntrItem(CXOfficeDoc* pContainer,LPCTSTR);
     bool m_isCreate;
     bool CreateItem(LPCTSTR);
    //...
    };
    
    // CntrItem.cpp
    CXOfficeCntrItem::CXOfficeCntrItem(CXOfficeDoc* pContainer,LPCTSTR templ)
     : COleDocObjectItem(pContainer), m_isCreate(false)
    {
     CreateItem(templ);
    }
    
    bool CXOfficeCntrItem::CreateItem(LPCTSTR templ)
    {
     USES_CONVERSION;
    
     // get storage for the object via virtual function call
     m_dwItemNumber = GetNewItemNumber();
     GetItemStorage();
    
     // add AfxOleInit(); in CXOfficeApp::InitInstance
     AfxOleGetMessageFilter()->EnableNotRespondingDialog(FALSE);
    
     // attempt to create the object
     LPOLECLIENTSITE lpClientSite = GetClientSite();
     SCODE sc = ::OleCreateFromFile(CLSID_NULL,
                                    T2COLE(templ),
                                    IID_IUnknown,
                                    OLERENDER_DRAW,
                                    NULL,
                                    lpClientSite,
                                    m_lpStorage,
                                    (LPVOID*)&m_lpObject);
    
     return m_isCreate = FinishCreate(sc) == TRUE;
    }
    
  7. And the last thing to do is to make changes in CXOfficeDoc and CXOfficeView classes for ActiveX document display
    // XOfficeDoc.h
    
    ...
    class CXOfficeCntrItem;
    
    class CXOfficeDoc : public COleDocument,
    ...
    {
    ...
    public:
     CXOfficeCntrItem *m_ctrl;
     CString           m_template;
     CString           m_str;
     double            m_double;
     long              m_long;
    
     bool LoadTemplate();
    ...
    };
    
    // XOfficeDoc.cpp
    
    CXOfficeDoc::CXOfficeDoc()
    : m_ctrl(0)
    {
     EnableCompoundFile();
    }
    
    BOOL CXOfficeDoc::OnNewDocument()
    {
     if (!COleDocument::OnNewDocument())
      return FALSE;
    
     m_template = g_template;
     m_str      = g_str;
     m_double   = g_double;
     m_long     = g_long;
    
     return LoadTemplate();
    }
    
    bool CXOfficeDoc::LoadTemplate()
    {
     char path [_MAX_PATH];
     char drive[_MAX_DRIVE];
     char dir  [_MAX_DIR];
     char fname[_MAX_FNAME];
     char ext  [_MAX_EXT];
    
     ::GetModuleFileName(NULL,path,sizeof(path));
     _splitpath(path,      drive,dir,0,    0);
     _splitpath(g_template,0,    0,  fname,ext);
     _makepath (path,      drive,dir,fname,ext);
    
     {
     CWaitCursor cw;
     m_ctrl = new CXOfficeCntrItem(this,path);
     }
     if (m_ctrl == 0  ||  m_ctrl->m_isCreate == false) 
     {
      CString str  = "Can not open the doc:\n";
      str += path;
      AfxMessageBox(str,MB_ICONSTOP);
      return false;
     }
     return true;
    }
    
    // XOfficeView.cpp
    
    void  CXOfficeView::OnInitialUpdate()
    {
     CView::OnInitialUpdate();
    
     CWaitCursor wc;
     m_pSelection = GetDocument()->m_ctrl;
     ...
    }
    
    So, now we are able to load ActiveX documents automatically. It is quite not bad already. J Pay attention to the fact that the procedures of preservation and loading of our documents work normally too, saving thus the contents of the initial template document. The truth is that we don't need it at all. The only thing that does not work is Print Preview. I was not able to understand it therefore, if someone manages to do it I shall be very obliged to find out about it first.
  8. Now we shall teach CXOfficeDoc class to store and to load only our data and not to ask questions about any data change of ActiveX document itself. For this purpose we shall add methods of OnOpenDocument and SaveModified and bring in the following changes with the help of ClassWizard:
    // XOfficeDoc.cpp
    
    void  CXOfficeDoc::Serialize(CArchive& ar)
    {
     if (ar.IsStoring()) 
     {
      ar << m_template << m_str << m_double << m_long;
     } else       {
      ar >> m_template >> m_str >> m_double >> m_long;
     }
    
     //COleDocument::Serialize(ar);
    }
    
    BOOL CXOfficeDoc::OnOpenDocument(LPCTSTR lpszPathName)
    {
     if (!COleDocument::OnOpenDocument(lpszPathName))
     return FALSE;
    
     return LoadTemplate();
    }
    
    BOOL CXOfficeDoc::SaveModified()
    {
     return CDocument::SaveModified();
    }
    
    // CntrItem.cpp
    
    void  CXOfficeCntrItem::OnChange(OLE_NOTIFICATION nCode, DWORD dwParam)
    {
     BOOL modified = m_pDocument->IsModified();
     COleDocObjectItem::OnChange(nCode, dwParam);
     m_pDocument->SetModifiedFlag(modified);
    
     GetDocument()->UpdateAllViews(NULL);
    }
    
    
  9. The following step shall be the reception of the IDispatch interface of ActiveX document. Let's bring in the following changes to CXOfficeCntrItem class:
    // CntrItem.h
    
    ...
    #include <comdef.h>
    ...
    
    class CXOfficeCntrItem : public COleDocObjectItem
    {
    public:
    ...
    
     int          m_who; // 0 - ?, 1 - Word, 2 - Excel
     IDispatchPtr m_disp;
    
     LPDISPATCH   GetIDispatch();
     void         AttachDisp  ();
     void         ActivateDisp();
     void         CloseDisp   ();
    
    ...
    };
    

    I did not want to present the text of the appropriate methods, as it would have taken too much space. They can be looked up in the source texts of the program. It shall be necessary also to bring in the alterations to CXOfficeDoc and CXOfficeView classes listed below.

    // CXOfficeView.cpp
    
    void CXOfficeView::OnInitialUpdate()
    {
     ...
    
     m_pSelection = GetDocument()->m_ctrl;
     m_pSelection->AttachDisp();
    
     //Active documents should always be activated
     ...
     m_pSelection->ActivateDisp();
    }
    
    // CXOfficeDoc.cpp
    
    void CXOfficeDoc::OnCloseDocument()
    {
     if (m_ctrl)
      m_ctrl->CloseDisp();
    
     COleDocument::OnCloseDocument();
    }
    
  10. Now it's high time to make our automation server intelligent. For this purpose we shall define ActiveDocument and IsActiveDocument properties for IApplication interface and also PStr, PDouble and PLong properties for IDocument interface.

    Is easy to do with the help of ATL Wizard.

    • Workspace->Class View->IApplication->Right button->Add Property
    • Workspace->Class View->IDocument->Right button->Add Property

    The realization of methods can be looked up in the source texts.

  11. The files XOffice.doc and XOffice.xls are the examples of Word and Excel documents. In Word the document initialization of field occurs in event Document_New, which is obviously called from the program. The value of fields is given to the named bookmarks. In Excel the document initialization of cells is made in event Workbook_Activate. It is not quite convenient, but I have tried a good deal of variants and has defined this one (Workbook_Activate) as the best and steady working. As I have already said, the direct call of macro from VBA leaves Excel in memory even when the program is finished.

Downloads

Download demo project - 36 Kb
Download source - 71 Kb


Comments

  • Not working in Office XP

    Posted by Legacy on 10/11/2003 12:00am

    Originally posted by: pardhu

    This program is not compiling in VC++ 6.0. I have office XP in my system.

    and the error is coming at the following line in office.h

    #import "C:\Program Files\Microsoft Office\Office\MSO9.DLL"

    this DLL was not found anywhere in my system

    plz let me what lines i have to change to work with Office XP application

    plz help me

    thank you in advance

    Regards
    Pardhu

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Businesses are moving more and more of their customer transactions to the web. Security is understandably a top concern as online transactions increase, so it is important to make sure your electronic signature provider meets the highest security standards. That means more than simply passing a security audit or obtaining a certification. This white paper provides recommendations for taking a broader view of e-signature security, and answers key questions that help identify the security requirements against …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds