c# - 从 .NET WebBrowser 打印到多台打印机 - 除非使用 MessageBox,否则程序会挂起
问题描述
该WebBrowser.Print
方法的限制是不允许调用者指定系统默认打印机以外的打印机。作为一种解决方法,建议[1],[2]在调用之前更改系统的默认打印机Print()
,但是也有报告[3](我亲身体验过)该WebBrowser
实例将继续打印到先前定义的打印机即使在系统默认值被更改后。
为了解决这个问题,建议通过访问托管 WebBrowser 实例的底层 ActiveX 对象并在打印更多文档之前等待事件触发来注册PrintTemplateTeardown
事件处理程序[4]、[5],这就是我的想法试图实施。我将一个更复杂的程序简化为下面介绍的 MVCE。
(该程序是一个 .NET Core 3.1 Windows 窗体应用程序,其中一个窗体只包含一个BackgroundWorker
名为 的对象bw
。)
Form1.cs
using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using System.Drawing.Printing;
using System.Management;
namespace Demo_1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
BwPolling();
Thread.Sleep(20000);
}
}
private void BwPolling()
{
string[] htmlStrings = { "test1", "test2" };
foreach (string html in htmlStrings)
{
Invoke((MethodInvoker)delegate
{
PrinterSettings.StringCollection installedPrinters = PrinterSettings.InstalledPrinters;
foreach (string printer in installedPrinters)
{
string[] validPrinterNames =
{
"Microsoft Print to PDF",
"Microsoft XPS Document Writer"
};
if ( validPrinterNames.Contains(printer) )
{
SetDefaultPrinter(printer);
var wb = new WebBrowser();
wb.DocumentText = html;
// With inspiration from code by Andrew Nosenko <https://stackoverflow.com/users/1768303/noseratio>
// From: https://stackoverflow.com/a/19737374/3258851
// CC BY-SA 3.0
var wbax = (SHDocVw.WebBrowser)wb.ActiveXInstance;
TaskCompletionSource<bool> printedTcs = null;
SHDocVw.DWebBrowserEvents2_PrintTemplateTeardownEventHandler printTemplateTeardownHandler =
(p)
=> printedTcs.TrySetResult(true); // turn event into awaitable task
printedTcs = new TaskCompletionSource<bool>();
wbax.PrintTemplateTeardown += printTemplateTeardownHandler;
try
{
MessageBox.Show("Printing to " + printer);
wb.Print();
printedTcs.Task.Wait();
}
finally
{
wbax.PrintTemplateTeardown -= printTemplateTeardownHandler;
}
wb.Dispose();
}
}
});
}
}
private static bool SetDefaultPrinter(string name)
{
// With credits to Austin Salonen <https://stackoverflow.com/users/4068/austin-salonen>
// From: https://stackoverflow.com/a/714543/3258851
// CC BY-SA 3.0
using ( ManagementObjectSearcher objectSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_Printer") )
{
using ( ManagementObjectCollection objectCollection = objectSearcher.Get() )
{
foreach (ManagementObject mo in objectCollection)
{
if ( string.Compare(mo["Name"].ToString(), name, true) == 0 )
{
mo.InvokeMethod("SetDefaultPrinter", null);
return true;
}
}
}
}
return false;
}
}
}
面临的问题是当我从方法中删除消息框时BwPolling()
,就在调用之前Print()
,即删除此行时:
MessageBox.Show("Printing to " + printer);
然后程序冻结,不打印任何内容,并且最终必须终止该过程。
我相信我可以从表面上理解这个问题:WebBrowser
需要一个带有活动消息循环的 STA 线程[6],[7];通过printedTcs.Task.Wait();
在Invoke((MethodInvoker)delegate
块内调用(在Form1
实例上调用;this.
被省略),我阻塞了 STA 线程并且应用程序挂起等待从未触发的事件。这实际上是在我在代码中记入的答案下的评论中提到的。
只是无法弄清楚适当的解决方案是什么。在尝试在辅助线程中运行打印例程时迷路了。也许我的执行过程中有问题,我想我需要帮助。有什么帮助吗?
谢谢。
解决方案
推荐阅读
- python - 在 MySQL 中按文件扩展名过滤
- javascript - 在 React.tsx 中使用 createContext 传递 useState
- sql - oracle sql包含
- python - 列 2D 张量乘以行 2D 张量等于 3d pytorch 张量
- ajax - 如何使用codeigniter在ajax成功中显示数组结果?
- microsoft-teams - 如何将图像添加到本地应用的 Teams 应用清单
- batch-file - 如何检查 zoom.exe 是否仍在运行以及是否要杀死它
- javascript - Jest 如何测试调用函数的行?
- symfony - Symfony 4.4 - “找不到“GET /”的路由”
- django - django celery 异步文件保存到存储