在MFC应用程序中动态嵌入Word文档

  引言

   Microsoft Office 办公套件以其功能强大、方便实用而被广泛使用。其中的Word软件更是作为最流行的文字处理软件而使Word格式文档成为事实上的文件交换标准之一。出于对当前Word格式文档文件流行程度的现实考虑,如果自己开发的字处理软件(或类似功能的软件)不支持Word格式文档那几乎是不可想象的,这样的软件也必然会由于不能处理占相当比例的Word文档文件而不被用户所认同。所以,在此类软件开发时,添加对Word格式文档文件的支持是必不可少的。本文将就此话题展开讨论。

   添加类型库到工程

  在应用程序中添加对Word支持的方法有很多,但无非以下两大类:一类是自己编写代码或借助第三方提供的程序开发包实现对Word的支持;另一类方法是直接在程序中对Word提供的各种外部功能接口进行调用而同样达到支持Word的目的。前一种方法工作量相对较大,而且无论是在功能上还是在可靠性方面都很难达到与Word一样的水准。后一类方法实际是采取某种技术途径而将支持Word文档的功能转交给Word软件去完成,这样的处理显然能够达到与Word一样的对Word文档文件的支持水平。这一类方法主要借助DDE或COM等技术途径实现,本文将通过COM中的自动化技术借助Word提供的各种外部功能接口实现对Word文档的支持。采用这种方式可以尽可能少的占用自动化客户的资源,并且不需要被访问对象的类型信息就可以进行调用。 

  下面给出具体实施过程。字处理软件通常采取单文档或多文档程序结构,为了使用Word提供的COM 组件,建立的应用程序需要是包容器,因此在用AppWizard创建一个新的MFC AppWizard(EXE)工程时选择单文档视图(SDI)或多文档视图(MDI)结构,并在第3步选中Container,以提供容器支持。 其它可采取默认选项。在ClassView中将产生如下类:

  应用类: CEmbed_WordApp in Embed_Word.h and Embed_Word.cpp 

  框架类: CMainFrame in MainFrm.h and MainFrm.cpp 

  文档类: CEmbed_WordDoc in Embed_WordDoc.h and Embed_WordDoc.cpp 

  视图类: CEmbed_WordView in Embed_WordView.h and Embed_WordView.cpp 

  容器类: CEmbed_WordCntrItem in CntrItem.h and CntrItem.cpp 

  接下来添加Word类型库到工程。在 View 菜单中选择ClassWizard子菜单,从弹出对话框的Automation选项卡中点击Add Class按钮,选择From a TypeLibrary并在Office目录中选中Microsoft Word 97/2000 类型库Word8.olb或Word9.olb,这将把类型库中的所有类添加到你的工程中。这时,ClassView中会多出几十个类,可以通过这些类提供的接口来实现对Word文档的支持。

  为了获取标准COM接口IDispach,可在CCntrItem类中添加返回数据类型为LPDISPATCH 的GetIDispatch()函数,该函数通过QueryInterface()方法对IID_Idispatch接口的查询而返回得到指向IDispach接口的指针: 

ASSERT_VALID(this);
ASSERT(m_lpObject != NULL);
LPUNKNOWN lpUnk = m_lpObject;
Run();
LPOLELINK lpOleLink = NULL;
if(m_lpObject->QueryInterface(IID_IOleLink,(LPVOID FAR*)&lpOleLink)== NOERROR)
{
 ASSERT(lpOleLink != NULL);
 lpUnk = NULL;
 if(lpOleLink->GetBoundSource(&lpUnk) != NOERROR)
 {
  TRACE0("Warning: Link is not connected!\n");
  lpOleLink->Release();
 }
 ASSERT(lpUnk != NULL);
}
LPDISPATCH lpDispatch = NULL;
if(lpUnk->QueryInterface(IID_IDispatch,(LPVOID FAR*)&lpDispatch) != NOERROR)
{
 TRACE0("Waring: does not support IDispatch!\n");
 return NULL;
}
ASSERT(lpDispatch != NULL);
return lpDispatch;

  为了使用类型库中的方法,需要在使用类型库的地方添加对“MSWord8.h”的引用(如使用Word 2000,则包含对“MSWord9.h”的引用)。

打开并显示Word文档

  在主框架类中添加ID_FILE_OPEN菜单命令响应函数,以便在打开Word文档时能够动态提供对其的支持。下面这段代码在得到Word文档文件的完整路径后,将通过发送WM_COMMAND消息来新建一个文档视图:

// 显示打开文件对话框
CFileDialog fileDlg(TRUE, "*.doc", "*.doc", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Word文件(*.doc)|*.doc||", NULL);
if (fileDlg.DoModal() == IDOK)
{
 // 得到文件路径
 m_sPath = fileDlg.GetPathName();
 // 新建文档
 PostMessage(WM_COMMAND, ID_FILE_NEW, 0);
}

  并在视图类的初始化更新函数OnInitialUpdate()中完成Word的动态嵌入:

CMainFrame* pFrame = (CMainFrame*)AfxGetApp()->GetMainWnd();
if (pFrame->m_sPath.Right(3) != "DOC" && pFrame->m_sPath.Right(3) != "doc")
 return;
m_sPath = pFrame->m_sPath;
EmbedAutomateWord();
if (m_pSelection != NULL)
{
 CRect rect;
 GetClientRect(&rect);
 CDC* pDC = GetDC();
 m_pSelection->Draw(pDC,rect);
 ReleaseDC(pDC);
}
m_pSelection = NULL;

  其中,EmbedAutomateWord ()函数将负责将Word嵌入到程序。其具体实现过程如下:

BeginWaitCursor();
CEmbed_WordCntrItem* pItem = NULL;
TRY

 CEmbed_WordDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 pItem = new CEmbed_WordCntrItem(pDoc);
 ASSERT_VALID(pItem);
 CLSID clsid;
 if (FAILED(::CLSIDFromProgID(L"Word.document", &clsid)))
  AfxThrowMemoryException();
 if (!pItem->CreateFromFile(m_sPath, clsid))
  AfxThrowMemoryException();
 pItem->DoVerb(OLEIVERB_SHOW, this);
 m_pSelection = pItem;
 pDoc->UpdateAllViews(NULL);
 LPDISPATCH lpDisp;
 lpDisp = pItem->GetIDispatch();
}
CATCH(CException, e)
{
 if (pItem != NULL)
 {
  ASSERT_VALID(pItem);
  pItem->Delete();
 }
 AfxMessageBox(IDP_FAILED_TO_CREATE); 
}
END_CATCH
EndWaitCursor();

  如果仔细研究过这段代码,会发现它同AppWizard自动生成的OnInsertObject()函数有着惊人的相似程度,事实上,上述代码只不过是OnInsertObject()的一个特例:OnInsertObject()允许用户从可用的OLE对象列表中选择其一插入到 应用 程序中。因为在此我们只需对Word进行自动化,所以派生了这一行为。在这里通过使用CreateFromFile()方法以打开由m_sPath指定的Word文档,并通过DoVerb()方法执行OLEIVERB_SHOW动词来完成文档显示动作。为了使嵌入的工作区占满整个客户区,需要在嵌入文档、客户区大小发生变化以及更新视图后调用Draw()方法进行重新绘制。

保存Word文档

  虽然已经将Word环境嵌入到应用程序,Word文档也已经打开并显示,但如果使用向导生成的“保存”功能去保存当前打开的Word文档却只能保存一个空的文档。这是因为当前嵌入Word后的程序界面中的“文件”菜单是属于包容器程序的,在没有和Word建立关联前是无法执行文档保存功能的。在其命令响应函数中添加下述代码以完成对文档的保存:

// 显示打开文件对话框
CFileDialog fileDlg(FALSE, "*.doc", "*.doc", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Word文件(*.doc)|*.doc||", NULL);
if (fileDlg.DoModal() != IDOK)
 return;
CString sPath = fileDlg.GetPathName();
char cPath[256];
memset(cPath, 0, sizeof(cPath));
memcpy(cPath, sPath, sPath.GetLength());
WCHAR wPath[256];
memset(wPath, 0, sizeof(wPath));
MultiByteToWideChar(CP_ACP, MB_COMPOSITE, cPath, sPath.GetLength(), wPath, sizeof(wPath));
IDispatch *pOle = m_pSelection->GetIDispatch();
if (!pOle )
 return; // No ole document
IPersistFile *pPFile = NULL;
pOle->QueryInterface(IID_IPersistFile, (void**)&pPFile);
if (!pPFile)
{
 // 不支持IPersistFile接口
 if (pOle)
  pOle->Release();
 if (pPFile)
  pPFile->Release();
}
else
pPFile->Save(wPath, FALSE);

  这段代码的核心思想是通过使用IPersistFile接口的Save()方法完成对当前文档的保存。由于该方法第一个参数是一个LPOLESTR型变量,查看其宏定义可以知道该变量是一个指向宽字符缓冲区的指针,由CFileDialog类成员函数GetPathName()所返回的CString型文档路径不能直接使用,必须通过MultiByteToWideChar()函数将其内容转换到一个WCHAR型数组中。其第一个参数由CP_ACP指定为使用ANSI编码包,第二个参数设置为MB_COMPOSITE以指出一直使用复合字符。后面四个变量分别为源缓冲区的地址、大小和目的缓冲区的地址和大小。

   小结

  编译执行程序,当程序打开Word文档时将动态启动Word自动化服务,这时 鼠标 会处于等待状态,当鼠标恢复正常状态时,Word已经被嵌入到了程序中来,工具条和菜单上增添有许多属于Word的工具条和菜单,可以通过它们对Word文档提供完善的支持和服务。本程序在Windows 2000 Professional 下由 Microsoft  Visual C++  6.0编译通过。程序运行需要有Microsoft Word 97 或 Word 2000支持。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值