首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++Builder OLE 自動化控制 Word 97 完整實戰指南

     編輯:關於C++

透過 OLE 自動化,C++Builder 應用程式可啟動 Word 97、建立/開啟文件、插入文字、圖片、繪圖物件,實現文件自動化產出。本文完整解析 ComObj.hpp 封裝的 Variant 操作介面、Word 物件模型對應關係、實戰代碼架構與常見坑點。

核心介面對應表

C++Builder 封裝Word VBA 對應用途
CreateOleObject("Word.Application")CreateObject("Word.Application")啟動/取得 Word 應用程式物件
GetActiveOleObject("Word.Application")GetObject(, "Word.Application")連接已執行的 Word 實例
Variant::OlePropertyGet/Set(name, val)obj.Property = val / val = obj.Property存取屬性
Variant::OleProcedure(name, args...)obj.Method args呼叫無傳回值方法
Variant::OleFunction(name, args...)Set obj = obj.Method(args)呼叫有傳回值方法(回傳子物件)

Word 97 物件模型關鍵路徑

Application
  ├─ Documents (集合)
  │    ├─ Add() → Document (新文件)
  │    └─ Open(FileName) → Document (開啟檔案)
  ├─ ActiveDocument → Document (目前作用中文件)
  └─ WordBasic (相容舊版巨集介面)

Document
  ├─ Paragraphs (集合)
  │    ├─ Add() → Paragraph (新增段落)
  │    └─ Item(Index) → Paragraph
  ├─ Range(Start, End) → Range (文字範圍)
  ├─ Shapes (集合)
  │    ├─ AddLine(x1,y1,x2,y2) → Shape (直線)
  │    └─ AddPicture(FileName, ...) → Shape (圖片)
  ├─ ComputeStatistics(wdStatisticPages/Paragraphs) → Long
  └─ Password (屬性) 設定開啟密碼

完整實戰代碼架構

表單類別宣告 (Unit1.h)

#ifndef Unit1H
#define Unit1H
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComObj.hpp> // 關鍵:OLE 自動化支援
#include <Dialogs.hpp>
#include <ExtCtrls.hpp>

class TForm1 : public TForm {
__published:
    TButton *Button1; // 開啟檔案
    TOpenDialog *OpenDialog1;
    TButton *Button2; // 新建文件
    TMemo *Memo1;     // 輸入文字
    TButton *Button3; // 插入文字到游標段落
    TButton *Button5; // 插入直線與圖片
    TLabel *Label1, *Label2, *Label3;
    TBevel *Bevel1, *Bevel2;
    void __fastcall Button1Click(TObject *Sender);
    void __fastcall Button2Click(TObject *Sender);
    void __fastcall Button3Click(TObject *Sender);
    void __fastcall Button5Click(TObject *Sender);
private:
    Variant V, Vdocuments, Vdocument1, Vdocument2, Vactive; // Word 物件參考
    int tag; // 追蹤段落索引
    Variant __fastcall GetWordObject(); // 取得/啟動 Word
    void __fastcall Add(int paraIndex); // 插入文字到指定段落
public:
    __fastcall TForm1(TComponent* Owner);
};
extern PACKAGE TForm1 *Form1;
#endif

核心實作 (Unit1.cpp)


#include <vcl.h>
#pragma hdrstop
#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
    tag = 1; // 段落計數器起始值
}

// 取得 Word.Application:優先連接現有實例,否則啟動新實例
Variant __fastcall TForm1::GetWordObject() {
    Variant Obj;
    AnsiString AppName = "Word.Application";
    HWND hPrevApp = ::FindWindow(NULL, "Microsoft Word");
    if (!hPrevApp)
        Obj = CreateOleObject(AppName); // 啟動新實例
    else
        Obj = GetActiveOleObject(AppName); // 連接現有實例
    Obj.OlePropertySet("Visible", true); // 顯示 Word 視窗
    return Obj;
}

// 開啟現有文件並顯示頁數
void __fastcall TForm1::Button1Click(TObject *Sender) {
    V = GetWordObject();
    Vdocuments = V.OleFunction("Documents"); // 取得 Documents 集合
    if (OpenDialog1->Execute()) {
        Vdocument1 = Vdocuments.OleFunction("Open", OpenDialog1->FileName);
        int pages = Vdocument1.OleFunction("ComputeStatistics", 2); // wdStatisticPages = 2
        Label1->Caption = "檔案 " + Vdocument1.OlePropertyGet("Name") + " 頁數: " + IntToStr(pages);
    }
}

// 新建文件並設定密碼
void __fastcall TForm1::Button2Click(TObject *Sender) {
    V = GetWordObject();
    Vdocuments = V.OleFunction("Documents");
    Vdocument2 = Vdocuments.OleFunction("Add"); // 新建空白文件
    Vdocument2.OlePropertySet("Password", "12345"); // 設定開啟密碼
}

// 在目前作用中文件新增段落並寫入 Memo1 文字
void __fastcall TForm1::Button3Click(TObject *Sender) {
    Vactive = V.OleFunction("ActiveDocument");
    int paras = Vactive.OleFunction("ComputeStatistics", 4); // wdStatisticParagraphs = 4
    Vactive.OleFunction("Paragraphs").OleFunction("Add"); // 新增段落
    Add(paras + tag); // 寫入新段落
    Memo1->Clear();
    tag++;
}

// 共用:向指定段落索引寫入格式化文字
void __fastcall TForm1::Add(int i) {
    Variant range = ((Vactive.OleFunction("Paragraphs")).OleFunction("Item", i)).OleFunction("Range");
    range.OleFunction("Font").OlePropertySet("Italic", true);
    range.OleFunction("Font").OlePropertySet("Size", 18);
    range.OleFunction("Font").OlePropertySet("ColorIndex", 6); // wdBlue
    range.OlePropertySet("Text", Memo1->Text);
}

// 插入直線與圖片
void __fastcall TForm1::Button5Click(TObject *Sender) {
    Variant Vactive1 = V.OleFunction("ActiveDocument");
    Variant Vshape = Vactive1.OleFunction("Shapes");
    // AddLine(Left, Top, Right, Bottom) - 單位為 Points
    Vshape.OleFunction("AddLine", 90, 80, 400, 80); 
    if (OpenDialog1->Execute()) {
        // AddPicture(FileName, LinkToFile, SaveWithDocument, Left, Top, Width, Height)
        Vshape.OleFunction("AddPicture", OpenDialog1->FileName, 
            false, true, 50, 50, 80, 100);
    }
}

關鍵技術細節與避坑指南

問題原因解決方案
第二次啟動程式無法連接 WordFindWindow 找到視窗但 GetActiveOleObject 失敗檢查 Word 是否以相同權限執行(UAC);嘗試 Obj = V.OleFunction("Application") 取得父應用程式
中文路徑/檔名開啟失敗AnsiString 編碼與 Word 期望不符使用 WideString 或 OleVariant 傳遞 Unicode 路徑
插入圖片後檔案過大AddPicture 預設 LinkToFile=false, SaveWithDocument=true 嵌入完整圖片若需連結不嵌入,設 LinkToFile=true;但移動檔案會斷連
段落索引計算錯誤ComputeStatistics 回傳總段落數,新增後索引為 n+1如代碼所示:先取得現有段落數,Add 後寫入 paras + tag
程式結束 Word 仍殘留進程未正確釋放 Variant 參考,或 Word 有未儲存提示對話框阻塞結束前呼叫 V.OleProcedure("Quit");確保所有 Variant 變數離開作用域
執行緒模型衝突 (CO_E_NOTINITIALIZED)VCL 主執行緒為 STA,若在背景執行緒呼叫 OLE 需 CoInitializeEx所有 OLE 操作留在主執行緒;或背景執行緒自行 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)

現代化建議:Word 97 → Office 365 / LibreOffice

  1. 改用 Microsoft.Office.Interop.Word (Primary Interop Assemblies):強型別、IntelliSense、編譯期檢查,但仍需安裝 Office。
  2. 無 Office 環境方案:
    • libreoffice --headless --convert-to pdf 命令列轉換
    • DocX (NuGet) 純 .NET 建立 .docx,無需 Word
    • OpenXML SDK 底層操作 .docx XML 結構
    • Aspose.Words / GemBox.Document 商業庫功能完整
  3. C++Builder 現代專案:可透過 TMS Office Bridge、CppInterop 封裝 .NET 庫,或直接呼叫 LibreOffice UNO API。

總結

C++Builder 的 ComObj.hpp + Variant 提供了極薄的 OLE 自動化封裝,核心邏輯在於:

  1. CreateOleObject/GetActiveOleObject 取得 Application 根物件
  2. 透過 OleFunction 走訪集合(Documents、Paragraphs、Shapes)
  3. 用 OlePropertyGet/Set 存取屬性,OleProcedure 執行動作
  4. 嚴格管理 Variant 生命週期,確保 Word 正常退出

雖然 Word 97 早已退休,但此物件模型在 Office 365 中大體相容,掌握此架構即可遷移至新版本或替代方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved