获取打印对话框的模态窗口句柄

5
我有一个基于.NET 2.0的Windows应用程序,在Form1中打开了一个PrintDialog。如何从我的代码中获取该对话框的句柄?
我尝试了很多Win32函数:EnumWindowsEnumChildWindowsFindWindowFindWindowEx,但都无法找到我的PrintDialog。我只能找到Form1和其上的控件,而没有办法获取PrintDialog的句柄。
一些我尝试过的代码:
导入Win32:
    [DllImport("user32.dll", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

调用Win32函数:

using (PrintDialog dlg = new PrintDialog
                                 {
                                     AllowCurrentPage = false,
                                     AllowSomePages = true,
                                     AllowSelection = false
                                 })
{    
      IntPtr printHandle = CustomPrintDialog.FindWindow("#32770", "Print");
      // some logic with printHandle go here
      if (dlg.ShowDialog(this)==DialogResult.OK){
          // some logic go here
      }
}

我使用Spy++检查了一下,发现还有一个PrintDialog窗口。这个PrintDialog窗口的父窗口句柄与Form1的句柄完全相同。

有人可以帮我从其父窗口获取PrintDialog的句柄吗?


1
示例代码在这里 - Hans Passant
1个回答

3
问题在于 PrintDialog 的基础窗口是在执行 ShowDialog 方法时创建的。在此方法被调用之前,它并不存在,这就是为什么你找不到该窗口的原因。 因此,你必须在 ShowDialog 内部注入对 PrintDialog 句柄的操作。可以通过使用 Control.BeginInvoke 方法来实现此目的。
public partial class Form1 : Form
{
    ...

    private ShowPrintDialog()
    {
        using (var pd = new PrintDialog())
        {
            BeginInvoke(new MethodInvoker(TweakPrintDialog));
            if (pd.ShowDialog(this) == DialogResult.OK)
            {
                // printing
            }
        }
    }

    private void TweakPrintDialog()
    {
        var printDialogHandle = GetActiveWindow();
        // do your tweaking here
    }

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr GetActiveWindow();
}

另一个问题是找到“PrintDialog”窗口。使用“GetActiveWindow”是一种非常直接的方法,因为在执行“ShowDialog”时,对话框应该处于活动状态。
更可靠的解决方案可能包括枚举顶层窗口并分析它们的所有者和/或其他属性。以下是一种可能的方法,假设打印对话框是目前唯一由窗体拥有的窗口。上一个片段中的“TweakPrintDialog”方法已被修改:
    private void TweakPrintDialog()
    {
        uint processId;
        var threadId = GetWindowThreadProcessId(this.Handle, out processId);
        EnumThreadWindows(threadId, FindPrintDialog, IntPtr.Zero);
        // printDialogHandle field now contains the found handle
        // do your tweaking here
    }

    private IntPtr printDialogHandle;

    private bool FindPrintDialog(IntPtr handle, IntPtr lParam)
    {
        if (GetWindow(handle, GW_OWNER) == this.Handle)
        {
            printDialogHandle = handle;
            return false;
        }
        else
        {
            return true;
        }
    }

    [DllImport("user32.dll", SetLastError = true)]
    private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

    private delegate bool EnumWindowProc(IntPtr handle, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool EnumThreadWindows(uint threadId, EnumWindowProc enumProc, IntPtr lParam);

    private const uint GW_OWNER = 4;

    [DllImport("user32.dll", SetLastError = true)]
    private static extern IntPtr GetWindow(IntPtr handle, uint cmd);

正是我所需要的。阅读了一些示例后,我知道在showdialog之前找不到对话框,但我不知道该如何处理它。你的答案完全解决了我的问题。 - khoa_chung_89
尝试在FolderBrowserDialog上执行此操作,窗口枚举会产生一个项目,对其进行GetWindow操作会返回零。窗口遍历是在对话框实际显示之前触发的。 - Loren Pechtel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接