1

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));
  }
}
Ryan
  • 29
  • 8
  • 1
    Possible duplicate of [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](https://stackoverflow.com/questions/31896952/how-to-programmatically-print-to-pdf-file-without-prompting-for-filename-in-c-sh) – pstrjds Sep 04 '18 at 18:44
  • @your edit: so if that API is controlling the printing, how are you even able to intercept the printer communication from user code? or is that your actual question? because I think when we could inject the file name into the process the "duplicate" post would still contribute to a possible solution. – Cee McSharpface Sep 04 '18 at 19:18
  • Is it your process that is calling print which then leads to the Save dialog or is that being launched by another process? Just wondering what you have tried so far to find the window handle. I am assuming you have tried `EnumWindows` from user32.dll. If you get the main window for the process that is launching the Save dialog I would think you should be able to find it under the child windows of that process. Can you post the code that isn't working so we know what you have tried? – pstrjds Sep 04 '18 at 19:54
  • @dlatikay - The API controls the printing process and i would not be able to intercept it to pass info. – Ryan Sep 04 '18 at 23:47
  • @pstrjds I can get the main window, but cant seem to get the dialog as a child. code posted – Ryan Sep 04 '18 at 23:47
  • Have you tried [spy++](https://learn.microsoft.com/en-us/visualstudio/debugger/introducing-spy-increment?view=vs-2017) just to verify who owns the window. I ran a quick experiment just "printing" from Chrome and although the window seemed to be owned by the Chrome process, it showed up as a top-level window with the only parent being the windows Desktop. Maybe you just need to search for it as a top-level window. – pstrjds Sep 05 '18 at 07:12

1 Answers1

1

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);
}
pstrjds
  • 16,840
  • 6
  • 52
  • 61
  • I was playing around with spy++ but haven't had much experience using it. I was stuck thinking the Save file Dialog was a child of Revit. – Ryan Sep 05 '18 at 14:43
  • @Ryan - I thought it would be a child window as well, but then I thought it's worth experimenting to see if it is a top level window, since you obviously had the code to enumerate the child windows and that wasn't working. I fired up Spy++ and sure enough it was listed as a top level window, so then I took the example code, made a couple tweaks and it worked a treat! – pstrjds Sep 05 '18 at 17:46