65.9K
CodeProject 正在变化。 阅读更多。
Home

Borland C++ MS Word 自动化

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.67/5 (15投票s)

2004年4月23日

9分钟阅读

viewsIcon

235878

downloadIcon

4553

此代码将演示如何自动化 MS Word 文档并从中获取内容。

Sample Image

引言

最初,我编写了一个 C++ 解析器,用于解析给定的 MS Word 文档,并将其放入某种更适合数据处理的结构中。编写完解析器后,我开始使用 .NET 和 C# 来重新创建该解析器。在此过程中,我还为 Code Project 撰写了我的第一篇文章,《使用 Visual Studio .NET 自动化 MS Word》。许多人请求查看该应用程序的 C++ 版本,因此,我终于有时间来整理一下。我撰写本文的目的是为了让寻找快速答案的人们更容易上手。我希望人们能从提供的信息中受益,并帮助他们更快地开始。

背景

不需要任何特殊的背景知识。只需具备一些 C++ 的实际操作经验即可。

使用代码

我认为呈现代码的最佳方式是首先提供获取 MS Word 实例的关键部分,然后提供执行特定功能的代码快照。我相信这种方式将有助于您更快地开始开发自己的程序。

以下代码块是 CPP 文件的头部分。

注意: 最重要的 include 文件是 <utilcls.h><comobj.hpp>。这些用于 COM 和 OLE。

// Vahe Karamian - 04-20-2004 - For Code Project
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

// We need this for the OLE object
#include <utilcls.h>
#include <comobj.hpp>
#include "Unit1.h"
#include <except.h>
//---------------------------------------------------------------------------

#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

以下代码块创建了 MS Word COM 对象。这个对象将用于访问 MS Word 应用程序的功能。要查看可用的功能,您可以在 MS Word 中进行操作。请参阅第一篇文章《使用 Visual Studio .NET 自动化 MS Word》。

与之前一样,您可以创建一个 Windows 窗体应用程序或一个命令行应用程序,过程是相同的。下面的代码基于一个 Windows 窗体应用程序,其中有一个按钮用于启动该过程。当用户单击按钮时,将调用 Button1Click(TObject *Sender) 事件并执行代码。

注意: 为了更好地理解代码,请忽略代码中的所有内容,除了粗体部分。

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    .
    .
    .

    // used for the file name
    OleVariant fileName;

    fileName = openDialog->FileName;

    Variant my_word;
    Variant my_docs;

    // create word object
    my_word = Variant::CreateObject( "word.application" );
    // make word visible, to make invisible put false
    my_word.OlePropertySet( "Visible", (Variant) true );

    // get document object
    my_docs = my_word.OlePropertyGet( "documents" );

    Variant wordActiveDocument = my_docs.OleFunction( "open",  fileName );

    .
    .
    .

因此,简而言之,我们定义了一个名为 fileNameOleVariant 数据类型,并将文件路径分配给我们的 fileName 变量。在上面的代码中,这是使用 OpenDialog 对象完成的。当然,您也可以像下面这样直接分配一个完整的路径进行测试,例如 c:\\test.doc

接下来,我们定义了两个 Variant 数据类型,分别称为 my_wordmy_docsmy_word 将用于创建 word.application 对象,而 my_docs 将用于创建 documents 对象。

接下来,我们定义了另一个 Variant 数据类型,称为 myActiveDocument。使用这个引用对象,我们现在就可以做任何我们想做的事情了!在这种情况下,我们将打开给定的 MS Word 文档。

请注意,大多数变量的类型都是 Variant

此时,我们已经有了一个 Word 文档,可以开始对其执行各种功能了。一开始,您可能需要花一些时间来理解它的工作原理,但一旦掌握了窍门,MS Word 领域内的任何事情都是可能的。

让我们看一下下面的代码,它将处理 MS Word 文档中的表格。

        .
        .
        Variant wordTables = wordActiveDocument.OlePropertyGet( "Tables" );
        long table_count = wordTables.OlePropertyGet( "count" );
        .
        .

正如我之前提到的,您所有的数据类型都将是 Variant。所以我们声明一个 Variant 数据类型,名为 wordTables,用于表示我们 Document 对象中的 Tables 对象。

Variant wordTables = wordActiveDocument.OlePropertyGet( "Tables" );

上面的行将返回我们当前 Document 对象中的所有 Table 对象。由于 TablesDocument 对象的一个属性,我们必须使用 OlePropertyGet( "Tables" ); 来获取该值。

long table_count = wordTables.OlePropertyGet( "count" );

上面的行将返回我们 Tables 对象中表格的数量。这是通过调用 OlePropertyGet( "count" ); 来获取该值来实现的。

您可能会想,这些信息是从哪里来的?这个问题的答案就在第一篇文章中:《使用 Visual Studio .NET 自动化 MS Word》。

下一个代码块将演示如何从 Tables 对象中提取内容。

.
.
.
int t, r, c;

try
{
    for( t=1; t<=table_count; t++ )
    {
        Variant wordTable1 = wordTables.OleFunction( "Item", (Variant) t );
        Variant tableRows = wordTable1.OlePropertyGet( "Rows" );
        Variant tableCols = wordTable1.OlePropertyGet( "Columns" );

        long row_count, col_count;
        row_count = tableRows.OlePropertyGet( "count" );
        col_count = tableCols.OlePropertyGet( "count" );

        // LET'S GET THE CONTENT FROM THE TABLES
        // THIS IS GOING TO BE FUN!!!
        for( r=1; r<=row_count; r++ )
        {
            Variant tableRow = tableRows.OleFunction( "Item", (Variant) r );
            tableRow.OleProcedure( "Select" );
            Variant rowSelection = my_word.OlePropertyGet( "Selection" );

            Variant rowColumns = rowSelection.OlePropertyGet( "Columns" );
            Variant selectionRows = rowSelection.OlePropertyGet( "Rows" );

            long rowColumn = rowColumns.OlePropertyGet( "count" );

            for( c=1; c<=rowColumn; c++ ) //col_count; c++ )
            {
                Variant rowCells = tableRow.OlePropertyGet( "cells" );
                Variant wordCell = wordTable1.OleFunction( "Cell", 
                                             (Variant) r, (Variant) c );
                Variant cellRange = wordCell.OlePropertyGet( "Range" );

                Variant rangeWords = cellRange.OlePropertyGet( "Words" );

                long words_count = rangeWords.OlePropertyGet( "count" );

                AnsiString test = '"';
                for( int v=1; v<=words_count; v++ )
                {
                    test = test + rangeWords.OleFunction( "Item", 
                                                 (Variant) v ) + " ";
                }
                test = test + '"';
            }
        }
    }
    my_word.OleFunction( "Quit" );
}
catch( Exception &e )
{
    ShowMessage( e.Message + "\nType: " + __ThrowExceptionName() +
        "\nFile: "+ __ThrowFileName() +
        "\nLine: " + AnsiString(__ThrowLineNumber()) );
}
.
.
.

好的,上面是实际遍历文档对象中所有表格并从中提取内容的的代码。所以我们有表格,表格有行和列。要遍历文档中的所有 Tables 对象,我们需要计算数量并获取文档中的表格数量。

所以我们有三个嵌套的 for 循环。第一个用于实际的 Table 对象,第二个和第三个用于当前 Table 对象的行和列。我们创建了三个新的 Variant 数据类型,分别称为 wordTable1tableRowstableCols

注意:请注意,wordTable1 来自 wordTables 对象。我们通过调用 wordTables.OleFunction( "Item", (Variant) t ); 来获取表格。这将从 Tables 对象中返回一个唯一的 Table 对象。

接下来,我们获取给定 Table 对象的 RowsColumns 对象。这是通过调用 wordTable1 对象的 OlePropertyGet( "Rows" );OlePropertyGet( "Columns" ); 来实现的!

接下来,我们获取给定 wordTable1 对象所属的 RowsColumns 对象中的行和列的数量。我们准备好遍历它们并获取内容。

现在,我们需要定义四个新的 Variant 数据类型,分别称为 tableRowrowSelectionrowColumsnselectionRows。现在,我们可以开始从选定行的每一列中获取内容了。

在最内层的 for 循环中,即最后一个循环,我们再次定义了四个新的 Variant 数据类型,分别称为 rowCellswordCellcellRangerangeWords。是的,这很繁琐,但我们必须这样做。

让我们总结一下我们到目前为止所做的:

  1. 我们获取了当前 Document 对象中 Tables 对象的集合。
  2. 我们获取了当前 Table 对象中行和列的集合。
  3. 我们遍历了每一行并获取了它拥有的列数。
  4. 我们获取了列和单元格,并遍历单元格以获取表格的内容。

注意:是的,有些步骤是重复的,但原因在于并非给定文档中的所有表格都是统一的!也就是说,并不一定意味着如果第 1 行有 3 列,那么第 2 行也必须有 3 列。更有可能的是,它会有不同数量的列。您对此可以归因于文档的作者/所有者。

所以,最后的步骤就是遍历单元格并获取内容,然后将它们连接成一个单一的字符串输出。

最后,我们要退出 Word 并关闭所有文档。

        ...
        my_word.OleFunction( "Quit" );
        ...

基本上就是这样了。代码有时会变得相当繁琐和混乱。自动化/使用 Word 的最佳方法是首先确切地知道您想要做什么。一旦您知道了要实现的目标,就需要找出您需要使用哪些对象或属性来执行您想要的操作。这部分很棘手,您需要阅读文档:《使用 Visual Studio .NET 自动化 MS Word》。

在接下来的代码块中,我将向您展示如何打开现有文档、创建新文档,使用选择性粘贴功能从现有文档中选择内容并将其粘贴到新文档中,然后进行清理,即查找和替换功能。

在您查看代码块之前,以下列表将标识哪个变量用于标识哪个对象以及可以应用于它们的函数。

变量和表示

  • vk_filename:现有文档名称
  • vk_converted_filename:新文档名称
  • vk_this_doc:现有文档对象
  • vk_converted_document:新文档对象
  • vk_this_doc_select:现有文档选择对象
  • vk_this_doc_selection:现有文档的选区
  • vk_converted_document_select:新文档选择对象
  • vk_converted_document_selection:新文档的选区
  • wordSelectionFind:查找和替换对象
// Get the filename from the list of files in the OpenDialog
vk_filename = openDialog->Files->Strings[i];
vk_converted_filename = openDialog->Files->Strings[i] + "_c.doc";

// Open the given Word file
vk_this_doc = vk_word_doc.OleFunction( "Open", vk_filename );

statusBar->Panels->Items[2]->Text = "READING";

// -------------------------------------------------------------------
// Vahe Karamian - 10-10-2003
// This portion of the code will convert the word document into
// unformatted text, and do extensive clean up
statusBar->Panels->Items[0]->Text = "Converting to text...";
vk_timerTimer( Sender );

// Create a new document
Variant vk_converted_document = vk_word_doc.OleFunction( "Add" );

// Select text from the original document
Variant vk_this_doc_select = vk_this_doc.OleFunction( "Select" );
Variant vk_this_doc_selection = vk_word_app.OlePropertyGet( "Selection" );

// Copy the selected text
vk_this_doc_selection.OleFunction( "Copy" );

// Paste selected text into the new document
Variant vk_converted_document_select = 
    vk_converted_document.OleFunction( "Select" );
Variant vk_converted_document_selection = 
    vk_word_app.OlePropertyGet( "Selection" );
vk_converted_document_selection.OleFunction( "PasteSpecial", 
    0, false, 0, false, 2 );

// Re-Select the text in the new document
vk_converted_document_select = 
   vk_converted_document.OleFunction( "Select" );
vk_converted_document_selection = 
   vk_word_app.OlePropertyGet( "Selection" );

// Close the original document
vk_this_doc.OleProcedure( "Close" );

// Let's do out clean-up here ...
Variant wordSelectionFind = 
    vk_converted_document_selection.OlePropertyGet( "Find" );

statusBar->Panels->Items[0]->Text = "Find & Replace...";
vk_timerTimer( Sender );

wordSelectionFind.OleFunction( "Execute", "^l", 
  false, false, false, false, false, true, 1, false, 
  " ", 2, false, false, false, false );
wordSelectionFind.OleFunction( "Execute", "^p", false, 
  false, false, false, false, true, 1, false, 
  " ", 2, false, false, false, false );

// Save the new document
vk_converted_document.OleFunction( "SaveAs", vk_converted_filename );

// Close the new document
vk_converted_document.OleProcedure( "Close" );
// -------------------------------------------------------------------

所以在上面的代码中,我们使用 vk_this_doc = vk_word_doc.OleFunction( "Open", vk_filename ); 来打开一个现有文档。接下来,我们使用 Variant vk_converted_document = vk_word_doc.OleFunction( "Add" ); 添加一个新文档。然后,我们想从现有文档中选择内容并将它们粘贴到我们的新文档中。这一部分是通过 Variant vk_this_doc_select = vk_this_doc.OleFunction( "Select" ); 获取一个选择对象,并通过 Variant vk_this_doc_selection = vk_word_app.OlePropertyGet( "Selection" ); 获取对实际选区的引用来完成的。然后,我们使用 vk_this_doc_selection.OleFunction( "Copy" ); 复制选区。接下来,我们对新文档执行相同的任务,使用 Variant vk_converted_document_select = vk_converted_document.OleFunction( "Select" );Variant vk_converted_document_selection = vk_word_app.OlePropertyGet( "Selection" );。此时,我们拥有了现有文档和新文档的选择对象。现在,我们将同时使用它们来进行特殊粘贴,使用 vk_converted_document_selection.OleFunction( "PasteSpecial", 0, false, 0, false, 2 );。现在,我们的原始内容已以特殊格式粘贴到新创建的文档中。在进行查找和替换之前,我们需要在新文档中进行新的选择调用。为此,我们只需使用相同的调用 vk_converted_document_select = vk_converted_document.OleFunction( "Select" );vk_converted_document_selection = vk_word_app.OlePropertyGet( "Selection" );。接下来,我们使用 Variant wordSelectionFind = vk_converted_document_selection.OlePropertyGet( "Find" ); 创建一个 Find 对象,最后,我们可以使用我们的 Find 对象来执行查找和替换,使用 wordSelectionFind.OleFunction( "Execute", "^l", false, false, false, false, false, true, 1, false, " ", 2, false, false, false, false );

就是这样!

关注点

将结构化应用于 Word 文档是一项艰巨的任务,因为许多人都有不同的文档编写方式。尽管如此,对于组织来说,开始对其文档进行建模将会有所帮助。这将允许他们将 XML 架构应用于其文档,并使其从文档中提取内容更加容易。这对于大多数公司来说是一项艰巨的任务;通常,他们要么缺乏专业知识,要么缺乏资源。而且这类项目规模庞大,因为它们将影响到一个以上的职能业务领域。但长远来看,这将使整个组织受益。您的文档由结构化数据驱动而非格式驱动,并且不会丢失文档,这将为您的业务带来许多增值。

© . All rights reserved.