Office 应用程序是最终用户应用程序,并未作为开发工具进行优化。这意味着它们在等待用户输入时可能会出现挂起,如问题中所述。没有简单、干净的方法可以解决这个问题,这就是为什么建议将 Open XML 文件格式用于消除对话框存在问题的需求...
如果必须使用自动化,那么我知道有两种可能性。详细信息不是python,但这确实记录了基本方法。
- 如果代码无法继续,请使用 Timer 函数和 SendKeys 自动关闭对话框。这有点像彩票,因为不可能知道哪个对话框被关闭了。通常,发送“Escape”键。曾几何时,有一组针对各种编程语言的知识库文章,但 Microsoft 站点上不再提供这些文章。我找到了一个存档C-Bit,并正在复制演示经典 VB6 原理的相关示例内容:
本节中的步骤演示 Microsoft Word 的自动化
打印文档。自动化客户端调用 PrintOut 方法
Word 文档对象。如果配置了用户的默认打印机
打印到 FILE 端口,然后调用 PrintOut 会产生一个对话框
框提示用户输入文件名。要确定是否
PrintOut 方法导致出现此对话框,Visual Basic
自动化客户端使用 Timer 控件来检测空闲时间
调用 PrintOut 方法。在调用 PrintOut 之前,定时器是
启用并设置为在五秒内触发。当 PrintOut 完成时,
定时器被禁用。因此,如果 PrintOut 方法在
五秒钟,计时器事件永远不会发生,也不会采取进一步的行动
采取。文档被打印出来,代码继续执行
打印输出方法。但是,如果 Timer 事件发生在
五秒间隔,假设PrintOut方法没有
完成,并且延迟是由等待的对话框引起的
用户输入。当 Timer 事件发生时,自动化客户端给出
焦点到 Word 并使用 SendKeys 关闭对话框。
注意 出于演示目的,此示例使用 PrintOut 方法
以这样一种方式,它会故意显示一个对话框,当它
打印到设置为 FILE 端口的打印机。请注意,打印输出
方法有两个参数,OutputfileName 和 PrintToFile,您可以
提供以避免此对话框。
此外,当使用这种“计时器”方法时,您可以自定义
等待时间大于或小于五秒,以及
自定义您发送到对话框的击键。
此演示包含两个 Visual Basic 项目:
提供用于检测延迟的 Timer 类的 ActiveX EXE。为 Timer 类使用 ActiveX EXE 的原因是在单独的进程中运行 Timer 代码,因此在单独的线程中运行。
这使得 Timer 类可以在
暂停自动呼叫。
使用 Word 自动化并调用 PrintOut 方法来打印文档的标准 EXE。它使用 ActiveX EXE 来检测延迟
调用 PrintOut 方法时。创建 ActiveX EXE 项目
- 启动 Visual Basic 并创建一个 ActiveX EXE 项目。 Class1 是默认创建的。
- 在项目菜单上,单击以选择属性,然后将项目名称更改为 MyTimer。
- 将以下代码复制并粘贴到 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
- 在“项目”菜单上,选择“添加表单”以将新表单添加到项目中。
- 将 Timer 控件添加到窗体。
- 将以下代码复制并粘贴到 Form1:Option Explicit 的代码模块中
Public oClass1 As Class1
Private Sub Timer1_Timer()
oClass1.TimerEvent
End Sub
- 将此项目保存在名为 Server 的新子文件夹中。
- 在“文件”菜单上,选择“生成 MyTimer.Exe”以构建和注册组件。创建自动化客户端
- 使用 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;
}
}
}