Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PrintDialog can only be invoked from Application.Current.Dispatcher.CurrentThread #3194

Open
ClosetBugSlayer opened this issue Jun 28, 2020 · 4 comments
Labels
Bug Product bug (most likely)
Milestone

Comments

@ClosetBugSlayer
Copy link

ClosetBugSlayer commented Jun 28, 2020

PrintDialog is not compatible with multithreaded WPF apps that create windows on threads other than the one specified by Application.Current.Dispatcher.CurrentThread.

PrintDialog.ShowDialog() copies the properties to an internal Win32PrintDialog class then calls Win32PrintDialog.ShowDialog():
https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Controls/PrintDialog.cs

In the beginning of Win32PrintDialog.ShowDialog() you have this:
https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/Printing/Win32PrintDialog.cs

//
// 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;
}

The problem is that if you are not running on the same thread as Application.Current.Dispatcher.CurrentThread, then you are not allowed to even access the MainWindow property because it does a hard thread check with VerifyAccess() which throws an InvalidOperationException. Furthermore, there doesn't even seem to be clear guarantees that MainWindow itself was created on the thread.
https://github.com/dotnet/wpf/blob/master/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Application.cs

public Window MainWindow
{
    get
    {
        base.VerifyAccess();
        return this._mainWindow;
    }
    set { }
}

If the PrintDialog needs to force an owner window, it should use the active window for the thread not the global window saved in Application.MainWindow. Unfortunately WPF seems to offer no way to do this. Individual Dispatchers don't seem to keep track of windows. Win32PrintDialog could use Win32's GetActiveWindow() directly because it doesn't use the Window object only the HWND value.

To prevent PrintDialog from being unusable in the interim, the if-statement could at least be stripped out completely or patched to the following:

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

or more cleanly:

var appInstance = System.Windows.Application.Current;
if ((appInstance?.CheckAccess() == true) &&
    (appInstance.MainWindow?.CheckAccess() == true)
{
    System.Windows.Interop.WindowInteropHelper helper =
        new System.Windows.Interop.WindowInteropHelper(appInstance.MainWindow);
    owner = helper.CriticalHandle;
}
@fabiant3 fabiant3 added the Bug Product bug (most likely) label Jun 29, 2020
@fabiant3 fabiant3 added this to the Future milestone Jun 29, 2020
@Zubastic
Copy link

@ClosetBugSlayer this is connected with this issue as well? #2609

@ClosetBugSlayer
Copy link
Author

@ClosetBugSlayer this is connected with this issue as well? #2609

I don't believe so, but I have figured out the root of this issue so it won't be hard to test.

@rufw91
Copy link

rufw91 commented Aug 13, 2020

@ClosetBugSlayer this is connected with this issue as well? #2609

I don't believe so, but I have figured out the root of this issue so it won't be hard to test.

OMG i cant believe this is happening, three weeks past the deadline and i have print issues i cant explain!!!!!!!!!!

@Manuzor
Copy link

Manuzor commented Jul 8, 2021

Another nice option would be to pass the owner directly to ShowDialog, giving users the chance to choose per call.

bool? ShowDialog(Window owner) { /* ... */ }

Quite frankly I'm kinda baffled that something like this currently not possible at all.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Product bug (most likely)
Projects
None yet
Development

No branches or pull requests

5 participants