How can I invoke the dialog to set printer options manually? - wpf

I'm using WPF and need to let users set some print related options like printer and printer properties (e.g. papertray, landscape/portrait, duplex, etc). I'm aware of the PrintDialog class to get a PrintQueue and PrintTicket object. However I need to create I custom solution and can not show the PrintDialog.
I manage to get the available PrintQueue objects and let users select a printer. I'm struggling with the printer properties.
My question is: how can I show the dialog in which a user can set the printer properties for the selected PrintQueue (the dialog that is shown when a user clicks on the Properties button in the WPF PrintDialog).

The following code was found here (minus the Window_Loaded event). I tested it and it seems to work like a charm. Obviously you'll have to set the printer name in the PrinterSettings object before displaying the dialog.
Hope this works for you:
[DllImport("kernel32.dll")]
static extern IntPtr GlobalLock(IntPtr hMem);
[DllImport("kernel32.dll")]
static extern bool GlobalUnlock(IntPtr hMem);
[DllImport("kernel32.dll")]
static extern bool GlobalFree(IntPtr hMem);
[DllImport("winspool.Drv", EntryPoint = "DocumentPropertiesW", SetLastError = true, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
static extern int DocumentProperties(IntPtr hwnd, IntPtr hPrinter, [MarshalAs(UnmanagedType.LPWStr)] string pDeviceName, IntPtr pDevModeOutput, IntPtr pDevModeInput, int fMode);
private const Int32 DM_OUT_BUFFER = 14;
public void OpenPrinterPropertiesDialog(PrinterSettings printerSettings, System.IntPtr pHandle) {
IntPtr hDevMode = printerSettings.GetHdevmode();
IntPtr pDevMode = GlobalLock(hDevMode);
Int32 fMode = 0;
int sizeNeeded = DocumentProperties(pHandle, IntPtr.Zero, printerSettings.PrinterName, pDevMode, pDevMode, fMode);
IntPtr devModeData = Marshal.AllocHGlobal(sizeNeeded);
fMode = DM_OUT_BUFFER;
DocumentProperties(pHandle, IntPtr.Zero, printerSettings.PrinterName, devModeData, pDevMode, fMode);
GlobalUnlock(hDevMode);
printerSettings.SetHdevmode(devModeData);
printerSettings.DefaultPageSettings.SetHdevmode(devModeData);
GlobalFree(hDevMode);
Marshal.FreeHGlobal(devModeData);
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
OpenPrinterPropertiesDialog(new PrinterSettings(), new WindowInteropHelper(this).Handle);
}

If you target x86 compilation and run from a x64 machine, the code from Pwninstein will not work: when allocating devModeData, DocumentPropreties will always fail and returns a sizeNeeded of -1, with a LastError code 13.
To solve the problem, either make sure you target AnyCPU or just change the call to DocumentPropreties to the following:
int sizeNeeded = DocumentProperties(pHandle,
IntPtr.Zero,
printerSettings.PrinterName,
IntPtr.Zero, // This solves it
pDevMode,
fMode);
Using IntPtr.Zero instead of a proper pointer to a DevMode structure looks wrong, but that first call to DocumentProperties does not attempt to modify the memory at that position. The only data returned by the call is the memory size needed to store the device mode data that represents the internal parameters of the print driver.
Reference:
PInvoke.Net page on DocumentProperties

Related

WPF Ctrl+C Hotkey Registration Overrides/Stops Typical Copy Functionality

I have tried looking into this for a while now and have not been able to come up with an answer that has worked. I am trying to register my WPF C# application to listen for Ctrl+C from anywhere while the application is running. The code below is part of my application and does trigger when the user presses Ctrl+C, however, the contents of the Clipboard are stale (the contents are from a previous copy before the application was running). The AnimateWindow function is being called so I know my hotkey registration is working.
So, my issue is that it appears registering for the hotkey below is overriding the copy functionality. I would like to keep this functionality intact as it is part of how my application works. I need to take what was copied and use that data for a database query and to fill a TextBox.
This is just a snippet of the entire project, please let me know if additional example code is needed to help answer. Thank you!
[DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
[DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
// CTRL + C Hotkey
private const int COPY_ID = 9001;
private const uint MOD_CONTROL = 0x0002; // Modifier: Control
private const uint VK_C = 0x43; // 'C' key
/* Registering the hotkeys to be monitored */
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_windowHandle = new WindowInteropHelper(this).Handle;
_source = HwndSource.FromHwnd(_windowHandle);
_source.AddHook(HwndHook);
RegisterHotKey(_windowHandle, COPY_ID, MOD_CONTROL, VK_C); // 'C' key
}
/* The hook for pressing a hotkey */
private IntPtr HwndHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
const int WM_HOTKEY = 0x0312;
switch (msg)
{
case WM_HOTKEY:
switch (wParam.ToInt32())
{
case COPY_ID:
AnimateWindow();
break;
}
}
}

HwndHost doesn't display content when Desktop Composition is enabled

I'm using HwndHost to embed an external application in my WPF window. I noticed on some Windows 7 machines, if an Aero Theme is selected and Desktop Composition is enabled, the external application starts, flickers on the screen for a split second and then it disappears. If I turn off Desktop Composition or use the basic theme, the application is embedded successfully inside the WPF window.
This is the code I use in a class derived from HwndHost:
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("USER32.DLL", SetLastError = true)]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong);
private const int GWL_STYLE = (-16);
private const int WS_CHILD = 0x40000000;
private const int WS_EX_APPWINDOW = 0x00040000;
[DllImport("user32.dll", SetLastError = true)]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
protected override HandleRef BuildWindowCore(HandleRef hwndParent)
{
while (Process.MainWindowHandle == IntPtr.Zero)
{
Process.Refresh();
System.Threading.Thread.Sleep(10);
}
SetLastError(0);
var ret = SetWindowLong(Process.MainWindowHandle, GWL_STYLE, WS_CHILD);
int e1 = Marshal.GetLastWin32Error();
SetParent(Process.MainWindowHandle, hwndParent.Handle);
int e2 = Marshal.GetLastWin32Error();
ShowWindow(Process.MainWindowHandle, 0);
int e3 = Marshal.GetLastWin32Error();
return new HandleRef(this, Process.MainWindowHandle);
}
I don't get any windows errors when the issue occurs. The process starts from another window which injects it to my class. I've checked with task manager and the process runs but it's not visible inside my WPF window. Any thoughts?
Look at the system addin namespace for how you can use the native HWIND_PTR as a control.
You don't need to use all of the library to do the work.
https://learn.microsoft.com/en-us/dotnet/framework/wpf/app-development/wpf-add-ins-overview

Is there a way of taking a screenshot of the whole screen with White?

Is there an API that would let me do it in http://teststack.github.com/White/?
I can't seem to find it.
Thanks
Pawel
I know this is a very old post. But I though can't hurt to update it. TestStack.White has these functions now:
//Takes a screenshot of the entire desktop, and saves it to disk
Desktop.TakeScreenshot("C:\\white-framework.png", System.Drawing.Imaging.ImageFormat.Png);
//Captures a screenshot of the entire desktop, and returns the bitmap
Bitmap bitmap = Desktop.CaptureScreenshot();
From looking through the code on GitHub, it doesn't appear to have an API for that (perhaps add it as a feature request?).
You can do it fairly simply yourself though using a combination of the Screen class and Graphics.CopyFromScreen. There are some examples of how to capture the screen or the active window in the answers to this question.
The White.Repository project actually records the flow of your test, with screenshots but it is not very well documented and is not released on NuGet yet (it will be soon).
Personally I use this class we have put together from a bunch of sources and forget where I got it originally. This captures modal dialogues and other things that many of the other implementations didn't capture for some reason.
/// <summary>
/// Provides functions to capture the entire screen, or a particular window, and save it to a file.
/// </summary>
public class ScreenCapture
{
[DllImport("gdi32.dll")]
static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop);
[DllImport("user32.dll")]
static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteDC(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr DeleteObject(IntPtr hDc);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);
[DllImport("gdi32.dll")]
static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll")]
static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr ptr);
public Bitmap CaptureScreenShot()
{
var sz = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Size;
var hDesk = GetDesktopWindow();
var hSrce = GetWindowDC(hDesk);
var hDest = CreateCompatibleDC(hSrce);
var hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height);
var hOldBmp = SelectObject(hDest, hBmp);
BitBlt(hDest, 0, 0, sz.Width, sz.Height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt);
var bmp = Image.FromHbitmap(hBmp);
SelectObject(hDest, hOldBmp);
DeleteObject(hBmp);
DeleteDC(hDest);
ReleaseDC(hDesk, hSrce);
return bmp;
}
}
Then to consume
var sc = new ScreenCapture();
var bitmap = sc.CaptureScreenShot();
bitmap.Save(fileName + ".png"), ImageFormat.Png);

How to turn off all touch input at application, window or control level?

Using c# for a wpf application, if in Windows 7 touch is enabled in the control panel, a user by default can 'write' on an InkCanvas with a finger. I want to disable that and force stylus input only.
I'd like to know how to do it more than one way if possible: first by disabling touch on the InkCanvas, second by disabling it for a particular window, and third by disabling it for the entire application. A bonus fourth would be knowing how to turn touch on or off system-wide.
I have tried UnregisterTouchWindow, and I have tried setting Stylus.SetIsTouchFeedbackEnabled to false for the InkCanvas, but neither has worked.
Further digging helped me put together the following as a way to toggle touch on/off system-wide. If anyone knows how to accomplish this in any of the other 3 ways, I'd still appreciate those answers.
The basic steps are to check the current registry status, change it if necessary (and then refresh the system to recognize the change), and make note of the initial state to restore if needed on program exit.
Thanks to the posters at these two links for the education.
public MainWindow(){
InitializeComponent();
RegistryKey regKey = Registry.CurrentUser;
regKey = regKey.OpenSubKey(#"Software\Microsoft\Wisp\Touch", true);
string currKey = regKey.GetValue("TouchGate").ToString();
if (currKey == "1")
{
regKey.SetValue("TouchGate", 0x00000000);
User32Utils.Notify_SettingChange();
UserConfig.TGate = "1";
}
regKey.Close();
...
}
public static class UserConfig {
public static string TGate { get; set; }
...
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e){
...
if (UserConfig.TGate == "1")
{
RegistryKey regKey = Registry.CurrentUser;
regKey = regKey.OpenSubKey(#"Software\Microsoft\Wisp\Touch", true);
regKey.SetValue("TouchGate", 0x00000001);
User32Utils.Notify_SettingChange();
regKey.Close();
}
}
//------------------User32Utils.cs
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;
namespace (...)
{
internal class User32Utils
{
#region USER32 Options
static IntPtr HWND_BROADCAST = new IntPtr(0xffffL);
static IntPtr WM_SETTINGCHANGE = new IntPtr(0x1a);
#endregion
#region STRUCT
enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL = 0x0000,
SMTO_BLOCK = 0x0001,
SMTO_ABORTIFHUNG = 0x2,
SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
}
#endregion
#region Interop
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern IntPtr SendMessageTimeout(IntPtr hWnd,
uint Msg,
UIntPtr wParam,
UIntPtr lParam,
SendMessageTimeoutFlags fuFlags,
uint uTimeout,
out UIntPtr lpdwResult);
#endregion
internal static void Notify_SettingChange()
{
UIntPtr result;
SendMessageTimeout(HWND_BROADCAST, (uint)WM_SETTINGCHANGE,
UIntPtr.Zero, UIntPtr.Zero,
SendMessageTimeoutFlags.SMTO_NORMAL, 1000, out result);
}
}
}

How to sort Windows by z-index?

If I'm enumerating windows in Application.Current.Windows, how can I tell, for any two windows, which one is "closer" (i.e. has greater z-index)?
Or, to say the same in other words, how can I sort those windows by z-index?
You cannot get a Window's Z Order information from WPF so you must resort to Win32.
Something like this ought to do the trick:
var topToBottom = SortWindowsTopToBottom(Application.Current.Windows.OfType<Window>());
...
public IEnumerable<Window> SortWindowsTopToBottom(IEnumerable<Window> unsorted)
{
var byHandle = unsorted.ToDictionary(win =>
((HwndSource)PresentationSource.FromVisual(win)).Handle);
for(IntPtr hWnd = GetTopWindow(IntPtr.Zero); hWnd!=IntPtr.Zero; hWnd = GetWindow(hWnd, GW_HWNDNEXT)
if(byHandle.ContainsKey(hWnd))
yield return byHandle[hWnd];
}
const uint GW_HWNDNEXT = 2;
[DllImport("User32")] static extern IntPtr GetTopWindow(IntPtr hWnd);
[DllImport("User32")] static extern IntPtr GetWindow(IntPtr hWnd, uint wCmd);
The way this works is:
It uses a dictionary to index the given windows by window handle, using the fact that in the Windows implementation of WPF, Window's PresentationSource is always HwndSource.
It uses Win32 to scan all unparented windows top to bottom to find the proper order.
Ah this was a really fun one:
[DllImport("user32.dll")]
static extern IntPtr GetActiveWindow();
public static Window ActiveWindow
{
get
{
return HwndSource.FromHwnd(GetActiveWindow()).RootVisual as Window;
}
}
It will give you the active window in your app (which is usually the foremost).

Resources