Hey all. i was wondering how would i make a GUI that is "transparent" in C#. Now im not talking about transparencykey etc.
I want to make a window that uses vistas aero theme but instead of a control in the form i just want it to show more of that seethru aero look. And also i want to remove all buttons and icons and text from the window. How would i do this?
EDIT... I found a PERFECT example of what i want to create. Load up the windows mobility center on vista. How can i create something like that but without the boxes in it.
How to Glass into your Client Area
Here is the API I used when I did this for our Windows app a few years ago.
Check out the method ExtendGlassIntoClientArea(Form form, int leftMargin, int topMargin, int rightMargin, int bottomMargin)
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace UserInterface
{
public enum TextStyle
{
Normal,
Glowing
}
public static class Glass
{
[DllImport("dwmapi.dll")]
private static extern void DwmIsCompositionEnabled(ref bool pfEnabled);
[DllImport("dwmapi.dll")]
private static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMargins);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern IntPtr CreateCompatibleDC(IntPtr hDC);
[DllImport("gdi32.dll", ExactSpelling = true)]
private static extern IntPtr SelectObject(IntPtr hDC, IntPtr hObject);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
private static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll")]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("UxTheme.dll", CharSet = CharSet.Unicode)]
private static extern int DrawThemeTextEx(IntPtr hTheme, IntPtr hdc, int iPartId, int iStateId, string text, int iCharCount, int dwFlags, ref RECT pRect, ref DTTOPTS pOptions);
[DllImport("gdi32.dll")]
static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BITMAPINFO pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);
[StructLayout(LayoutKind.Sequential)]
private struct DTTOPTS
{
public int dwSize;
public int dwFlags;
public int crText;
public int crBorder;
public int crShadow;
public int iTextShadowType;
public POINT ptShadowOffset;
public int iBorderSize;
public int iFontPropId;
public int iColorPropId;
public int iStateId;
public bool fApplyOverlay;
public int iGlowSize;
public int pfnDrawTextCallback;
public IntPtr lParam;
}
private const int DTT_COMPOSITED = 8192;
private const int DTT_GLOWSIZE = 2048;
private const int DTT_TEXTCOLOR = 1;
[StructLayout(LayoutKind.Sequential)]
private struct POINT
{
public POINT(int x, int y)
{
this.x = x;
this.y = y;
}
public int x;
public int y;
}
[StructLayout(LayoutKind.Sequential)]
private class BITMAPINFO
{
public int biSize;
public int biWidth;
public int biHeight;
public short biPlanes;
public short biBitCount;
public int biCompression;
public int biSizeImage;
public int biXPelsPerMeter;
public int biYPelsPerMeter;
public int biClrUsed;
public int biClrImportant;
public byte bmiColors_rgbBlue;
public byte bmiColors_rgbGreen;
public byte bmiColors_rgbRed;
public byte bmiColors_rgbReserved;
}
[StructLayout(LayoutKind.Sequential)]
private struct MARGINS
{
public int left, right, top, bottom;
}
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
public RECT(int left, int top, int right, int bottom)
{
Left = left;
Top = top;
Right = right;
Bottom = bottom;
}
public RECT(Rectangle rectangle)
{
Left = rectangle.X;
Top = rectangle.Y;
Right = rectangle.Right;
Bottom = rectangle.Bottom;
}
public Rectangle ToRectangle()
{
return new Rectangle(Left, Top, Right - Left, Bottom - Top);
}
public override string ToString()
{
return "Left: " + Left + ", " + "Top: " + Top + ", Right: " + Right + ", Bottom: " + Bottom;
}
}
public static bool IsCompositionEnabled
{
get
{
if (Environment.OSVersion.Version.Major < 6)
return false;
bool compositionEnabled = false;
DwmIsCompositionEnabled(ref compositionEnabled);
return compositionEnabled;
}
}
public static void ExtendGlassIntoClientArea(Form form, int leftMargin, int topMargin, int rightMargin, int bottomMargin)
{
MARGINS m = new MARGINS();
m.left = leftMargin;
m.right = rightMargin;
m.top = topMargin;
m.bottom = bottomMargin;
DwmExtendFrameIntoClientArea(form.Handle, ref m);
}
public static void DrawText(Graphics graphics, string text, Font font, Rectangle bounds, Color color, TextFormatFlags flags)
{
DrawText(graphics, text, font, bounds, color, flags, TextStyle.Normal);
}
public static void DrawText(Graphics graphics, string text, Font font, Rectangle bounds, Color color, TextFormatFlags flags, TextStyle textStyle)
{
IntPtr primaryHdc = graphics.GetHdc();
// Create a memory DC so we can work offscreen
IntPtr memoryHdc = CreateCompatibleDC(primaryHdc);
// Create a device-independent bitmap and select it into our DC
BITMAPINFO info = new BITMAPINFO();
info.biSize = Marshal.SizeOf(info);
info.biWidth = bounds.Width;
info.biHeight = -bounds.Height;
info.biPlanes = 1;
info.biBitCount = 32;
info.biCompression = 0; // BI_RGB
int ppvBits;
IntPtr dib = CreateDIBSection(primaryHdc, ref info, 0, out ppvBits, IntPtr.Zero, 0);
SelectObject(memoryHdc, dib);
// Create and select font
IntPtr fontHandle = font.ToHfont();
SelectObject(memoryHdc, fontHandle);
// Draw glowing text
System.Windows.Forms.VisualStyles.VisualStyleRenderer renderer = new System.Windows.Forms.VisualStyles.VisualStyleRenderer(System.Windows.Forms.VisualStyles.VisualStyleElement.Window.Caption.Active);
DTTOPTS dttOpts = new DTTOPTS();
dttOpts.dwSize = Marshal.SizeOf(typeof(DTTOPTS));
if (textStyle == TextStyle.Glowing)
{
dttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE | DTT_TEXTCOLOR;
}
else
{
dttOpts.dwFlags = DTT_COMPOSITED | DTT_TEXTCOLOR;
}
dttOpts.crText = ColorTranslator.ToWin32(color);
dttOpts.iGlowSize = 8; // This is about the size Microsoft Word 2007 uses
RECT textBounds = new RECT(0, 0, bounds.Right - bounds.Left, bounds.Bottom - bounds.Top);
DrawThemeTextEx(renderer.Handle, memoryHdc, 0, 0, text, -1, (int)flags, ref textBounds, ref dttOpts);
// Copy to foreground
const int SRCCOPY = 0x00CC0020;
BitBlt(primaryHdc, bounds.Left, bounds.Top, bounds.Width, bounds.Height, memoryHdc, 0, 0, SRCCOPY);
// Clean up
DeleteObject(fontHandle);
DeleteObject(dib);
DeleteDC(memoryHdc);
graphics.ReleaseHdc(primaryHdc);
}
}
}
if WPF, it's a matter of turning transparency on, and setting the right values for the background (additionally, WPF supports AERO natively for borders and what-not).
For traditional winforms... things start getting hard.
plus there's always Form.Opacity property of the windows Form.
Have you tried to check out this which was referred in another question?
Related
I know this has been asked before, but I've tried all the answers I've found and none of them seem to work for me. Answers seem to work on a single monitor, or require a window handle, or to be in a WPF application. I've a C# class library with no UI that is called from a different language all together.
I've been asked to determine the scaling factor, e.g. 1, 1.25, 1.5, etc. for each monitor attached to the current PC in a C# class library.
I also need to provide the resolution and colour depth for each monitor. The registry does hold the DpiValue, whatever that is, in Windows 10 under
Computer\HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings
However I have no idea how to map those to a Screen in order to get the matching resolution returned in
System.Windows.Forms.Screen.AllScreens
So does anyone have a way of getting this information?
I believe I have finally (after a long time of searching) found an answer that works, it even works on my high DPI Surface Book 2 screen. I have tested it as much as I can, and so far it's always returned the correct value.
Here's how I did it, thanks to whoever posted the code fragments in the past where I gathered this from.
First you need a structure to call EnumDisplaySettings in user32.dll
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
private const int CCHDEVICENAME = 0x20;
private const int CCHFORMNAME = 0x20;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public ScreenOrientation dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
Then you need to declare the external function call
[DllImport("user32.dll")]
public static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);
Then you need to use it to calculate the screen scaling
Screen[] screenList = Screen.AllScreens;
foreach (Screen screen in screenList)
{
DEVMODE dm = new DEVMODE();
dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
EnumDisplaySettings(screen.DeviceName, -1, ref dm);
var scalingFactor = Math.Round(Decimal.Divide(dm.dmPelsWidth, screen.Bounds.Width), 2);
}
Hope others find this useful.
I think you can get scaling factor for each monitor like this.
public void GetScalingFactor()
{
List<double> physicalWidths = new List<double>();
//Get physical width for each monitor
ManagementObjectSearcher searcher = new ManagementObjectSearcher("\\root\\wmi", "SELECT * FROM WmiMonitorBasicDisplayParams");
foreach (ManagementObject monitor in searcher.Get())
{
//Get the physical width (inch)
double width = (byte)monitor["MaxHorizontalImageSize"] / 2.54;
physicalWidths.Add(width);
}
//Get screen info for each monitor
Screen[] screenList = Screen.AllScreens;
int i = 0;
foreach (Screen screen in screenList)
{
//Get the physical width (pixel)
double physicalWidth;
if (i < physicalWidths.Count)
{
//Get the DPI
uint x, y;
GetDpi(screen, DpiType.Effective, out x, out y);
//Convert inch to pixel
physicalWidth = physicalWidths[i] * x;
}
else
{
physicalWidth = SystemParameters.PrimaryScreenWidth;
}
i++;
//Calculate the scaling
double scaling = 100 * (physicalWidth / screen.Bounds.Width);
double scalingFactor = physicalWidth / screen.Bounds.Width;
//Output the result
Console.WriteLine(scalingFactor);
}
}
And you need also add these codes to use to get the monitor DPI (these code is from https://stackoverflow.com/a/29463627/12949439, thanks #Koopakiller):
public void GetDpi(Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062(v=vs.85).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(v=vs.85).aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511(v=vs.85).aspx
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
Unfortunately, the answer of user3225503 seems not to work (anymore?)
My scenario: WIN10 20H2, WPF-App with dpi-awareness "PerMonitor", Framework 4.7.2, 2 Monitors with different resolutions and different screen scalings ("Horror scenario"):
the dm.dmPelsWidth member of the DEVMODE structure has always the physical resolution of my monitors, so the scaling is always 1.0.
All what we want is to restore our program and its windows like we left it in the last session right? This seems to be incredibly hard, thanks to MS!
But a different approach seems to work:
Switch on per-monitor dpi-awareness in the manifest file of your application:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect :
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
Always use GetPlacement and SetPlacement win32-api calls for storing/restoring window placements
SetPlacement will set the wrong dialog width/height if the dialog is on a secondary display and each display has different scalings. So we need a new factor depending on scaling factors of each display to correct this in the Loading-event of the window:
event code:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
There are some more goodies in the code example, e.g. serialization of the WINDOWPLACEMENT structure. don't forget to create a member "Placement" in your application settings! Tell me if this works for you:
Example code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Windows;
namespace DpiApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
var plc = ScreenExtensions.GetPlacement(shwnd.Handle);
Properties.Settings.Default.Placement = plc.ToString();
Properties.Settings.Default.Save();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
}
public static class ScreenExtensions
{
public const string User32 = "user32.dll";
public const string shcore = "Shcore.dll";
public static void GetDpi(this System.Windows.Forms.Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
public static double GetScalingForPoint(System.Drawing.Point aPoint)
{
var mon = MonitorFromPoint(aPoint, 2/*MONITOR_DEFAULTTONEAREST*/);
uint dpiX, dpiY;
GetDpiForMonitor(mon, DpiType.Effective, out dpiX, out dpiY);
return (double)dpiX / 96.0;
}
[DllImport(User32)]
private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
[DllImport(shcore)]
private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
[DllImport(User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport(User32, CharSet = CharSet.Auto, SetLastError = true)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
public static WINDOWPLACEMENT GetPlacement(IntPtr hWnd)
{
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(hWnd, ref placement);
return placement;
}
public static bool SetPlacement(IntPtr hWnd, WINDOWPLACEMENT aPlacement)
{
bool erg = SetWindowPlacement(hWnd, ref aPlacement);
return erg;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINTSTRUCT
{
public int x;
public int y;
public POINTSTRUCT(int x, int y)
{
this.x = x;
this.y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public RECT(Rect r)
{
this.left = (int)r.Left;
this.top = (int)r.Top;
this.right = (int)r.Right;
this.bottom = (int)r.Bottom;
}
public static RECT FromXYWH(int x, int y, int width, int height)
{
return new RECT(x, y, x + width, y + height);
}
public Size Size
{
get { return new Size(this.right - this.left, this.bottom - this.top); }
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT
{
public int length;
public uint flags;
public uint showCmd;
public POINTSTRUCT ptMinPosition;
public POINTSTRUCT ptMaxPosition;
public RECT rcNormalPosition;
public override string ToString()
{
byte[] StructBytes = RawSerialize(this);
return System.Convert.ToBase64String(StructBytes);
}
public void ReadFromBase64String(string aB64)
{
byte[] b64 = System.Convert.FromBase64String(aB64);
var NewWP = ReadStruct<WINDOWPLACEMENT>(b64, 0);
length = NewWP.length;
flags = NewWP.flags;
showCmd = NewWP.showCmd;
ptMinPosition.x = NewWP.ptMinPosition.x;
ptMinPosition.y = NewWP.ptMinPosition.y;
ptMaxPosition.x = NewWP.ptMaxPosition.x;
ptMaxPosition.y = NewWP.ptMaxPosition.y;
rcNormalPosition.left = NewWP.rcNormalPosition.left;
rcNormalPosition.top = NewWP.rcNormalPosition.top;
rcNormalPosition.right = NewWP.rcNormalPosition.right;
rcNormalPosition.bottom = NewWP.rcNormalPosition.bottom;
}
static public T ReadStruct<T>(byte[] aSrcBuffer, int aOffset)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
Buffer.BlockCopy(aSrcBuffer, aOffset, buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
static public T ReadStruct<T>(Stream fs)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
fs.Read(buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
public static byte[] RawSerialize(object anything)
{
int rawsize = Marshal.SizeOf(anything);
byte[] rawdata = new byte[rawsize];
GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned);
Marshal.StructureToPtr(anything, handle.AddrOfPinnedObject(), false);
handle.Free();
return rawdata;
}
}
}
}
I'm working with a C# Console Application. How can I move the application window to the center of the screen?
Additional details
Framework version: .NET Core 3.1
Size of the window: 37x20 (Console.SetWindowSize(37, 20);)
Made a small utility class that allows you to center your console window.
Usage example:
WindowUtility.MoveWindowToCenter();
Full source code:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
static class WindowUtility
{
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
const uint SWP_NOSIZE = 0x0001;
const uint SWP_NOZORDER = 0x0004;
private static Size GetScreenSize() => new Size(GetSystemMetrics(0), GetSystemMetrics(1));
private struct Size
{
public int Width { get; set; }
public int Height { get; set; }
public Size(int width, int height)
{
Width = width;
Height = height;
}
}
[DllImport("User32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
private static extern int GetSystemMetrics(int nIndex);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowRect(HandleRef hWnd, out Rect lpRect);
[StructLayout(LayoutKind.Sequential)]
private 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 static Size GetWindowSize(IntPtr window)
{
if (!GetWindowRect(new HandleRef(null, window), out Rect rect))
throw new Exception("Unable to get window rect!");
int width = rect.Right - rect.Left;
int height = rect.Bottom - rect.Top;
return new Size(width, height);
}
public static void MoveWindowToCenter()
{
IntPtr window = Process.GetCurrentProcess().MainWindowHandle;
if (window == IntPtr.Zero)
throw new Exception("Couldn't find a window to center!");
Size screenSize = GetScreenSize();
Size windowSize = GetWindowSize(window);
int x = (screenSize.Width - windowSize.Width) / 2;
int y = (screenSize.Height - windowSize.Height) / 2;
SetWindowPos(window, IntPtr.Zero, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}
References:
Bunch of StackOverflow posts that helped me put together this solution.
https://stackoverflow.com/a/42306412/5946094
https://stackoverflow.com/a/13547659/5946094
https://stackoverflow.com/a/43793468/5946094
https://stackoverflow.com/a/31273557/5946094
I know this has been asked before, but I've tried all the answers I've found and none of them seem to work for me. Answers seem to work on a single monitor, or require a window handle, or to be in a WPF application. I've a C# class library with no UI that is called from a different language all together.
I've been asked to determine the scaling factor, e.g. 1, 1.25, 1.5, etc. for each monitor attached to the current PC in a C# class library.
I also need to provide the resolution and colour depth for each monitor. The registry does hold the DpiValue, whatever that is, in Windows 10 under
Computer\HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings
However I have no idea how to map those to a Screen in order to get the matching resolution returned in
System.Windows.Forms.Screen.AllScreens
So does anyone have a way of getting this information?
I believe I have finally (after a long time of searching) found an answer that works, it even works on my high DPI Surface Book 2 screen. I have tested it as much as I can, and so far it's always returned the correct value.
Here's how I did it, thanks to whoever posted the code fragments in the past where I gathered this from.
First you need a structure to call EnumDisplaySettings in user32.dll
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
private const int CCHDEVICENAME = 0x20;
private const int CCHFORMNAME = 0x20;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public ScreenOrientation dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
Then you need to declare the external function call
[DllImport("user32.dll")]
public static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);
Then you need to use it to calculate the screen scaling
Screen[] screenList = Screen.AllScreens;
foreach (Screen screen in screenList)
{
DEVMODE dm = new DEVMODE();
dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
EnumDisplaySettings(screen.DeviceName, -1, ref dm);
var scalingFactor = Math.Round(Decimal.Divide(dm.dmPelsWidth, screen.Bounds.Width), 2);
}
Hope others find this useful.
I think you can get scaling factor for each monitor like this.
public void GetScalingFactor()
{
List<double> physicalWidths = new List<double>();
//Get physical width for each monitor
ManagementObjectSearcher searcher = new ManagementObjectSearcher("\\root\\wmi", "SELECT * FROM WmiMonitorBasicDisplayParams");
foreach (ManagementObject monitor in searcher.Get())
{
//Get the physical width (inch)
double width = (byte)monitor["MaxHorizontalImageSize"] / 2.54;
physicalWidths.Add(width);
}
//Get screen info for each monitor
Screen[] screenList = Screen.AllScreens;
int i = 0;
foreach (Screen screen in screenList)
{
//Get the physical width (pixel)
double physicalWidth;
if (i < physicalWidths.Count)
{
//Get the DPI
uint x, y;
GetDpi(screen, DpiType.Effective, out x, out y);
//Convert inch to pixel
physicalWidth = physicalWidths[i] * x;
}
else
{
physicalWidth = SystemParameters.PrimaryScreenWidth;
}
i++;
//Calculate the scaling
double scaling = 100 * (physicalWidth / screen.Bounds.Width);
double scalingFactor = physicalWidth / screen.Bounds.Width;
//Output the result
Console.WriteLine(scalingFactor);
}
}
And you need also add these codes to use to get the monitor DPI (these code is from https://stackoverflow.com/a/29463627/12949439, thanks #Koopakiller):
public void GetDpi(Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062(v=vs.85).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(v=vs.85).aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511(v=vs.85).aspx
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
Unfortunately, the answer of user3225503 seems not to work (anymore?)
My scenario: WIN10 20H2, WPF-App with dpi-awareness "PerMonitor", Framework 4.7.2, 2 Monitors with different resolutions and different screen scalings ("Horror scenario"):
the dm.dmPelsWidth member of the DEVMODE structure has always the physical resolution of my monitors, so the scaling is always 1.0.
All what we want is to restore our program and its windows like we left it in the last session right? This seems to be incredibly hard, thanks to MS!
But a different approach seems to work:
Switch on per-monitor dpi-awareness in the manifest file of your application:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect :
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
Always use GetPlacement and SetPlacement win32-api calls for storing/restoring window placements
SetPlacement will set the wrong dialog width/height if the dialog is on a secondary display and each display has different scalings. So we need a new factor depending on scaling factors of each display to correct this in the Loading-event of the window:
event code:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
There are some more goodies in the code example, e.g. serialization of the WINDOWPLACEMENT structure. don't forget to create a member "Placement" in your application settings! Tell me if this works for you:
Example code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Windows;
namespace DpiApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
var plc = ScreenExtensions.GetPlacement(shwnd.Handle);
Properties.Settings.Default.Placement = plc.ToString();
Properties.Settings.Default.Save();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
}
public static class ScreenExtensions
{
public const string User32 = "user32.dll";
public const string shcore = "Shcore.dll";
public static void GetDpi(this System.Windows.Forms.Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
public static double GetScalingForPoint(System.Drawing.Point aPoint)
{
var mon = MonitorFromPoint(aPoint, 2/*MONITOR_DEFAULTTONEAREST*/);
uint dpiX, dpiY;
GetDpiForMonitor(mon, DpiType.Effective, out dpiX, out dpiY);
return (double)dpiX / 96.0;
}
[DllImport(User32)]
private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
[DllImport(shcore)]
private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
[DllImport(User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport(User32, CharSet = CharSet.Auto, SetLastError = true)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
public static WINDOWPLACEMENT GetPlacement(IntPtr hWnd)
{
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(hWnd, ref placement);
return placement;
}
public static bool SetPlacement(IntPtr hWnd, WINDOWPLACEMENT aPlacement)
{
bool erg = SetWindowPlacement(hWnd, ref aPlacement);
return erg;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINTSTRUCT
{
public int x;
public int y;
public POINTSTRUCT(int x, int y)
{
this.x = x;
this.y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public RECT(Rect r)
{
this.left = (int)r.Left;
this.top = (int)r.Top;
this.right = (int)r.Right;
this.bottom = (int)r.Bottom;
}
public static RECT FromXYWH(int x, int y, int width, int height)
{
return new RECT(x, y, x + width, y + height);
}
public Size Size
{
get { return new Size(this.right - this.left, this.bottom - this.top); }
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT
{
public int length;
public uint flags;
public uint showCmd;
public POINTSTRUCT ptMinPosition;
public POINTSTRUCT ptMaxPosition;
public RECT rcNormalPosition;
public override string ToString()
{
byte[] StructBytes = RawSerialize(this);
return System.Convert.ToBase64String(StructBytes);
}
public void ReadFromBase64String(string aB64)
{
byte[] b64 = System.Convert.FromBase64String(aB64);
var NewWP = ReadStruct<WINDOWPLACEMENT>(b64, 0);
length = NewWP.length;
flags = NewWP.flags;
showCmd = NewWP.showCmd;
ptMinPosition.x = NewWP.ptMinPosition.x;
ptMinPosition.y = NewWP.ptMinPosition.y;
ptMaxPosition.x = NewWP.ptMaxPosition.x;
ptMaxPosition.y = NewWP.ptMaxPosition.y;
rcNormalPosition.left = NewWP.rcNormalPosition.left;
rcNormalPosition.top = NewWP.rcNormalPosition.top;
rcNormalPosition.right = NewWP.rcNormalPosition.right;
rcNormalPosition.bottom = NewWP.rcNormalPosition.bottom;
}
static public T ReadStruct<T>(byte[] aSrcBuffer, int aOffset)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
Buffer.BlockCopy(aSrcBuffer, aOffset, buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
static public T ReadStruct<T>(Stream fs)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
fs.Read(buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
public static byte[] RawSerialize(object anything)
{
int rawsize = Marshal.SizeOf(anything);
byte[] rawdata = new byte[rawsize];
GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned);
Marshal.StructureToPtr(anything, handle.AddrOfPinnedObject(), false);
handle.Free();
return rawdata;
}
}
}
}
I am making a PictureBox move in the direction of the mouse, but it only works inside the PictureBox. The PictureBoxis 200x200 so it makes a glitchy thing, which I'm guessing is because the space is too tight. I also have a function that makes the PictureBoxfollow the mouse. Is there a way that I can move my mouse around the screen meanwhile the PictureBoxrotates in the direction of the mouse?
private float _angle;
private void Spiller_MouseMove(object sender, MouseEventArgs e)
{
(float centerX, float centerY) = GetCenter(spiller.ClientRectangle);
_angle = (float)(Math.Atan2(e.Y - centerY, e.X - centerX) * 180.0 / Math.PI);
spiller.Invalidate();
}
private void Spiller_Paint(object sender, PaintEventArgs e)
{
Bitmap image = Resource1.spillerr;
float scale = (float)spiller.Width / image.Width;
(float centerX, float centerY) = GetCenter(e.ClipRectangle);
e.Graphics.TranslateTransform(centerX, centerY);
e.Graphics.RotateTransform(_angle);
e.Graphics.TranslateTransform(-centerX, -centerY);
e.Graphics.ScaleTransform(scale, scale);
e.Graphics.DrawImage(image, 0, 0);
}
private static (float, float) GetCenter(Rectangle rect)
{
float centerX = (rect.Left + rect.Right) * 0.5f;
float centerY = (rect.Top + rect.Bottom) * 0.5f;
return (centerX, centerY);
}
You can create sudo-active graphics by using a Timer component that has a small interval (16ms ~= 60fps). And calling Invalidate every tick of the timer.
To reduce the flickering of the control that is being constantly refreshed, you'll likely want to set its style as follows:
class CustomPictureBox : PictureBox
{
public CustomPictureBox() {
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);
}
}
// change the type of spiller in Form1.designer.cs
private System.Windows.Forms.PictureBox spiller;
// becomes
private CustomPictureBox spiller;
// and
spiller = new System.Windows.Forms.PictureBox();
// becomes
spiller = new CustomPictureBox();
Global mouse position on the screen can be obtained in the paint routine via Control.MousePosition.
A way is with a WH_MOUSE_LL hook .
I just tested on Windows 10 by adapting your sample in VS 2015, it works fine without any flickering, and just needs ScreenToClient to convert screen coordinates into picture coordinates
Main declarations :
public const int WH_MIN = (-1);
public const int WH_MSGFILTER = (-1);
public const int WH_JOURNALRECORD = 0;
public const int WH_JOURNALPLAYBACK = 1;
public const int WH_KEYBOARD = 2;
public const int WH_GETMESSAGE = 3;
public const int WH_CALLWNDPROC = 4;
public const int WH_CBT = 5;
public const int WH_SYSMSGFILTER = 6;
public const int WH_MOUSE = 7;
public const int WH_HARDWARE = 8;
public const int WH_DEBUG = 9;
public const int WH_SHELL = 10;
public const int WH_FOREGROUNDIDLE = 11;
public const int WH_CALLWNDPROCRET = 12;
public const int WH_KEYBOARD_LL = 13;
public const int WH_MOUSE_LL = 14;
public const int WH_MAX = 14;
public const int WH_MINHOOK = WH_MIN;
public const int WH_MAXHOOK = WH_MAX;
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MSLLHOOKSTRUCT
{
public System.Drawing.Point pt;
public int mouseData;
public int flags;
public int time;
public uint dwExtraInfo;
}
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct KBDLLHOOKSTRUCT
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public uint dwExtraInfo;
}
public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("User32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
[DllImport("User32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(int idHook);
[DllImport("User32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int x;
public int y;
public POINT(int X, int Y)
{
this.x = X;
this.y = Y;
}
}
[DllImport("User32.dll", SetLastError = true)]
public static extern bool ScreenToClient(IntPtr hWnd, ref POINT lpPoint);
Is there anyway to delete certain parts of a console window using the (Left,Top) coordinates used with Console.SetCursorPosition()?
Could you make a custom method for it?
Silky's comment is the right answer:
Set an appropriate background colour
Loop for each line you wish to clear part of:
Set the cursor position to left hand side
Write out a string of spaces of the right width
For example:
public static void ClearArea(int top, int left, int height, int width)
{
ConsoleColor colorBefore = Console.BackgroundColor;
try
{
Console.BackgroundColor = ConsoleColor.Black;
string spaces = new string(' ', width);
for (int i = 0; i < height; i++)
{
Console.SetCursorPosition(left, top + i);
Console.Write(spaces);
}
}
finally
{
Console.BackgroundColor = colorBefore;
}
}
Note that this will restore the background colour, but not the previous cursor location.
If performance is a concern you can directly manipulate the console buffer. Unfortuantely this will require some interop, here is an example that I have adapted from a previous answer I gave. This will clear an area of the console buffer very quickly. It is a bit lengthy because of the interop, but if you wrap this nicely into a console helper class you can extend it to do all kinds of high performance console output.
using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
namespace ConsoleApplication1
{
class Program
{
[STAThread]
static void Main(string[] args)
{
Console.SetCursorPosition(0, 0);
for (int x = 0; x < 80 * 25; ++x)
{
Console.Write("A");
}
Console.SetCursorPosition(0, 0);
Console.ReadKey(true);
ClearArea(1, 1, 78, 23);
Console.ReadKey();
}
static void ClearArea(short left, short top, short width, short height)
{
ClearArea(left, top, width, height, new CharInfo() { Char = new CharUnion() { AsciiChar = 32 } });
}
static void ClearArea(short left, short top, short width, short height, CharInfo charAttr)
{
using (SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero))
{
if (!h.IsInvalid)
{
CharInfo[] buf = new CharInfo[width * height];
for (int i = 0; i < buf.Length; ++i)
{
buf[i] = charAttr;
}
SmallRect rect = new SmallRect() { Left = left, Top = top, Right = (short)(left + width), Bottom = (short)(top + height) };
WriteConsoleOutput(h, buf,
new Coord() { X = width, Y = height },
new Coord() { X = 0, Y = 0 },
ref rect);
}
}
}
[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern SafeFileHandle CreateFile(
string fileName,
[MarshalAs(UnmanagedType.U4)] uint fileAccess,
[MarshalAs(UnmanagedType.U4)] uint fileShare,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] int flags,
IntPtr template);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool CloseHandle(IntPtr hObject);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutput(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);
[StructLayout(LayoutKind.Sequential)]
public struct Coord
{
public short X;
public short Y;
public Coord(short X, short Y)
{
this.X = X;
this.Y = Y;
}
};
[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
[FieldOffset(0)]
public char UnicodeChar;
[FieldOffset(0)]
public byte AsciiChar;
}
[StructLayout(LayoutKind.Explicit)]
public struct CharInfo
{
[FieldOffset(0)]
public CharUnion Char;
[FieldOffset(2)]
public short Attributes;
}
[StructLayout(LayoutKind.Sequential)]
public struct SmallRect
{
public short Left;
public short Top;
public short Right;
public short Bottom;
}
}
}