When a NM_CUSTOMDRAW notification code is send by a WM_NOTIFY message to the parent of a control, the lParam parameter of this WM_NOTIFY message is the address of an NMCUSTOMDRAW structure, as written here :
http://msdn.microsoft.com/en-us/library/windows/desktop/ff919569(v=vs.85).aspx
On the otherhand, the lParam of a WM_NOTIFY message should be
A pointer to an NMHDR structure that contains the notification code and additional
information. For some notification messages, this parameter points to a larger
structure that has the NMHDR structure as its first member.
In many code examples, for the handler for WM_NOTIFY message, I see
LPNMLISTVIEW pnm = (LPNMLISTVIEW)lParam;
switch (pnm->hdr.code){ ....
But the question is, we don't know this lParam is a NMHDR structure or a NM_CUSTOMDRAW structure a proior, how could one cast lParam correctly? If the lParam is a just a NMHDR,
one should use LPNMHDR and pnm->code, isn't it?
Worse, different control use different type of structures: NMLVCUSTOMDRAW, NMTVCUSTOMDRAW, NMLVCUTTOMDRAW and NMTBCUSTOMDRAW. So what is the correct way to do at the beginning of a WM_NOTIFY handler, distinguish it is with a NM_CUSTOMDRAW or not, and decide the correct NMCUSTOMDRAW structure type.
When I read the article
http://msdn.microsoft.com/en-us/library/windows/desktop/ff919569%28v=vs.85%29.aspx
on MSDN, I can't understand some parts. For example,
CDRF_NOTIFYPOSTPAINT The control will send an NM_CUSTOMDRAW notification when the painting cycle for the entire control is complete. This occurs when dwDrawStage equals CDDS_PREPAINT.
When a control send an NM_CUSTOMDRAW notification when the painting cycle for the entire control is complete, this NM_CUSTOMDRAW should have dwDrawState equals CDDS_PREPOSTPAINT, isn't it?
Can someone explain what "This occurs when...." in the list of the meaning of return value in that MSDN article.
It is easy. The lParam pointer is both NMHDR* and NMLVCUSTOMDRAW* in the same time. If you look at how NMLVCUSTOMDRAW is defined you will see it starts with NMHDR member.
Having said that, you can reliably cast your LPARAM to anything of your choice: NMHDR*, NMCUSTOMDRAW*, NMLVCUSTOMDRAW*. If the notification is coming from listview, the message itself is WM_NOTIFY and NMHDR::code equals to NM_CUSTOMDRAW, then any and all of the three casts will be correct.
Think of NMLVCUSTOMDRAW as of a notification-specific extension of general purpose NMHDR.
The way Microsoft Windows structures it's structures is in a Plain Old Data format. As such, if I define:
struct A {
int a;
};
struct B {
A a;
int b;
};
struct C {
A a;
int c;
};
The layout of B in memory is actually two integers, the entire contents of A (an integer) then the entire contents of B (another integer). As such, it's safe to treat B like A in so far as I can get the a value and from the a member, determine whether my pointer is really of type B or type C. In the example you present NMHDR is the generic value (A) and NMLISTVIEW is the specific value (B or C). You test the hdr value (of type NMHDR) to see what type the lParam is and from that you cast it to the correct subtype. This is true for C and C++ Windows development.
Related
My parent dialog has a CComboBoxEx control (which is mapped to a derived class called CDatesComboBoxEx).
In one part of the application this dialog displays a popup modal dialog. And, inside the modal dialog, it needs access to information from the dates combo.
What I decided to do (which works fine) is pass the address of my combo in the constructor of the popup dialog. So I can now do things like:
m_pComboDates->GetCount()
m_pComboDates->GetItemDataPtr(i)
I was wondering if there was any way to use native Win32 code here instead?
We can get access to the parents handle (GetParent()->GetSafeHWnd()).
We know the ID of the control on the parent dialog (IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING).
So is it possible to somehow directly get the count and item data?
I know that there are these macros:
ComboBox_GetCount
ComboBox_GetItemData
But:
Can these be macros be used with CComboBoxEx control? And ...
How do we get the HWND on the combo given the context I previously described?
Actually, I think I missunderstood the purpose of those "macros". I can get the combo handle like this:
HWND hDatesCombo = ::GetDlgItem(
GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
But, ComboBox_GetCount does not return a value. Nor the others. So I am somewhat confused.
Based on the answer, this bit is now fine:
HWND hDatesCombo = ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
int iNumDates = static_cast<int>(::SendMessage(hDatesCombo, CB_GETCOUNT, 0, 0));
And inside my for loop I am doing this:
LRESULT itemData = ::SendMessage(hDatesCombo, CB_GETITEMDATA, static_cast<WPARAM>(i), 0);
auto* pEntry = static_cast<CChristianLifeMinistryEntry*>((LPVOID)itemData);
That is the only way I can find to cast it. If I try static_cast<LPVOID> it won't work either.
I was wondering if there was any way to use native Win32 code here instead?
Yes, there is. The SendMessage function (and its returned value) is what you need …
Once you have the HWND of your combo-box, you can send it the CB_GETCOUNT message to ask it how many items it contains:
HWND hDatesCombo = ::GetDlgItem(GetParent()->GetSafeHwnd(), IDC_COMBOBOXEX_OCLM_WEEK_OF_MEETING);
LRESULT nItems = ::SendMessage(hDatesCombo, CB_GETCOUNT, 0, 0);
And, to get the item data associated with a particular entry, send the CB_GETITEMDATA message, with the (zero-based) index of the item in question as the wParam argument:
//...
LRESULT *ItemData = new LRESULT[static_cast<size_t>(nItems)];
for (int i = 0; i < nItems; ++i) {
ItemData[i] = ::SendMessage(hDatesCombo, CB_GETITEMDATA, static_cast<WPARAM>(i), 0);
}
//...
delete[] ItemData; // When you're done with the data list
Of course, if your item data are pointers (such as if you have an owner-drawn combo with1 the CBS_HASSTRINGS style), you would need to modify the second code snippet accordingly, adding relevant the reinterpret_cast operations where necessary. (Note that both the LRESULT and WPARAM types are defined as being suitable for storing pointers.)
1 The linked M/S documentation page is a bit fuzzy on whether this applies to owner-drawn combos with or without the CBS_HASSTRINGS style.
In a MFC application within PreTranslateMessage(MSG *pMsg) inherited from a CView, I have this:
if (pMsg->message == WM_KEYDOWN) ...
The fields in a WM_KEYDOWN are documented here. The virtual key VK_ value is in pMsg->wParam and pMsg->lParam contains several field, of which bits 16-23 is the keyboard scan code.
So in my code I use:
const int virtualKey = pMsg->wParam;
const int hardwareScanCode = (pMsg->lParam >> 16) & 0x00ff; // bits 16-23
On my non-US keyboard for example, when I press the "#" character, I get the following:
virtualKey == 0xde --> VK_OEM_7 "Used for miscellaneous characters; it can vary by keyboard."
hardwareScanCode == 0x29 (41 decimal)
The character I'd like to "capture" or process differently is ASCII "#", 0x23 (35 decimal).
MY QUESTION
How do I translate the WM_KEYDOWN information to get something I can compare against, regardless of language or keyboard layout? I need to identify the # key whether the user has a standard US keyboard, or something different.
For example, I've been looking at the following functions such as:
MapVirtualKey(virtualkey, MAPVK_VSC_TO_VK);
// previous line is useless, the key VK_OEM_7 doesn't map to anything without the scan code
ToAscii(virtualKey, hardwareScanCode, nullptr, &word, 0);
// previous line returns zero, and zero is written to `word`
Edit:
Long story short: On a U.S. keyboard, SHIFT+3 = #, while on a French keyboard SHIFT+3 = /. So I don't want to look at individual keys, instead I want to know about the character.
When handling WM_KEYDOWN, how do I translate lParam and wParam (the "keys") to find out the character which the keyboard and Windows is about to generate?
I believe this is a better solution. This one was tested with both the standard U.S. keyboard layout and the Canadian-French keyboard layout.
const int wParam = pMsg->wParam;
const int lParam = pMsg->lParam;
const int keyboardScanCode = (lParam >> 16) & 0x00ff;
const int virtualKey = wParam;
BYTE keyboardState[256];
GetKeyboardState(keyboardState);
WORD ascii = 0;
const int len = ToAscii(virtualKey, keyboardScanCode, keyboardState, &ascii, 0);
if (len == 1 && ascii == '#')
{
// ...etc...
}
Even though the help page seems to hint that keyboardState is optional for the call to ToAscii(), I found that it was required with the character I was trying to detect.
Found the magic API call that gets me what I need: GetKeyNameText()
if (pMsg->message == WM_KEYDOWN)
{
char buffer[20];
const int len = GetKeyNameTextA(pMsg->lParam, buffer, sizeof(buffer));
if (len == 1 && buffer[0] == '#')
{
// ...etc...
}
}
Nope, that code only works on keyboard layouts that have an explicit '#' key. Doesn't work on layouts like the standard U.S. layout where '#' is a combination of other keys like SHIFT + 3.
I'm not an MFC expert, but here's roughly what I believe its message loop looks like:
while (::GetMessage(&msg, NULL, 0, 0) > 0) {
if (!app->PreTranslateMessage(&msg)) { // the hook you want to use
TranslateMessage(&msg); // where WM_CHAR messages are generated
DispatchMessage(&msg); // where the original message is dispatched
}
}
Suppose a U.S. user (for whom 3 and # are on the same key) presses that key.
The PreTranslateMessage hook will see the WM_KEYDOWN message.
If it allows the message to pass through, then TranslateMessage will generate a WM_CHAR message (or something from that family of messages) and dispatch it directly. PreTranslateMessage will never see the WM_CHAR.
Whether that WM_CHAR is a '3' or a '#' depends on the keyboard state, specifically whether a Shift key is currently pressed. But the WM_KEYDOWN message doesn't contain all the keyboard state. TranslateMessage keeps track of the state by taking notes on the keyboard messages that pass through it, so it knows whether the Shift (or Ctrl or Alt) is already down.
Then DispatchMessage will dispatch the original WM_KEYDOWN message.
If you want to catch only the '#' and not the '3's, then you have two options:
Make your PreTranslateMessage hook keep track of all the keyboard state (like TranslateMessage would normally do). It would have to watch for all of the keyboard messages to track the keyboard state and use that in conjunction with the keyboard layout to figure whether the current message would normally generate a '#'. You'd then have to manually dispatch the WM_KEYDOWN message and return TRUE (so that the normal translate/dispatch doesn't happen). You'd also have to be careful to also filter the corresponding WM_KEYUP messages so that you don't confuse TranslateMessage's internal state. That's a lot of work and lots to test.
Find a place to intercept the WM_CHAR messages that TranslateMessage generates.
For that second option, you could subclass the destination window, have it intercept WM_CHAR messages when the character is '#' and pass everything else through. That seems a lot simpler and well targeted.
I primarily come from a .NET background, but there is one small task that I have to accomplish for a Windows Control, specifically a ComboBox.
I understand that the CBS_SORT message is needed for the ComboBox to sort items alphabetically. This works just fine for me when I add strings to the combobox using the CB_ADDSTRING flag.
I'm trying to make the ComboBox use a reverse (or other custom) sorting order.
If I use CreateWindow to make my combobox, is there a way I can take the HWND it returns and hook up a custom method that processes the WM_COMPAREITEM message?
This is the article from Microsoft that talked about processing the WM_COMPAREITEM message. https://msdn.microsoft.com/en-us/library/windows/desktop/bb775791(v=vs.85).aspx
Edit:
I suppose one workaround is to use the CB_INSERTSTRING flag for custom sorting, but I'd really prefer to implement something similar to a CompareTo in other languages if I can.
Edit:
It's worth mentioning that class which calls CreateWindow (w/o using the CBS_HASSTRINGS flag) has a message handler for the WM_COMMAND message. It switches in the HIWORD to handle notifications like CBN_SELCHANGE.
I looked at the documentation for Combo box and noticed a slight difference between CBN_SELCHANGE and WM_COMPAREITEM.
The CBN_SELCHANGE notification code is sent when the user changes the current selection in the list box of a combo box. The user can change the selection by clicking in the list box or by using the arrow keys. The parent window of the combo box receives this notification in the form of a WM_COMMAND message with CBN_SELCHANGE in the high-order word of the wParam parameter.
And here's what it says about WM_COMPAREITEM
The system sends the WM_COMPAREITEM message to determine the relative position of a new item in the sorted list of an owner-drawn combo box or list box. Whenever the application adds a new item, the system sends this message to the owner of a combo box or list box created with the CBS_SORT or LBS_SORT style.
As far as I can tell, the DWORD Style that is passed into the CreateWindow method is WS_VISIBLE | WS_CHILD | WS_VSCROLL | CBS_AUTOHSCROLL | CBS_DROPDOWNLIST | CBS_SORT
Perhaps the problem is that the combobox that is created is not an "owner-drawn" combobox?
If that is the case, I would need to find a way to do that, but only handle the WM_COMPAREITEM case and not any other custom painting code.
Update
I've created the ComboBox without the CBS_HASSTRINGS and with the CB_OWNERDRAWFIXED (Which then lets me process the WM_COMPAREITEM message).
Here's the message handling code
LRESULT MyComboBoxParent::OnCompareItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
//umsg is always wm_compareitem
COMPAREITEMSTRUCT* pCompareItemStruct = (COMPAREITEMSTRUCT*)lParam;
ATLASSERT(pCompareItemStruct->CtlType == ODT_COMBOBOX); //this assert works
int iItemId1 = pCompareItemStruct->itemID1;
int iItemId2 = pCompareItemStruct->itemID2;
LPCTSTR item1 = (LPCTSTR)pCompareItemStruct ->itemData1;
ATLASSERT(item1 != NULL);
LPCTSTR item2 = (LPCTSTR)pCompareItemStruct->itemData2;
ATLASSERT(item2 != NULL);
int iStringCompareResult = _tcscmp(item1, item2);
//should reverse the sorting order
int iReturn = -1 * iStringCompareResult;
return iReturn;
}
//and the existing code snippet that adds the values to the combobox
::SendMessage(m_hwndCombo,CB_ADDSTRING,0,(LPARAM) ((wchar_t*)bstr_tDisplayedValue))
The data coming in from the COMPAREITEMSTRUCT is really strange.
I add the following values to the combo box in order.
aaaa
bbbb
this triggers wm_compareitem
iItemId1 is 0
iItemId2 is -1
item1 is 0x00000000 (null ptr)
item2 is L"bbbb"
(this causes the string compare to fail, so I just force return of -1)
cccc
iItemId1 is 0
iItemId2 is -1
item1 is 0x00000000 (null ptr)
item2 is L"cccc"
zzzz
yyyy
xxxx
z,y and x all follow the same pattern. I may have found one other guy on the internet with the same problem here. But hard to say for sure.
I have subclassed Edit control window procedure and then found out it no longer
sent the EN_UPDATE.
Am I missing something , and could somebody suggest me a workaround on this one?
LRESULT CALLBACK EditBoxProc_textbox3( HWND hwnd, UINT message , WPARAM wParam, LPARAM lParam )
{
int previous_position = 0 ;
WCHAR previous_text [1024];
WCHAR current_text [1024];
switch (message)
{
case WM_CREATE:
previous_text[0] = L'\0';
current_text[0] = L'\0';
break;
case EN_SETFOCUS:
// :TODO: read the current text of the textbox3 and update textbox2
// text according to it. //
Edit_Enable(hwndTextBox2,FALSE);
break;
case EN_UPDATE:
MessageBox(NULL,L"EN_UPDATE", lpszAppName,MB_OK);
GetWindowText( hwndTextBox3, current_text ,1024);
if( is_valid_textbox3(current_text))
{
wcscpy(previous_text,current_text);
previous_position = LOWORD(Edit_GetSel(hwndTextBox3));
update_textbox2(NULL);
}else
{
SetWindowText(hwndTextBox3, previous_text );
Edit_SetSel(hwndTextBox3,previous_position, previous_position);
}
break;
case EN_KILLFOCUS:
Edit_Enable(hwndTextBox2,TRUE);
break;
default:
break;
}
return CallWindowProc(edit_old_wndproc_textbox3,hwnd,message,\
wParam,lParam);
}
and then found out it no longer sent the EN_UPDATE
It never sent EN_UPDATE in the first place. It is a notification message that's actually sent as a WM_COMMAND message. And it is sent to the parent of the Edit control, not the control itself. Same goes for EN_SET/KILLFOCUS.
The design philosophy here is that an Edit control can simply be put on, say, a dialog. And the custom code that makes the dialog behave in a certain way is written in the parent window's procedure with no requirement to subclass the control. Which is fine but it makes it difficult to create a customized edit control that can have its own behavior. Or in other words, it makes it hard to componentize an edit control. The EN_SET/KILLFOCUS notifications are not a problem, you can simple detect their corresponding WM_SET/KILLFOCUS messages. But you'll hit the wall on EN_UPDATE, the control doesn't send any message like that to itself. Only the parent window can detect it.
Componentizing an edit control is pretty desirable and actively pursued by object-oriented class libraries like Winforms and Qt. They have a class wrapper for the control (TextBox, QLineEdit) that has a virtual method that can be overridden (OnTextChanged, changeEvent) so that the control can be customized into a derived class with its own behavior. And generate an event (aka signal) that anybody can subscribe to, not just the parent (TextChanged, textChanged). To make that work, the parent window needs to participate. When it gets the WM_COMMAND message, it must echo the notification back to the child control. Either by sending a special message back or by calling a virtual method on the child class.
You can of course implement this yourself as well, albeit that you are liable of reinventing such a class library. Consider using an existing one instead.
I found this keyboard hook code, which I'm trying to slightly modify for my purposes: http://blogs.msdn.com/toub/archive/2006/05/03/589423.aspx
As an overview, I want to have the user press a key, say 'E', and have the keyboard return a different character, 'Z', to whatever app is in focus.
The relevant method I changed now looks like:
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
//The truely typed character:
int vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine((Keys)vkCode);
KBDLLHOOKSTRUCT replacementKey = (KBDLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(KBDLLHOOKSTRUCT));
replacementKey.vkCode = 90; // char 'Z'
Marshal.StructureToPtr(replacementKey, lParam, false);
//Now changed to my set character
vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine((Keys)vkCode);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
The console correctly outputs this as:
E
Z
T
Z
G
Z
etc.
HOWEVER, the in focus app still types 'E' instead of 'Z'. Why? I changed the hooked keyboard input to contain 'Z' instead of 'E', and the console lines show that it was changed correctly!
As I understand it, calling the return CallNextHookEx(_hookID, nCode, wParam, lParam); is what sends the "print this now" command to the open app. Is that not how it works? Is there something that's preventing me from typing the character I want? I know apps like AutoHotkey take an input key, check it, and return a different character. How do I do the same here?
Thanks!
I've done this before but a little different.
Instead of trying to change the parameters sent to CallNextHookEx, I 'swallowed' the key press (you can do this by returning a nonzero value from the hook procedure to prevent subsequent procedures from being called).
Then I used SendInput to send the new key that I wanted to 'inject'.
So basically it works like this:
Hook procedure identifies a target key is pressed
Call to SendInput, with the new key
Return 1 from the hook procedure to ignore the original key
Be careful of cyclic redirects, i.e. 'a' redirected to 'b' redirected to 'a', it can easily blow up ;)
You have, most likely, installed the hook "thread wide" and not "system wide", which means that the key translation will occur only for the thread installing the hook.
In order to install it "system wide" you will need two pieces: one dll having the "hook provider" and an exe managing it.
Here is a good tutorial
http://www.codeproject.com/KB/system/hooksys.aspx
and here an example:
http://www.codeguru.com/cpp/com-tech/shell/article.php/c4509/
But:
1. Installing system wide hooks can seriously screw up you system (make sure that you forward the keys that you don't translate).
2. Please... don't create another keylogger