我正在开发一个小型企业的Intranet ASP.NET MVC2应用程序。小型企业具有多种类型的打印机,并且根据需要将打印请求(从浏览器/用户)发送到服务器,并且服务器将相应地将打印作业分派到合适的打印机。请注意,这是一个全新的环境,我几乎可以控制所有的东西。我很可能会使用一个非常轻量级的操作系统(也许Asus ExpressGate或Chrome OS取决于发布日期?),因此用户不能安装任何打印机,但服务器将安装一切。使用c#/ ASP.NET MVC2打印
这里是我的问题:
有没有一种简单的方法来从服务器端进行打印(当然没有对话,因为不会有任何人等着点击它们)使用HTML链接的页面一个参数并保持HTML格式当然。
我见过的COM东西几种可能性在那里,但如果有可能通过使用.NET类我希望避免这种情况。我正在使用.net 4.0。不过,我会采取任何建议,即使它是基于COM的。
编辑:请注意,这是有道理的任何变通办法也将被考虑在内,快速(非研究还)例子是这个网站转移到一个doc文件并将该文件发送到打印机。
编辑码起飞的缺乏使用。
Edits2: 以下网址:Print html document from Windows Service in C# without print dialog
瓦迪姆的圣杯解决方案确实工作。但是,它具有仅使用默认打印机的限制。我在打印发生之前修改了默认打印机,这导致打印进入正确的打印机。我可以看到在这里发生,但一些并发问题,到目前为止,这是我想出的最好的(大多数代码是由瓦迪姆,我给了他这样做的全部学分):
/// <summary>Provides a scheduler that uses STA threads.</summary>
public sealed class StaTaskScheduler : TaskScheduler, IDisposable
{
/// <summary>Stores the queued tasks to be executed by our pool of STA threads.</summary>
private BlockingCollection<Task> _tasks;
/// <summary>The STA threads used by the scheduler.</summary>
private readonly List<Thread> _threads;
/// <summary>Initializes a new instance of the StaTaskScheduler class with the specified concurrency level.</summary>
/// <param name="numberOfThreads">The number of threads that should be created and used by this scheduler.</param>
public StaTaskScheduler(int numberOfThreads)
{
// Validate arguments
if (numberOfThreads < 1) throw new ArgumentOutOfRangeException("concurrencyLevel");
// Initialize the tasks collection
_tasks = new BlockingCollection<Task>();
// Create the threads to be used by this scheduler
_threads = Enumerable.Range(0, numberOfThreads).Select(i =>
{
var thread = new Thread(() =>
{
// Continually get the next task and try to execute it.
// This will continue until the scheduler is disposed and no more tasks remain.
foreach (var t in _tasks.GetConsumingEnumerable())
{
TryExecuteTask(t);
}
});
thread.IsBackground = true;
thread.SetApartmentState(ApartmentState.STA);
return thread;
}).ToList();
// Start all of the threads
_threads.ForEach(t => t.Start());
}
/// <summary>Queues a Task to be executed by this scheduler.</summary>
/// <param name="task">The task to be executed.</param>
protected override void QueueTask(Task task)
{
// Push it into the blocking collection of tasks
_tasks.Add(task);
}
/// <summary>Provides a list of the scheduled tasks for the debugger to consume.</summary>
/// <returns>An enumerable of all tasks currently scheduled.</returns>
protected override IEnumerable<Task> GetScheduledTasks()
{
// Serialize the contents of the blocking collection of tasks for the debugger
return _tasks.ToArray();
}
/// <summary>Determines whether a Task may be inlined.</summary>
/// <param name="task">The task to be executed.</param>
/// <param name="taskWasPreviouslyQueued">Whether the task was previously queued.</param>
/// <returns>true if the task was successfully inlined; otherwise, false.</returns>
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{
// Try to inline if the current thread is STA
return
Thread.CurrentThread.GetApartmentState() == ApartmentState.STA &&
TryExecuteTask(task);
}
/// <summary>Gets the maximum concurrency level supported by this scheduler.</summary>
public override int MaximumConcurrencyLevel
{
get { return _threads.Count; }
}
/// <summary>
/// Cleans up the scheduler by indicating that no more tasks will be queued.
/// This method blocks until all threads successfully shutdown.
/// </summary>
public void Dispose()
{
if (_tasks != null)
{
// Indicate that no new tasks will be coming in
_tasks.CompleteAdding();
// Wait for all threads to finish processing tasks
foreach (var thread in _threads) thread.Join();
// Cleanup
_tasks.Dispose();
_tasks = null;
}
}
}
public class PrinterHelper
{
readonly TaskScheduler _sta = new StaTaskScheduler(1);
public void PrintHtml(string htmlPath, string printerDevice)
{
if (!string.IsNullOrEmpty(printerDevice))
SetAsDefaultPrinter(printerDevice);
Task.Factory.StartNew(() => PrintOnStaThread(htmlPath), CancellationToken.None, TaskCreationOptions.None, _sta).Wait();
}
static void PrintOnStaThread(string htmlPath)
{
const short PRINT_WAITFORCOMPLETION = 2;
const int OLECMDID_PRINT = 6;
const int OLECMDEXECOPT_DONTPROMPTUSER = 2;
using(var browser = new WebBrowser())
{
browser.Navigate(htmlPath);
while(browser.ReadyState != WebBrowserReadyState.Complete)
Application.DoEvents();
dynamic ie = browser.ActiveXInstance;
ie.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DONTPROMPTUSER, PRINT_WAITFORCOMPLETION);
}
}
static void SetAsDefaultPrinter(string printerDevice)
{
foreach (var printer in PrinterSettings.InstalledPrinters)
{
//Verify that the printer exists here
}
var path = "win32_printer.DeviceId='" + printerDevice + "'";
using (var printer = new ManagementObject(path))
{
ManagementBaseObject outParams =
printer.InvokeMethod("SetDefaultPrinter",
null, null);
}
return;
}
}
是的我目前有一个打印机助手,我在这里找到了我在上面作为编辑添加的stackoverflow。不幸的是,这并不能帮助我打印HTML呈现的文件。 – 2010-10-20 02:06:22
我还没看,但有没有办法打印流?如果是这样,那么你可以潜力得到创建的HTML,然后打印包含html的响应流......只是一个疯狂的想法:) – WestDiscGolf 2010-10-20 11:20:46
使用我在那里显示的代码,它确实有效。对于这个项目的需求,因为它是一个小企业。我可能会重新考虑这是由于与更改默认打印机的同时发生而将被使用更多的东西。我讨厌使用COM对象,这需要在服务器上安装IE,但是要做你应该做的事情?我仍然接受其他更具扩展性的建议! – 2010-10-21 23:14:50