vba key pause/resume - toggle - excel

if the code - GetAsyncKeyState replaced with MsgBox, it's working fine however though there is no error, code doesn't work with GetAsyncKeyState.
or can command/toggle button will work with EXCEL VBA?
#If VBA7 Then
'declare virtual key event listener
Private Declare PtrSafe Function GetAsyncKeyState Lib "user32" _
(ByVal vKey As Long) As Integer
#Else
'declare virtual key event listener
Private Declare Function GetAsyncKeyState Lib "user32" _
(ByVal vKey As Long) As Integer
#End If
Private Const VK_RA = &H27
Sub Hide_Next()
Dim a As Range
Dim b As Range
Cells.EntireColumn.Hidden = True
Columns("a").EntireColumn.Hidden = False
If GetAsyncKeyState(vbKeyRight) Then
Application.Goto Reference:=Range("a1"), Scroll:=True
For Each a In Range("A2:A23").Cells
If a.Value = Empty Then
a.EntireRow.Hidden = True
End If
Next a
End If
If GetAsyncKeyState(vbKeyRight) Then
Columns("b").EntireColumn.Hidden = False
For Each b In Range("B2:B23").Cells
If b.Value <> Empty Then
b.EntireRow.Hidden = False
End If
Next b
End If
If GetAsyncKeyState(VK_RA) Then
Cells.EntireColumn.Hidden = False
End If
End Sub
Working Code, trying to replace MagBox with Keypress:
Sub Hide_Next()
Dim a As Range
Dim b As Range
Cells.EntireColumn.Hidden = True
Columns("a").EntireColumn.Hidden = False
MsgBox "Pause-A"
Application.Goto Reference:=Range("a1"), Scroll:=True
For Each a In Range("A2:A23").Cells
If a.Value = Empty Then
a.EntireRow.Hidden = True
End If
Next a
MsgBox "Pause-B"
Columns("b").EntireColumn.Hidden = False
For Each b In Range("B2:B23").Cells
If b.Value <> Empty Then
b.EntireRow.Hidden = False
End If
Next b
'and so on for the next column ...
End Sub
Example:

There's some strange things happening in your code.
The way this is written is that you would have to be pressing the right arrow key at the exact moment this subroutine is run. That's not preposterous or anything, but you don't state in your question what your expectation of this code is.
You check 3 times if that right key is being pressed and between each check you do some very slow front/ui actions like hiding rows and unhiding columns. If the user takes their finger off the right arrow key while excel is bopping around making changes the next block will not execute.
You check to see if the right arrow is pressed using both vbKeyRight and VK_RA constant. I'm not expert here but it DOES seem like both of those should return &H27... I would stick to one or the other though for debugging.
Rewriting:
#If VBA7 Then
'declare virtual key event listener
Private Declare PtrSafe Function GetAsyncKeyState Lib "user32" _
(ByVal vKey As Long) As Integer
#Else
'declare virtual key event listener
Private Declare Function GetAsyncKeyState Lib "user32" _
(ByVal vKey As Long) As Integer
#End If
Private Const VK_RA = &H27
Sub Hide_Next()
Dim a As Range
Dim rightKeyDown As Boolean
'Check if the right arrow key is being pressed before interacting with excel's slow UI.
If GetAsyncKeyState(VK_RA) Then
rightKeyDown = True
End If
'hide all the columns, except column A before doing anything
Cells.EntireColumn.Hidden = True
Columns("a").EntireColumn.Hidden = False
'Let's see if that right key was down
If rightKeyDown Then
Application.Goto Reference:=Range("a1"), Scroll:=True
For Each a In Range("A2:A23").Cells
If a.Value = "" AND a.Offset(,1).Value = "" Then
a.EntireRow.Hidden = True
End If
Next a
Cells.EntireColumn.Hidden = False
End If
End Sub
You also ask about a toggle button. It's not clear what your intentions are though (what is being toggled amongst these multiple steps?). A toggle button is an option though.

Related

Best way to activate an unsaved workbook

I have various code to create reports. The reports are added to a new workbook that does not get saved, the theory being that the user can choose whether to save the workbook or just close it after looking at the results. My code below will activate the unsaved workbook.
Sub ActivateWorkbook(wbResults As Workbook)
Dim objWindow As Window
With Application
.VBE.MainWindow.WindowState = vbext_ws_Minimize
For Each objWindow In .Windows
With objWindow
If .Caption <> wbResults.Name Then .WindowState = xlMinimized
End With
Next objWindow
With .Windows(wbResults.Name)
.WindowState = xlMaximized
.Activate
End With
End With
End Sub
This works okay with a single monitor. But if there is already more than one workbook and they are different monitors, it minimises windows in both (all) monitors and looks less than ideal. I am thinking that if I am able to identify which monitor has the active workbook, I could only minimize windows for that monitor (including the VBE, if required).
In reply to chris neilsen, I will include some basic code to illustrate what I'm calling the above procedure with. Please keep in mind that each procedure is varied in purpose and most of the code in each doesn't really pertain to this particular problem.
Sub ExampleCode()
Dim wbXXX As Workbook
Set wbXXX = Workbooks.Add
With wbXXX
'Main code here
End With
Call ActivateWorkbook(wbXXX)
Set wbXXX = Nothing
End Sub
Thanks to anybody trying to help. It is appreciated.
Okay, this seems to be working for me. It's not pretty. Note that "Microsoft Visual Basic for Applications Extensibility 5.3" is required to minimise the VBE, which is where the code is being run from, not the main Excel application. In any case, Activate has not worked for me reliably in the past. If it works for you, no need for any of this I guess. If anybody is game to test it, please let me know how you go. I have only tested on a dual monitor setup so far.
Objective: Show the new workbook in the same monitor as the active workbook when Activate does not work.
Private Declare PtrSafe Function FindWindow _
Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) _
As LongPtr
Private Declare PtrSafe Function MonitorFromWindow _
Lib "user32" _
(ByVal hwnd As LongPtr, _
ByVal dwFlags As Long) _
As LongPtr
Private Declare PtrSafe Function EnumDisplayMonitors _
Lib "user32.dll" _
(ByVal hdc As Long, _
ByRef lprcClip As Any, _
ByVal lpfnEnum As Long, _
ByVal dwData As Long) _
As Long
Private Declare PtrSafe Function GetSystemMetrics _
Lib "user32" _
(ByVal Index As Long) _
As Long
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Const MONITOR_DEFAULTTONEAREST = &H2
Private Const SM_CMONITORS As Long = 80
Private hWndMonitor As LongPtr
Private hActiveWorkbook As LongPtr
Private hVBE As LongPtr
Private lngMode As Long
Function MonitorCount() As Long
MonitorCount = GetSystemMetrics(SM_CMONITORS)
End Function
Function MonitorsAreTheSame() As Boolean
MonitorsAreTheSame = True
'Count of monitors
If MonitorCount > 1 Then
'Check the ActiveWorkbook
lngMode = 0
hWndMonitor = FindWindow("XLMAIN", Application.Caption)
EnumDisplayMonitors ByVal 0&, ByVal 0&, AddressOf MonitorEnumProc, ByVal 0&
'Check the VBE
lngMode = 1
hWndMonitor = FindWindow("wndclass_desked_gsk", Application.VBE.MainWindow.Caption)
EnumDisplayMonitors ByVal 0&, ByVal 0&, AddressOf MonitorEnumProc, ByVal 0&
MonitorsAreTheSame = CBool(hActiveWorkbook = hVBE)
End If
End Function
Private Function MonitorEnumProc(ByVal hMonitor As Long, ByVal hdcMonitor As Long, lprcMonitor As RECT, ByVal dwData As Long) As Long
If MonitorFromWindow(hWndMonitor, MONITOR_DEFAULTTONEAREST) = hMonitor Then
Select Case lngMode
Case 0
hActiveWorkbook = CStr(hMonitor)
Case 1
hVBE = CStr(hMonitor)
End Select
End If
MonitorEnumProc = MonitorCount
End Function
Sub Test()
Dim wbkTest As Workbook
Set wbkTest = Workbooks.Add
Call ActivateWorkbook(wbkTest)
Set wbkTest = Nothing
End Sub
Sub ActivateWorkbook(wbkResults As Workbook)
Dim objWindow As Window
With Application
If MonitorsAreTheSame = True Then
.VBE.MainWindow.WindowState = vbext_ws_Minimize
For Each objWindow In .Windows
With objWindow
If .Left = Application.VBE.MainWindow.Left Then
If .Caption <> wbkResults.Name Then .WindowState = xlMinimized
End If
End With
Next objWindow
Else
For Each objWindow In .Windows
With objWindow
If .Left <> Application.VBE.MainWindow.Left Then
If .Caption <> wbkResults.Name Then .WindowState = xlMinimized
End If
End With
Next objWindow
End If
.Windows(wbkResults.Name).WindowState = xlMaximized
AppActivate (.Caption)
End With
End Sub
this should be sufficient - no second sub for activation needed. These should show the new workbook in the foreground, no other windows changed.
Sub ExampleCode()
Dim wbXXX As Workbook
Set wbXXX = Workbooks.Add
With wbXXX
'Main code here
End With
wbXXX.Activate
Set wbXXX = Nothing
End Sub

How to trap Left Click in Excel?

I want to know if the selection of a cell is caused by a cursor move or by a mouse action.
There are a lot of articles explaining how to trap mouse click in Excel, even some explaining that left click can be trapped.
This code is found many times on the web:
' The declaration tells VBA where to find and how to call the API
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
' The function returns whether a key (or mouse button) is pressed or not
Public Function KeyPressed(ByVal Key As Long) As Boolean
KeyPressed = CBool((GetAsyncKeyState(Key) And &H8000) = &H8000)
End Function
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
If (KeyPressed(&H1) = True) Then
MsgBox "Left click"
End If
If (KeyPressed(&H2) = True) Then
MsgBox "Right click"
End If
End Sub
This code traps the right click event, but not the left! Probably because it is placed in the Worksheet_SelectionChange event which is only called when a SelectionChanged has occurred and therefore when the left button has already been released!
How to detect a left click on a cell of a sheet to know if the selection of a cell is caused by a keyboard input (arrows or enter) or by a mouse left/right click action?
I found this great article and adapt it for mouse button check : https://www.mrexcel.com/board/threads/keypress-event-for-worksheet-cells.181654/
Add this module:
Option Explicit
Declare Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" _
(ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Declare Function CallNextHookEx Lib "user32" _
(ByVal hHook As Long, ByVal nCode As Long, ByVal wParam As Long, lParam As Any) As Long
Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
Declare Function GetActiveWindow Lib "user32" () As Long
Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Const HC_ACTION = 0
Const WH_MOUSE_LL = 14 ' Type of windows message to be hooked
Const WM_RBUTTONDOWN = &H204 ' Mouse message for right button down
Const WM_LBUTTONDOWN = &H201 ' Mouse message for left button down
Type KBDLLHOOKSTRUCT
vkCode As Long
scanCode As Long
flags As Long
time As Long
dwExtraInfo As Long
End Type
Dim hkLowLevelID As Long ' Hook id of the LowLevelMouseProc function
Dim LeftMouseDown As Boolean ' Flag to trap left mouse down events
Dim RightMouseDown As Boolean ' Flag to trap left mouse down events
Dim EllapsedTimer As Date
Function LowLevelMouseProc _
(ByVal nCode As Long, ByVal wParam As Long, lParam As KBDLLHOOKSTRUCT) As Long
On Error GoTo ResumeHere
' CAUTION !!!
' We can't do any action which envolves UI interaction because Excel is already beeing to update UI
' Hook mouse events only if XL is the active window
If GetActiveWindow = FindWindow("XLMAIN", Application.Caption) Then
If (nCode = HC_ACTION) Then
' Check if the left button is pressed
If (wParam = WM_LBUTTONDOWN) Then
LeftMouseDown = True
EllapsedTimer = Now() + TimeValue("00:00:01")
Application.OnTime EllapsedTimer, "ResetFlags"
ElseIf (wParam = WM_RBUTTONDOWN) Then
RightMouseDown = True
EllapsedTimer = Now() + TimeValue("00:00:01")
Application.OnTime EllapsedTimer, "ResetFlags"
End If
End If
End If
ResumeHere:
' Pass function to next hook if there is one
LowLevelMouseProc = CallNextHookEx(0, nCode, wParam, ByVal lParam)
End Function
Function isLeftMouseDown()
isLeftMouseDown = LeftMouseDown
End Function
Function isRightMouseDown()
isRightMouseDown = RightMouseDown
End Function
' Reset the flags if the click has been thrown too long ago
Sub ResetFlags()
RightMouseDown = False
LeftMouseDown = False
End Sub
' Call this proc when opening Workbook
Sub StartHook()
If (hkLowLevelID = 0) Then
' Initiate the hooking process
hkLowLevelID = SetWindowsHookEx(WH_MOUSE_LL, AddressOf LowLevelMouseProc, Application.Hinstance, 0)
End If
End Sub
' Call this proc when closing Workbook
Sub StopHook()
If hkLowLevelID <> 0 Then
UnhookWindowsHookEx hkLowLevelID
hkLowLevelID = 0
End If
End Sub
It defines 2 procs StartHook and StopHook that you use in "ThisWoorkbook":
Private Sub Workbook_Open()
Call StartHook
End Sub
Private Sub Workbook_BeforeClose(Cancel As Boolean)
Call StopHook
End Sub
And 2 functions that you can use in the macro for the Sheets like this:
Private Sub Worksheet_SelectionChange(ByVal Target As Range)
' Check if the mouse Left button was pressed
If (isLeftMouseDown()) Then
... do some stuff on left click - for example ...
If (ActiveCell.Column = 1) Then
MsgBox "You LeftClick in column A"
End If
...
End If
End Sub
Caution :
The flag can be read for 1 second after the click event, they are then reseted. That is to prevent some side effect when leaving excel and coming back to it.
Addendum to the code answer:
As of VBA 7 and above, the 'Declare' statements at the beginning need to also include 'PtrSafe'. Microsft added this check to ensure that the 'Declare' statement is safe to run in 64-bit versions of Office. See the article here:
https://learn.microsoft.com/en-us/office/vba/language/concepts/getting-started/64-bit-visual-basic-for-applications-overview

userform loop for hiding and unhiding through multiple sequences

So i have this string of code... It is in a userform and is all vba based (IE not pulling data from a spreadsheet.)
Private Sub CHECK1_Click()
If CHECK1.value = False Then
COMBO1.visible = False
Else
COMBO1.visible = True
End If
End Sub
It works for perfectly for exactly one checkbox and combobox pair, I need it to work on all 61 on of them, individually... Being new to this I looked at case select possability but it looks like i would have to spell in out.
the userform is called "ORDER1"
All of the check boxes are named "CHECK1" THROUGH "CHECK61"
They all correspond to the combobox' aptly named "COMBO1" THROUGH "COMBO61"
(CHECK1=COMBO1 throguh the entire form.)
How can I make this work without putting 61 'click' events into the code?
oh and I'm on excel 2010
In the comments there's already a "control array" with WithEvents as a possible solution, below I'll show another solution without the WithEvents:
Copy this code to Notepad and save it as CatchEvents.cls
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "CatchEvents"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Private Type GUID
Data1 As Long
Data2 As Integer
Data3 As Integer
Data4(0 To 7) As Byte
End Type
#If VBA7 And Win64 Then
Private Declare PtrSafe Function ConnectToConnectionPoint Lib "shlwapi" Alias "#168" (ByVal punk As stdole.IUnknown, _
ByRef riidEvent As GUID, ByVal fConnect As Long, ByVal punkTarget As stdole.IUnknown, ByRef pdwCookie As Long, _
Optional ByVal ppcpOut As LongPtr) As Long
#Else
Private Declare Function ConnectToConnectionPoint Lib "shlwapi" Alias "#168" (ByVal punk As stdole.IUnknown, ByRef riidEvent As GUID, _
ByVal fConnect As Long, ByVal punkTarget As stdole.IUnknown, ByRef pdwCookie As Long, Optional ByVal ppcpOut As Long) As Long
#End If
'All Other Control-Events also possible
'No need to use WithEvents
'No need to put every Type of control in seperate collection or array, each event will fire no matter which control,
'so in the eventcode controls can be separated from others
'you can give your controls additional properties
'Arguments as Cancel, KeyCode, KeyAscii , Button , x and Y still can be used
Private EventGuide As GUID
Private Ck As Long
Private ctl As Object
Private CustomProp As String
Public Sub MyListClick()
Attribute MyListClick.VB_UserMemId = -610
If TypeName(ctl) = "CheckBox" Then
ctl.Parent.Controls(Replace(ctl.Name, "CHECK", "COMBO")).Visible = ctl.Value
End If
End Sub
Public Sub ConnectAllEvents(ByVal Connect As Boolean)
With EventGuide
.Data1 = &H20400
.Data4(0) = &HC0
.Data4(7) = &H46
End With
ConnectToConnectionPoint Me, EventGuide, Connect, ctl, Ck, 0&
End Sub
Public Property Let Item(Ctrl As Object)
Set ctl = Ctrl
Call ConnectAllEvents(True)
End Property
Public Sub Clear()
If (Ck <> 0) Then Call ConnectAllEvents(False)
Set ctl = Nothing
End Sub
In the VBA-editor right click somewhere on your Project and choose Import file, a Class named CatchEvents will now be in your Class Modules.
Finally paste code below behind your Userform:
Private AllControls() As New CatchEvents
Private Sub UserForm_Initialize()
Dim j As Long
ReDim AllControls(Controls.Count - 1)
For j = 0 To Controls.Count - 1
AllControls(j).Item = Controls(j)
Next
End Sub
Private Sub UserForm_Terminate()
Dim j As Long
For j = LBound(AllControls) To UBound(AllControls)
AllControls(j).Clear
Next j
Erase AllControls
End Sub

Barcode Scanner in excel

Right now I have a spreadsheet in excel with some vba in it to use as an inventory database for our small business. The problem is that we are growing and I need to get more sophisticated.
The scanner is used with a Userform with a textbox control monitoring the number of characters that come into the textbox. When the specified number of characters is triggered the system does its job. What I need to accomplish is a way to monitor the input coming from the scanner itself without using a textbox control so that I can set up multiple scanners without them interfering with one another.
Any direction on this is greatly appreciated.
Here is the code:
Private Sub TextBox1_Change()
On Error GoTo endgame
Dim barCode As String
Dim charNumb As Long
barCode = TextBox1.Text
charNumb = Len(barCode)
'This triggers the system to perform actions based on the barcode number
'received. All of my barcodes for this version are formatted to have only 5
'characters. Works great with a single user and scanner.
If charNumb = 5 Then
Cells.Find(barCode).Activate
ActiveCell.Offset(0, 1).Activate
ActiveCell = ActiveCell + 1
ActiveCell.Offset(0, 17).Activate
ActiveCell = ActiveCell + 1
If ActiveCell = ActiveCell.Offset(0, -1) Then
ActiveCell.Offset(0, -1).Clear
ActiveCell.Clear
GoTo TIMESTAMPER
Else
GoTo TIMESTAMPER
End If
TIMESTAMPER:
TextBox1.Text = ""
'Timestamp
ActiveCell.Offset(0, -5).Activate
With ActiveCell
.Formula = Now
.NumberFormat = "m/d/yyyy h:mm:ss AM/PM"
End With
ActiveWorkbook.Save
ActiveCell.EntireRow.Select
TextBox1.SetFocus
End If
GoTo AllEndsWell
endgame:
Call errorsound
AllEndsWell:
End Sub
I have previously attempted to add barcode reader support to Excel and while the following has not been fully tested, I recall it was working; however there are some requirements to make it work
Within the code to follow, a barcode read is performed when a system message has been 'peaked' at and starts with a specific character. Most barcode readers can be programmed to output text in a certain way; the code requires an invisible precursor to be added to strings which is detected via msgMessage.wParam (Code example Case 17) and an enter character to follow the string to show when the barcode read is complete and reset the listener
For your barcode readers you may need to alter which character is prefixed and it's associated detection character (Ascii value. i.e. 17)
My current code:
The following code should be placed in a Class Module 'KeyPressApi'
Option Explicit
Private Type BARCODEBUFFER
strBuf As String
bCode As Boolean
End Type
Private Type POINTAPI
x As Long
y As Long
End Type
Private Type MSG
hwnd As Long
Message As Long
wParam As Long
lParam As Long
time As Long
pt As POINTAPI
End Type
Private Declare Function WaitMessage Lib "user32" () As Long
Private Declare Function PeekMessage Lib "user32" Alias "PeekMessageA" _
(ByRef lpMsg As MSG, ByVal hwnd As Long, _
ByVal wMsgFilterMin As Long, _
ByVal wMsgFilterMax As Long, _
ByVal wRemoveMsg As Long) As Long
Private Declare Function TranslateMessage Lib "user32" _
(ByRef lpMsg As MSG) As Long
Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" _
(ByVal hwnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
lParam As Any) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
Private Const WM_KEYDOWN As Long = &H100
Private Const PM_REMOVE As Long = &H1
Private Const WM_CHAR As Long = &H102
Private bExitLoop As Boolean
Private bufBuffer As BARCODEBUFFER
Public Event BarcodeRead(Barcode As String, ByRef Cancel As Boolean)
Public Sub StartKeyPressInit()
Dim msgMessage As MSG
Dim bCancel As Boolean
Dim iMessage As Integer
Dim iKeyCode As Integer
Dim lXLhwnd As Long
On Error GoTo errHandler
Application.EnableCancelKey = xlErrorHandler
bExitLoop = False 'Initialize boolean flag.
lXLhwnd = FindWindow("XLMAIN", Application.Caption) 'Get the app hwnd.
Do
WaitMessage 'check for a key press and remove it from the msg queue.
If PeekMessage(msgMessage, lXLhwnd, WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE) Then
iKeyCode = msgMessage.wParam 'store the virtual key code for later use.
iMessage = msgMessage.Message
TranslateMessage msgMessage 'translate the virtual key code into a char msg.
PeekMessage msgMessage, lXLhwnd, WM_CHAR, WM_CHAR, PM_REMOVE
bCancel = False
Select Case iKeyCode 'Enter and backspace not handled correctly by below case statement
Case 8 ' Backspace
If bufBuffer.bCode = True Then
If Len(bufBuffer.strBuf) > 0 Then
bufBuffer.strBuf = Left(bufBuffer.strBuf, Len(bufBuffer.strBuf) - 1)
bCancel = True
End If
End If
Case 13 ' End of barcode string so reset to off mode
If bufBuffer.bCode = True Then
bufBuffer.bCode = False
RaiseEvent BarcodeRead(ReadBuffer(), 0)
bCancel = True
End If
Case Else
End Select
Select Case msgMessage.wParam
Case 17 ' Start of Barcode; Initialize buffer array
If bufBuffer.bCode = False Then
bufBuffer.bCode = True
bufBuffer.strBuf = ""
bCancel = True
End If
Case Else ' All other data
If bufBuffer.bCode = True Then
If iKeyCode <> 0 Then
bufBuffer.strBuf = bufBuffer.strBuf & Chr(msgMessage.wParam)
bCancel = True
End If
End If
End Select
'if the key pressed is allowed post it to the application.
If Not bCancel Then PostMessage lXLhwnd, iMessage, iKeyCode, 0
End If
errHandler: 'Allow the processing of other msgs.
DoEvents
Loop Until bExitLoop
End Sub
Public Sub StopKeyPressWatch()
bExitLoop = True 'Set this boolean flag to exit the above loop.
End Sub
Public Function ReadBuffer() As String
ReadBuffer = bufBuffer.strBuf
Dim i As Integer
For i = 1 To 31
ReadBuffer = Replace(ReadBuffer, Chr(i), "")
Next
End Function
Then within the sheet that you want to override the listener
Option Explicit
Dim WithEvents CKeyWatcher As KeyPressApi
Private Sub Worksheet_Activate()
If CKeyWatcher Is Nothing Then Set CKeyWatcher = New KeyPressApi
If Not CKeyWatcher Is Nothing Then CKeyWatcher.StartKeyPressInit
End Sub
Private Sub Worksheet_Deactivate()
If Not CKeyWatcher Is Nothing Then CKeyWatcher.StopKeyPressWatch
End Sub
Private Sub CKeyWatcher_BarcodeRead(strBuffer As String, Cancel As Boolean)
MsgBox strBuffer
End Sub

Excel VBA application.sendkeys "^C", True not working

I am using Excel VBA to copy text selection from an Access file (I'd prefer not to get into details as to why). I have it in a Do While loop that SHOULD press the tab key (works), then copies the data (fails), puts it into the clipboard (works), and sets the clipboard information to a variable (works), which then, for debugging purposes, does a debug.print of the variable (works). This is to cycle through a form to get to a "base point" where I can 100% use tabs and such to navigate to other parts of the form. See code please:
AppActivate ("Microsoft Access - Filename that is constant")
X = 0
Do While X < 14
Application.SendKeys "{TAB}", True
Application.SendKeys "^C", True
Sleep (500)
mydata.GetFromClipboard
cb = mydata.GetText
Debug.Print (cb)
If Len(cb) = 5 Then
X = 14
End If
X = X + 1
Loop
Set mydata = Nothing
I've tried getting this to work, but to no avail. What am I doing wrong or perhaps what would be a better solution?
Though I hate Sendkeys and was wondering whether I should ask you about it but since you said not to ask why, I will keep my trap shut. :P
Try this small fix... If this works then that means, you need to give it some time before issuing the next sendkeys command.
Sub Sample()
'
'~~> Rest of your code
'
Application.SendKeys "{TAB}", True
Wait 2
Application.SendKeys "^{C}", True
'
'~~> Rest of your code
'
End Sub
Private Sub Wait(ByVal nSec As Long)
nSec = nSec + Timer
While nSec > Timer
DoEvents
Wend
End Sub
what would be a better solution?
Use APIs as shown Here. This doesn't directly answer your question but it explains how the concept works.
So applying that would be something like this
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Dim Ret As Long
Sub Sample()
Ret = FindWindow(vbNullString, "Microsoft Access - Filename that is constant")
If Ret <> 0 Then
MsgBox "Window Found"
Else
MsgBox "Window Not Found"
End If
End Sub
If you wish to become good at API’s like FindWindow, FindWindowEx and SendMessage then get a tool that gives you a graphical view of the system’s processes, threads, windows, and window messages. For Ex: uuSpy or Spy++. Another example which demonstrates how this API is used.
I figured it out. I copied the code from here: http://www.vbaexpress.com/forum/showthread.php?38826-SendInput()-in-Excel-64Bit
I changed VkkeyMenu to VbKeyControl and the "f" key to "C". I know it could be simplified to take up less lines, but I'd rather not mess with it if it works like the saying "If it ain't broke, don't fix it." Code:
Private Declare PtrSafe Function SendInput Lib "user32" (ByVal nInputs As LongPtr, pInputs As Any, ByVal cbSize As LongPtr) As LongPtr
Private Declare PtrSafe Function VkKeyScan Lib "user32" Alias "VkKeyScanA" (ByVal cChar As Byte) As Integer
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Type KeyboardInput ' creating variable type
dwType As Long ' input type (keyboard or mouse)
wVk As Integer ' the key to press/release as ASCSI scan code
wScan As Integer ' not required
dwFlags As Long ' specify if key is pressed or released
dwTime As Long ' not required
dwExtraInfo As Long ' not required
dwPadding As Currency ' only required for mouse inputs
End Type
' SendInput constants
Private Const INPUT_KEYBOARD As Long = 1
Private Const KEYEVENTF_EXTENDEDKEY As Long = 1
Private Const KEYEVENTF_KEYUP As Long = 2
' Member variables
Private TheKeys() As KeyboardInput
Private NEvents As Long
Sub testage()
ReDim TheKeys(0 To 3)
With TheKeys(0)
.dwType = INPUT_KEYBOARD 'operation type
.wVk = vbKeyControl 'press CTRL key
End With
With TheKeys(1)
.dwType = INPUT_KEYBOARD ' operation
.wVk = VkKeyScan(Asc("C")) 'press chr key
End With
With TheKeys(2)
.dwType = INPUT_KEYBOARD 'operation type
.wVk = VkKeyScan(Asc("C"))
.dwFlags = KEYEVENTF_KEYUP 'release chr key
End With
With TheKeys(3)
.dwType = INPUT_KEYBOARD ' operation type
.wVk = vbKeyControl
.dwFlags = KEYEVENTF_KEYUP 'release CTRL Key
End With
Call SendInput(4, TheKeys(0), Len(TheKeys(0)))
Erase TheKeys
End Sub

Resources