Это параллельный помощник только для .net 4.0, который управляет коллизиями / потоками.
/// <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("numberOfThreads");
// 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);
}
}) {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;
}
}
}
Я использую 2 перечисления, одно для управления веб-браузером OLECMDID:
public enum OLECMDID
{
OLECMDID_OPEN = 1,
OLECMDID_NEW = 2,
OLECMDID_SAVE = 3,
OLECMDID_SAVEAS = 4,
OLECMDID_SAVECOPYAS = 5,
OLECMDID_PRINT = 6,
OLECMDID_PRINTPREVIEW = 7,
OLECMDID_PAGESETUP = 8,
OLECMDID_SPELL = 9,
OLECMDID_PROPERTIES = 10,
OLECMDID_CUT = 11,
OLECMDID_COPY = 12,
OLECMDID_PASTE = 13,
OLECMDID_PASTESPECIAL = 14,
OLECMDID_UNDO = 15,
OLECMDID_REDO = 16,
OLECMDID_SELECTALL = 17,
OLECMDID_CLEARSELECTION = 18,
OLECMDID_ZOOM = 19,
OLECMDID_GETZOOMRANGE = 20,
OLECMDID_UPDATECOMMANDS = 21,
OLECMDID_REFRESH = 22,
OLECMDID_STOP = 23,
OLECMDID_HIDETOOLBARS = 24,
OLECMDID_SETPROGRESSMAX = 25,
OLECMDID_SETPROGRESSPOS = 26,
OLECMDID_SETPROGRESSTEXT = 27,
OLECMDID_SETTITLE = 28,
OLECMDID_SETDOWNLOADSTATE = 29,
OLECMDID_STOPDOWNLOAD = 30,
OLECMDID_FIND = 32,
OLECMDID_DELETE = 33,
OLECMDID_PRINT2 = 49,
OLECMDID_PRINTPREVIEW2 = 50,
OLECMDID_PAGEACTIONBLOCKED = 55,
OLECMDID_PAGEACTIONUIQUERY = 56,
OLECMDID_FOCUSVIEWCONTROLS = 57,
OLECMDID_FOCUSVIEWCONTROLSQUERY = 58,
OLECMDID_SHOWPAGEACTIONMENU = 59,
OLECMDID_ADDTRAVELENTRY = 60,
OLECMDID_UPDATETRAVELENTRY = 61,
OLECMDID_UPDATEBACKFORWARDSTATE = 62,
OLECMDID_OPTICAL_ZOOM = 63,
OLECMDID_OPTICAL_GETZOOMRANGE = 64,
OLECMDID_WINDOWSTATECHANGED = 65,
OLECMDID_ACTIVEXINSTALLSCOPE = 66,
OLECMDID_UPDATETRAVELENTRY_DATARECOVERY = 67
}
Другой - это пользовательский инструмент для печати того, что вам нужно:
public enum PrintDocumentType
{
Bill,
Label //etc...
}
Теперь, вот мой помощник для принтера, который я использую, который устанавливает принтер по умолчанию (и печатает на него), также изменяетполе в соответствии с тем, что мне нужно, напечатано:
public class PrinterHelper
{
readonly TaskScheduler _sta = new StaTaskScheduler(1);
public void PrintHtml(string htmlPath, string printerDevice, PrintDocumentType printDocumentType)
{
if (!string.IsNullOrEmpty(printerDevice))
SetAsDefaultPrinter(printerDevice);
IeSetup(printDocumentType);
Task.Factory.StartNew(() => PrintOnStaThread(htmlPath), CancellationToken.None, TaskCreationOptions.None, _sta).Wait();
}
static void PrintOnStaThread(string htmlPath)
{
const short printWaitForCompletion = 2;
const int oleCmdExecOptDontPromptUser = 2;
using(var browser = new WebBrowser())
{
WebBrowserHelper.ClearCache(); /*needed since there is a major cache flaw. The WebBrowserHelper class is available at http://www.gutgames.com/post/Clearing-the-Cache-of-a-WebBrowser-Control.aspx with some slight changes or if website is taken off, it is based heavily on http://support.microsoft.com/kb/326201*/
browser.Navigate(htmlPath);
while(browser.ReadyState != WebBrowserReadyState.Complete)
Application.DoEvents();
dynamic ie = browser.ActiveXInstance;
((IWebBrowser2)ie).ExecWB(SHDocVw.OLECMDID.OLECMDID_PRINT, OLECMDEXECOPT.OLECMDEXECOPT_DONTPROMPTUSER, oleCmdExecOptDontPromptUser, printWaitForCompletion);
}
}
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))
{
printer.InvokeMethod("SetDefaultPrinter",
null, null);
}
return;
}
/// <summary>
/// Making sure the printer doesn't output the default footer and header of Internet Explorer (url, pagenumber, title, etc.).
/// </summary>
public void IeSetup(PrintDocumentType printDocumentType)
{
const string keyName = @"Software\Microsoft\Internet Explorer\PageSetup";
using (RegistryKey key = Registry.CurrentUser.OpenSubKey(keyName, true)) {
if (key == null) return;
key.SetValue("footer", "");
key.SetValue("header", "");
switch (printDocumentType)
{
case PrintDocumentType.Label:
key.SetValue("margin_top", "0.12500");
key.SetValue("margin_bottom", "0.12500");
key.SetValue("margin_left", "0.25000");
key.SetValue("margin_right", "0.25000");
break;
case PrintDocumentType.Bill:
key.SetValue("margin_top", "0.75000");
key.SetValue("margin_bottom", "0.75000");
key.SetValue("margin_left", "0.75000");
key.SetValue("margin_right", "0.75000");
break;
}
}
}
}
Как вы заметили, у меня есть webbrowserhelper, который является другим классом.Это довольно большой, и я не буду вставлять его.Тем не менее, я добавляю ссылки в комментариях рядом с ним, где вы можете получить код.У самого веб-браузера есть большой недостаток кэша, и даже если вы заставите его обновить страницу, он всегда будет захватывать кэш, поэтому очистка кэша в порядке.Я узнал на своей шкуре.
Надеюсь, это всем поможет.Обратите внимание, что это для .net 4.0.