用PowerShell仿Snipaste截图工具遇区域偏移缩放问题求助
修复PowerShell截图工具的位置偏移与尺寸缩放问题
你的PowerShell截图工具出现的位置偏移和尺寸异常,主要由高DPI屏幕适配缺失和选区计算逻辑漏洞导致,以下是具体修复方案:
核心修复点
1. 解决高DPI屏幕坐标偏移
Windows高DPI缩放会使Cursor.Position返回的物理坐标与GDI+绘图逻辑坐标不匹配,直接使用会导致截图偏移。添加DPI感知配置,并修正坐标获取方式:
- 调用Windows API获取当前屏幕的DPI缩放比例
- 用缩放比例转换鼠标坐标,确保截图位置准确
2. 修正选区尺寸计算逻辑
当反向拖动鼠标(右下→左上)时,选区宽高会变为负数,导致截图尺寸错误;同时设置窗体大小时未考虑控件布局,需调整:
- 计算选区时先确定左上角坐标,确保宽高始终为正
- 设置窗体大小前先隐藏标签控件,避免布局干扰
完整修复代码
Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing # 添加DPI感知API Add-Type @" using System; using System.Runtime.InteropServices; public class DpiHelper { [DllImport("user32.dll")] public static extern int GetDpiForWindow(IntPtr hWnd); [DllImport("user32.dll")] public static extern bool SetProcessDpiAwarenessContext(int dpiFlag); } "@ # 设置进程DPI感知 [DpiHelper]::SetProcessDpiAwarenessContext(-4) # DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 # Create form and set properties $form = New-Object System.Windows.Forms.Form $form.Text = "Snipaste" $form.StartPosition = "CenterScreen" $form.Size = New-Object System.Drawing.Size(400, 300) $form.TopMost = $true $form.FormBorderStyle = "None" $label = New-Object System.Windows.Forms.Label $label.Location = New-Object System.Drawing.Point(10, 10) $label.Size = New-Object System.Drawing.Size(400, 300) $label.Text = "INSTRUCTIONS:`n1. Alt+S to capture region.`n2. Alt+Q to minimize.`n3. Scroll to scale.`n4. Escape to quit." $font = New-Object System.Drawing.Font("Arial", 12, [System.Drawing.FontStyle]::Bold) $label.Font = $font $form.Controls.Add($label) # Create picture frame and set properties $pictureFrame = New-Object System.Windows.Forms.PictureBox $pictureFrame.Dock = "Fill" $pictureFrame.SizeMode = [System.Windows.Forms.PictureBoxSizeMode]::StretchImage $form.Controls.Add($pictureFrame) $global:bitmap = $null # Add mouse wheel event to zoom in/out the picture $pictureFrame.Add_MouseWheel({ if ($_.Delta -gt 0) { $pictureFrame.Width *= 1.1 $pictureFrame.Height *= 1.1 $form.Width *= 1.1 $form.Height *= 1.1 } else { $pictureFrame.Width /= 1.1 $pictureFrame.Height /= 1.1 $form.Width /= 1.1 $form.Height /= 1.1 } }) $form.Add_KeyDown({ if ($_.KeyCode -eq "S" -and $_.Modifiers -eq "Alt") { $form.Hide() $label.Hide() $selectorForm = New-Object System.Windows.Forms.Form $selectorForm.FormBorderStyle = "None" $selectorForm.WindowState = "Maximized" $selectorForm.BackColor = "Black" $selectorForm.Opacity = 0.2 $selectorForm.TopMost = $true # 获取当前窗口DPI $dpi = [DpiHelper]::GetDpiForWindow($selectorForm.Handle) $scaleFactor = $dpi / 96.0 $startPoint = $null $currentRect = New-Object System.Drawing.Rectangle $selectorForm.Add_Paint({ $g = $selectorForm.CreateGraphics() $g.DrawRectangle([System.Drawing.Pens]::Red, $currentRect) }) $selectorForm.Add_MouseDown({ # 转换鼠标坐标为物理坐标 $startPoint = [System.Drawing.Point]::new( [int]([System.Windows.Forms.Cursor]::Position.X / $scaleFactor), [int]([System.Windows.Forms.Cursor]::Position.Y / $scaleFactor) ) $currentRect.Location = $startPoint $currentRect.Size = [System.Drawing.Size]::Empty }) $selectorForm.Add_MouseMove({ if ([System.Windows.Forms.Control]::MouseButtons -eq "Left") { $selectorForm.Opacity = 0.0 $currentPoint = [System.Drawing.Point]::new( [int]([System.Windows.Forms.Cursor]::Position.X / $scaleFactor), [int]([System.Windows.Forms.Cursor]::Position.Y / $scaleFactor) ) # 确保选区左上角为起始点,宽高为正 $currentRect.X = [Math]::Min($startPoint.X, $currentPoint.X) $currentRect.Y = [Math]::Min($startPoint.Y, $currentPoint.Y) $currentRect.Width = [Math]::Abs($currentPoint.X - $startPoint.X) $currentRect.Height = [Math]::Abs($currentPoint.Y - $startPoint.Y) $selectorForm.Invalidate() } }) $selectorForm.Add_MouseUp({ if ($currentRect.Width -gt 0 -and $currentRect.Height -gt 0) { $global:bitmap = New-Object System.Drawing.Bitmap($currentRect.Width, $currentRect.Height) $graphics = [System.Drawing.Graphics]::FromImage($global:bitmap) # 使用转换后的物理坐标截图 $graphics.CopyFromScreen($currentRect.X, $currentRect.Y, 0, 0, $currentRect.Size) $pictureFrame.Image = $global:bitmap [System.Windows.Forms.Clipboard]::SetImage($global:bitmap) # 设置窗体大小前先确保标签隐藏,避免布局影响 $form.Width = $currentRect.Width $form.Height = $currentRect.Height } $selectorForm.Close() $form.Show() }) $selectorForm.Add_KeyDown({ if ($_.KeyCode -eq "Escape") { $selectorForm.Close() $form.Show() } }) $selectorForm.ShowDialog() } }) $form.Add_KeyDown({ if ($_.KeyCode -eq "Q" -and $_.Alt) { $form.WindowState = "Minimized" } if ($_.KeyCode -eq "Escape") { $form.Close() } }) $global:mouseDownPoint = $null $pictureFrame.Add_MouseDown({ $global:mouseDownPoint = [System.Windows.Forms.Cursor]::Position $global:formTop = $form.Top $global:formLeft = $form.Left }) $pictureFrame.Add_MouseMove({ if ([System.Windows.Forms.Control]::MouseButtons -eq "Left") { $currentMousePoint = [System.Windows.Forms.Cursor]::Position $deltaX = $currentMousePoint.X - $global:mouseDownPoint.X $deltaY = $currentMousePoint.Y - $global:mouseDownPoint.Y $form.Top = $global:formTop + $deltaY $form.Left = $global:formLeft + $deltaX } }) $label.Add_MouseDown({ $global:mouseDownPoint = [System.Windows.Forms.Cursor]::Position $global:formTop = $form.Top $global:formLeft = $form.Left }) $label.Add_MouseMove({ if ([System.Windows.Forms.Control]::MouseButtons -eq "Left") { $currentMousePoint = [System.Windows.Forms.Cursor]::Position $deltaX = $currentMousePoint.X - $global:mouseDownPoint.X $deltaY = $currentMousePoint.Y - $global:mouseDownPoint.Y $form.Top = $global:formTop + $deltaY $form.Left = $global:formLeft + $deltaX } }) $form.ShowDialog() | Out-Null
内容的提问来源于stack exchange,提问作者hiroshi kohama
相关产品推荐
相关产品推荐

