Ryan
Ryan

Reputation: 29

C# Capture Microsoft Print to PDF dialog

I would like to capture and suppress the Savefiledialog that is shown when using the Microsoft Print to PDF driver from an application that is not office. Then programmatically enter the file path, possibly with System.Windows.Automation.

I can’t seem to find a handle to the SaveFileDialog when its displayed. I believe I could handle the Windows.Automation part.

I would like to use the Microsoft driver, since it is shipped with all windows 10.

Are there other ways to capture/suppress this dialog? I currently do this with another pdf driver on my local machine through the registry. But I would like to move to the Microsoft PDF since it is standard.

The Thread: How to programmatically print to PDF file without prompting for filename in C# using the Microsoft Print To PDF printer that comes with Windows 10 -Does not solve my problem, and prints a blank page when run from the Revit API. Autodesk Revit has to initiate the printing (and is done through its api).

Code for trying to find the dialog from user32.dll

public static List<IntPtr>GetChildWindows( IntPtr parent) {
  List<IntPtr>result=new List<IntPtr>();
  GCHandle listHandle=GCHandle.Alloc(result);
  try {
    EnumWindowProc childProc=new EnumWindowProc(EnumWindow);
    EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
  }
  finally {
    if (listHandle.IsAllocated) listHandle.Free();
  }
  return result;
}

public static string GetText(IntPtr hWnd) {
  int length=GetWindowTextLength(hWnd);
  StringBuilder sb=new StringBuilder(length + 1);
  GetWindowText(hWnd, sb, sb.Capacity);
  return sb.ToString();
}

private void Test() {
  Process[] revits=Process.GetProcessesByName( "Revit");
  List<IntPtr>children=GetChildWindows( revits[0].MainWindowHandle);
  var names=new List<string>();
  foreach (var child in children) {
    names.Add(GetText(child));
  }
}

Upvotes: 1

Views: 1806

Answers (1)

pstrjds
pstrjds

Reputation: 17428

I ran a few tests on my own system and it appears that enumerating the top level windows will find the Save file dialog. I tried with printing to the MS PDF printer from multiple programs and the results were all the same. Below is some code adapted from the MS Docs window enumeration example. I added in the code to get the process ID so you can check to be sure it is your window.

// P/Invoke declarations
protected delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
protected static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll")]
protected static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam);
[DllImport("user32.dll")]
protected static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);

// Callback for examining the window
protected static bool EnumTheWindows(IntPtr hWnd, IntPtr lParam)
{
    int size = GetWindowTextLength(hWnd);
    if (size++ > 0 && IsWindowVisible(hWnd))
    {
        StringBuilder sb = new StringBuilder(size);
        GetWindowText(hWnd, sb, size);
        if (sb.ToString().Equals("Save Print Output As", StringComparison.Ordinal))
        {
            uint procId = 0;
            GetWindowThreadProcessId(hWnd, out procId);
            Console.WriteLine($"Found it! ProcID: {procId}");
        }
    }
    return true;
}

void Main()
{
   EnumWindows(new EnumWindowsProc(EnumTheWindows), IntPtr.Zero);
}

Upvotes: 1

Related Questions