Mouse message handling in win32 - c++

When a user clicks on window B the window C becomes visible.
Situation 1 : While window C is visible and user clicks anywhere on window A or B or any other child windows of window A, window C becomes invisible.
Situation 2 : While window C is visible and user clicks on window C then program does few things and makes window C invisible. [window C has more child windows]
How do I achieve this functionality ?
The SetCapture won't work for Situation 2.
In the window B this is what I got, it works for Situation 1, but obviously not for Situation 2.
LRESULT WindowB::LButtonDown ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam )
{
if ( !capture )
{
::SetCapture ( hwnd );
capture = true;
windowC->show ( );
}
else
{
::ReleaseCapture ( );
capture = false;
windowC->hide ( );
}
return 0;
};
P.S : I am using pure winapi with oop c++, and my own wrapper for windows and message handling.
EDIT: More code added as requested.
LRESULT WindowC::lButton ( UINT message, WPARAM wParam, LPARAM lParam )
{
doStuff ( );
::PostMessage ( hParent, WM_COMMAND, WPARAM ( BN_CLICKED ), LPARAM ( hwnd ) );
return 0;
};

Related

How do I set the "Select Precision" cursor in a C++ application?

I need to somehow set the cursor to the "Select Precision" pointer (the horizontal and vertical cross-over) for a C++ application.
Does anyone know how this would be integrated using the WinApi protocol?
Somewhere in initialization code:
HCURSOR precision_cursor = LoadCursor( NULL, IDC_CROSS );
And window procedure:
LRESULT CALLBACK YourWindowProc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
switch ( msg )
{
case WM_SETCURSOR:
// If you omit test below, you will change cursor also for scrollbars, frames, etc.
if ( LOWORD( lparam ) == HTCLIENT )
{
SetCursor( precision_cursor );
return TRUE;
}
break;
}
// This will also handle cursor for scrollbars and frames.
return DefWindowProc( hwnd, msg, wparam, lparam );
}

Windows keyboard hook successfully set, but isn't called

I'm trying to create a hook to a keyboard event but the hook is never called on a key stroke. I set the hook with:
HHOOK hookRes = SetWindowsHookEx( WH_KEYBOARD, &KeyStrokeHook, NULL, GetCurrentThreadId() );
And the callback function is:
LRESULT CALLBACK KeyStrokeHook( _In_ int code, _In_ WPARAM wParam, _In_ LPARAM lParam )
{
if( code < 0 )
return CallNextHookEx( NULL, code, wParam, lParam );
if( lParam & 0x80000000 == 0 ) // If key pressed, not released
{
keyStroke = wParam;
SetEvent( keyEvent );
}
return CallNextHookEx( NULL, code, wParam, lParam );
}
hookRes has a valid hook, but the hook function is never called.
Can the hook be triggered if the thread that set the hook is currently blocked on a mutex?

GET_X_LPARAM gives negative value

I have created a hook for mouse. I wanted to get mouse click coordinates, but the GET_X_LPARAM() gives me negative value (and always the same when clicking on different places). My problem becomes solved with GetCursorPos(), but I wonder why it is not working with GET_X_LPARAM/GET_Y_LPARAM. Here's the code:
LRESULT CALLBACK Recorder::mouseHook( int code, WPARAM wParam, LPARAM lParam ) {
if( code < 0 )
return CallNextHookEx( m_mouseHook, code, wParam, lParam );
switch( wParam ) {
case WM_LBUTTONDOWN:{
int _hereIsANegativeNumber = GET_X_LPARAM( lParam );
break;}
}
return CallNextHookEx( 0, code, wParam, lParam );
}
This is how I set the hook:
m_mouseHook = SetWindowsHookEx( WH_MOUSE_LL, &mouseHook, GetModuleHandle( NULL ), 0 );
In a WH_MOUSE_LL hook, lParam is not the mouse coordinates - instead it is a pointer to a MSLLHOOKSTRUCT.
So, to get coordinates:
POINT pt = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam)->pt;
See LowLevelMouseProc for more details.
Because for the WH_MOUSE_LL in the LowLevelMouseProc procedure what you get as the LPARAM variable is a pointer to a MSLLHOOKSTRUCT structure and use the pt member of it for getting the mouse coordinates

GetWindowLongPtr fails returning Retrieves the user data

I created win32 wrapper before but I lost the files... So I'm gonna creating them again. The problem is with my router(static window processor).
Here is my code
CFramework *wnd = 0;
// retrieve associated Window instance
wnd = reinterpret_cast<CFramework *>(::GetWindowLongPtr(hWnd, GWL_USERDATA));
// call the windows message handler
wnd->WndProc(hWnd, msg, wParam, lParam);
return true;
When I call GetWindowLongPtr it won't retrieves the user data.
UPDATE:
I also tired this code that uses SetWindowLongPtr
if ( msg == WM_CREATE )
{
SetWindowLongPtr( hWnd, GWLP_USERDATA, (LONG)((CREATESTRUCT *)lParam)->lpCreateParams );
}
Window *targetApp = (Window*)GetWindowLongPtr( hWnd, GWLP_USERDATA );
if ( targetApp )
{
return targetApp->WndProc( hWnd, msg, wParam, lParam );
}
return DefWindowProc( hWnd, msg, wParam, lParam );
I used my second code.^^^^
I put break point at SetWindow...
It seems it never Get called!
Your window may receive some messages before WM_CREATE. I did something like this a while ago, and there were sizing and positioning messages, along with WM_NCCREATE, that arrived before the WM_CREATE. So you shouldn't expect to see the value there on those messages.
Are you compiling for 32-bit or 64-bit? If 64-bit, then the cast to LONG may be whacking your pointer.
And, as Rup said in the comments, you have to be really certain that nobody else is using WM_USERDATA.

ComboBoxEx32 (CComboBoxEx) keyboard behaviour

I have a WTL application that uses an extended combobox control (the Win32 class ComboBoxEx32) with the CBS_DROPDOWNLIST style. It works well (I can have images against each item in the box) but the keyboard behaviour is different to a normal combobox - pressing a key will not jump to the first item in the combo that starts with that letter.
For example, if I add the strings 'Arnold', 'Bob' and 'Charlie' to the combo, if I then select the combo and press 'B', then 'Bob' won't be selected.
Does anyone know how to make this work? Currently the only idea I can think of is to somehow subclass the 'actual' combobox (I can get the handle to this using the CBEM_GETCOMBOCONTROL message) and process WM_CHARTOITEM. This is a PITA so I thought I'd ask if anyone else has come across this issue before.
In the end I hooked the combobox control (obtained with CBEM_GETCOMBOCONTROL) and trapped the WM_CHARTOITEM message and performed my own lookup. I can post code if anyone else is interested.
I created a working solution and want to share that:
ComboBoxExKeyboardSupport.h
#pragma once
class CComboBoxExKeyboardSupport
{
// Construction
public:
CComboBoxExKeyboardSupport( void );
~CComboBoxExKeyboardSupport( void );
// Attributes
private:
static CSimpleMap<HWND, CComboBoxExKeyboardSupport*> responsibleMap;
HWND hComboBoxHwnd;
WNDPROC fpOriginalWndProc;
// Operations
private:
static LRESULT CALLBACK StaticWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
LRESULT WndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
LRESULT HandleCharToItemMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );
bool IsWindowsXPPlatform( void );
bool InputMatches( CString inputChar, CString& itemText );
public:
void Attach( CComboBoxEx& comboBoxEx );
void Detach( void );
};
ComboBoxExKeyboardSupport.cpp
#include "StdAfx.h"
#include "ComboBoxExKeyboardSupport.h"
// Static member
CSimpleMap<HWND, CComboBoxExKeyboardSupport*> CComboBoxExKeyboardSupport::responsibleMap;
CComboBoxExKeyboardSupport::CComboBoxExKeyboardSupport( void )
{
hComboBoxHwnd = nullptr;
fpOriginalWndProc = nullptr;
}
CComboBoxExKeyboardSupport::~CComboBoxExKeyboardSupport( void )
{
Detach( );
}
void CComboBoxExKeyboardSupport::Attach( CComboBoxEx& comboBoxEx )
{
ATLASSERT( hComboBoxHwnd == nullptr );
if( hComboBoxHwnd != nullptr )
return;
if( !IsWindowsXPPlatform( ) )
return;
LONG_PTR lpNewWndProc = reinterpret_cast<LONG_PTR>( StaticWndProc );
LONG_PTR lpOldWndProc = 0;
//----
hComboBoxHwnd = comboBoxEx.GetComboBoxCtrl( )->GetSafeHwnd( );
ATLASSERT( hComboBoxHwnd != nullptr );
// Exchange the WndProc
lpOldWndProc = SetWindowLongPtr( hComboBoxHwnd, GWLP_WNDPROC, lpNewWndProc );
ATLASSERT( lpOldWndProc != 0 );
fpOriginalWndProc = reinterpret_cast<WNDPROC>( lpOldWndProc );
// Remember the handle and the old WndProc
responsibleMap.Add( hComboBoxHwnd, this );
}
void CComboBoxExKeyboardSupport::Detach( void )
{
if( hComboBoxHwnd == nullptr )
return;
//----
LONG_PTR lpResult = 0;
// Reset original WndProc
lpResult = SetWindowLongPtr( hComboBoxHwnd, GWLP_WNDPROC,
reinterpret_cast<LONG_PTR>( fpOriginalWndProc ) );
ATLASSERT( lpResult != 0 );
// Remove handle and WndProc from map
responsibleMap.Remove( hComboBoxHwnd );
//----
hComboBoxHwnd = nullptr;
fpOriginalWndProc = nullptr;
}
bool CComboBoxExKeyboardSupport::IsWindowsXPPlatform( void )
{
OSVERSIONINFO osvi = {0};
bool bResult = false;
//----
osvi.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
if( GetVersionEx( &osvi ) )
{
// 5.1 = Windows XP
// 5.2 = Windows Server 2003, Windows Server 2003 R2
bResult = ( osvi.dwMajorVersion == 5 &&
( osvi.dwMinorVersion == 1 || osvi.dwMinorVersion == 2 ) );
}
return bResult;
}
LRESULT CComboBoxExKeyboardSupport::StaticWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
CComboBoxExKeyboardSupport* pResponsibleClass = nullptr;
// Get responsible class from map
pResponsibleClass = responsibleMap.Lookup( hwnd );
ATLASSERT( pResponsibleClass != nullptr );
//----
return pResponsibleClass->WndProc( hwnd, uMsg, wParam, lParam );
}
LRESULT CComboBoxExKeyboardSupport::WndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
// Save originalWndProc because after WM_DESTROY/Detach the member variable is nullptr.
WNDPROC fpOriginalWndProc = this->fpOriginalWndProc;
//----
if( uMsg == WM_DESTROY )
{
Detach( );
}
else if( uMsg == WM_CHARTOITEM )
{
return HandleCharToItemMessage( hwnd, uMsg, wParam, lParam );
}
//----
return ::CallWindowProc( fpOriginalWndProc, hwnd, uMsg, wParam, lParam );
}
LRESULT CComboBoxExKeyboardSupport::HandleCharToItemMessage(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam )
{
//----
LRESULT lResult = CB_ERR;
CComboBox* pComboBox = nullptr;
int itemCount = 0;
int itemSelected = 0;
CString itemText;
TCHAR inputCharacter = 0;
//----
pComboBox = (CComboBox*)CComboBox::FromHandle( hwnd );
//----
itemCount = pComboBox->GetCount( );
itemSelected = pComboBox->GetCurSel( );
inputCharacter = static_cast<TCHAR>( LOWORD( wParam ) );
// Search from the current selected item plus one to the end
for( int i = (itemSelected + 1); i < itemCount; i++ )
{
pComboBox->GetLBText( i, itemText );
if( InputMatches( inputCharacter, itemText ) )
{
lResult = i;
break;
}
}
if( lResult == CB_ERR )
{
// Search from the beginning to the selected item minus one.
for( int i = 0; i < itemSelected; i++ )
{
pComboBox->GetLBText( i, itemText );
if( InputMatches( inputCharacter, itemText ) )
{
lResult = i;
break;
}
}
}
//----
return lResult;
}
bool CComboBoxExKeyboardSupport::InputMatches( CString inputChar, CString& itemText )
{
CString firstCharString;
bool bInputMatches = false;
//----
firstCharString = itemText;
firstCharString.Left( 1 );
//----
bInputMatches = firstCharString.CompareNoCase( inputChar ) == 0;
//----
return bInputMatches;
}
My suggestion is to ditch CComboBoxEx and draw the icon with an owner-draw regular combo box. CComboBoxEx is slightly different from the 'normal' combobox but in just enough ways that I suspect it's a complete re-implementation. Notice how a selected item looks slightly different from one that is selected in a normal combo box, as well.
Owner draw controls in WTL are quite easy to implement with the COwnerDraw mixin.
Not an answer to your question, just letting you know that that's how I deal CComboBoxEx nowadays :)
In our application, the keyboard behaviour you described was lost between versions. As it turns out, we removed an additional manifest dependency, which resulted in a dependency on an older version of comctl32.dll (5.82). This line in the project settings, Configuration Properties -> Linker -> Manifest File -> Additional Manifest Dependencies:
type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='' publicKeyToken='6595b64144ccf1df' language=''
fixed it for us.
Using Dependency Walker, one can check that the app is now only dependent on comctl32.dll version 6.10, which has the correct behaviour.