I'm writing a program for a handheld barcode scanner which uses Windows Embedded Handheld 6.5 Classic.
I have added a LinkLabel on my form and have coded it so that when you press Shift and click the link it brings up an input box for you to enter a password (I'll be replacing this with an actual form eventually but it is an InputBox for the time being)
When I let go of shift and begin to type my password using the physical keys (it is currently a 4 digit number) it treats the first character as though I am still holding shift.
Is there any way, in code, to revert it back so that shift isn't pressed?
I have tried to use the keybd_event api and it doesn't seem to work
This is what I've tried but I can't seem to find anything else to try.
Public Declare Sub keybd_event Lib "coredll.dll" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Integer, ByVal dwExtraInfo As Integer)
Private Sub lblTitle_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles lblTitle.Click
Try
If GetKeyState(Keys.ShiftKey) = 0 Then Exit Sub
Dim StaffPassword as String = "1234"
Dim _Continue As Boolean = False
keybd_event(VK_Shift, 0, KEYEVENTF_KEYDOWN, 0)
keybd_event(VK_Shift, 0, KEYEVENTF_KEYUP, 0)
Do Until _Continue
Dim _Password As String = InputBox("Please enter the staff password to go into the Admin Screen.", "Enter Password", "", True)
If Not _Password = StaffPassword Then
Dim _Ans As MsgBoxResult = MsgBox("You entered an incorrect password!" & vbNewLine & vbNewLine & "Would you like to try again?", MsgBoxStyle.Exclamation + MsgBoxStyle.YesNo, "Incorrect Password")
If _Ans = MsgBoxResult.No Then Exit Sub
Else
_Continue = True
End If
Loop
frmAdmin.ShowDialog()
Catch ex As Exception
MsgBox(ex.ToString, MsgBoxStyle.Critical, "Error")
End Try
End Sub
Please could someone help?
I know you want to revert the shift effect but maybe a simple workaround could do it. I'm only suggesting this approach because windows ce devices often have non-standard (hardware) keyboards whose drivers are not always perfect.
Since you are only expecting numbers you could try to detect a non-number char and map it to the corresponding number on the keyboard.
Related
I'm trying to write an app to perform some basic process automation by sending keyboard events (i.e. simulating single key presses as well as holding keys down) to a window in focus (any window, such as Notepad). I can get single key presses to work just fine, but I can't get it to hold a key down. Even if I do a key down event, followed by a lengthy delay, followed by a key up... all I get is a single keypress.
I've read so many tutorials, and many of them multiple times over to ensure I haven't missed something. Every single time however, all I get is a single key press, it fails to hold the key down.
The following is a code sample I found from:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/bad5b1f3-cf59-4a2b-889b-257ee590bf99/vb-advanced-key-bot?forum=vbgeneral
What I'm expecting to have happen is that it would send a keyboard event that tells the system to hold down a key (e.g. aaaaaaaaaaaaaaaaaaaa), but all I get is a single character. I've tried spamming the system with repeat keypresses, but the receiving app sees the different keyboard code for keydowns and keyups, as opposed to a key in a held status, and thus is not responding as though the key were actually held key down.
What am I doing wrong? Did they maybe change this dll?
A huge thanks to anyone who can help me get this working.
Public Class Form1
Private Declare Sub keybd_event Lib "user32.dll" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Integer, ByVal dwExtraInfo As Integer)
Private Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Integer, ByVal wMapType As Integer) As Integer
' flag to indicate completion
Dim finished As Boolean = True
' how long to 'press' the Space key
Dim delay As Integer = 3
' how many times to repeat Q and Space
Dim Repeats As Integer
' User closes application during processing
Dim UserInterupt As Boolean = False
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
KeyPreview = True
End Sub
Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing
If Not finished Then
TextBox1.AppendText("USER closing" & vbCrLf)
UserInterupt = True
e.Cancel = True
End If
End Sub
Private Sub Form1_KeyPress(sender As Object, e As KeyPressEventArgs) Handles Me.KeyPress
Select Case e.KeyChar
Case "z", "Z"
e.Handled = True
Repeats = 12
finished = False
Do While Not finished
TextBox1.AppendText("Pressing SPACE" & vbCrLf)
HoldKeyDown(Keys.Space, delay)
Loop
Case "x", "X"
e.Handled = True
TextBox1.AppendText("USER stopping" & vbCrLf)
finished = True
End Select
End Sub
Private Sub HoldKeyDown(ByVal k As Keys, ByVal Hold As Integer)
Dim HoldFor As DateTime = DateTime.Now().AddSeconds(Hold)
keybd_event(k, MapVirtualKey(k, 0), 0, 0)
While HoldFor.Subtract(DateTime.Now()).TotalSeconds > 0
Application.DoEvents()
End While
keybd_event(k, MapVirtualKey(k, 0), 2, 0)
TextBox1.AppendText("SPACE released" & vbCrLf)
Repeats -= 1
If Repeats = 0 Then
finished = True
TextBox1.AppendText("REPEATS completed" & vbCrLf)
End If
If UserInterupt Then End
End Sub
End Class
Answering my own question after going right down the rabbit hole on this one.
Basically put, the only way to do this is with SendKeys. The other methods are all deprecated and so will not work in this way anymore.
However this isn't a dead-end for you. If you want to use SendKeys to "hold down" a key, then spam the key at 10ms intervals and this should trigger the receiving app to think the key is held down.
I'm trying to create an audio recorder using VB.Net.
This is how I'm doing it:
Private Declare Function mciSendString Lib "winmm.dll" Alias "mciSendStringA" (ByVal lpstrCommand As String, ByVal lpstrReturnString As String, ByVal uReturnLength As Integer, ByVal hwndCallback As Integer) As Integer
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
mciSendString("open new Type waveaudio Alias recsound", "", 0, 0)
mciSendString("set recsound samplespersec 11025 channels 2 bitspersample 16 alignment 4 bytespersec 44100", vbNullString, 0, 0)
mciSendString("record capture", vbNullString, 0, 0)
Label1.Text = "Recording..."
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
mciSendString("save recsound c:\recsound.wav", "", 0, 0)
mciSendString("close recsound", "", 0, 0)
Label1.Text = "Stopped."
End Sub
It works fine, the only problem is that I have two mics, one of them is built-in and the other one is connected via USB. The second one has a way better recording quality but this application always records from the built-in mic.
I've searched all over the internet and I can't find a way to select the recording device. The only thing I was able to find was:
Dim DeviceId As Integer = 2
mciSendString("set recsound input " & DeviceId.ToString())
I've tried different values to no avail.
I have also tried the following code that successfully listed all the properties of all the recording devices found in my computer but I couldn't find anything that could help:
Private Sub Button6_Click(sender As Object, e As EventArgs) Handles Button6.Click
Dim objSearcher As New System.Management.ManagementObjectSearcher("SELECT * FROM Win32_SoundDevice")
Dim objCollection As System.Management.ManagementObjectCollection = objSearcher.Get()
Me.TextBox1.Text = ""
For Each obj As ManagementObject In objCollection
Me.TextBox1.Text &= "---------------------------------" & vbCrLf
For Each myProperty As PropertyData In obj.Properties
Me.TextBox1.Text &= myProperty.Name & " - " & myProperty.Value & vbCrLf
Next
Next
End Sub
Any suggestions will be appreciated.
I realize that you asked this question almost two years ago and so you may never see this answer. Perhaps you have notifications turned on and this may help you.
I have switched to Naudio (available through Nuget) for my recording applications and have had much better luck with it. I can specify the recording device as follows:
waveIn = New WaveIn
waveIn.DeviceNumber = 0 '0 - Stereo Mix, 1 - Microphone, 2 - Line In
Figure out what device number your USB microphone is and use that. Do a search on "Naudio" and you will find example code.
Private Sub txtAddress_Leave (ByVal sender As Object, ByVal e As
System.EventArgs) Handles txtAddress.Leave
If Len (txtAddress.Text) >= 0 Then
MsgBox ("Need to enter address", MsgBoxStyle.OKOnly, _
txtAddress.Focus()
End if
End Sub
Any help would be appreciated.
I assume you want to check if the user entered text, then you should change this
if Len (txtAddress.Text) >= 0 Then
to
if Len (txtAddress.Text) = 0 Then
However, you should better use .NET methods:
If String.IsNullOrEmpty(txtAddress.Text) Then ' or String.IsNullOrWhiteSpace
You should also use Select instead of Focus.
txtAddress.Select()
textbox.Focus() not working in C#
If Len(txtAddress.Text) >= 0 Then
should be
If txtAddress.Text.Trim = "" Then
That way it takes into account if someone decides to just enter "spaces" as an address. Right now your code will cause the message box to popup if they actually did enter something.
i have the following code:
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_SYSCOMMAND As Integer = &H112
Const SC_SCREENSAVE As Integer = &HF140
MyBase.WndProc(m)
If bloqueado = 0 Then
If m.Msg = WM_SYSCOMMAND AndAlso m.WParam.ToInt32 = SC_SCREENSAVE Then
Timer2.Start()
inicio = Now
pausa = pausa + 1
AddHandler Application.Idle, AddressOf Application_Idle
End If
End If
End Sub
Private Sub Application_Idle(ByVal sender As Object, ByVal e As EventArgs)
Dim newitem As ListViewItem
Dim diferença As TimeSpan
'MsgBox(Now.ToString)'
Debug.Print(Now.ToString)
fim = Now
diferença = fim - inicio
Timer2.Stop()
newitem = New ListViewItem
newitem.Text = pausa
newitem.SubItems.Add(inicio.ToLongTimeString)
newitem.SubItems.Add(fim.ToLongTimeString)
newitem.SubItems.Add(diferença.ToString.Substring(0, 8))
ListView1.Items.Add(newitem)
parcial = parcial & pausa & vbTab & vbTab & inicio.ToLongTimeString & vbTab & vbTab & fim.ToLongTimeString _
& vbTab & vbTab & diferença.ToString.Substring(0, 8) & vbTab & vbTab & " screensaver" & System.Environment.NewLine
RemoveHandler Application.Idle, AddressOf Application_Idle
End Sub
Basically the first part detect when screensaver activates and creates a application.idle event handler and the second part, when activity is detected a bunch of code is run and the handler removed.
It's all works fine except for one point:
As you can see i have inicio = now when screensaver becomes active and fim = now when activity is detected (when screensaver becomes inactive), so i should have 2 differente times, but if i have it like i posted the 2 datetime will be the same. If you notice i have a msgbox displaying the now (when screensaver stops) in comment, if i take it out of comment the 2 datetimes will be differente and correct (i used a cronometer to make sure of the results)
Now my questions:
Why does it need the messagebox for the now to be updated and why doesn't it work it debug.print?
Is there a way to solve this problem/update the now var, without having to use a messagebox (i wouldn't like for the app to have pop-up messages)
If i really have to use msgbox for this purpose is there a way for it not to send the pop-up or to autoclick ok right after so it disappears instantly?
EDIT:
I have been searching and i found this code:
Public Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Public Function IsSNRunning() As Boolean
IsSNRunning = (FindWindow("WindowsScreenSaverClass", vbNullString) <> 0)
End Function
Private Sub Timer3_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer3.Tick
If IsSNRunning() Then
'Screen Saver Is Running
Else
Timer3.Stop()
code
End If
End Sub
i used Timer3.Start() when in the part that captures the start of the screensaver, my idea being if i start the timer when i know the screensaver if on, then when i get IsSNRunning as false is when the screensaver stops running, but it doesn't work, any ideas why?
Doing anything with Application.Idle is a lost cause. Not only does your app go idle immediately after the screen saver activates, you also never stop being idle while it is running. The screen saver switches the active desktop to a dedicated secure desktop, none of the running programs will ever get any input, not until it de-activates.
You can observe the desktop switch, the SystemEvents.SessionSwitch event fires.
Do note the considerable lack of practical usefulness of code like this. Curiosity is okay but there are always a lot of things to learn. The screen saver should be at the bottom of your list.
First i'll thank you guys for the help, like you said application.idle doesn't work, with you help i got this solution i VB:
Imports System
Imports Microsoft.Win32
Imports System.Windows.Forms
Imports System.Runtime.InteropServices
<DllImport("user32.dll", CharSet:=CharSet.Auto)> Public Shared Function SystemParametersInfo(uAction As UInteger, _
uParam As UInteger, ByRef lpvParam As Boolean, fWinIni As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
' Check if the screensaver is busy running.'
Public Shared Function IsScreensaverRunning() As Boolean
Const SPI_GETSCREENSAVERRUNNING As Integer = 114
Dim isRunning As Boolean = False
If Not SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, isRunning, 0) Then
' Could not detect screen saver status...'
Return False
End If
If isRunning Then
' Screen saver is ON.'
Return True
End If
' Screen saver is OFF.'
Return False
End Function
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
Const WM_SYSCOMMAND As Integer = &H112
Const SC_SCREENSAVE As Integer = &HF140
MyBase.WndProc(m)
If bloqueado = 0 Then
If m.Msg = WM_SYSCOMMAND AndAlso m.WParam.ToInt32 = SC_SCREENSAVE Then
Timer2.Start()
Timer3.Enabled = True
Timer3.Start()
'here we that that the screensaver started running so we start a timer'
End If
End If
End Sub
Private Sub Timer3_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer3.Tick
If IsScreensaverRunning() Then
'Screen Saver Is Running'
Else
Timer3.Stop()
Timer3.Enabled = False
'Screen Saver Is not Running'
End If
End Sub
Because the timer only starts running when the screensaver is running we know that when you get timer3.stop is when the screensaver stopped running
Important, don't put a msgbox before the timer stop because it wont work, the pop-up will show and it wont get to the stop so innumerous pop-up will appear (yeah... i made that mistake :S)
Again, thanks for helping me and hope it will help someone in the future
I'm working on an encryption program and it uses a "PIN" to calculate some stuff for the encryption. I have a textbox where the user can insert the "PIN". I'd like to prevent people from entering anything but numbers. I added this on the KeyPress event:
If Not Char.IsControl(e.KeyChar) Then
If Not Char.IsNumber(e.KeyChar) Then
MsgBox("Invalid character", , "WARNING!")
TextBox3.Clear()
End If
End If
It shows the msgbox and it doesn't write to the textbox until i close th emsgbox. The typed character appears in the textbox. When I write another one it works the same as before, but it only replaces the last character instead of writing another one. Is there something I'm missing because that looks like a bug to me?
Set the ES_NUMBER windows style for your TextBox:
Public Class Form1
Public Const GWL_STYLE As Integer = (-16)
Public Const ES_NUMBER As Integer = &H2000
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal handle As IntPtr, ByVal nIndex As Integer) As Integer
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
(ByVal handle As IntPtr, ByVal nIndex As Integer, ByVal dwNewLong As Integer) As Integer
Public Sub SetNumbersOnlyTextBox(ByVal TB As TextBox)
SetWindowLong(TB.Handle, GWL_STYLE, GetWindowLong(TB.Handle, GWL_STYLE) Or ES_NUMBER)
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
SetNumbersOnlyTextBox(TextBox3)
End Sub
End Class
It shows the msgbox and it doesn't write to the textbox until i close th emsgbox.
Yes, that's what modal dialogs do. They block the caller from updates until closed. That's the point; the user cannot interact with the parent until they clear the modal child.
Why not simply clear the textbox first? Better yet; don't show an annoying dialog at all. Simply disallow the user from entering invalid characters by setting e.Handled to true. However, it's a bit trickier than it sounds as you need to allow for the backspace and delete keys, disable pasting, etc.
Here's an example of a NumericTextbox: http://msdn.microsoft.com/en-us/library/ms229644(v=vs.80).aspx
You just need to set the Handled property to true instead of clear:
e.Handled = True
As MarkPM notes above, if its a key you don't want you can set e.handle=true (as you intercept the key on the keypress event) to have the system eat it.
Along with this, in stead of a pop-up, you can have a label on the form that says "Only numbers can be entered here" or something like that. Set it up so that the color of the text is red. Also set it up so the label is not normally visible.
Finally, also in the keypress event, beyond setting e.handle=true for unwanted keys, when an unwanted key comes along make the label that says "Only numbers can be entered here" visible - you can also set up a timed event to turn the label's visibility off after a few seconds. You can also throw a Beep() into the mix if you like :-)
This is less invasive then a pop-up and moves things along nicely for the user.