I use this code to create a caret in a textbox (vb.net):
Private Declare Function CreateCaret Lib "user32" (ByVal hwnd As Long, ByVal hBitmap As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function DestroyCaret Lib "user32" () As Long
Private Declare Function SetCaretBlinkTime Lib "user32" (ByVal wMSeconds As Long) As Long
Private Declare Function SetCaretPos Lib "user32" (ByVal x As Long, ByVal y As Long) As Long
Private Declare Function ShowCaret Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function HideCaret Lib "user32" (ByVal hwnd As Long) As Long
Private Sub txtOutput_GotFocus(sender As Object, e As EventArgs) Handles txtOutput.GotFocus
CreateCaret(txtOutput.Handle, IntPtr.Zero, 9, 12)
ShowCaret(txtOutput.Handle)
End Sub
Is it possible to change the color of the caret?
The problem you have is your PInvoke functions aren't declared properly. Look them up and match them with http://pinvoke.net/.
The calls to create the caret will take a bitmap handle. So depending on what your caret looks like, you just need to create a bitmap and pass it to the CreateCaret function to change it's color. Here's some (very) rough code I used to change the caret color of a regular textbox. Just note that I only changed CreateCaret and ShowCaret to the proper signatures from http://www.pinvoke.net. You will have to change the rest.
Public Class Form1
Private Declare Function CreateCaret Lib "user32" (ByVal hWnd As IntPtr, ByVal hBitmap As IntPtr, ByVal nWidth As Integer, ByVal nHeight As Integer) As Boolean
Private Declare Function DestroyCaret Lib "user32" () As Long
Private Declare Function SetCaretBlinkTime Lib "user32" (ByVal wMSeconds As Long) As Long
Private Declare Function SetCaretPos Lib "user32" (ByVal x As Long, ByVal y As Long) As Long
Private Declare Function ShowCaret Lib "user32" (ByVal hwnd As IntPtr) As Boolean
Private Declare Function HideCaret Lib "user32" (ByVal hwnd As Long) As Long
Private caretBitmap as Bitmap
Private Sub txtOutput_GotFocus(sender As Object, e As EventArgs) Handles txtOutput.GotFocus
If caretBitmap Is Nothing Then
caretBitmap = CreateCaretBitmap()
End If
CreateCaret(txtOutput.Handle, caretBitmap.GetHbitmap(), 5, 10)
ShowCaret(txtOutput.Handle)
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
End Sub
Private Function CreateCaretBitmap() As Bitmap
Dim flag As New Bitmap(5, 5)
Dim flagGraphics As Graphics = Graphics.FromImage(flag)
flagGraphics.FillRectangle(Brushes.Chartreuse, 0, 0, 5, 10)
Return flag
End Function
End Class
You can create the bitmap however you want obviously.
Hope that helps.
Related
i am using Office 2016 and i want to do a PowerPoint presentation where you can't exit slide show just with hitting ESC key, so you can interact with slides only by your mouse ( or eventually exit it with a key combination but not by just clicking ESC ). Kiosk mode do most of work but still ESC is available. I know about NoEsc add-in but it does not work for me. It just not showing me that menu in Ribbon or elsewhere, but other add-ins do and they appear in Add-ins tab next to View tab in. So i found a code on other website for keyboard disabling macro but it works only on 32-bit and can't run on 64-bit. Im not a coder so i need a little help how can i make it work on 64-bit or 32+64-bit.
Here is an original code from website:
Option Explicit
'Esc Disable Key
Private Const WH_KEYBOARD_LL = 13&
Private Const HC_ACTION = 0&
Private Const VK_ESCAPE = &H1B
Private Type KBDLLHOOKSTRUCT
vkCode As Long
scanCode As Long
flags As Long
time As Long
dwExtraInfo As Long
End Type
Dim Response As Integer
Private 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
Public Declare Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
Private Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal cb As Long)
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Public m_hDllKbdHook As Long
Public Sub hookup()
Call UnhookWindowsHookEx(m_hDllKbdHook)
m_hDllKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LowLevelKeyboardProc, App.hInstance, 0&)
End Sub
Public Function LowLevelKeyboardProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Static kbdllhs As KBDLLHOOKSTRUCT
If nCode = HC_ACTION Then
Call CopyMemory(kbdllhs, ByVal lParam, Len(kbdllhs))
If (kbdllhs.vkCode = VK_ESCAPE) Then
LowLevelKeyboardProc = 1
End If
End If
End Function
And Here is what i did so far:
Change App.hInstance to 0&, because i got an error that App. is not defined.
Public Sub hookup()
Call UnhookWindowsHookEx(m_hDllKbdHook)
m_hDllKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LowLevelKeyboardProc, App.hInstance, 0&)
End Sub
To
Public Sub hookup()
Call UnhookWindowsHookEx(m_hDllKbdHook)
m_hDllKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LowLevelKeyboardProc, 0&, 0&)
End Sub
Added PtrSafe next to all Declare
But then mismatch appeared here and highlight "AddressOf LowLevelKeyboardProc"
Public Sub hookup()
Call UnhookWindowsHookEx(m_hDllKbdHook)
m_hDllKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LowLevelKeyboardProc, 0&, 0&)
End Sub
So i changed "lpfn As Long" to "lpfn As LongPtr" and then mismatch error is gone.
Private Declare PtrSafe Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As Long, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
To
Private Declare PtrSafe Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As LongPtr, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
But the problem is, even if i got rid of all error messages in macro editor and i can run this macro with no troubles seems like it does nothing during slide show. ESC key is still working even after running it by Macro Window or clicking action button for "Run macro" during show.
Macros are set to Always Enabled ( Lowest security mode ) in Office Options and presentation is saved as (.ppsm), so macro-enabled format.
Here is my full modified code:
Option Explicit
'Esc Disable Key
Private Const WH_KEYBOARD_LL = 13&
Private Const HC_ACTION = 0&
Private Const VK_ESCAPE = &H1B
Private Type KBDLLHOOKSTRUCT
vkCode As Long
scanCode As Long
flags As Long
time As Long
dwExtraInfo As Long
End Type
Dim Response As Integer
Private Declare PtrSafe Function SetWindowsHookEx Lib "user32" Alias "SetWindowsHookExA" (ByVal idHook As Long, ByVal lpfn As LongPtr, ByVal hmod As Long, ByVal dwThreadId As Long) As Long
Public Declare PtrSafe Function UnhookWindowsHookEx Lib "user32" (ByVal hHook As Long) As Long
Private Declare PtrSafe Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal cb As Long)
Private Declare PtrSafe Function GetAsyncKeyState Lib "user32" (ByVal vKey As Long) As Integer
Public m_hDllKbdHook As Long
Public Sub hookup()
Call UnhookWindowsHookEx(m_hDllKbdHook)
m_hDllKbdHook = SetWindowsHookEx(WH_KEYBOARD_LL, AddressOf LowLevelKeyboardProc, 0&, 0&)
End Sub
Public Function LowLevelKeyboardProc(ByVal nCode As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Static kbdllhs As KBDLLHOOKSTRUCT
If nCode = HC_ACTION Then
Call CopyMemory(kbdllhs, ByVal lParam, Len(kbdllhs))
If (kbdllhs.vkCode = VK_ESCAPE) Then
LowLevelKeyboardProc = 1
End If
End If
End Function
Thank you, and sorry for my bad english :)
This used to be a big problem when people were deep diving into Excel API functions. Luckily this website has a lot of what you need all in one spot:
https://jkp-ads.com/Articles/apideclarations.asp
It's pretty much what you need : )
I'm working on an Excel Workbook that uses VBA for data input, since I don't want the application itself to be available to the user if the user does not know the password.
I managed to set up the Userform for data input and then a new Userform for the password input.
However, I noticed that the password is easily bypassed if the Password Userform is terminated.
I tried to make the Userform_Terminate() take the user back to the previous Userform, but it just creates an endless loop.
Anyone know a workaround for this?
Private Sub UserForm_Terminate()
Unload Me
UserForm1.Show
End Sub
If what you need is disallowing user closing UserForm, then here is a solution.
Disable leaving form with either close button click or Alt+F4:
Code within UserForm:
Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer)
If CloseMode = 0 Then Cancel = True
End Sub
Make close button on form unclickable and grayed out:
Code within UserForm:
Private Sub UserForm_Initialize()
DisableCloseButton (Me.Caption) 'disable close button (X)
End Sub
Code within a module, works for 32 and 64 bit:
Option Explicit
Private Const MF_BYPOSITION = &H400
Private Const MF_REMOVE = &H1000
#If VBA7 Then '64 bit
Private Declare PtrSafe Function DrawMenuBar Lib "User32" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function GetMenuItemCount Lib "User32" (ByVal hMenu As LongPtr) As LongPtr
Private Declare PtrSafe Function GetSystemMenu Lib "User32" (ByVal hwnd As LongPtr, ByVal bRevert As LongPtr) As LongPtr
Private Declare PtrSafe Function RemoveMenu Lib "User32" (ByVal hMenu As LongPtr, ByVal nPosition As LongPtr, ByVal wFlags As LongPtr) As LongPtr
Private Declare PtrSafe Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private hwnd As LongPtr
#Else '32 bit
Private Declare Function DrawMenuBar Lib "User32" (ByVal hwnd As Long) As Long
Private Declare Function GetMenuItemCount Lib "User32" (ByVal hMenu As Long) As Long
Private Declare Function GetSystemMenu Lib "User32" (ByVal hwnd As Long, ByVal bRevert As Long) As Long
Private Declare Function RemoveMenu Lib "User32" (ByVal hMenu As Long, ByVal nPosition As Long, ByVal wFlags As Long) As Long
Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private hwnd As Long
#End If
Public Sub DisableCloseButton(ByVal formCaption As String) 'deactivates the upper right "x" in the user form
#If VBA7 Then '64 bit
Dim hMenu As LongPtr, menuItemCount As LongPtr
#Else '32 bit
Dim hMenu As Long, menuItemCount As Long
#End If
hwnd = FindWindow(vbNullString, formCaption) 'Obtain the window handle to the userform
hMenu = GetSystemMenu(hwnd, 0) 'Obtain the handle to the form's system menu
'Clear list box
If hMenu Then
menuItemCount = GetMenuItemCount(hMenu) 'Obtain the number of items in the menu
'Remove the system menu Close menu item. The menu item is 0-based, so the last item on the menu is menuItemCount - 1
Call RemoveMenu(hMenu, menuItemCount - 1, MF_REMOVE Or MF_BYPOSITION)
Call RemoveMenu(hMenu, menuItemCount - 2, MF_REMOVE Or MF_BYPOSITION) 'Remove the system menu separator line
Call DrawMenuBar(hwnd) 'Force a redraw of the menu. This refreshes the titlebar, dimming the X
End If
End Sub
I haven't learned yet how to correctly convert the Windows API functions to be used on Excel VBA. If I can't find it online and it isn't something easy like SwapMouseButton or SetDoubleClickTime I just get stuck on getting it to work.
Can I get some help fixing my call of the GetMouseMovePointsEx function and maybe tips to understand better how to do it for other complex function?
Here's what I have in a module:
Private Declare Function SwapMouseButton Lib "user32" (ByVal fSwap As Boolean) As Boolean
Private Declare Function SetDoubleClickTime Lib "user32" (ByVal uInterval As Integer) As Boolean
Private Type MOUSEMOVEPOINT
x As Integer
y As Integer
time As Long
dwExtraInfo As Long
End Type
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
Private Declare Function GetActiveWindow Lib "user32" () As Long
'Private Declare Function WindowProc Lib "user32" (ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
Private Const WM_MOUSEDOWN = &H200
Private Declare Function GetMouseMovePointsEx Lib "user32" (ByVal cbSize As Integer, ByRef lppt As MOUSEMOVEPOINT, ByRef lpptBuf() As MOUSEMOVEPOINT, ByVal nBufPoints As Integer, ByVal resolution As Long) As Integer
Private Sub Test1()
SwapMouseButton False 'True|False
End Sub
Private Sub Test2()
SetDoubleClickTime 0 '0=default(500), 48~=fastest I could get, >5000=5000
End Sub
Private Sub Test3()
Dim hwnd As Long, mmp As MOUSEMOVEPOINT, mmpBuf(64) As MOUSEMOVEPOINT, returnedValue As Integer
Sleep 1000
hwnd = GetActiveWindow()
'WindowProc hwnd, WS_MOUSEMOVE, WS_MOUSEMOVE, WS_MOUSEMOVE
mmp.x = 600
mmp.y = 300
mmp.time = 200
returnedValue = GetMouseMovePointsEx(Len(mmp), mmp, mmpBuf, 1000, 1)
If returnedValue = -1 Then
Debug.Print "Error"
Else
Debug.Print mmpBuf(0).x
Debug.Print mmpBuf(0).y
End If
End Sub
I have found a previous thread that shows how to get all open windows and their names using C# : get the titles of all open windows
However, I need to do this in VBA: does anyone know if that's possible?
Thank you so much.
Best regards
Here's a scrape from this microsoft answer by Andreas Killer that uses EnumWindows() which did the trick for me:
Option Explicit
#If Win64 Then
Private Declare PtrSafe Function GetWindowTextLengthA Lib "user32" ( _
ByVal hWnd As LongPtr) As Long
Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" ( _
ByVal hWnd As LongPtr, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" ( _
ByVal hWnd As LongPtr, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function EnumWindows Lib "user32" ( _
ByVal lpEnumFunc As LongPtr, ByVal lParam As LongPtr) As Long
#Else
Private Declare Function GetWindowTextLengthA Lib "user32" ( _
ByVal hWnd As Long) As Long
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" ( _
ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" ( _
ByVal hWnd As Long, ByVal nIndex As Long) As Long
Private Declare Function EnumWindows Lib "user32" ( _
ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
#End If
Private Sub debug_ListWindows()
ListWindows
End Sub
Private Function ListWindows()
EnumWindows AddressOf EnumFindWindowLikeProc, 0
End Function
#If Win64 Then
Private Function EnumFindWindowLikeProc(ByVal hWnd As LongPtr, ByVal lParam As Long) As Long
#Else
Private Function EnumFindWindowLikeProc(ByVal hWnd As Long, ByVal lParam As Long) As Long
#End If
If IsWindowVisible(hWnd) Then
If Len(Trim(WindowTitle(hWnd))) > 0 Then
Debug.Print WindowTitle(hWnd)
End If
End If
EnumFindWindowLikeProc = 1
End Function
#If Win64 Then
Private Function IsWindowVisible(ByVal hWnd As LongPtr) As Boolean
#Else
Private Function IsWindowVisible(ByVal hWnd As Long) As Boolean
#End If
Const GWL_STYLE = -16 'Sets a new window style
Const WS_VISIBLE = &H10000000 'The window is initially visible
Dim lngStyle As Long
lngStyle = GetWindowLong(hWnd, GWL_STYLE)
IsWindowVisible = ((lngStyle And WS_VISIBLE) = WS_VISIBLE)
End Function
#If Win64 Then
Property Get WindowTitle(ByVal hWnd As LongPtr) As String
#Else
Property Get WindowTitle(ByVal hWnd As Long) As String
#End If
Dim Contents As String, i As Long
Contents = Space$(GetWindowTextLengthA(hWnd) + 1)
i = GetWindowText(hWnd, Contents, Len(Contents))
WindowTitle = Left$(Contents, i)
End Property
and here is a pretty truncated version that loops using GetWindow() which should work on sufficiently new office installations:
Option Explicit
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" ( _
ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function GetWindowTextLengthA Lib "user32" ( _
ByVal hWnd As LongPtr) As Long
Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" ( _
ByVal hWnd As LongPtr, ByVal lpString As String, _
ByVal cch As LongPtr) As Long
Private Declare PtrSafe Function GetWindowLong Lib "user32" Alias "GetWindowLongA" ( _
ByVal hWnd As LongPtr, ByVal nIndex As Long) As Long
Private Declare PtrSafe Function GetWindow Lib "user32" ( _
ByVal hWnd As LongPtr, ByVal wCmd As Long) As LongPtr
Private Sub debug_ListWindows()
ListWindows
End Sub
Private Function ListWindows()
Const GWL_STYLE = -16 'Sets a new window style
Const WS_VISIBLE = &H10000000 'The window is initially visible
Const GW_HWNDNEXT = 2 'The retrieved handle identifies the window below the specified window in the Z order
Dim hWnd As LongPtr
hWnd = FindWindow(vbNullString, vbNullString)
While hWnd
Dim sTitle As String
sTitle = Space$(GetWindowTextLengthA(hWnd) + 1)
sTitle = Left$(sTitle, GetWindowText(hWnd, sTitle, Len(sTitle)))
If (GetWindowLong(hWnd, GWL_STYLE) And WS_VISIBLE) = WS_VISIBLE Then 'only list visible windows
If Len(Trim(sTitle)) > 0 Then 'ignore blank window titles
Debug.Print sTitle
End If
End If
hWnd = GetWindow(hWnd, GW_HWNDNEXT)
Wend
End Function
Sub ListWindows()
Dim wn As Excel.Window
For Each wn In Application.Windows
Debug.Print wn.Caption
Next wn
End Sub
if you wanted to activate any of them simply use:
wn.Activate
Is it possible to change the color of the title bar for a VBA userform using Windows API. Please note that I am only interested in changing the color of the title bar for a particular userform and not a system-wide them change. Thanks!
Just for fun;
UserForm:
Private gHWND As Long
Private Sub Label1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
If Button = 1 Then HandleDragMove gHWND
End Sub
Private Sub UserForm_Initialize()
gHWND = Setup(Me)
End Sub
Private Sub UserForm_Click()
Unload Me
End Sub
*.BAS
Option Explicit
Private Const WM_NCLBUTTONDOWN = &HA1&
Private Const HTCAPTION = 2&
Private Const GWL_STYLE = (-16)
Private Const WS_BORDER = &H800000
Private Const WS_DLGFRAME = &H400000
Private Const WS_CAPTION = WS_BORDER Or WS_DLGFRAME
Private Declare Sub ReleaseCapture Lib "User32" ()
Private Declare Function SendMessage Lib "User32" Alias "SendMessageA" (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 Declare Function GetWindowLong Lib "User32" Alias "GetWindowLongA" (ByVal HWND As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "User32" Alias "SetWindowLongA" (ByVal HWND As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Function Setup(objForm As Object) As Long
Setup = FindWindow("ThunderDFrame", objForm.Caption)
SetWindowLong Setup, GWL_STYLE, GetWindowLong(Setup, GWL_STYLE) And Not WS_CAPTION
End Function
Public Sub HandleDragMove(HWND As Long)
Call ReleaseCapture
Call SendMessage(HWND, WM_NCLBUTTONDOWN, HTCAPTION, 0&)
End Sub
(Would need mod for 64bit Office)