Click here to Skip to main content
15,867,308 members
Articles / Desktop Programming / ATL
Article

Using MS Office in an MFC Application

Rate me:
Please Sign up or sign in to vote.
4.90/5 (24 votes)
4 Nov 2000 645.1K   11.3K   206   160
Integrating MS Office in your MFC Application using ActiveX Document mode.
  • Download source files - 46 Kb
  • Download demo project - 36 Kb
  • Sample Image - xoffice.jpg

    Introduction

    Once I was engaged in a project whose main features were the 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:
      <FONT color=green>// Office.h</FONT>
      
      <FONT color=blue>#define</FONT> Uses_MSO2000
      
      <FONT color=blue>#ifdef</FONT> Uses_MSO2000
      <FONT color=green>// for MS Office 2000</FONT>
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Microsoft Office\Office\MSO9.DLL"</FONT>
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Common Files\Microsoft Shared\VBA\VBA6\VBE6EXT.OLB"</FONT>
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Microsoft Office\Office\MSWORD9.OLB"</FONT> \
              rename(<FONT color=teal>"ExitWindows"</FONT>,<FONT color=teal>"_ExitWindows"</FONT>)
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Microsoft Office\Office\EXCEL9.OLB"</FONT> \
              rename(<FONT color=teal>"DialogBox"</FONT>,<FONT color=teal>"_DialogBox"</FONT>) \
              rename(<FONT color=teal>"RGB"</FONT>,<FONT color=teal>"_RGB"</FONT>) \
              exclude(<FONT color=teal>"IFont"</FONT>,<FONT color=teal>"IPicture"</FONT>)
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Common Files\Microsoft Shared\DAO\DAO360.DLL"</FONT> \
              rename(<FONT color=teal>"EOF"</FONT>,<FONT color=teal>"EndOfFile"</FONT>) rename(<FONT color=teal>"BOF"</FONT>,<FONT color=teal>"BegOfFile"</FONT>)
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Microsoft Office\Office\MSACC9.OLB"</FONT>
      <FONT color=blue>#else</FONT>
      <FONT color=green>// for MS Office 97</FONT>
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Microsoft Office\Office\MSO97.DLL"</FONT>
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Common Files\Microsoft Shared\VBA\VBEEXT1.OLB"</FONT>
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Microsoft Office\Office\MSWORD8.OLB"</FONT> \
              rename(<FONT color=teal>"ExitWindows"</FONT>,<FONT color=teal>"_ExitWindows"</FONT>)
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Microsoft Office\Office\EXCEL8.OLB"</FONT> \
              rename(<FONT color=teal>"DialogBox"</FONT>,<FONT color=teal>"_DialogBox"</FONT>) \
              rename(<FONT color=teal>"RGB"</FONT>,<FONT color=teal>"_RGB"</FONT>) \
              exclude(<FONT color=teal>"IFont"</FONT>,<FONT color=teal>"IPicture"</FONT>)
      <FONT color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Common Files\Microsoft Shared\DAO\DAO350.DLL"</FONT> \
              rename(<FONT color=teal>"EOF"</FONT>,<FONT color=teal>"EndOfFile"</FONT>)
              rename(<FONT color=teal>"BOF"</FONT>,<FONT color=teal>"BegOfFile"</FONT>)
      <font color=blue>#import </FONT><FONT color=teal>"C:\Program Files\Microsoft Office\Office\MSACC8.OLB"</FONT>
      <font color=blue>#endif</FONT>
    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:
      <FONT color=green>// FormDemo.cpp</FONT>
      
      <FONT color=blue>void </FONT>NewXOfficeDoc(LPCTSTR,LPCTSTR,<FONT color=blue>double</FONT>,<FONT color=blue>long</FONT>);
      
      <FONT color=blue>void</FONT> CFormDemo::OnButton1()
      {
          UpdateData();
          NewXOfficeDoc(<FONT color=teal>"XOffice.doc"</FONT>,m_str,m_double,m_long);
      }
      
      <FONT color=blue>void</FONT> CFormDemo::OnButton2()
      {
          UpdateData();
          NewXOfficeDoc(<FONT color=teal>"XOffice.xls"</FONT>,m_str,m_double,m_long);
      }
      We shall add the following code to the beginning of XOfficeDoc.cpp file:
      <FONT color=green>// XOfficeDoc.cpp</FONT>
      
      <FONT color=blue>static </FONT>CString g_template;
      <FONT color=blue>static </FONT>CString g_str;
      <FONT color=blue>static double</FONT>  g_double;
      <FONT color=blue>static long</FONT>    g_long;
      
      <FONT color=blue>void</FONT> NewXOfficeDoc(LPCTSTR aTemplate,LPCTSTR aStr,
                         <FONT color=blue>double </FONT>aDouble,<FONT color=blue>long </FONT>aLong)
      {
          CString   str;
          POSITION  pos = AfxGetApp()->GetFirstDocTemplatePosition();
          <FONT color=blue>while </FONT>(pos != NULL) {
      	   CDocTemplate *temp = AfxGetApp()->GetNextDocTemplate(pos);
              if (temp->GetDocString(str,CDocTemplate::docName) {
                  str == _T(<FONT color=teal>"XOffice"</FONT>)) {
                  g_template = aTemplate;
                  g_str      = aStr;
                  g_double   = aDouble;
                  g_long     = aLong;
                  temp->OpenDocumentFile(NULL);
                  <FONT color=blue>return</FONT>;
              }
          }
      }

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

    6. The MFC class ColeDocObjectItem provides the support for ActiveX documents. 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:

      <FONT color=green>// CntrItem.h</FONT>
      
      <FONT color=blue>class</FONT> CXOfficeCntrItem : <FONT color=blue>public</FONT> ColeDocObjectItem
      {
      ...
      <FONT color=blue>public</FONT>:
          CXOfficeCntrItem(CXOfficeDoc* pContainer,LPCTSTR);
          <FONT color=blue>bool</FONT> m_isCreate;
          <FONT color=blue>bool</FONT> CreateItem(LPCTSTR);
      ...
      };
      <FONT color=green>// CntrItem.cpp</FONT>
      
      CXOfficeCntrItem::CXOfficeCntrItem(CXOfficeDoc* pContainer,LPCTSTR templ)
          : COleDocObjectItem(pContainer), m_isCreate(<FONT color=blue>false</FONT>)
      {
          CreateItem(templ);
      }
      
      <FONT color=blue>bool </FONT> CXOfficeCntrItem::CreateItem(LPCTSTR templ)
      {
          USES_CONVERSION;
      
          <FONT color=green>// get storage for the object via virtual function call</FONT>
          m_dwItemNumber = GetNewItemNumber();
          GetItemStorage();
      
          <FONT color=green>// add AfxOleInit(); in CXOfficeApp::InitInstance</FONT>
          AfxOleGetMessageFilter()->EnableNotRespondingDialog(FALSE);
      
          <FONT color=green>// attempt to create the object</FONT>
          LPOLECLIENTSITE lpClientSite = GetClientSite();
          SCODE sc = ::OleCreateFromFile(CLSID_NULL,
                                         T2COLE(templ),
                                         IID_IUnknown,
                                         OLERENDER_DRAW,
                                         NULL,
                                         lpClientSite,
                                         m_lpStorage,
                                        (LPVOID*)&m_lpObject);
          <FONT color=blue>return</FONT> 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.
      <FONT color=green>// XOfficeDoc.h</FONT>
      
      ...
      <FONT color=blue>class</FONT> CXOfficeCntrItem;
      
      <FONT color=blue>class</FONT> CXOfficeDoc : <FONT color=blue>public</FONT> COleDocument,
      ...
      {
      ...
      <FONT color=blue>public</FONT>:
          CXOfficeCntrItem *m_ctrl;
          CString           m_template;
          CString           m_str;
          <FONT color=blue>double            </FONT>m_double;
          <FONT color=blue>long              </FONT>m_long;
      
          <FONT color=blue>bool </FONT>LoadTemplate();
      ...
      };
      <FONT color=green>// XOfficeDoc.cpp</FONT>
      
      CXOfficeDoc::CXOfficeDoc()
      : m_ctrl(0)
      {
          EnableCompoundFile();
      }
      
      BOOL CXOfficeDoc::OnNewDocument()
      {
      <FONT color=blue>    if </FONT>(!COleDocument::OnNewDocument())
      <FONT color=blue>        return </FONT>FALSE;
      
          m_template = g_template;
          m_str      = g_str;
          m_double   = g_double;
          m_long     = g_long;
      
      <FONT color=blue>    return </FONT>LoadTemplate();
      }
      
      <FONT color=blue>bool </FONT>CXOfficeDoc::LoadTemplate()
      {
          <FONT color=blue>char </FONT>path [_MAX_PATH];
          <FONT color=blue>char </FONT>drive[_MAX_DRIVE];
          <FONT color=blue>char </FONT>dir  [_MAX_DIR];
          <FONT color=blue>char </FONT>fname[_MAX_FNAME];
          <FONT color=blue>char </FONT>ext  [_MAX_EXT];
      
          ::GetModuleFileName(NULL,path,<FONT color=blue>sizeof</FONT>(path));
          _splitpath(path,      drive,dir,0,    0);
          _splitpath(g_template,0,    0,  fname,ext);
          _makepath (path,      drive,dir,fname,ext);
      
          {
              CWaitCursor cw;
              m_ctrl = <FONT color=blue>new </FONT>CXOfficeCntrItem(<FONT color=blue>this</FONT>,path);
          }
          <FONT color=blue>if </FONT>(m_ctrl == 0  ||  m_ctrl->m_isCreate == <FONT color=blue>false</FONT>) {
              CString str  = <FONT color=teal>"Can not open the doc:\n"</FONT>;
                      str += path;
              AfxMessageBox(str,MB_ICONSTOP);
      <FONT color=blue>        return false</FONT>;
          }
      <FONT color=blue>    return true</FONT>;
      }
      <FONT color=green>// XOfficeView.cpp</FONT>
      
      <FONT color=blue>void </FONT> 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.

      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:
      <FONT color=green>// XOfficeDoc.cpp</FONT>
      
      <FONT color=blue>void </FONT> CXOfficeDoc::Serialize(CArchive& ar)
      {
          <FONT color=blue>if </FONT>(ar.IsStoring()) {
              ar << m_template << m_str << m_double << m_long;
          } <FONT color=blue>else </FONT>      {
              ar >> m_template >> m_str >> m_double >> m_long;
          }
      <FONT color=green>//    COleDocument::Serialize(ar);</FONT>
      }
      
      BOOL CXOfficeDoc::OnOpenDocument(LPCTSTR lpszPathName)
      {
          <FONT color=blue>if </FONT>(!COleDocument::OnOpenDocument(lpszPathName))
              <FONT color=blue>return </FONT>FALSE;
          <FONT color=blue>return </FONT>LoadTemplate();
      }
      
      BOOL CXOfficeDoc::SaveModified()
      {
          <FONT color=blue>return </FONT>CDocument::SaveModified();
      }
      <FONT color=green>// CntrItem.cpp</FONT>
      
      <FONT color=blue>void </FONT> 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:
      <FONT color=green>// CntrItem.h</FONT>
      
      ...
      <FONT color=blue>#include </FONT><comdef.h> <comdef.h>
      ...
      <FONT color=blue>class </FONT>CXOfficeCntrItem : <FONT color=blue>public </FONT>COleDocObjectItem
      {
      <FONT color=blue>public</FONT>:
      ...
          <FONT color=blue>int          </FONT>m_who;      // 0 - ?, 1 - Word, 2 - Excel
          IDispatchPtr m_disp;
      
          LPDISPATCH   GetIDispatch();
          <FONT color=blue>void         </FONT>AttachDisp  ();
          <FONT color=blue>void         </FONT>ActivateDisp();
          <FONT color=blue>void         </FONT>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.

      <FONT color=green>// CXOfficeView.cpp</FONT>
      
      <FONT color=blue>void </FONT>CXOfficeView::OnInitialUpdate()
      {
      ...
          m_pSelection = GetDocument()->m_ctrl;
          m_pSelection->AttachDisp();
      
      <FONT color=green>    //Active documents should always be activated</FONT>
      ...
          m_pSelection->ActivateDisp();
      }
      <FONT color=green>// CXOfficeDoc.cpp</FONT>
      
      <FONT color=blue>void </FONT>CXOfficeDoc::OnCloseDocument()
      {
          <FONT color=blue>if </FONT>(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 codeb>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 have defined this one (Workbook_Activate) as the best and most stable. As I have already said, the direct call of macros from VBA leaves Excel in memory even when the program is finished.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here


    Written By
    Architect
    United States United States
    This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

    Comments and Discussions

     
    GeneralRe: Open .csv file using excel Pin
    Anonymous12-Nov-03 12:32
    Anonymous12-Nov-03 12:32 
    GeneralEmbed Excel Without Merged Menus.. Pin
    Mardigin13-Jul-03 5:10
    Mardigin13-Jul-03 5:10 
    GeneralRe: Embed Excel Without Merged Menus.. Pin
    Anonymous20-Aug-04 22:29
    Anonymous20-Aug-04 22:29 
    GeneralLink Dead Pin
    Mardigin12-Jul-03 8:10
    Mardigin12-Jul-03 8:10 
    GeneralMaking Word Read-Only Pin
    Neil Katalino20-Jun-03 19:12
    Neil Katalino20-Jun-03 19:12 
    GeneralRe: Making Word Read-Only Pin
    triendl.kj14-May-04 2:38
    professionaltriendl.kj14-May-04 2:38 
    GeneralRe: Making Word Read-Only Pin
    Rebecca9931-May-16 19:42
    Rebecca9931-May-16 19:42 
    General_CommandBarsPtr error Pin
    Amateur20-Jun-03 0:49
    Amateur20-Jun-03 0:49 
    GeneralRe: _CommandBarsPtr error Pin
    Tarek Ahmed Abdel Rahmane20-Mar-05 13:27
    Tarek Ahmed Abdel Rahmane20-Mar-05 13:27 
    GeneralNo Toolbar's Pin
    _Grisu15-Jun-03 23:41
    _Grisu15-Jun-03 23:41 
    GeneralRe: No Toolbar's Pin
    triendl.kj14-May-04 2:36
    professionaltriendl.kj14-May-04 2:36 
    Generalneed to open (or create) a MS Word Doc file Pin
    jameshou11-Jun-03 6:06
    jameshou11-Jun-03 6:06 
    QuestionHow can i load doc in a activeX control? Pin
    oiq24-May-03 20:41
    oiq24-May-03 20:41 
    Generalerror C2039: '_WorkbookPtr' : is not a member of 'Excel' Pin
    Ben G.22-May-03 5:59
    Ben G.22-May-03 5:59 
    GeneralThe excel server does not repaint properly in the active document container Pin
    venu koduru22-May-03 5:51
    venu koduru22-May-03 5:51 
    Questioncan MFC not to load the menu and toolbar? Pin
    sparklee13-May-03 2:43
    sparklee13-May-03 2:43 
    QuestionWeb toolbar disabled? Pin
    Anonymous7-May-03 1:47
    Anonymous7-May-03 1:47 
    GeneralDead link Pin
    JockeP3-Apr-03 12:48
    JockeP3-Apr-03 12:48 
    GeneralRe: Dead link Pin
    dpitman8-Apr-03 14:42
    dpitman8-Apr-03 14:42 
    QuestionHow to get data from ms office documents Pin
    lri_1224-Mar-03 23:12
    lri_1224-Mar-03 23:12 
    GeneralAdd VBA in my MFC application Pin
    Nader Michael8-Mar-03 3:48
    Nader Michael8-Mar-03 3:48 
    QuestionHow to add MS Paint in application Pin
    Hemant kulkarni27-Feb-03 18:31
    Hemant kulkarni27-Feb-03 18:31 
    GeneralGlobal Object- Urgent Pin
    rishindra12-Feb-03 18:15
    rishindra12-Feb-03 18:15 
    GeneralRe: Global Object- Urgent Pin
    13-Feb-03 22:55
    suss13-Feb-03 22:55 
    GeneralRe: Global Object- Urgent Pin
    rishindra16-Apr-03 0:09
    rishindra16-Apr-03 0:09 

    General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

    Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.