Detect when windows display settings are not default [duplicate] - c#

How can I get the DPI in WPF?

https://learn.microsoft.com/en-us/archive/blogs/jaimer/getting-system-dpi-in-wpf-app seems to work
PresentationSource source = PresentationSource.FromVisual(this);
double dpiX, dpiY;
if (source != null) {
dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
}

var dpiXProperty = typeof(SystemParameters).GetProperty("DpiX", BindingFlags.NonPublic | BindingFlags.Static);
var dpiYProperty = typeof(SystemParameters).GetProperty("Dpi", BindingFlags.NonPublic | BindingFlags.Static);
var dpiX = (int)dpiXProperty.GetValue(null, null);
var dpiY = (int)dpiYProperty.GetValue(null, null);

With .NET 4.6.2 Preview and higher, you can call VisualTreeHelper.GetDpi(Visual visual). It returns a DpiScale structure, which tells you the DPI at which the given Visual will be or has been rendered.

I have updated my answer from 2015. Here is some utility code that uses the latest DPI functions from Windows 10 (specifically GetDpiForWindow function which is the only method that supports the DPI_AWARENESS of the window/application/process, etc.) but falls back to older ones (dpi per monitor, and desktop dpi) so it should still work with Windows 7.
It has not dependency on WPF nor Winforms, only on Windows itself.
// note this class considers dpix = dpiy
public static class DpiUtilities
{
// you should always use this one and it will fallback if necessary
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdpiforwindow
public static int GetDpiForWindow(IntPtr hwnd)
{
var h = LoadLibrary("user32.dll");
var ptr = GetProcAddress(h, "GetDpiForWindow"); // Windows 10 1607
if (ptr == IntPtr.Zero)
return GetDpiForNearestMonitor(hwnd);
return Marshal.GetDelegateForFunctionPointer<GetDpiForWindowFn>(ptr)(hwnd);
}
public static int GetDpiForNearestMonitor(IntPtr hwnd) => GetDpiForMonitor(GetNearestMonitorFromWindow(hwnd));
public static int GetDpiForNearestMonitor(int x, int y) => GetDpiForMonitor(GetNearestMonitorFromPoint(x, y));
public static int GetDpiForMonitor(IntPtr monitor, MonitorDpiType type = MonitorDpiType.Effective)
{
var h = LoadLibrary("shcore.dll");
var ptr = GetProcAddress(h, "GetDpiForMonitor"); // Windows 8.1
if (ptr == IntPtr.Zero)
return GetDpiForDesktop();
int hr = Marshal.GetDelegateForFunctionPointer<GetDpiForMonitorFn>(ptr)(monitor, type, out int x, out int y);
if (hr < 0)
return GetDpiForDesktop();
return x;
}
public static int GetDpiForDesktop()
{
int hr = D2D1CreateFactory(D2D1_FACTORY_TYPE.D2D1_FACTORY_TYPE_SINGLE_THREADED, typeof(ID2D1Factory).GUID, IntPtr.Zero, out ID2D1Factory factory);
if (hr < 0)
return 96; // we really hit the ground, don't know what to do next!
factory.GetDesktopDpi(out float x, out float y); // Windows 7
Marshal.ReleaseComObject(factory);
return (int)x;
}
public static IntPtr GetDesktopMonitor() => GetNearestMonitorFromWindow(GetDesktopWindow());
public static IntPtr GetShellMonitor() => GetNearestMonitorFromWindow(GetShellWindow());
public static IntPtr GetNearestMonitorFromWindow(IntPtr hwnd) => MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
public static IntPtr GetNearestMonitorFromPoint(int x, int y) => MonitorFromPoint(new POINT { x = x, y = y }, MONITOR_DEFAULTTONEAREST);
private delegate int GetDpiForWindowFn(IntPtr hwnd);
private delegate int GetDpiForMonitorFn(IntPtr hmonitor, MonitorDpiType dpiType, out int dpiX, out int dpiY);
private const int MONITOR_DEFAULTTONEAREST = 2;
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr LoadLibrary(string lpLibFileName);
[DllImport("kernel32", CharSet = CharSet.Ansi, SetLastError = true)]
private static extern IntPtr GetProcAddress(IntPtr hModule, string lpProcName);
[DllImport("user32")]
private static extern IntPtr MonitorFromPoint(POINT pt, int flags);
[DllImport("user32")]
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, int flags);
[DllImport("user32")]
private static extern IntPtr GetDesktopWindow();
[DllImport("user32")]
private static extern IntPtr GetShellWindow();
[StructLayout(LayoutKind.Sequential)]
private partial struct POINT
{
public int x;
public int y;
}
[DllImport("d2d1")]
private static extern int D2D1CreateFactory(D2D1_FACTORY_TYPE factoryType, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, IntPtr pFactoryOptions, out ID2D1Factory ppIFactory);
private enum D2D1_FACTORY_TYPE
{
D2D1_FACTORY_TYPE_SINGLE_THREADED = 0,
D2D1_FACTORY_TYPE_MULTI_THREADED = 1,
}
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("06152247-6f50-465a-9245-118bfd3b6007")]
private interface ID2D1Factory
{
int ReloadSystemMetrics();
[PreserveSig]
void GetDesktopDpi(out float dpiX, out float dpiY);
// the rest is not implemented as we don't need it
}
}
public enum MonitorDpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}

The only way I found to get the "real" monitor dpi is the following. All other mentioned techniques just say 96 which is not correct for the most monitors.
public class ScreenInformations
{
public static uint RawDpi { get; private set; }
static ScreenInformations()
{
uint dpiX;
uint dpiY;
GetDpi(DpiType.RAW, out dpiX, out dpiY);
RawDpi = dpiX;
}
/// <summary>
/// Returns the scaling of the given screen.
/// </summary>
/// <param name="dpiType">The type of dpi that should be given back..</param>
/// <param name="dpiX">Gives the horizontal scaling back (in dpi).</param>
/// <param name="dpiY">Gives the vertical scaling back (in dpi).</param>
private static void GetDpi(DpiType dpiType, out uint dpiX, out uint dpiY)
{
var point = new System.Drawing.Point(1, 1);
var hmonitor = MonitorFromPoint(point, _MONITOR_DEFAULTTONEAREST);
switch (GetDpiForMonitor(hmonitor, dpiType, out dpiX, out dpiY).ToInt32())
{
case _S_OK: return;
case _E_INVALIDARG:
throw new ArgumentException("Unknown error. See https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510.aspx for more information.");
default:
throw new COMException("Unknown error. See https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510.aspx for more information.");
}
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062.aspx
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510.aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
const int _S_OK = 0;
const int _MONITOR_DEFAULTTONEAREST = 2;
const int _E_INVALIDARG = -2147024809;
}
/// <summary>
/// Represents the different types of scaling.
/// </summary>
/// <seealso cref="https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511.aspx"/>
public enum DpiType
{
EFFECTIVE = 0,
ANGULAR = 1,
RAW = 2,
}

This is how I managed to get a "scale factor" in WPF.
My laptop's resolution is 1920x1440.
int resHeight = System.Windows.Forms.Screen.PrimaryScreen.Bounds.Height; // 1440
int actualHeight = SystemParameters.PrimaryScreenHeight; // 960
double ratio = actualHeight / resHeight;
double dpi = resHeigh / actualHeight; // 1.5 which is true because my settings says my scale is 150%

Use GetDeviceCaps function:
static void Main(string[] args)
{
// 1.25 = 125%
var dpi = GetDpi();
}
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hwnd);
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
private static float GetDpi()
{
IntPtr desktopWnd = IntPtr.Zero;
IntPtr dc = GetDC(desktopWnd);
var dpi = 100f;
const int LOGPIXELSX = 88;
try
{
dpi = GetDeviceCaps(dc, LOGPIXELSX);
}
finally
{
ReleaseDC(desktopWnd, dc);
}
return dpi / 96f;
}

You can try using ManagementClass:
public static string GetDPI()
{
using (ManagementClass mc = new ManagementClass("Win32_DesktopMonitor"))
{
using (ManagementObjectCollection moc = mc.GetInstances())
{
int PixelsPerXLogicalInch = 0; // dpi for x
int PixelsPerYLogicalInch = 0; // dpi for y
foreach (ManagementObject each in moc)
{
PixelsPerXLogicalInch = int.Parse((each.Properties["PixelsPerXLogicalInch"].Value.ToString()));
PixelsPerYLogicalInch = int.Parse((each.Properties["PixelsPerYLogicalInch"].Value.ToString()));
}
return PixelsPerXLogicalInch + "," + PixelsPerYLogicalInch;
}
}
}

There are
https://blogs.windows.com/buildingapps/2017/01/25/calling-windows-10-apis-desktop-application/#FJtMAIFjbtXiLQAp.97
January 25, 2017 3:54 pm
"Calling Windows 10 APIs From a Desktop Application"
and
https://learn.microsoft.com/en-us/uwp/api/windows.devices.display.displaymonitor
"Display​Monitor Class"
Namespace: Windows.Devices.Display Assemblies:Windows.Devices.Display.dll, Windows.dll
Provides information about a display monitor device connected to the system.
These data include commonly used information from the monitor's Extended Display Identification Data (EDID, which is an industry-standard display descriptor block that nearly all monitors use to provide descriptions of supported modes and general device information) and DisplayID (which is a newer industry standard that provides a superset of EDID).
Raw​DpiX
Gets the physical horizontal DPI of the monitor (based on the monitor’s native resolution and physical size).
Raw​DpiY
Gets the physical vertical DPI of the monitor (based on the monitor’s native resolution and physical size).

Basic monitor info in Windows from 2006
https://learn.microsoft.com/en-us/windows/desktop/wmicoreprov/msmonitorclass
MSMonitorClass class
WmiMonitorRawEEdidV1Block class
WmiMonitorBasicDisplayParams class
MaxHorizontalImageSize ( EDID byte 21 )
MaxVerticalImageSize ( EDID byte 22 )
( Sizes in EDID are in centimeters above and in millimeters in EDID Detailed Timing Descriptor
12 Horizontal image size, mm, 8 lsbits (0–4095 mm, 161 in)
13 Vertical image size, mm, 8 lsbits (0–4095 mm, 161 in)
14 Bits 7–4 Horizontal image size, mm, 4 msbits
Bits 3–0 Vertical image size, mm, 4 msbits
)
and
https://social.msdn.microsoft.com/Forums/vstudio/en-US/e7bb9384-b343-4543-ac0f-c98b88a7196f/wpf-wmi-just-get-an-empty-string

Related

Compute total width of title bar buttons for 3rd party window on windows 10

My initial approach to this was using GetSystemMetrics with SystemMetric.SM_CXSIZE and some simple math based on which buttons are available (times 3, or times 1), via WindowStyle.
[DllImport("user32.dll")]
private static extern int GetSystemMetrics(SystemMetric smIndex);
This has an issue on Windows 10, where the calculated width is approximately 70% of actual. So the width covers just two buttons - maximize and close. Windows 7 and 8.1 are fine, same DPI setting, where it covers all buttons.
I checked a few existing questions on Stack Overflow, and had most success with this one from 2011:
How do I compute the non-client window size in WPF?
Unfortunately, while the suggested approach does work in windows 8.1, it calculates 0 on Windows 10 (latest version, all recommended updates). Is there a way that works on all OS from 7 to 10?
Code was taken from the above answer and modified to calculate total width of window's control buttons, by window handle (hwnd), and changed marshalling to RECT from Rectangle (then I get correct values of left/right).
public static int GetControlButtonsWidth(IntPtr hwnd)
{
// Create and initialize the structure
TITLEBARINFOEX tbi = new TITLEBARINFOEX();
tbi.cbSize = Marshal.SizeOf(typeof(TITLEBARINFOEX));
// Send the WM_GETTITLEBARINFOEX message
SendMessage(hwnd, WM_GETTITLEBARINFOEX, IntPtr.Zero, ref tbi);
int sum = tbi.rgrect.Sum(r => r.right - r.left);
// Return the filled-in structure
return sum;
}
internal const int WM_GETTITLEBARINFOEX = 0x033F;
internal const int CCHILDREN_TITLEBAR = 5;
[StructLayout(LayoutKind.Sequential)]
internal struct TITLEBARINFOEX
{
public int cbSize;
public RECT rcTitleBar;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
public int[] rgstate;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = CCHILDREN_TITLEBAR + 1)]
public RECT[] rgrect;
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr SendMessage(
IntPtr hWnd,
int uMsg,
IntPtr wParam,
ref TITLEBARINFOEX lParam);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left, top, right, bottom;
}
You can use DwmGetWindowAttribute, the combined width for those 3 buttons should be 185 pixels on Windows 10, at 125% DPI. Note that if your application is not DPI aware, then the result will still be the same, 185 for example.
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("dwmapi.dll")]
public static extern int DwmGetWindowAttribute(
IntPtr hwnd, int attr, out RECT ptr, int size);
public void foo()
{
int DWMWA_CAPTION_BUTTON_BOUNDS = 5;
RECT rc;
if (0 != DwmGetWindowAttribute(this.Handle, DWMWA_CAPTION_BUTTON_BOUNDS,
out rc, Marshal.SizeOf(typeof(RECT))))
{
//error
}
int width = rc.right - rc.left;
}

SendInput not working perfectly on extended screen

Mouse stimulation using SendInput works perfectly on MainDisplay. However when I use SendInput for extended screen (e.g. Second screen placed to the left of the main display in my case. Issues is replicable irrespective of the extended display any place around main display but with different resolution then main display):
If I use SendInput on extended screen, the mouse position has offset in both X and Y position, ever so slightly ranging from 40 to 80 points in x and 10 to 20 points in Y based on if X (width) and Y(height) of extended screen is different to main display width/height)
Thanks in advance for any support as to why difference on extended screen
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetCursorPos(ref Win32Point pt);
[DllImport("user32.dll")]
internal static extern bool SetCursorPos(int X, int Y);
[StructLayout(LayoutKind.Sequential)]
internal struct Win32Point
{
public Int32 X;
public Int32 Y;
};
internal enum SendInputEventType : int
{
InputMouse,
InputKeyboard
}
[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint nInputs, ref Input pInputs, int cbSize);
public struct Input
{
public uint InputType;
public MouseInput MI;
}
public struct MouseInput
{
public int Dx;
public int Dy;
public uint MouseData;
public uint DwFlags;
public uint Time;
public IntPtr DwExtraInfo;
}
public enum MouseEventInfo
{
mouseEventfMove = 0x0001,
mouseEventfLeftdown = 0x0002,
mouseEventfLeftup = 0x0004,
mouseEventfRightdown = 0x0008,
mouseEventfRightup = 0x0010,
mouseEventfWheel = 0x0800,
mouseEventfAbsolute = 0x8000,
wheelDelta = 0x0078
}
static int CalculateAbsoluteCoordinateX(int x, System.Drawing.Rectangle currentBounds)
{
return ((currentBounds.X + x) * 65536) / (currentBounds.Width);
}
static int CalculateAbsoluteCoordinateY(int y, System.Drawing.Rectangle currentBounds)
{
return (((currentBounds.Y + y) * 65536) / currentBounds.Height);
}
// for me screen at index 0 (screen no 1) is main display. Screen id 2
//placed to the left of the main display as per resolution screen i.e.at
//index 1 (Screen.AllScreens[1]) is extended display and Bound.X is a -ve value
public static int ScreenId = 2;
public static System.Drawing.Rectangle CurrentBounds
{
get
{
return SysForms.Screen.AllScreens[ScreenId - 1].Bounds;
}
}
public static void ClickLeftMouseButton(int x, int y)
{
Input mouseInput = new Input();
mouseInput.InputType = SendInputEventType.InputMouse;
mouseInput.MI.Dx = CalculateAbsoluteCoordinateX(x, CurrentBounds);
mouseInput.MI.Dy = CalculateAbsoluteCoordinateY(y, CurrentBounds);
mouseInput.MI.MouseData = 0;
mouseInput.MI.DwFlags = MouseEventInfo.mouseEventfMove | MouseEventInfo.mouseEventfAbsolute;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
mouseInput.MI.DwFlags = MouseEventInfo.mouseEventfLeftdown;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
mouseInput.MI.DwFlags = MouseEventFlags.mouseEventfLeftup;
SendInput(1, ref mouseInput, Marshal.SizeOf(new INPUT()));
}
//Below is code of the WPF MainWindow for testing. Two buttons with click event.
// For main display with screenid as 1 both setcursor position and sendinput
//work perfectly, as I get the MousePosition, but when I apply this to
//extended screen (currently with two screen, main display is screen 1 in my
//case and screen 2 is extended screen, they put the mouse at two different positions.
//I have my doubts the way I am using the extended screen Bounds.X, but
//haven't will able to fix the issue
int x = 600;
int y = 300;
private void btnSend_Click(object sender, RoutedEventArgs e)
{
SetCursorPos(SysForms.Screen.AllScreens[ScreenId - 1].Bounds.X + x, SysForms.Screen.AllScreens[screenId - 1].Bounds.Y + y);
}
private void btnSend1_Click(object sender, RoutedEventArgs e)
{
ClickLeftMouseButton(x, y);
}
Found the issue. While using SendInput, the conversion of x,y in absolute value must be done in relation to Main/Primary screen.
Thus the changes:
static int CalculateAbsoluteCoordinateX(int x, System.Drawing.Rectangle currentBounds)
{
return ((currentBounds.X + x) * 65536) / (SystemParameters.PrimaryScreenWidth);
}
static int CalculateAbsoluteCoordinateY(int y, System.Drawing.Rectangle currentBounds)
{
return (((currentBounds.Y + y) * 65536) / SystemParameters.PrimaryScreenHeight);
}

How can i get pixels from a specific area of the screen in C# very fast?

I want to calculate average pixels color from a specific area of the screen. I'm making a LED backlight for my TV so it needs to be very fast. At least 30fps. Bitmap.GetPixel() is too slow for it. I found an OpenGL method Gl.ReadPixels() but i don't know how it works. It recieving int[] as a data to return. But after calling this method i only get an array full of 0. How can i use this method? Or maybe there is some other way for this task?
By far the fastest way to process images in C# is through pinvoke using Win32 Gdi32 and User32 functionality.
Here is some C# code which captures the current desktop. Optionally you can pass the filepath of a 'mask' image which can be used to isolate a region(s) of the desktop capture. In the mask, texel which is black (with alpha 0) will be discarded from the screen grab. It's quite rudimental since it only supports 32-bit images and requires the mask and screen capture to be the same size. You welcome to improve on it in your own way, or you can see how image processing is done on a texel by texel basis by hooking directly into the Win32 functions with pinvoke (see constructor).
public class ScreenCapture
{
private System.Drawing.Bitmap capture;
/// <summary>
/// Constructor
/// </summary>
/// <param name="maskFile">Optional mask file, use "" for no mask</param>
public ScreenCapture( string maskFile )
{
// capture the screen
capture = CaptureWindow(User32.GetDesktopWindow());
// if there is a mask file, load it and apply it to the capture
if (maskFile != "")
{
string maskfilename = maskFile + ".png";
System.Drawing.Bitmap maskImage = System.Drawing.Image.FromFile(maskfilename) as Bitmap;
// ensure mask is same dim as capture...
if ((capture.Width != maskImage.Width) || (capture.Height != maskImage.Height))
throw new System.Exception("Mask image is required to be " + capture.Width + " x " + capture.Height + " RGBA for this screen capture");
Rectangle rectCapture = new Rectangle(0, 0, capture.Width, capture.Height);
Rectangle rectMask = new Rectangle(0, 0, maskImage.Width, maskImage.Height);
System.Drawing.Imaging.BitmapData dataCapture = capture.LockBits(rectCapture, System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
System.Drawing.Imaging.BitmapData dataMask = maskImage.LockBits(rectCapture, System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
IntPtr ptrCapture = dataCapture.Scan0;
IntPtr ptrMask = dataMask.Scan0;
int size = Math.Abs(dataCapture.Stride) * capture.Height;
byte[] bitsCapture = new byte[size];
byte[] bitsMask = new byte[size];
System.Runtime.InteropServices.Marshal.Copy(ptrCapture, bitsCapture, 0, size);
System.Runtime.InteropServices.Marshal.Copy(ptrMask, bitsMask, 0, size);
// check each pixel of the image... if the mask is 0 for each channel, set the capture pixel to 0.
for (int n = 0; n < size; n += 4)
{
bool clearPixel = true;
for ( int c = 0; c < 4; ++c )
{
// if any pixel of the mask is not black, do not clear the capture pixel
if (bitsMask[n + c] != 0)
{
clearPixel = false;
break;
}
}
if ( clearPixel )
{
bitsCapture[n] = 0;
bitsCapture[n + 1] = 0;
bitsCapture[n + 2] = 0;
bitsCapture[n + 3] = 0;
}
}
System.Runtime.InteropServices.Marshal.Copy(bitsCapture, 0, ptrCapture, size);
System.Runtime.InteropServices.Marshal.Copy(bitsMask, 0, ptrMask, size);
capture_.UnlockBits(dataCapture);
maskImage.UnlockBits(dataMask);
}
}
/// <summary>
/// Creates an Image object containing a screen shot of a specific window
/// </summary>
/// <param name="handle">The handle to the window.
/// (In windows forms, this is obtained by the Handle property)</param>
/// <returns>Screen grab image</returns>
private System.Drawing.Bitmap CaptureWindow(IntPtr handle)
{
// get te hDC of the target window
IntPtr hdcSrc = User32.GetWindowDC(handle);
// get the size
User32.RECT windowRect = new User32.RECT();
User32.GetWindowRect(handle, ref windowRect);
int width = windowRect.right - windowRect.left;
int height = windowRect.bottom - windowRect.top;
// create a device context we can copy to
IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc);
// create a bitmap we can copy it to,
// using GetDeviceCaps to get the width/height
IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc, width, height);
// select the bitmap object
IntPtr hOld = GDI32.SelectObject(hdcDest, hBitmap);
// bitblt over
GDI32.BitBlt(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, GDI32.SRCCOPY);
// restore selection
GDI32.SelectObject(hdcDest, hOld);
// clean up
GDI32.DeleteDC(hdcDest);
User32.ReleaseDC(handle, hdcSrc);
// get a .NET image object for it
System.Drawing.Bitmap img = System.Drawing.Image.FromHbitmap(hBitmap);
// free up the Bitmap object
GDI32.DeleteObject(hBitmap);
return img;
}
/// <summary>
/// Save this capture as a Png image.
/// </summary>
/// <param name="filename">File path and name not including extension</param>
public void SaveCapture( string filename )
{
capture.Save(filename + ".png", System.Drawing.Imaging.ImageFormat.Png);
}
/// <summary>
/// Helper class containing Gdi32 API functions
/// </summary>
private class GDI32
{
public const int SRCCOPY = 0x00CC0020; // BitBlt dwRop parameter
[DllImport("gdi32.dll")]
public static extern bool BitBlt(IntPtr hObject, int nXDest, int nYDest,
int nWidth, int nHeight, IntPtr hObjectSource,
int nXSrc, int nYSrc, int dwRop);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int nWidth,
int nHeight);
[DllImport("gdi32.dll")]
public static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteDC(IntPtr hDC);
[DllImport("gdi32.dll")]
public static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll")]
public static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
}
/// <summary>
/// Helper class containing User32 API functions
/// </summary>
private class User32
{
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll")]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowRect(IntPtr hWnd, ref RECT rect);
}
}
It throws exceptions, so be sure to catch them. To use...
ScreenCapture grab = new ScreenCapture( "screenGrabMask.png" );
grab.SaveCapture( "screenGrab.png" );
Take a look at Bitmap.LockBits
https://msdn.microsoft.com/de-de/library/5ey6h79d(v=vs.110).aspx
to provide some basic idea
private unsafe void demo(Bitmap bm)
{
System.Drawing.Imaging.BitmapData D = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
int stride = Math.Abs(D.Stride);
byte* pImg = (byte*)D.Scan0.ToPointer();
for(int y = 0; y < bm.Height; y++)
{
byte* pRow = (byte*)(pImg + y * stride);
for(int x = 0; x < bm.Width; x++)
{
uint b = *pRow++;
uint g = *pRow++;
uint r = *pRow++;
pRow++; // skip alpha
// do something with r g b
}
}
bm.UnlockBits(D);
}
Allow unsafe code (be careful) to avoid extra memory copies.

c# move Winamp main window

I am trying to move winamps main window, with this code:
[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
static void resize()
{
Process w = new Process();
w.StartInfo = new ProcessStartInfo("winamp.exe");
w.Start();
Thread.Sleep(5000);
IntPtr hWnd = GetForegroundWindow();
RECT rect;
GetWindowRect(hWnd, out rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
MoveWindow(hWnd, 0, 0, width, height, true);
}
This code snippet works with all processes that I tested, except Winamp. When I use the mainWindowHandle of the process it moves another window.
Anyone an idea how to get it working with Winamp?
With the following code I can confirm changing WinAmp's main window size does not work via the Win32 API's.
Changing other winamp window sizes via the Win32 API's did work, but is not a solution:
public partial class Form1 : Form
{
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr hWnd, out RECT rect);
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left; // x position of upper-left corner
public int Top; // y position of upper-left corner
public int Right; // x position of lower-right corner
public int Bottom; // y position of lower-right corner
}
private System.IntPtr hWnd;
private void button1_Click(object sender, EventArgs e)
{
Process p = Process.Start(#"C:\Program Files\Winamp\winamp.exe");
try
{
do
{
p.Refresh();
}
while (p.MainWindowHandle.ToInt32() == 0);
hWnd = new IntPtr(p.MainWindowHandle.ToInt32());
}
catch (Exception ex)
{
//Do some stuff...
throw;
}
}
private void button2_Click(object sender, EventArgs e)
{
//Make sure we have a handle to the shelled exe
if (hWnd == new IntPtr(0)) return;
ResizeExternalExeChildWindows(hWnd);
}
private void ResizeExternalExeChildWindows(IntPtr parent)
{
List<IntPtr> childWindows = GetChildWindows(hWnd);
foreach (IntPtr childWindow in childWindows)
{
RECT rect;
GetWindowRect(childWindow, out rect);
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
MoveWindow(hWnd, 0, 0, width, height, true);
}
}
// <summary>
/// Returns a list of child windows
/// </summary>
/// <param name="parent">Parent of the windows to return</param>
/// <returns>List of child windows</returns>
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;
}
/// <summary>
/// Callback method to be used when enumerating windows.
/// </summary>
/// <param name="handle">Handle of the next window</param>
/// <param name="pointer">Pointer to a GCHandle that holds a reference to the list to fill</param>
/// <returns>True to continue the enumeration, false to bail</returns>
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
// You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}
/// <summary>
/// Delegate for the EnumChildWindows method
/// </summary>
/// <param name="hWnd">Window handle</param>
/// <param name="parameter">Caller-defined variable; we use it for a pointer to our list</param>
/// <returns>True to continue enumerating, false to bail.</returns>
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
}
}
Cause
Because WinAmp is skinnable it supports resizing via config files (not via external apps using win32 API's).
Solution
Open the file C:\Users[username]\AppData\Roaming\Winamp\studio.xnf and edit the following values:
<entry name="Bento_nomax_h" value="492" />
<entry name="Bento_nomax_w" value="633" />
<entry name="Bento_nomax_x" value="27" />
<entry name="Bento_nomax_y" value="16" />
I am using the Bento Skin. If you open the WinAmp.ini file you can detect the skin being used:
skin=Bento
The solution to set the initial size for WinAmp's main window is to edit the config file before shelling WinAmp with Process.Start.
I have tried your code, it is not working always, and it is not re-sizing windows, it was moving it only,
Actually I found a problem in the 2000 ms sleep, which you can change to a while loop checking if the handle is zero then proceed with the code,
while (p.MainWindowHandle == 0)
{
}
IntPtr hWnd = p.MainWindowHandle;
this may help if the winamp takes too much time to build its own window
This worked for me using the latest version, please give it a try:
internal struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall, ExactSpelling = true, SetLastError = true)]
internal static extern bool GetWindowRect(IntPtr hWnd, ref RECT rect);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
public static class HWND
{
public static IntPtr
NoTopMost = new IntPtr(-2),
TopMost = new IntPtr(-1),
Top = new IntPtr(0),
Bottom = new IntPtr(1);
}
[Flags]
public enum SetWindowPosFlags : uint
{
SWP_ASYNCWINDOWPOS = 0x4000,
SWP_DEFERERASE = 0x2000,
SWP_DRAWFRAME = 0x0020,
SWP_FRAMECHANGED = 0x0020,
SWP_HIDEWINDOW = 0x0080,
SWP_NOACTIVATE = 0x0010,
SWP_NOCOPYBITS = 0x0100,
SWP_NOMOVE = 0x0002,
SWP_NOOWNERZORDER = 0x0200,
SWP_NOREDRAW = 0x0008,
SWP_NOREPOSITION = 0x0200,
SWP_NOSENDCHANGING = 0x0400,
SWP_NOSIZE = 0x0001,
SWP_NOZORDER = 0x0004,
SWP_SHOWWINDOW = 0x0040,
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
static void Resize()
{
IntPtr winampMainWindow = IntPtr.Zero;
while (true)
{
winampMainWindow = FindWindow("BaseWindow_RootWnd", "Main Window");
if (winampMainWindow != IntPtr.Zero)
{
RECT rect = new RECT();
GetWindowRect(winampMainWindow, ref rect);
int width = rect.right - rect.left;
int height = rect.bottom - rect.top;
SetWindowPos(winampMainWindow,
HWND.Top,
0,
0,
width,
height,
SetWindowPosFlags.SWP_SHOWWINDOW | SetWindowPosFlags.SWP_NOZORDER | SetWindowPosFlags.SWP_NOSIZE | SetWindowPosFlags.SWP_NOSENDCHANGING | SetWindowPosFlags.SWP_NOOWNERZORDER | SetWindowPosFlags.SWP_NOCOPYBITS);
break;
}
Thread.Sleep(1000);
}
}
[STAThread]
static void Main()
{
Process.Start("winamp.exe");
Resize();
}
Absolute nightmare!
However, right clicking in the "song window" DID open up the docking menu - I think the undock-(something) worked. One of these options allowed me to move the god-damned thing. It was a fluke, but something opened up there that worked.

C# - How to print aspect ratio / full page

I am printing the CHART control on button click:
chart1.SaveImage(ms, ChartImageFormat.Bmp);
Bitmap bm = new Bitmap(ms);
PrintDocument doc = new PrintDocument();
doc.PrintPage += (s, ev) =>
{
ev.Graphics.DrawImage(bm, Point.Empty); // adjust this to put the image elsewhere
ev.HasMorePages = false;
};
doc.DefaultPageSettings.Landscape = true;
doc.Print();
How do I force it to print the control so that it fits to the size of the page (preserving the aspect ratio)?
There are at least two different ways to do it, both include scaling the image to be printed to fit the page size of the selected printer:
1) using .NET facilities (haven't tested it myself, lifted from this post):
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
Image i = pictureBox1.Image;
float newWidth = i.Width * 100 / i.HorizontalResolution;
float newHeight = i.Height * 100 / i.VerticalResolution;
float widthFactor = newWidth / e.MarginBounds.Width;
float heightFactor = newHeight / e.MarginBounds.Height;
if(widthFactor>1 | heightFactor > 1)
{
if(widthFactor > heightFactor)
{
newWidth = newWidth / widthFactor;
newHeight = newHeight / widthFactor;
}
else
{
newWidth = newWidth / heightFactor;
newHeight = newHeight / heightFactor;
}
}
e.Graphics.DrawImage(i, 0, 0, (int)newWidth, (int)newHeight);
}
}
2) P/Invoke'ing Windows API calls from the GDI and flat GDI (this is much more complex but faster and you can pass a plain byte array of a bitmap file (read file as byte[]), provide an email to me if need this code):
private static extern bool ClosePrinter(IntPtr hPrinter);
private static extern bool OpenPrinter([MarshalAs(UnmanagedType.LPStr)] string szPrinter, out IntPtr hPrinter, IntPtr pd);
private static extern int SetJob(IntPtr hPrinter, int JobId, int Level, ref byte pJob, int Command_Renamed);
private static extern int GdiplusStartup(out IntPtr token, ref StartupInput input, out StartupOutput output);
private static extern int GdiplusShutdown(IntPtr token);
internal static extern int GdipLoadImageFromStream([In, MarshalAs(UnmanagedType.Interface)]IStream stream, out IntPtr image);
internal static extern int GdipDisposeImage(IntPtr image);
static internal extern int GdipCreateFromHDC2(IntPtr hDC, IntPtr hDevice, out IntPtr graphics);
static internal extern int GdipDeleteGraphics(IntPtr graphics);
static internal extern int GdipReleaseDC(IntPtr graphics, IntPtr hdc);
internal static extern int GdipGetImageDimension(IntPtr image, out float width, out float height);
internal static extern int GdipGetDpiX(IntPtr graphics, out float dpi);
internal static extern int GdipGetDpiY(IntPtr graphics, out float dpi);
static internal extern int GdipDrawImageRectI(IntPtr graphics, IntPtr image, int x, int y, int width, int height);
private static extern IntPtr CreateDC([MarshalAs(UnmanagedType.LPStr)] string lpszDriver, [MarshalAs(UnmanagedType.LPStr)] string lpszDevice, [MarshalAs(UnmanagedType.LPStr)] string lpszOutput, IntPtr lpInitData);
private static extern bool DeleteDC(IntPtr hdc);
private static extern int StartDoc(IntPtr hdc, DOCINFO lpdi);
private static extern int EndDoc(IntPtr hdc);
private static extern int StartPage(IntPtr hdc);
private static extern int EndPage(IntPtr hdc);
private static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
Here's what I did to get this working. What's annoying is that the chart control will only draw its contents as the same size that it is sized to on the screen. The only way I found to get around this is to manually resize it. And then, since it may not like being resized when it's on a form, this also involved temporarily removing it from the form.
So with all that, I end up with something like this:
Chart ChartBox { get; private set; }
...
var oldParent = ChartBox.Parent;
var oldSize = ChartBox.Size;
ChartBox.Parent = null;
ChartBox.Size = new Size(width, height);
ChartBox.Printing.Print();
ChartBox.Parent = oldParent;
ChartBox.Size = oldSize;
Depending on your form, you may need to save and restore other properties of the chart control as well.

Categories

Resources