我正在尝试创建一个应用程序来为我执行一些任务。基本上,我希望能够使用热键(F12)在屏幕上分配一个点,这样可以保存该点的颜色。
此像素颜色会更改,然后经常重置为原始颜色。
在应用程序的整个运行过程中,每次颜色从原始颜色更改为红色时,都会打开一个msgbox,让我知道颜色再次更改。
我做了很多谷歌搜索,但是对于我一生来说,我无法弄清楚。
这是我到目前为止所拥有的。
我有一个timer2,它可以检测我的热键(F12),获取颜色,甚至获取鼠标的坐标:
Private Sub Timer2_Tick(sender As Object, e As EventArgs) Handles Timer2.Tick
For i = 1 To 255
result = 0
result = GetAsyncKeyState(i)
If result = -32767 Then
If i = 123 Then
Dim myBmp As New Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)
Dim g As Graphics = Graphics.FromImage(myBmp)
g.CopyFromScreen(Point.Empty, Point.Empty, myBmp.Size)
g.Dispose()
Label1.Text = MousePosition.X.ToString & "," & MousePosition.Y.ToString
PictureBox1.BackColor = myBmp.GetPixel(MousePosition.X, MousePosition.Y)
'Label1.BackColor = myBmp.GetPixel(MousePosition.X, MousePosition.Y)
myBmp.Dispose()
End If
End If
Next i
End Sub
现在,如果我尝试捕获另一台监视器(我有多于1台监视器)上的像素颜色,该代码将出错:
“在System.Drawing.dll中发生了'System.ArgumentOutOfRangeException类型的未处理的异常
附加信息:参数必须为正且<宽度。”
在此行:PictureBox1.BackColor = myBmp.GetPixel(MousePosition.X,MousePosition.Y)
此外,鉴于我能够某种程度地获取鼠标的坐标,所以我不知道如何让我的应用程序以资源节约的方式监视这些坐标(而鼠标不必停留在该像素位置或表格聚焦)以改变颜色。有人可以帮忙吗?
答案 0 :(得分:1)
在我的多显示器设置中, F12 部分和计时器都在工作
<DllImport("user32.dll")>
Shared Function GetAsyncKeyState(ByVal vKey As Keys) As Short
End Function
Private savedColor As Color
Private savedPosition As Point
Private timer As New Threading.Timer(AddressOf timerCallback)
Private timerInterval As Integer = 100
Private Sub setPositionAndColor(ByRef position As Point, ByRef color As Color)
Using myBmp As New Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height)
Using g As Graphics = Graphics.FromImage(myBmp)
g.CopyFromScreen(Point.Empty, Point.Empty, myBmp.Size)
End Using
position = Cursor.Position
color = myBmp.GetPixel(position.X, position.Y)
End Using
End Sub
Private Sub timerCallback(state As Object)
If GetAsyncKeyState(Keys.F12) <> 0 Then
setPositionAndColor(savedPosition, savedColor)
Me.Invoke(
Sub()
Label1.Text = $"{savedPosition.X}, {savedPosition.Y}"
PictureBox1.BackColor = savedColor
End Sub)
Else
Dim currentColor As Color
Dim currentPosition As Point
setPositionAndColor(currentPosition, currentColor)
If currentPosition = savedPosition AndAlso currentColor <> savedColor Then
MessageBox.Show("!")
End If
End If
timer.Change(timerInterval, -1)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
timer.Change(timerInterval, -1)
End Sub
运行它后,我发现它有点断断续续。但这似乎可以满足您的要求。
答案 1 :(得分:0)
使用GDI时,您尝试执行的操作可能会非常慢。实现此类功能的一种好方法是查看SharpDX之类的东西,并利用DirectX为您捕获屏幕,然后循环遍历数组中的像素以获取其颜色。
https://github.com/sharpdx/SharpDX-Samples/blob/master/Desktop/Direct3D11.1/ScreenCapture/Program.cs
我意识到代码是用C#编写的,但是它与VB非常相似,并且我相信转换它不会很困难。
答案 2 :(得分:0)
使用RegisterHotkey安装系统范围内的热键的方法,该键可激活在任何屏幕中的“光标”下捕获颜色的过程。
按下注册为热键的键盘键时,无论窗口是否具有焦点,被最小化或在另一个屏幕中,都将通知注册它的窗口(或线程)。
按下热键后,将使用Screen.FromPoint()方法识别光标所在的屏幕。
有关与Screen对象和Screens / VirtualScreen坐标有关的方法的更多信息,请参见Using SetWindowPos with multiple monitors中的答案。
然后使用CreateDC函数来获取与光标位置相对应的Screen的设备上下文,并使用GetPixel函数来捕获光标下的像素的颜色。
任何时候都不会创建位图。
注意:
WM_HOTKEY
和wParam
表示的值的解释,请参见MSDN Docs中与lParam
消息有关的注释。 HotkeyPressed
)。光标下的颜色设置为PicureBox控件的BackColor(在这里,名为PicureBox1
)。 myHotkeyID
的字段)。
以选择的形式插入以下代码。
热键( F11
)在Form.Load()
事件中注册,而在Form.FormClosed()
事件中未注册。
Imports System.Runtime.InteropServices
Imports System.Security
Imports System.Security.Permissions
Private Const WM_HOTKEY As Integer = &H312
Private myHotkeyID As Integer = 327680
Private myHotkeyModifiers As RegHotkeyModifiers = 0
Private currentScreen As Screen
Private cursorPosition As Point = Point.Empty
Friend Shared HotkeyPressed As Action
Public Sub New()
InitializeComponent()
HotkeyPressed =
Sub()
currentScreen = Screen.FromPoint(cursorPosition)
Me.PictureBox1.BackColor = PixelColorFromScreen(currentScreen, cursorPosition)
End Sub
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim result = RegisterHotKey(Me.Handle, myHotkeyID, myHotkeyModifiers, Keys.F11)
End Sub
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles MyBase.FormClosed
UnregisterHotKey(Me.Handle, myHotkeyID)
End Sub
<SecurityCritical>
<SecurityPermission(SecurityAction.Demand, Flags:=SecurityPermissionFlag.UnmanagedCode)>
Protected Overrides Sub WndProc(ByRef m As Message)
Select Case m.Msg
Case WM_HOTKEY
If CType(m.WParam, Integer) > 0 Then
Dim keyPressed As Keys = CType(CType(m.LParam, Integer) >> 16, Keys)
Dim modifiers = CInt(m.LParam) And &HFF
If myHotkeyID = CInt(m.WParam) AndAlso CInt(myHotkeyModifiers) = modifiers Then
Select Case keyPressed
Case Keys.F11
cursorPosition = Cursor.Position
HotkeyPressed()
End Select
End If
End If
Exit Select
End Select
MyBase.WndProc(m)
End Sub
Win32函数声明和辅助方法:
<Flags>
Friend Enum RegHotkeyModifiers As UInteger
MOD_ALT = &H1
MOD_CONTROL = &H2
MOD_SHIFT = &H4
MOD_WIN = &H8 ' WINDOWS key was held down. Reserved for use by the operating system.
MOD_NOREPEAT = &H4000 ' Keyboard auto-repeat does not yield multiple hotkey notifications.
End Enum
<DllImport("user32.dll")>
Friend Shared Function RegisterHotKey(hWnd As IntPtr, ActionHotkeyId As Integer, fsModifiers As RegHotkeyModifiers, VirtualKey As Integer) As Boolean
End Function
<DllImport("user32.dll")>
Friend Shared Function UnregisterHotKey(hWnd As IntPtr, ActionHotkeyId As Integer) As Boolean
End Function
<DllImport("gdi32.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Friend Shared Function CreateDC(lpszDriver As String, lpszDevice As String, lpszOutput As String, lpInitData As IntPtr) As IntPtr
End Function
<DllImport("gdi32.dll", SetLastError:=True, EntryPoint:="DeleteDC")>
Friend Shared Function DeleteDC(<[In]> hdc As IntPtr) As Boolean
End Function
<DllImport("gdi32.dll", SetLastError:=True)>
Friend Shared Function GetPixel(hdc As IntPtr, nXPos As Integer, nYPos As Integer) As UInteger
End Function
Friend Function CreateDCFromDeviceName(deviceName As String) As IntPtr
Return CreateDC(deviceName, Nothing, Nothing, IntPtr.Zero)
End Function
Private Function PixelColorFromScreen(screen As Screen, position As Point) As Color
Dim screenDC As IntPtr = CreateDCFromDeviceName(screen.DeviceName)
Dim pixel As UInteger = GetPixel(screenDC, position.X, position.Y)
Try
Return Color.FromArgb(CType(pixel And &HFF, Integer),
CType(pixel And &HFF00, Integer) >> 8,
CType(pixel And &HFF0000, Integer) >> 16)
Finally
DeleteDC(screenDC)
End Try
End Function