Findwindow and SendMessage - vb6

I am trying to figure out why this is not sending a ALT+F to notepad,
Private Declare Function FindWindow1 Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Const WM_KEYUP = &H101
Private Const WM_KEYDOWN = &H100
Private Const WM_SYSKEYDOWN = &H104
Private Sub Command_Click()
Dim parenthwnd As Long
Dim hwnd As Long
parenthwnd = FindWindow1(vbNullString, "Untitled - Notepad")
retvalue = SendMessage(parenthwnd, WM_SYSKEYDOWN, VK_MENU, 1&)
retvalue = SendMessage(parenthwnd, WM_KEYDOWN, VK_F, 1&)
End Sub
I get a value for parenthwnd but not for any of the retvalue values (0).
What am I missing?

You can try to send/post these messages all you want. Unfortunately they don't effect the return value of GetAsyncKeyState - which is what the system uses, while processing the VK_F message - to see if the alt is down.
As a result you can't fake keystrokes with modifiers to other applications using this mechanism.
To get around this, you need to use SendInput - but this requires that the application to receive the keystrokes is the foreground/focus window.

I think this is the problem: you're sending your message to the frame around the notepad window, and need to send it to the menu window. Use your handle with FindWindowEx and the 32768 class name (that's a menu) to get the menu window, which is a child of the one you've got the handle to. Here are two pages: http://msdn.microsoft.com/en-us/library/ms633500(v=VS.85).aspx and http://msdn.microsoft.com/en-us/library/ms633574(VS.85).aspx#class_name that should help.

Use Postmessage instead of Sendmessage
Public Const WM_SYSKEYDOWN = &H104
PostMessage hwnd, WM_SYSKEYDOWN, vbKeyF, 2 ^ 29
'Simulates Alt + F (2^29 sets the 29 bit of lParam indicating Alt is being pressed.

Related

'Close' option is not available when program minimized to the taskbar?

I am programming VB6 in Win7. I have a program with a borderless window, no caption, no icon, no control box, etc. just a window. Using a command button, I can minimize the window to the Task Bar, and from there return it back.
My problem is, when minimized to the Task Bar, I right-click on the icon, and I wish to close the program from there. Win7 won't let me close the program via the pop-up menu. The close option is on the menu, but it does nothing.
How can I close this program from the task bar menu?
This seems to be a bug in VB6 Forms subsystem -- when form's BorderStyle is set to none Close menu on the taskbar and Alt+F4 shortcut as well just stop working as there is no system menu on the form.
Unfortunately a workaround involves subclassing and here is one way to deal with the issue:
Option Explicit
Private Const WM_SYSCOMMAND As Long = &H112
Private Const SC_CLOSE As Long = &HF060&
Private m_pSubclass As IUnknown
Private Property Get pvAddressOfSubclassProc() As Form1 '-- change Form1 to current form name
Set pvAddressOfSubclassProc = InitAddressOfMethod(Me, 5)
End Property
Private Sub Form_Load()
Set m_pSubclass = InitSubclassingThunk(hWnd, Me, pvAddressOfSubclassProc.SubclassProc(0, 0, 0, 0, 0))
End Sub
Public Function SubclassProc(ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long, Handled As Boolean) As Long
Select Case wMsg
Case WM_SYSCOMMAND
If wParam = SC_CLOSE Then
Unload Me
Handled = True
End If
End Select
End Function
This will need mdModernSubclassing.bas from Moderen Subclassing Thunk repository added to your project for the IDE-safe subclassing implementation.

VB6 sidebar app

All.
I'm attempting to develop a 'sidebar' application with vb6, which I want to behave like windows Vista's gadget sidebar or Google Desktop sidebar, in the respect that other windows could not maximize over it.
I'm aware that chances of this happening are probably very little, but I'm asking just in case.
Currently, I've got a form that has multiple controls, and runs a function on load which makes itself the exact height of the screen, minus the taskbar, and it's 'left' location is set by a timer to be 'screen.width - me.width', so it will start at full height on the far right of the screen, and cannot be moved. Code for the height is as follows, if it is necessary.
Declare Function GetUserNameA Lib "advapi32.dll" (ByVal lpBuffer As String, nSize As Long) As Long
Public Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" ( _
ByVal hwnd As Long, _
ByVal nIndex As Long) As Long
Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" ( _
ByVal hwnd As Long, _
ByVal nIndex As Long, _
ByVal dwNewLong As Long) As Long
Public Declare Function SetLayeredWindowAttributes Lib "user32" ( _
ByVal hwnd As Long, _
ByVal crKey As Long, _
ByVal bAlpha As Byte, _
ByVal dwFlags As Long) As Long
Public Const GWL_STYLE = (-16)
Public Const GWL_EXSTYLE = (-20)
Public Const WS_EX_LAYERED = &H80000
Public Const LWA_COLORKEY = &H1
Public Const LWA_ALPHA = &H2
Private Const ABM_GETTASKBARPOS = &H5
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Type APPBARDATA
cbSize As Long
hwnd As Long
uCallbackMessage As Long
uEdge As Long
rc As RECT
lParam As Long
End Type
Private Declare Function SHAppBarMessage Lib "shell32.dll" (ByVal dwMessage As Long, pData As APPBARDATA) As Long
Function Fixheight()
Dim ABD As APPBARDATA
SHAppBarMessage ABM_GETTASKBARPOS, ABD
Form1.Height = Screen.Height - ((ABD.rc.Bottom - ABD.rc.Top) * 12)
If Form1.Height <= 600 Then
Form1.Height = Screen.Height
End If
End Function
To be clear, I do not want an 'always on top' function. I already have that, and it's driving me insane, as the form has to me closed or minimized in order to maximize, minimize of close another program (i.e. chrome, word, etc) behind it. This form must instead not allow other programs to maximize over it, so that if for example, the user maximized Chrome, chrome would maximize minus form1.width.
I doubt that this is possible because as far as I'm concerned, that would mean taking control of chrome, and essentially making it's maximize function as
me.height = screen.height - ((ABD.rc.Bottom - ABD.rc.Top) * 12)
me.width = screen.width - form1.width
which isn't possible.
Anyway, hopefully someone out there can help. As I said, I seriously doubt the possibility of having this work, but if so, all the better.
Thanks in advance!
Thanks to Ken White, I googled SHAppBarMessage and found the following website, offering a downloadable source with the very feature I needed. I just have to implement it now!!
Very glad I asked! Thank you!
Edit: Found this spanish website, which while needed some help from Google Translate, is more suited to my needs. Just need to figure out how to make it work on the Right Hand Side! Thanks again!

vba How to control message boxes from another program?

So I want to use
pid = Shell(MyApp, 1)
with VBA to automate the use of a crappy external program. This program unfortunately has all sorts of annoying dialog boxes and popups that must be clicked. Is there any way to directly control each dialog box to ensure that the "OK" button is pressed rather than "Cancel"?
Currently I am using
AppActivate pid
Application.SendKeys ("%R")
But this command can only guarantee the "OK" of the initial pop-up dialog. Subsequent pop-ups may not be clicked, especially because I'm not sure how to guarantee focus on the new popup. Is there a way to find the child process ID's of any new popups? Is there any way to directly "click" a particularly labeled control button?
How to Interact with a Button on a Dialog
Well, there is a way of sending a WM_COMMAND message to common buttons on a dialog box effectively closing it:
Declare Function SendMessageA Lib "user32" (ByVal hWnd As Long, ByVal Msg As Integer, ByVal wParam As Long, ByVal lParam As Long) As Long
Declare Function FindWindowA Lib "user32" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Const WM_COMMAND As Integer = 273
Const WM_CLOSE As Integer = 16
Const IDOK As Integer = 1
Const IDCANCEL As Integer = 2
Sub ShowDialogs()
If Shell("C:\Users\myself\dialogs.exe") <> 0 Then
Dim hWnd As Long
hWnd = FindWindowA("#32770", "Info")
Debug.Print hWnd
SendMessageA hWnd, WM_COMMAND, IDOK, 0
End If
End Sub
Important Notes:
I had to use Spy++ to find out what class name Windows gives to message box dialogs.
The Windows SDK headers contained useful constants, especially the value for WM_COMMAND which was obtained from WinUser.h.
Each time a dialog is a created, you will need to obtain the handle for the window again using FindWindowA, as shown in the preceding code.

Placeholder Text in VB6

I know this is an odd one, but is there a way to emulate the placeholder text functionality in VB6? If not, does anyone know of a good OCX control I could get somewhere that will do this? I'm sure it can be programmed in with a set of functions to do this, just looking for something already done.
The placeholder I'm asking about isn't the "formatting" in VB6, but like the text you see on a webform instead of a label for instance.
The text inside of a text box that tells you what information goes in that box, or provides and example of the information you want the user to enter into that particular box.
Any help is greatly appreciated, as always.
It sounds like you want Cue Banners. These also work on ComboBox controls.
Private Const CBM_FIRST As Long = &H1700&
Private Const CB_SETCUEBANNER As Long = CBM_FIRST + 3
Private Const ECM_FIRST As Long = &H1500&
Private Const EM_SETCUEBANNER As Long = ECM_FIRST + 1
Private Declare Function SendMessage Lib "user32" Alias "SendMessageW" ( _
ByVal hWnd As Long, _
ByVal wMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long) As Long
Private Sub SetCueBannerTXT(ByVal TextBox As TextBox, ByVal CueText As String)
SendMessage TextBox.hWnd, EM_SETCUEBANNER, 0, StrPtr(CueText)
End Sub
Private Sub SetCueBannerCBO(ByVal ComboBox As ComboBox, ByVal CueText As String)
SendMessage ComboBox.hWnd, CB_SETCUEBANNER, 0, StrPtr(CueText)
End Sub
Note To use this API, you must provide a manifest specifying
Comclt32.dll version 6.0.
Let's see if I understand what you want correctly. You want a textbox that says (for example): "First Name" inside of it to show users what to enter?
This can be accomplished by setting the text value to "First Name" in design mode. Then, on the GotFocus event, you delete the text inside giving the user a blank textbox to enter their info.
To make it more user friendly, you can have grey text when it's just a label, and black text when its the users entry. You can also test for the text color so you don't delete the user's info if they reenter a textbox.

How do I click a button on a vb6 form?

I have a vb6 form with an ocx control on it. The ocx control has a button on it that I want to press from code. How do I do this?
I have:
Dim b As CommandButton
Set b = ocx.GetButton("btnPrint")
SendMessage ocx.hwnd, WM_COMMAND, GetWindowLong(b.hwnd, GWL_ID), b.hwnd
but it doesn't seem to work.
I believe the following will work:
Dim b As CommandButton
Set b = ocx.GetButton("btnPrint")
b = True
CommandButtons actually have two functions. One is the usual click button and the other is a toggle button that acts similar to a CheckBox. The default property of the CommandButton is actually the Value property that indicates whether a button is toggled. By setting the property, the Click event is generated. This is done even if the button is not styled as a ToggleButton and therefore doesn't change its state.
If you have access to the OCX code, you could expose the associated event handler and invoke it directly.
Don't know if an equivalent of .Net Button's Click() method existed back in VB6 days
For keypress you can also use sendmessage sending both keydown and keyup:
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Long) As Long
Const WM_KEYDOWN As Integer = &H100
Const WM_KEYUP As Integer = &H101
Const VK_SPACE = &H20
Private Sub cmdCommand1_Click()
Dim b As CommandButton
Set b = ocx.GetButton("btnPrint")
SendMessage b.hWnd, WM_KEYDOWN, VK_SPACE, 0&
SendMessage b.hWnd, WM_KEYUP, VK_SPACE, 0&
End Sub
This:
Dim b As CommandButton
Set b = ocx.GetButton("btnPrint")
b = True
does work. Completely unintuitive. I'd expect it to throw an error since a bool is not a valid CommandButton, but it is because of the default property thing.
WM_LBUTTONDOWN would be a mouse click, what I want is a button click (button as in a hwnd button, not a mouse button).
I don't have access to the source of the ocx (it's a 3rd party control). If I did, I would expose the function that I wanted to call (the original writer of the ocx should have exposed it).
Do you have access to the OCX code? You shouldn't really be directly invoking the click of a button. You should refactor the code so that the OCX button click code calls a function, e.g.
CMyWindow::OnLButtonDown()
{
this->FooBar();
}
Then from your VB6 app, directly call the FooBar method. If you can't directly call functions from VB6 you can wrap the FooBar() method with a windows message proc function, e.g.
#define WM_FOOBAR WM_APP + 1
Then use SendMessage in the VB6, like SendMessage(WM_FOOBAR, ...)

Resources