簡體   English   中英

無法使用win32com.client打開只讀Microsoft Word文件

[英]Cannot open read-only Microsoft Word files with win32com.client

我有數千個docx文件,我需要通過python提取其中的某些元素。 我在Python腳本中使用win32com.client完成此任務。

import win32com.client
doc = win32com.client.GetObject(some_path_to_a_docx_file)

這對於99%的文件都可以正常工作; 但是,對於一些文件,將啟動Microsoft Word對話框:“ 作者希望您以只讀方式打開它,除非您需要進行更改。以只讀方式打開? ”。 此時腳本停止,等待用戶輸入。

在對話框上按是繼續運行腳本; 但是,這是不合格的,因為我需要它完全自動化,而不會出現任何對話框。 有什么方法可以通過win32com Python或通過MS Word永久禁用MS Word提示符嗎? (注意:在這里,不能將import docx替換為win32com。)

Office應用程序是最終用戶應用程序,並未作為開發工具進行優化。 這意味着它們在等待用戶輸入時似乎掛起,如問題中所述。 沒有簡單,干凈的方法可以解決這個問題,這就是為什么建議在需要關閉對話框的情況下利用Open XML文件格式的原因...

如果必須使用自動化,那么我知道有兩種可能性。 詳細信息不是python,但這確實記錄了基本方法。

  1. 如果代碼無法繼續執行,請使用Timer函數和SendKeys自動關閉對話框。 這有點彩票,因為不可能知道哪個對話框被關閉了。 通常,發送“轉義”鍵。 曾幾何時,有一系列針對各種編程語言的知識庫文章,但在Microsoft網站上已不再提供。 我找到一個檔案庫C-Bit,並在復制相關樣本內容,以演示經典VB6的原理:

本節中的步驟演示了Microsoft Word的自動化以打印文檔。 自動化客戶端調用Word Document對象的PrintOut方法。 如果將用戶的默認打印機配置為打印到FILE端口,則對PrintOut的調用將產生一個對話框,提示用戶輸入文件名。 若要確定PrintOut方法是否導致此對話框出現,Visual Basic Automation客戶端使用Timer控件在調用PrintOut方法后檢測空閑時間。 在調用PrintOut之前,已啟用計時器並將其設置為在五秒鍾內觸發。 打印輸出完成后,計時器將被禁用。 因此,如果PrintOut方法在五秒鍾內完成,則Timer事件永遠不會發生,並且不會采取進一步的措施。 打印文檔,並且代碼執行繼續超出PrintOut方法。 但是,如果Timer事件在五秒鍾的間隔內發生,則認為PrintOut方法尚未完成,並且該延遲是由對話框等待用戶輸入引起的。 發生Timer事件時,自動化客戶端會將焦點放在Word上,並使用SendKeys關閉該對話框。

注意:出於演示目的,此示例使用PrintOut方法,以便在打印到設置為FILE端口的打印機時有意顯示對話框。 請注意,可以使用PrintOut方法提供兩個參數OutputfileName和PrintToFile來避免出現此對話框。

此外,使用這種“計時器”方法時,您可以將等待時間自定義為大於或小於5秒,以及自定義發送到對話框的擊鍵。

該演示包含兩個Visual Basic項目:

  1. 提供用於檢測延遲的Timer類的ActiveX EXE。 將ActiveX EXE用於Timer類的原因是在單獨的進程(因此,在單獨的線程)中運行Timer代碼。 這使Timer類可以在暫停的自動化調用期間引發事件。

  2. 一個標准EXE,它使用Word的自動化功能,並調用PrintOut方法來打印文檔。 調用PrintOut方法時,它使用ActiveX EXE來檢測延遲。 創建ActiveX EXE項目

  3. 啟動Visual Basic並創建一個ActiveX EXE項目。 默認情況下創建Class1。
  4. 在項目菜單上,單擊以選擇屬性,然后將項目名稱更改為MyTimer。
  5. 將以下代碼復制並粘貼到Class1模塊中:Option Explicit
 Public Event Timer() Private oForm1 As Form1

 Private Sub Class_Initialize()
     Set oForm1 = New Form1
     oForm1.Timer1.Enabled = False 
End Sub

 Private Sub Class_Terminate()
     Me.Enabled = False
     Unload oForm1
     Set oForm1 = Nothing 
End Sub

Public Property Get Enabled() As Boolean
     Enabled = oForm1.Timer1.Enabled 
End Property

Public Property Let Enabled(ByVal vNewValue As Boolean)
     oForm1.Timer1.Enabled = vNewValue
     If vNewValue = True Then
         Set oForm1.oClass1 = Me
     Else
         Set oForm1.oClass1 = Nothing
     End If 
End Property

 Public Property Get Interval() As Integer
     Interval = oForm1.Timer1.Interval 
End Property

 Public Property Let Interval(ByVal vNewValue As Integer)
     oForm1.Timer1.Interval = vNewValue End Property

 Friend Sub TimerEvent()
     RaiseEvent Timer 
End Sub                 
  1. 在項目菜單上,選擇添加表單以將新表單添加到項目。
  2. 將計時器控件添加到窗體。
  3. 將以下代碼復制並粘貼到Form1的代碼模塊中:Option Explicit
 Public oClass1 As Class1

 Private Sub Timer1_Timer()
     oClass1.TimerEvent 
End Sub
  1. 將此項目保存在一個名為Server的新子文件夾中。
  2. 在“文件”菜單上,選擇“制作MyTimer.Exe”以生成並注冊該組件。 創建自動化客戶端
  1. 與Windows API一起使用,以識別和消除可能的問題對話框。 我在這里復制的MSDN論壇上找到了一些與此相關的代碼。 歸屬是用戶名yet

這是通過C#中的pinvoke使用Win32 API的示例。 我可以通過FindWindow和SendMessage或PostMessage處理已知的Word窗口,例如Word-> File-> Options對話框窗口。 請仔細閱讀示例,看看它是否對您有用。 由於您知道要丟棄的對話框,因此請使用spy ++查找窗口標題和窗口類,並在此示例中使用它。

對於您的方案,可能不需要SendKeys。

希望這可以幫助。

using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Data;
 using System.Drawing;
 using System.Linq;
 using System.Text;
 using System.Windows.Forms;

using System.Runtime.InteropServices;

namespace SendKeys
 {

    public partial class Form1 : Form
     {
         // For Windows Mobile, replace user32.dll with coredll.dll

         [DllImport("user32.dll", SetLastError = true)]
         static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        // Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.

        [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
         static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);

        [DllImport("user32.dll")]
         public static extern bool SetForegroundWindow(IntPtr hWnd);


         [return: MarshalAs(UnmanagedType.Bool)]
         [DllImport("user32.dll", SetLastError = true)]
         static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
         static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

        static uint WM_CLOSE = 0x10;

        public Form1()
         {
             InitializeComponent();
         }

        private void button1_Click(object sender, EventArgs e)
         {

             // the caption and the className is for the Word -> File -> Options window
             // the caption and the className are got by using spy++ application and focussing on the window we are researching.
             string caption = "Word Options";
             string className = "NUIDialog";
             IntPtr hWnd= (IntPtr)(0);

             // Win 32 API being called through PInvoke
             hWnd = FindWindow(className, caption);

            /*bool retVal = false;
             if ((int)hWnd != 0)
             {
                // Win 32 API being called through PInvoke 
               retVal = SetForegroundWindow(hWnd);
             }*/



            if ((int)hWnd != 0)
             {
                 CloseWindow2(hWnd);
                 //CloseWindow(hWnd); // either sendMessage or PostMessage can be used.
             }
         }



        static bool CloseWindow(IntPtr hWnd)
         {
             // Win 32 API being called through PInvoke
             SendMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
             return true;
         }

        static bool CloseWindow2(IntPtr hWnd)
         {
             // Win 32 API being called through PInvoke
             PostMessage(hWnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
             return true;

         }

    }
 }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM