Не удается открыть файлы Microsoft Word, доступные только для чтения, с помощью win32com.client - PullRequest
1 голос
/ 19 мая 2019

У меня есть тысячи docx-файлов, из которых мне нужно извлечь некоторые элементы через Python. Я использую win32com.client в своем скрипте Python для выполнения этой задачи.

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

Это прекрасно работает для 99% файлов; однако для некоторых файлов открывается диалоговое окно Microsoft Word: « Автор хотел бы, чтобы вы открыли его только для чтения, если только вам не нужно вносить изменения. Открыть только для чтения? ». На этом этапе сценарий останавливается в ожидании ввода пользователя.

Нажатие Да в диалоговом окне продолжает запускать скрипт по мере необходимости; однако, это подпункт, так как мне нужно, чтобы он был полностью автоматизирован без каких-либо диалоговых окон, так как это всплывающее окно. Есть ли способ отключить приглашение MS Word, либо через Win32com Python, либо через MS Word? (Примечание: импорт docx как альтернативы win32com здесь не вариант.)

1 Ответ

0 голосов
/ 20 мая 2019

Приложения Office являются приложениями для конечных пользователей и не оптимизированы как инструменты разработки. Это означает, что они могут зависать при ожидании пользовательского ввода, как описано в вопросе. Не существует простого, чистого способа обойти это, поэтому использование формата файла Open XML рекомендуется для требований, когда закрытие диалоговых окон является проблемой ...

Если необходимо использовать автоматизацию, я знаю о двух возможностях. Подробная информация не Python, но это документирует основные подходы.

  1. Используйте функцию Timer и SendKeys для автоматического закрытия диалогового окна, если код не может быть запущен. Это немного лотерея, так как невозможно узнать, какое диалоговое окно закрывается. Обычно отправляется ключ «Escape». Когда-то был набор статей KB, нацеленных на разные языки программирования, но их больше нет на сайте Microsoft. Я нахожу архив C-Bit и копирую соответствующий пример контента, который демонстрирует принцип для классического VB6:

Шаги в этом разделе демонстрируют автоматизацию Microsoft Word для распечатать документ. Клиент автоматизации вызывает метод PrintOut для объект Word Document. Если принтер пользователя по умолчанию настроен для печати на порт FILE, затем вызов PrintOut производит диалог окно, предлагающее пользователю ввести имя файла. Чтобы определить, если Метод PrintOut вызывает это диалоговое окно, Visual Basic Клиент автоматизации использует элемент управления Timer для определения времени простоя после вызов метода PrintOut. До вызова PrintOut таймер включается и запускается через пять секунд. Когда PrintOut завершается, Таймер отключен. Поэтому, если метод PrintOut завершается в течение пять секунд, событие Таймер никогда не происходит, и никаких дальнейших действий приняты. Документ распечатан и выполнение кода продолжается метод PrintOut. Однако если событие Timer происходит в пределах с интервалом в пять секунд предполагается, что метод PrintOut не имеет завершена и что задержка вызвана диалоговым окном, ожидающим пользовательский ввод. Когда происходит событие Timer, клиент Automation дает сосредоточиться на Word и использует SendKeys, чтобы закрыть диалоговое окно.

Примечание. В демонстрационных целях в этом примере используется метод PrintOut. таким образом, что он отображает диалоговое окно намеренно, когда он печатает на принтере, настроенном на порт FILE. Обратите внимание, что PrintOut Метод имеет два аргумента, OutputfileName и PrintToFile, которые вы можете обеспечить, чтобы избежать этого диалогового окна.

Кроме того, при использовании этого подхода «таймера» вы можете настроить время ожидания должно быть больше или меньше пяти секунд, а также настроить нажатия клавиш в диалоговом окне.

Эта демонстрация состоит из двух проектов Visual Basic:

  1. ActiveX EXE, который предоставляет класс Timer, используемый для обнаружения задержки. Причина использования ActiveX EXE для класса Timer заключается в запуске кода Timer в отдельном процессе и, следовательно, в отдельном потоке. Это позволяет классу Timer вызывать событие во время приостановлена ​​автоматизация вызова.

  2. Стандартный EXE-файл, который использует автоматизацию для Word и вызывает метод PrintOut для печати документа. Он использует ActiveX EXE для обнаружения задержки при вызове метода PrintOut. Создать проект 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. Добавить элемент управления Timer в форму.
  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:

Вот пример, который использует Win32 API через pinvoke в C #. я возможность распоряжаться известными окнами Word, такими как Word-> File-> диалог настроек окно через 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;

         }

    }
 }
...