使用 Visual C++ 的 Office 自動化

农民小工程师發表於2024-05-01

摘要

本文解答了有關從 Visual C++ 自動化到 Microsoft Office 的常見問題。

更多資訊

目錄

  1. 什麼是自動化?

  2. 我不熟悉自動化,在哪裡可以找到要了解詳細資訊的好資源?

  3. 是否有其他方法可以使用自動化?

  4. 什麼是 COM?

  5. 如何實現附加到正在執行的 Office 應用程式例項?

  6. 如何實現傳遞可選引數?

  7. 如何實現捕獲 Office 應用程式公開的事件?

  8. 我的自動化程式碼太慢了。 如何加快速度?

  9. 這些巨大的錯誤值(如 -2147352573 或 0x80030002)是什麼意思?

  10. 什麼是型別庫?

  11. 我的自動化程式碼適用於 Microsoft Excel 95,但 Microsoft Excel 97 失敗。 為什麼?

  12. 為什麼在程式完成後,我自動執行的應用程式會保留在記憶體中?

  13. 我知道作為 Microsoft Office 應用程式使用者我想做什麼,但如何使用自動化以程式設計方式執行此操作?

  14. 是否可以自動執行嵌入式 Microsoft Office 應用程式?

  15. 如何實現 Microsoft Office 文件中訪問我的文件屬性?

問題和解答

  1. 什麼是自動化?

    自動化 (以前為 OLE 自動化) 是一種技術,可用於利用現有程式的功能並將其合併到自己的應用程式中。 例如,可以在應用程式中利用 Microsoft Word 拼寫和語法檢查功能,而使用者看不到 Microsoft Word。 甚至可以使用所有 Microsoft Excel 圖表、列印和資料分析工具。 這項技術可以大大簡化和加快開發速度。

  2. 我不熟悉自動化,在哪裡可以找到要了解詳細資訊的好資源? David Kruglinski 的“Visual C++內部” (ISBN:1-57231-565-2) 提供了一般概述和一些很好的示例。 此外,Microsoft 知識庫是一個很好的資訊來源。
    如果你更喜歡透過示例學習,請參閱 Microsoft 知識庫中的以下文章:

    179706 如何使用 MFC 自動執行 Excel &建立新工作簿/設定其格式

  3. 是否有其他方法可以使用自動化?

    可以使用自動化的三種基本方法:MFC、#import和 C/C++:

    • 使用 MFC 時,使用 Visual C++ ClassWizard 從 Microsoft Office 型別庫生成“包裝類”。 這些類以及其他 MFC 類(如 COleVariant、COleSafeArray、COleException)簡化了自動化的任務。 通常建議使用此方法,而其他方法,並且大多數 Microsoft 知識庫示例都使用 MFC。

    • #import是 Visual C++ 5.0 中提供的一個新指令,它從指定的型別庫建立 VC++“智慧指標”。 它非常強大,但通常不建議使用,因為與 Microsoft Office 應用程式一起使用時通常會發生引用計數問題。

    • C/C++ 自動化要困難得多,但有時需要避免 MFC 開銷或#import問題。 基本上,可以使用 CoCreateInstance () 等 API 以及 IDispatch 和 IUnknown 等 COM 介面。

    請務必注意,與純 C 相比,C++ 中的自動化之間存在一些細微差別,因為 COM 是圍繞 C++ 類設計的。

  4. 什麼是 COM?

    自動化基於元件物件模型 (COM) 。 COM 是基於介面的標準軟體體系結構,旨在將程式碼分隔成獨立的物件。 將其視為物件導向的程式設計 (OOP) 範例的擴充套件,但適用於單獨的應用程式。 每個物件公開一組介面,與物件的所有通訊(如初始化、通知和資料傳輸)都透過這些介面進行。

    COM 也是動態連結庫提供的一組服務, (DLL) 隨作業系統一起安裝。 自動化使用其中許多服務。 一個示例是“封送”服務,該服務打包客戶端應用程式對伺服器應用程式介面成員函式的呼叫,並將這些呼叫及其引數傳遞給伺服器應用程式。 它使伺服器的介面似乎在客戶端的記憶體空間中公開,當客戶端是在其自己的程序空間中執行的.exe時,情況並非如此。 封送處理還會跨程序邊界從伺服器的方法獲取返回值,並安全地交到客戶端呼叫手中。 還有許多其他對自動化至關重要的服務,這些服務由各種 COM 庫提供。 有關這些資訊的來源包括 Kraig Brockschmidt 的“Inside Ole - 第二版”、ISBN 1-55615-843-2、戴爾·羅傑森的“Inside COM”-ISBN 1-57231-349-8 和“自動化程式設計師參考”,ISBN 1-57231-584-9。

  5. 如何實現附加到 Office 應用程式的正在執行的例項?

    使用 GetActiveObject () API。 自動化伺服器透過 RegisterActiveObject () API 在 ROT (Running Object Table) 中註冊自己。 自動化客戶端可以使用以下程式碼訪問正在執行的例項:

          // Translate server ProgID into a CLSID. ClsidFromProgID
          // gets this information from the registry.
          CLSID clsid;
          CLSIDFromProgID(L"Excel.Application", &clsid);  
    
          // Get an interface to the running instance, if any..
          IUnknown *pUnk;
          HRESULT hr = GetActiveObject(clsid, NULL, (IUnknown**)&pUnk);
    
          ASSERT(!FAILED(hr));
    
          // Get IDispatch interface for Automation...
          IDispatch *pDisp;
          hr = pUnk->QueryInterface(IID_IDispatch, (void **)&pDisp);
          ASSERT(!FAILED(hr));
    
          // Release the no-longer-needed IUnknown...
          pUnk->Release();
    
    

    注意:如果要附加的 Office 應用程式執行多個例項,則只能附加到使用 GetActiveObject () API 啟動的第一個例項。

    從理論上講,可以迴圈訪問每個單獨例項的 ROT,但如果另一個例項已在 ROT 中,Office 應用不會自行註冊,因為本身的名字物件始終是相同的 (它無論如何都無法區分) 。 這意味著不能附加到除第一個例項之外的任何例項。 但是,由於 Office 應用也在 ROT 中註冊其文件,因此可以透過迴圈訪問 ROT 查詢特定文件、附加該文件,然後從中獲取 Application 物件來成功附加到其他例項。
    無需為 PowerPoint 執行此操作,因為它是單例項應用程式;只能執行它的一個例項。

  6. 如何實現傳遞可選引數?

    某些方法具有“可選”引數。 在 Visual Basic 中,可以在呼叫 方法時隨意省略它們。 但是,在使用 Visual C++ 呼叫時,必須傳遞一個特殊 VARIANT,該變數的 .vt 欄位VT_ERROR,而 .scode 欄位DISP_E_PARAMNOTFOUND。 那是:

          // VARIANT used in place of optional-parameters.
          VARIANT varOpt;
          varOpt.vt = VT_ERROR;
          varOpt.scode = DISP_E_PARAMNOTFOUND;
    

    這確實是 Visual Basic 在幕後執行的操作。

  7. 如何實現捕獲 Office 應用程式公開的事件?

    基本上,實現要捕獲 (“接收器”) 的事件介面,並 (“源”) 與應用程式建立諮詢連線。

    通常,若要設定諮詢連線,請獲取伺服器的 IConnectionPointContainer,並使用事件介面的 IID 呼叫 FindConnectionPoint () 。 這為你提供了一個 IConnectionPoint 介面,剩下的就是使用事件介面的例項呼叫 Advise () 。 然後,當發生這些事件時,伺服器將透過此介面回撥。

  8. 我的自動化程式碼太慢了。 如何加快速度?

    自動化速度問題的一個常見原因是重複讀取和寫入資料。 這是 Excel 自動化客戶端的典型應用。 但是,大多數人不知道此資料通常可以使用 SAFEARRAY 一次性寫入或讀取。 有關詳細資訊和資訊性示例,請參閱以下 Microsoft 知識庫文章:

    179706 HOWTO:使用 MFC 自動執行 Excel 和建立新工作簿/設定新工作簿
    的格式 此外,請務必指出,使用剪貼簿有時可以提高效能。 例如,法律時刻可以將資料複製到剪貼簿,然後使用自動化來告知伺服器進行貼上。 反之亦然;告知伺服器複製到剪貼簿,並貼上到應用程式中。

  9. 這些巨大的錯誤值(如 -2147352573 或 0x80030002)是什麼意思?

    這些值稱為 HRESULT,在 winerror.h 中定義。 數字如此之大,因為第一個位表示它是否是錯誤結果。 可以使用 Visual C++ 附帶的 ErrLook.Exe 實用工具將這些數字轉換為有意義的說明。

    如果要以程式設計方式獲取錯誤說明,可以使用 FormatMessage () API。

    注意:如果使用 Visual C++ 6.0 並在除錯監視視窗中有一個包含此值的變數,請追加“, hr” (不帶引號) ,以便 Visual C++ 為你翻譯它!

  10. 什麼是型別庫?

    型別庫類似於 C/C++ 標頭檔案。 它包含伺服器正在釋出的介面、方法和屬性。 可以使用 Visual C++ 附帶的 OLE/COM 物件檢視器 (Oleview.exe) 檢視型別庫。 下面是 Microsoft Office 95、97 和 2000 的型別庫檔名列表:



    Office 應用程式|型別庫
    ------------------------+----------------
    Word 95 和以前的|wb70en32.tlb
    Excel 95 及更早版本的|xl5en32.olb
    Powerpoint 95 和以前的|Powerpoint.tlb
    Access 95 及更早|msaccess.tlb
    Binder 95 |binder.tlb
    Schedule+ |sp7en32.olb
    專案|pj4en32.olb
    團隊經理|mstmgr1.olb
    Word 97 |msword8.olb
    Excel 97 |excel8.olb
    Powerpoint 97 |msppt8.olb
    Access 97 |msacc8.olb
    Binder 97 |msbdr8.olb
    圖 97 |graph8.olb
    Outlook 97 |msoutl8.olb
    Outlook 98 |msoutl85.olb
    Word 2000 |msword9.olb
    Excel 2000 |excel9.olb
    Powerpoint 2000 |msppt9.olb
    Access 2000 |msacc9.olb
    Outlook 2000 |msoutl9.olb
    Word 2002 |msword.olb
    Excel 2002 |
    excel.exe Powerpoint 2002 |msppt.olb
    Access 2002 |msacc.olb
    Outlook 2002 |msoutl.olb

  1. 我的自動化程式碼適用於 Excel 95,但 Excel 97 失敗。 發生了什麼事情?

    Excel 的物件模型進行了從版本 95 到 97 的重大更改。 Excel 95 在 IDispatch 的單個實現中實現了其所有方法和屬性。 這意味著,通常可以從物件 Y 呼叫用於物件 X 的方法。這不是一個很好的設計,因此在 Office 97 中,每個物件都有其自己的獨立 Idispatch 實現。 這意味著,如果從單獨的物件 Y 從物件 X 請求方法或屬性,則會收到錯誤0x80020003 -2147352573,“找不到成員”。 若要避免此錯誤,需要確保呼叫的基礎 IDispatch 介面在語義上正確。

  2. 程式完成後,我自動執行的應用程式將保留在記憶體中。 發生了什麼事情?

    很可能是因為你忘記了釋出獲取的介面,需要跟蹤它。 下面是一些常規建議和要查詢的事項:

    • 如果使用 #import,則很可能遇到與之關聯的引用計數 bug 之一。 通常,可以解決 bug,但通常首選使用其他自動化方法之一。 #import不太適用於 Office 應用程式,因為它的型別庫和用法相當複雜。 此外,此類引用計數問題很難跟蹤,因為使用 #import 時,許多介面級 COM 呼叫都是幕後呼叫。

    • 檢查是否呼叫任何方法(如 Open 或 New),這些方法返回 IDispatch * (LPDISPATCH) ,並忽略返回值。 如果是,則放棄此返回的介面,並且需要更改程式碼,以便在不再需要時將其釋放。

    • 逐步註釋掉程式碼的各部分,直到問題消失,然後明智地將其新增,以跟蹤問題開始的位置。

    • 請注意,如果使用者已“觸控”應用程式,某些應用程式將保持執行。 如果在自動化時發生這種情況,則應用程式可能會在之後繼續執行。 Office 應用程式在 Application 物件上具有“UserControl”屬性,可讀寫該屬性來更改此行為。

    • 此外,如果發生了足夠的使用者介面“操作”,某些應用程式將決定繼續執行。 如果打算退出應用程式,請在 Application 物件上呼叫其 Quit () 方法。 呼叫 Quit 時,無論 Word 的引用計數如何,Word 都將關閉。 這不是預期的 COM 行為。 但是,Excel 將正確隱藏自身,但會一直執行,直到釋放所有未完成的介面。 通常,應釋放所有未完成的引用,並且僅當打算退出應用程式時呼叫 Quit () 。

  3. 我知道作為 Office 應用程式使用者我想做什麼,但如何透過自動化以程式設計方式執行此操作?

    你感興趣的是需要使用哪些物件、方法和屬性。 若要了解如何根據使用者需要執行的操作導航 Word、Excel 和 Powerpoint 的物件模型,最佳方法是使用宏錄製器。 只需從“工具”選單中選擇“宏”“錄製新宏”,執行感興趣的任務,然後選擇“宏”“停止錄製”。 錄製完成後,從“工具”選單中選擇“宏\宏”,選擇錄製的宏,然後單擊“編輯”。 這會將你帶到生成的 VBA 程式碼,該程式碼將完成你記錄的任務。 請記住,在大多數情況下,錄製的宏不是最好的程式碼,但對於快速示例來說,它非常有用。

  4. 是否可以自動執行嵌入式 Office 應用程式?

    絕對。 技巧在於獲取 IDispatch 指標:Visual C++ 技術說明 39 (TN039) 中提供了此指標。

  5. 如何實現 Office 文件中訪問我的文件屬性?

    文件屬性可透過自動化或直接透過 IPropertyStorage 進行訪問。

相關文章