---
# System prepended metadata

title: WPF `PrintQueue.CreateXpsDocumentWriter` を使用して別スレッドから印刷しようとするとエラーが発生する。 2022-06-17
tags: [PrintQueue.CreateXpsDocumentWriter, WPF]

---

---
lang: ja-jp
breaks: true
---
# WPF `PrintQueue.CreateXpsDocumentWriter` を使用して別スレッドから印刷しようとするとエラーが発生する。 2022-06-17

## .NET Frameworkのソースを参照

Win32PrintDialog.cs

```csharp=
        [SecurityCritical, SecurityTreatAsSafe]
        internal
        UInt32
        ShowDialog()
        {
            UInt32 result = NativeMethods.PD_RESULT_CANCEL;

            //
            // Get the process main window handle
            //
            IntPtr owner = IntPtr.Zero;

            if ((System.Windows.Application.Current != null) &&
                (System.Windows.Application.Current.MainWindow != null))
            {
                System.Windows.Interop.WindowInteropHelper helper =
                    new System.Windows.Interop.WindowInteropHelper(System.Windows.Application.Current.MainWindow);
                owner = helper.CriticalHandle;
            }

            try
            {
                if (this._printQueue == null || this._printTicket == null)
                {
                    // Normally printDlgEx.SyncToStruct() probes the printer if both the print queue and print
                    // ticket are not null.
                    // If either is null we probe the printer ourselves
                    // If we dont end users will get notified that printing is disabled *after*
                    // the print dialog has been displayed.

                    ProbeForPrintingSupport();
                }

                //
                // Create a PrintDlgEx instance to invoke the Win32 Print Dialog
                //
                using (PrintDlgExMarshaler printDlgEx = new PrintDlgExMarshaler(owner, this))
                {
                    printDlgEx.SyncToStruct();

                    //
                    // Display the Win32 print dialog
                    //
                    Int32 hr = UnsafeNativeMethods.PrintDlgEx(printDlgEx.UnmanagedPrintDlgEx);
                    if (hr == MS.Win32.NativeMethods.S_OK)
                    {
                        result = printDlgEx.SyncFromStruct();
                    }
                }
            }
```

:::info
`System.Windows.Application.Current.MainWindow`が所有するスレッドから印刷ダイアログを表示している模様。
:::

## `Application.Current.Dispatcher.Invoke` によりメインスレッドで処理を行っても、以下でエラーとなる。

:::info
`Application.Current.Dispatcher.Invoke`により、印刷ダイアログの表示までは正常に動作する。
:::

```csharp=
paginator.PageSize = new Size(
    ia.MediaSizeWidth,
    ia.MediaSizeHeight
    );
```

```shell=
System.InvalidOperationException
  HResult=0x80131509
  Message=このオブジェクトは別のスレッドに所有されているため、呼び出しスレッドはこのオブジェクトにアクセスできません。
  Source=WindowsBase
  スタック トレース:
   場所 System.Windows.Threading.Dispatcher.VerifyAccess()
   場所 MS.Internal.Documents.FlowDocumentPaginator.set_PageSize(Size value)
```

## ぐぐる

> wpf - Printing the content of a DocumentViewer in a different UI thread
> https://jonic.cn/qa/?qa=1008550/wpf-printing-the-content-of-a-documentviewer-in-a-different-ui-thread

> PrintDialog and a secondary UI thread severe problem
> https://social.msdn.microsoft.com/Forums/en-US/3aad21a4-17fc-40de-badb-8f1c20b25dbe/printdialog-and-a-secondary-ui-thread-severe-problem?forum=wpf

> Replacement class for System.Windows.PrintDialog
> https://pastebin.com/f1a411397

## 印刷対象のXamlをUIスレッド上で一旦文字列として取得し、あとはメインスレッド上でXaml（XPS）を再構築して印刷することで解決

Xmalを文字列として取得

```csharp=
// WPFのUIスレッドで実行
public string GetFlowDocumentXmlString(FlowDocument doc)
{
    return XamlWriter.Save(doc);
}
```

```csharp=
// WPFのUIスレッドで実行
string xmalString = GetFlowDocumentXmlString(doc);

// `PrintQueue.CreateXpsDocumentWriter`がメインスレッド上でのみ実行可能な為、
// メインスレッド上で実行する。
Application.Current.Dispatcher.Invoke(() => { 
            
    // メインスレッド上でXaml（XPS）を再構築。
    FlowDocument doc;
    {
        XmlDocument document = new XmlDocument();
        document.LoadXml(xmalString);
        doc =(FlowDocument)XamlReader.Load(new XmlNodeReader(document));
    }

    PrintDocumentImageableArea ia = null;
    XpsDocumentWriter docWriter = null;
    docWriter = PrintQueue.CreateXpsDocumentWriter(ref ia);
    
    if (   docWriter != null 
        && ia != null
        )
    {
        DocumentPaginator paginator = ((IDocumentPaginatorSource)doc).DocumentPaginator;
        Size pageSize = default;
        pageSize = new Size(
            ia.MediaSizeWidth,
            ia.MediaSizeHeight
            );

        paginator.PageSize = pageSize;
        
        doc.ColumnWidth = double.PositiveInfinity;
        
        docWriter.Write(paginator);
    }
    
});
```

###### tags: `WPF` `PrintQueue.CreateXpsDocumentWriter`
