プライマリのモニタの拡大率に対してセカンダリ以降の拡大率が大きいか小さいかで計算式が変わるっぽい。
using System;
using System.Linq;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace WinFormsApp1
{
public partial class Form1 : Form
{
private PictureBox pictureBoxRange;
private PictureBox pictureBoxFill;
private Label label1;
private Label label2;
private Timer timer1;
private const int picW = 64;
private const int picH = 64;
public Form1()
{
this.StartPosition = FormStartPosition.Manual;
this.Left = 0;
this.Top = 0;
this.AutoScaleMode = AutoScaleMode.Dpi;
this.pictureBoxRange = new PictureBox() { Width = picW, Height = picH, SizeMode = PictureBoxSizeMode.Normal, Top = 10, BorderStyle = BorderStyle.FixedSingle };
this.pictureBoxRange.Left = 10;
this.Controls.Add(this.pictureBoxRange);
this.pictureBoxFill = new PictureBox() { Width = picW, Height = picH, SizeMode = PictureBoxSizeMode.Normal, Top = 10, BorderStyle = BorderStyle.FixedSingle };
this.pictureBoxFill.Left = pictureBoxRange.Right + 10;
this.Controls.Add(this.pictureBoxFill);
this.label1 = new Label() { AutoSize = true, Left = 10, Top = this.pictureBoxRange.Bottom + 2 };
this.label2 = new Label() { AutoSize = true, Left = 10, Top = this.label1.Bottom + 2 };
this.Controls.Add(this.label1);
this.Controls.Add(this.label2);
this.ClientSize = new Size(300, this.label2.Bottom + 5);
this.timer1 = new Timer();
this.timer1.Tick += (s, e) => Test();
this.timer1.Interval = 100;
this.timer1.Start();
}
private Point CalcCapturePoint(Point p1)
{
// nugetでCSWin32を入れる
// NativeMethods.txtに EnumDisplaySettings を記入しておく
var screen = System.Windows.Forms.Screen.FromPoint(p1);
var devmode = new Windows.Win32.Graphics.Gdi.DEVMODEW();
Windows.Win32.PInvoke.EnumDisplaySettings(screen.DeviceName, Windows.Win32.Graphics.Gdi.ENUM_DISPLAY_SETTINGS_MODE.ENUM_CURRENT_SETTINGS, ref devmode);
var scaleX = (double)devmode.dmPelsWidth / screen.Bounds.Width;
var scaleY = (double)devmode.dmPelsHeight / screen.Bounds.Height;
if (scaleX > 1)
{
var x2 = screen.Bounds.Left + (p1.X - screen.Bounds.Left) * scaleX;
var y2 = screen.Bounds.Top + (p1.Y - screen.Bounds.Top) * scaleY;
return new Point((int)x2, (int)y2);
}
else
{
var x2 = p1.X * scaleX;
var y2 = p1.Y * scaleY;
return new Point((int)x2, (int)y2);
}
}
private void Test()
{
pictureBoxRange.Image?.Dispose();
pictureBoxRange.Image = null;
pictureBoxFill.Image?.Dispose();
pictureBoxFill.Image = null;
Point p1 = Cursor.Position;
Point p2 = CalcCapturePoint(Cursor.Position);
System.Drawing.Bitmap bmpRange = new Bitmap(this.pictureBoxRange.Width, this.pictureBoxRange.Height);
using (var g = System.Drawing.Graphics.FromImage(bmpRange))
{
//カーソル下の色をビットマップにコピー
g.CopyFromScreen(p2, System.Drawing.Point.Empty, new Size(bmpRange.Width, bmpRange.Height));
}
this.pictureBoxRange.Image = bmpRange;
////コピーされた1ピクセルの色を取得
Color color = bmpRange.GetPixel(0, 0);
System.Drawing.Bitmap bmpFill = new Bitmap(this.pictureBoxRange.Width, this.pictureBoxRange.Height);
using (var g = System.Drawing.Graphics.FromImage(bmpFill))
{
//取得した色でビットマップ全体を塗りつぶし
using (Brush brush = new SolidBrush(color))
{
g.FillRectangle(brush, 0, 0, bmpFill.Width, bmpFill.Height);
}
}
this.pictureBoxFill.Image = bmpFill;
this.label1.Text = $"カーソルの位置={p1.X},{p1.Y}";
this.label2.Text = $"補正後の位置={p2.X},{p2.Y}";
}
const int WM_SETTINGCHANGE = 0x1A;
const nint SPI_SETLOGICALDPIOVERRIDE = 0x9F;
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_SETTINGCHANGE && m.WParam == SPI_SETLOGICALDPIOVERRIDE)
{
System.Diagnostics.Debug.WriteLine("設定が変更?");
}
base.WndProc(ref m);
}
}
}
# DPIは拡大率とは別