多线程wpf窗口中的Printdialog引发了TargetInvocationException

时间:2010-06-04 08:22:27

标签: wpf multithreading printing

我设计了一个多线程应用程序,它在this这样的专用线程中启动大多数窗口:

Thread newWindowThread = new Thread(new ThreadStart(ThreadStartingPoint));
newWindowThread.SetApartmentState(ApartmentState.STA);
newWindowThread.IsBackground = true;
newWindowThread.Start();

但是,如果在其中一个窗口自己的线程中,我尝试通过简单地调用

来打印一些东西
PrintDialog pDialog = new PrintDialog();
bool? doPrint = pDialog.ShowDialog();

我得到一个TargetInvocationException - 它看起来像PrintDialog与我的窗口不在同一个线程中。

有没有办法创建与线程无关(或“线程保存”)的PrinterDialog?

3 个答案:

答案 0 :(得分:3)

答案是here

  

PrintDialog使用私有类   Win32PrintDialog似乎访问   Application.Current.MainWindow in   以获取父HWND窗口   句柄用作本机打印对话框   窗口父母

有人构建了支持Thread的PrintDialog here。这似乎是使打印在启用Thread的应用程序中工作的唯一方法。

(我喜欢在这里重复代码,但它超过了答案的最大长度)

注:

  • System.Drawing中
  • System.Printing
  • System.Windows.Forms

是必需的参考资料。

答案 1 :(得分:0)

using System.Drawing;
using System.Printing;
using System.Windows.Forms;
using System.Windows.Controls;
using System.Drawing.Printing;
using System.Security;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using System.Windows.Documents.Serialization;
using System.Windows.Xps;
using System.Printing.Interop;
using System.Windows.Documents;
using System.Windows.Xps.Packaging;
using System.Windows.Xps.Serialization;

程序集: System.Printing ReachFramework

答案 2 :(得分:0)

您可以使用方法扩展名ShowDialogEx:

public static class PrintDialogExtensions
{
    private static readonly PropertyInfo CriticalHandleProperty;

    private static readonly FieldInfo DialogInvokedField;
    private static readonly FieldInfo PrintTicketField;
    private static readonly FieldInfo PrintQueueField;
    private static readonly FieldInfo MinPageField;
    private static readonly FieldInfo MaxPageField;
    private static readonly FieldInfo UserPageRangeEnabledField;
    private static readonly FieldInfo SelectedPagesEnabledField;
    private static readonly FieldInfo CurrentPageEnabledField;
    private static readonly FieldInfo PageRangeField;
    private static readonly FieldInfo PageRangeSelectionField;

    private static readonly PropertyInfo Win32PrintDialogPrintTicketProperty;
    private static readonly PropertyInfo Win32PrintDialogPrintQueueProperty;
    private static readonly PropertyInfo Win32PrintDialogMinPageProperty;
    private static readonly PropertyInfo Win32PrintDialogMaxPageProperty;
    private static readonly PropertyInfo Win32PrintDialogPageRangeEnabledProperty;
    private static readonly PropertyInfo Win32PrintDialogSelectedPagesEnabledProperty;
    private static readonly PropertyInfo Win32PrintDialogSelectedCurrentPageEnabledProperty;
    private static readonly PropertyInfo Win32PrintDialogPageRangeProperty;
    private static readonly PropertyInfo Win32PrintDialogPageRangeSelectionProperty;
    private static readonly MethodInfo Win32PrintDialogProbeForPrintingSupportMethod;

    private static readonly Type Win32PrintDialogType;
    private static readonly Type PrintDlgExMarshalerType;

    private static readonly MethodInfo PrintDlgExMarshalerSyncToStructMethod;
    private static readonly MethodInfo PrintDlgExMarshalerSyncFromStructMethod;
    private static readonly PropertyInfo PrintDlgExMarshalerTypeUnmanagedPrintDlgExProperty;

    private static readonly MethodInfo UnsafeNativeMethodsTypePrintDlgExMethod;

    private static readonly MethodInfo SrTypeGetMethod;

    static PrintDialogExtensions()
    {
        var windowInteropType = typeof(WindowInteropHelper);
        CriticalHandleProperty = windowInteropType.GetProperty("CriticalHandle", BindingFlags.Instance | BindingFlags.NonPublic);

        var type = typeof(PrintDialog);
        DialogInvokedField = type.GetField("_dialogInvoked", BindingFlags.Instance | BindingFlags.NonPublic);
        PrintTicketField = type.GetField("_printTicket", BindingFlags.Instance | BindingFlags.NonPublic);
        PrintQueueField = type.GetField("_printQueue", BindingFlags.Instance | BindingFlags.NonPublic);
        MinPageField = type.GetField("_minPage", BindingFlags.Instance | BindingFlags.NonPublic);
        MaxPageField = type.GetField("_maxPage", BindingFlags.Instance | BindingFlags.NonPublic);
        UserPageRangeEnabledField = type.GetField("_userPageRangeEnabled", BindingFlags.Instance | BindingFlags.NonPublic);
        SelectedPagesEnabledField = type.GetField("_selectedPagesEnabled", BindingFlags.Instance | BindingFlags.NonPublic);
        CurrentPageEnabledField = type.GetField("_currentPageEnabled", BindingFlags.Instance | BindingFlags.NonPublic);
        PageRangeField = type.GetField("_pageRange", BindingFlags.Instance | BindingFlags.NonPublic);
        PageRangeSelectionField = type.GetField("_pageRangeSelection", BindingFlags.Instance | BindingFlags.NonPublic);

        var presentationAssembly = Assembly.Load(new AssemblyName("PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"));

        var unsafeNativeMethodsType = presentationAssembly.GetType("MS.Internal.Printing.UnsafeNativeMethods");
        var srType = presentationAssembly.GetType("System.Windows.SR");
        Win32PrintDialogType = presentationAssembly.GetType("MS.Internal.Printing.Win32PrintDialog");
        PrintDlgExMarshalerType = Win32PrintDialogType.GetNestedType("PrintDlgExMarshaler", BindingFlags.NonPublic);

        Win32PrintDialogPrintTicketProperty = Win32PrintDialogType.GetProperty("PrintTicket", BindingFlags.Instance | BindingFlags.NonPublic);
        Win32PrintDialogPrintQueueProperty = Win32PrintDialogType.GetProperty("PrintQueue", BindingFlags.Instance | BindingFlags.NonPublic);
        Win32PrintDialogMinPageProperty = Win32PrintDialogType.GetProperty("MinPage", BindingFlags.Instance | BindingFlags.NonPublic);
        Win32PrintDialogMaxPageProperty = Win32PrintDialogType.GetProperty("MaxPage", BindingFlags.Instance | BindingFlags.NonPublic);
        Win32PrintDialogPageRangeEnabledProperty = Win32PrintDialogType.GetProperty("PageRangeEnabled", BindingFlags.Instance | BindingFlags.NonPublic);
        Win32PrintDialogSelectedPagesEnabledProperty = Win32PrintDialogType.GetProperty("SelectedPagesEnabled", BindingFlags.Instance | BindingFlags.NonPublic);
        Win32PrintDialogSelectedCurrentPageEnabledProperty = Win32PrintDialogType.GetProperty("CurrentPageEnabled", BindingFlags.Instance | BindingFlags.NonPublic);
        Win32PrintDialogPageRangeProperty = Win32PrintDialogType.GetProperty("PageRange", BindingFlags.Instance | BindingFlags.NonPublic);
        Win32PrintDialogPageRangeSelectionProperty = Win32PrintDialogType.GetProperty("PageRangeSelection", BindingFlags.Instance | BindingFlags.NonPublic);
        Win32PrintDialogProbeForPrintingSupportMethod = Win32PrintDialogType.GetMethod("ProbeForPrintingSupport", BindingFlags.Instance | BindingFlags.NonPublic);

        PrintDlgExMarshalerSyncToStructMethod = PrintDlgExMarshalerType.GetMethod("SyncToStruct", BindingFlags.Instance | BindingFlags.NonPublic);
        PrintDlgExMarshalerSyncFromStructMethod = PrintDlgExMarshalerType.GetMethod("SyncFromStruct", BindingFlags.Instance | BindingFlags.NonPublic);
        PrintDlgExMarshalerTypeUnmanagedPrintDlgExProperty = PrintDlgExMarshalerType.GetProperty("UnmanagedPrintDlgEx", BindingFlags.Instance | BindingFlags.NonPublic);

        UnsafeNativeMethodsTypePrintDlgExMethod = unsafeNativeMethodsType.GetMethod("PrintDlgEx", BindingFlags.Static | BindingFlags.NonPublic);

        SrTypeGetMethod = srType.GetMethods(BindingFlags.Static | BindingFlags.NonPublic).Single(m => String.Equals(m.Name, "Get", StringComparison.OrdinalIgnoreCase) && m.GetParameters().Length == 1);
    }

    [SecurityCritical]
    [SuppressUnmanagedCodeSecurity]
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern IntPtr GetActiveWindow();

    [SuppressUnmanagedCodeSecurity]
    [SecurityCritical]
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, BestFitMapping = false)]
    private static extern Int32 MessageBox(HandleRef hWnd, String text, String caption, Int32 type);

    public static Boolean? ShowDialogEx(this PrintDialog dialog)
    {
        var ticket = PrintTicketField.GetValue(dialog);
        var queue = PrintQueueField.GetValue(dialog);
        var minPage = (UInt32)MinPageField.GetValue(dialog);
        var maxPage = (UInt32)MaxPageField.GetValue(dialog);
        var userPageRangeEnabled = (Boolean)UserPageRangeEnabledField.GetValue(dialog);
        var selectedPagesEnabled = (Boolean)SelectedPagesEnabledField.GetValue(dialog);
        var currentPageEnabled = (Boolean)CurrentPageEnabledField.GetValue(dialog);
        var pageRange = (PageRange)PageRangeField.GetValue(dialog);
        var pageRangeSelection = (PageRangeSelection)PageRangeSelectionField.GetValue(dialog);

        DialogInvokedField.SetValue(dialog, false);
        var win32PrintDialog = Activator.CreateInstance(Win32PrintDialogType);
        var win32MinPage = (UInt32)Win32PrintDialogMinPageProperty.GetValue(win32PrintDialog, null);

        Win32PrintDialogPrintTicketProperty.SetValue(win32PrintDialog, ticket, null);
        Win32PrintDialogPrintQueueProperty.SetValue(win32PrintDialog, queue, null);
        Win32PrintDialogMinPageProperty.SetValue(win32PrintDialog, Math.Max(1U, Math.Min(minPage, maxPage)), null);
        Win32PrintDialogMaxPageProperty.SetValue(win32PrintDialog, Math.Max(win32MinPage, Math.Max(minPage, maxPage)), null);
        Win32PrintDialogPageRangeEnabledProperty.SetValue(win32PrintDialog, userPageRangeEnabled, null);
        Win32PrintDialogSelectedPagesEnabledProperty.SetValue(win32PrintDialog, selectedPagesEnabled, null);
        Win32PrintDialogSelectedCurrentPageEnabledProperty.SetValue(win32PrintDialog, currentPageEnabled, null);
        var win32PrintDialogMinPage = (UInt32)Win32PrintDialogMinPageProperty.GetValue(win32PrintDialog, null);
        var win32PrintDialogMaxPage = (UInt32)Win32PrintDialogMaxPageProperty.GetValue(win32PrintDialog, null);
        Win32PrintDialogPageRangeProperty.SetValue(win32PrintDialog, new PageRange(Math.Max((Int32)win32PrintDialogMinPage, pageRange.PageFrom), Math.Min((Int32)win32PrintDialogMaxPage, pageRange.PageTo)), null);
        Win32PrintDialogPageRangeSelectionProperty.SetValue(win32PrintDialog, pageRangeSelection, null);

        var num = ShowWin32Dialog(win32PrintDialog);
        switch (num)
        {
            case 2U:
            case 1U:
                PrintTicketField.SetValue(dialog, Win32PrintDialogPrintTicketProperty.GetValue(win32PrintDialog, null));
                PrintQueueField.SetValue(dialog, Win32PrintDialogPrintQueueProperty.GetValue(win32PrintDialog, null));
                PageRangeField.SetValue(dialog, Win32PrintDialogPageRangeProperty.GetValue(win32PrintDialog, null));
                PageRangeSelectionField.SetValue(dialog, Win32PrintDialogPageRangeSelectionProperty.GetValue(win32PrintDialog, null));
                DialogInvokedField.SetValue(dialog, true);
                break;
        }
        return (Int32)num == 1;
    }

    private static UInt32 ShowWin32Dialog(Object win32Dialog)
    {
        var num1 = 0U;
        var num2 = IntPtr.Zero;

        if (Application.Current != null)
        {
            Application.Current.Dispatcher.Invoke(new Action(() =>
            {
                if (Application.Current.MainWindow != null)
                {
                    var windowInteropHelper = new WindowInteropHelper(Application.Current.MainWindow);
                    num2 = (IntPtr)CriticalHandleProperty.GetValue(windowInteropHelper, null);
                }
            }));
        }

        try
        {
            var queue = (PrintQueue)Win32PrintDialogPrintQueueProperty.GetValue(win32Dialog, null);
            var ticket = (PrintTicket)Win32PrintDialogPrintTicketProperty.GetValue(win32Dialog, null);

            if (queue == null || ticket == null)
                Win32PrintDialogProbeForPrintingSupportMethod.Invoke(win32Dialog, null);

            using (var printDlgExMarshaler = (IDisposable)Activator.CreateInstance(PrintDlgExMarshalerType, BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { num2, win32Dialog }, null))
            {
                PrintDlgExMarshalerSyncToStructMethod.Invoke(printDlgExMarshaler, null);
                var unmanagedPrintDlgEx = PrintDlgExMarshalerTypeUnmanagedPrintDlgExProperty.GetValue(printDlgExMarshaler, null);
                if ((Int32)UnsafeNativeMethodsTypePrintDlgExMethod.Invoke(null, new[] { unmanagedPrintDlgEx }) == 0)
                    num1 = (UInt32)PrintDlgExMarshalerSyncFromStructMethod.Invoke(printDlgExMarshaler, null);
            }

        }
        catch (Exception ex)
        {
            if (String.Equals(ex.GetType().FullName, "System.Printing.PrintingNotSupportedException", StringComparison.Ordinal))
            {
                string text = (String)SrTypeGetMethod.Invoke(null, new Object[] { "PrintDialogInstallPrintSupportMessageBox" });
                string caption = (String)SrTypeGetMethod.Invoke(null, new Object[] { "PrintDialogInstallPrintSupportCaption" });
                int type = 64 | ((caption == null || caption.Length <= 0 ? 0 : ((int)caption[0] == 8207 ? 1 : 0)) != 0 ? 1048576 : 0);
                if (num2 == IntPtr.Zero)
                    num2 = GetActiveWindow();
                if (MessageBox(new HandleRef(null, num2), text, caption, type) != 0)
                    num1 = 0U;
            }
            else
                throw;
        }

        return num1;
    }
}