Excel Interop 禁用 Msgbox 而不禁用所有 VBA
Excel Interop disable Msgbox without disabling all VBA
我正在尝试自动执行其中包含 VBA 的 excel 文件。此 VBA 受保护,因此我无法访问它。
这是我需要自动脚本执行的操作。
- 打开工作簿
- 单击/关闭任何 Msgbox 的(卡住部分)
- 输入一个单元格,然后让工作簿的 vba 执行此操作
所以我发现我可以使用以下方式打开这本书而不弹出窗口:
var app = new Excel.Application();
app.DisplayAlerts = false;
app.Visible = false;
app.EnableEvents = false;
app.Workbooks.Open(@"path...");
但是书中的 VBA 也被禁用了,所以我无法执行上面的第 3 步。
我怎样才能禁用所有消息框,然后在最后重新启用它们?
可以使用的技巧是:
运行 另一个线程上函数中的 Excel 代码。这是因为 Excel 可以设置很多东西来阻止执行,例如来自 Excel 的 Msgboxes 和其他对话框,如果您不控制 Excel 代码隐藏,那么您应该希望在超时的基础上中止该任务。
在您的主线程中,只需检查任务是否完成,并添加一个超时。
我将 WindowHandler 作为一个单独的 class 使用来自 user32.dll 的 winAPI 函数等来自这里的示例:Close window via SendMessage AND here: FindWindow Function Codes
class WindowHandler {
[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", CharSet = CharSet.Auto)]
private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
private const UInt32 WM_CLOSE = 0x0010;
public static void CloseWindow(IntPtr hwnd) {
SendMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
}
public static IntPtr FindWindow(string windowName) {
var hWnd = FindWindowByCaption(IntPtr.Zero, windowName);
return hWnd;
}
public static void CloseMsgBox() {
CloseWindow(FindWindow("Microsoft Excel"));
}
}
所以现在代码执行大致如下:
// The OpenExcel Action would actually be all the Excel code encapsulated into one function to run in a separate thread
Task t = Task.Run(OpenExcel);
// Be aware that Excel can have many different popups or VBA issues which may cause execution to stall.
TimeSpan timeLimit = new TimeSpan(0, 0, 10); // 10 secs or acceptable time limit for Excel
DateTime startTime = DateTime.Now;
while (!t.IsCompleted) {
if (DateTime.Now - startTime > timeLimit)
break; //or do other exception routine, if Excel execution is taking an unacceptable amount of time!
WindowHandler.CloseMsgBox(); //close any Msgboxes
Thread.Sleep(200);
}
我正在尝试自动执行其中包含 VBA 的 excel 文件。此 VBA 受保护,因此我无法访问它。
这是我需要自动脚本执行的操作。
- 打开工作簿
- 单击/关闭任何 Msgbox 的(卡住部分)
- 输入一个单元格,然后让工作簿的 vba 执行此操作
所以我发现我可以使用以下方式打开这本书而不弹出窗口:
var app = new Excel.Application();
app.DisplayAlerts = false;
app.Visible = false;
app.EnableEvents = false;
app.Workbooks.Open(@"path...");
但是书中的 VBA 也被禁用了,所以我无法执行上面的第 3 步。
我怎样才能禁用所有消息框,然后在最后重新启用它们?
可以使用的技巧是:
运行 另一个线程上函数中的 Excel 代码。这是因为 Excel 可以设置很多东西来阻止执行,例如来自 Excel 的 Msgboxes 和其他对话框,如果您不控制 Excel 代码隐藏,那么您应该希望在超时的基础上中止该任务。
在您的主线程中,只需检查任务是否完成,并添加一个超时。
我将 WindowHandler 作为一个单独的 class 使用来自 user32.dll 的 winAPI 函数等来自这里的示例:Close window via SendMessage AND here: FindWindow Function Codes
class WindowHandler { [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", CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); private const UInt32 WM_CLOSE = 0x0010; public static void CloseWindow(IntPtr hwnd) { SendMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); } public static IntPtr FindWindow(string windowName) { var hWnd = FindWindowByCaption(IntPtr.Zero, windowName); return hWnd; } public static void CloseMsgBox() { CloseWindow(FindWindow("Microsoft Excel")); } }
所以现在代码执行大致如下:
// The OpenExcel Action would actually be all the Excel code encapsulated into one function to run in a separate thread
Task t = Task.Run(OpenExcel);
// Be aware that Excel can have many different popups or VBA issues which may cause execution to stall.
TimeSpan timeLimit = new TimeSpan(0, 0, 10); // 10 secs or acceptable time limit for Excel
DateTime startTime = DateTime.Now;
while (!t.IsCompleted) {
if (DateTime.Now - startTime > timeLimit)
break; //or do other exception routine, if Excel execution is taking an unacceptable amount of time!
WindowHandler.CloseMsgBox(); //close any Msgboxes
Thread.Sleep(200);
}