繁体   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