无法使用win32com.client打开只读Microsoft Word文件

时间:2019-05-19 17:22:33

标签: python ms-word win32com

我有数千个docx文件,我需要通过python提取其中的某些元素。我在Python脚本中使用win32com.client完成此任务。

def checkinc(inputstring, straightlength):
    for startdigit in range(len(inputstring)-straightlength+1):
        for i in range(straightlength):
            if int(inputstring[startdigit+i]) != (int(inputstring[startdigit]) + i)%10:
                return False
    return True

这对于99%的文件都适用;但是,对于几个文件,将启动Microsoft Word对话框:“ 作者希望您以只读方式打开它,除非您需要进行更改。以只读方式打开?”。此时脚本停止,等待用户输入。

在对话框上按是继续运行脚本;但是,这是不合格的,因为我需要它完全自动化,而不会出现任何对话框。有什么方法可以通过win32com Python或通过MS Word永久禁用MS Word提示符吗? (注意:在这里,不能将import docx替换为win32com。)

1 个答案:

答案 0 :(得分:0)

Office应用程序是最终用户应用程序,并未作为开发工具进行优化。这意味着它们在等待用户输入时似乎挂起,如问题中所述。没有简单,干净的方法可以解决此问题,这就是为什么建议在需要关闭对话框的情况下利用Open XML文件格式的原因...

如果必须使用自动化,那么我知道有两种可能性。详细信息不是python,但这确实记录了基本方法。

  1. 如果无法继续执行代码,请使用Timer函数和SendKeys自动关闭对话框。这有点彩票,因为不可能知道哪个对话框被关闭了。通常,发送“转义”键。曾几何时,有一系列针对各种编程语言的知识库文章,但在Microsoft网站上已不再提供。我找到了一个档案C-Bit,并且正在复制演示经典VB6原理的相关样本内容:
  

本节中的步骤演示了Microsoft Word的自动化   打印文档。自动化客户端调用PrintOut方法用于   Word文档对象。如果配置了用户的默认打印机   打印到FILE端口,然后调用PrintOut会产生一个对话框   提示用户输入文件名的框。确定是否   PrintOut方法使此对话框出现,Visual Basic   自动化客户端使用Timer控件来检测之后的空闲时间   调用PrintOut方法。在调用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.   
  3. 一个标准EXE,它使用Word的自动化功能,并调用PrintOut方法来打印文档。它使用ActiveX EXE来检测延迟   调用PrintOut方法时。创建ActiveX EXE项目

  4.   
  5. 启动Visual Basic并创建一个ActiveX EXE项目。默认情况下创建Class1。
  6.   
  7. 在“项目”菜单上,单击以选择“属性”,然后将“项目”名称更改为MyTimer。
  8.   
  9. 将以下代码复制并粘贴到Class1模块中:Option Explicit
  10.   
 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. 将Timer控件添加到窗体。
  4.   
  5. 将以下代码复制并粘贴到Form1:Option Explicit的代码模块中
  6.   
 Public oClass1 As Class1

 Private Sub Timer1_Timer()
     oClass1.TimerEvent 
End Sub
  
      
  1. 将此项目保存在名为Server的新子文件夹中。
  2.   
  3. 在“文件”菜单上,选择“制作MyTimer.Exe”以生成并注册该组件。创建自动化客户端
  4.   
  1. 使用Windows API来识别和消除可能的问题对话框。我在这里复制的MSDN forum上找到了一些代码。归因于用户名yet
  

这里是一个示例,该示例通过C#中的pinvoke使用Win32 API。我是   能够处理已知的Word窗口,例如Word-> File-> Options对话框   窗口通过FindWindow和SendMessage或PostMessage。请离开   通过示例,看看它是否对您有用。自从你   知道要丢弃的对话框,请使用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;

         }

    }
 }