Hi could anyone explain to me what this function is doing, I am currently reading a programming book and am struggling to follow this function.
From what I can gather the function take in a handle to a window (in this case a dialog box), then information is passed in the second param and the third param being a pointer to the actual object.
To give some context I'm trying to populate a combo box with the adapters that a computer has.
void AddItem(HWND hWnd, char *ch, void *pData)
{
WPARAM nI = (WPARAM)((int)(DWORD)SendMessage(hWnd,CB_ADDSTRING,0,(LPARAM)ch));
SendMessage(hWnd,CB_SETITEMDATA, nI, (LPARAM)pData);
}
here is an example call:
for (UINT a=0; a<m_dwNumAdapters; a++)
{
AddItem(m_hADAPTER, m_xAdapterInfo[a].d3dAdapterIdentifier.Description,
&m_xAdapterInfo[a]);
}
Thanks.
I used earlier something like this to add items to combo box, I might be of help.
SendDlgItemMessage(hwnd, IDC_COMBOSTATUS, CB_ADDSTRING, 0, (LPARAM) (LPCTSTR) "Available");
Where hwnd is handle to dialog, IDC_COMBOSTATUS is resource ID, and other is pretty much clear.
Try SendDlgItemMessage function rather SendMessage.
Regards,
Vajda
It adds a string value and associated integer to a combo box.
The aspect you may be missing is that list boxes, combos etc. store an integer value (the same size as a pointer) in a list parallel to the list of text labels. You can put whatever you like in the integer value. For example you could put in a pointer to some struct containing further information.
When you need to respond to the user's selection, you simply read out the selected integer value, cast it to a pointer, and do something with that information.
Of course, many lists and combos don't need any of this (a simple string suffices) and so 0 would typically be passed.
Related
I have an alphabetically sorted combobox in a dialog. This combo contains multiple strings, but some are duplicated with different cases. i.e. we have an 'On' and an 'ON', an 'Off' and an 'OFF'. This may seem redundant but there is a reason, although this is not important right now.
The duplicates obviously appear one after the other in the list, with the capitalized strings first. i.e.:
OFF
Off
ON
On
When the user selects the 'On' (lower case), the correct index is set as CurSel and the correct string is displayed. However, when I click on the arrow of the combobox to drop down the list, it does not highlight the CurSel, but the one previous to it, the capitalized string. See images below.
This is was is selected in the dropdown:
This is what is selected in the combobox when expanding the dropdown.
I have captured the ON_CBN_DROPDOWN message, and checked the cursel value and it is as I expected.
I have also already subclassed this combobox so that I can search for strings in this list in a case-sensitive way, as I know its not implemented normally, so it may be what is causing my issue.
But I don't understand why the string would be overriding the cursel value at this stage? Should the CurSel value not be the one used to select the relevant item?
Any ideas on how I can fix this would be greatly appreciated.
EDIT:
I have tried to capture the CBN_DROPDOWN message by overwriting the OnWndMsg. When this message occurs, I get the currently selected item (which is the correct item) before dropping down the menu. I then drop the menu, and call SetCurSel to what I retrieved before.
BOOL CMyComboBox::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT *pResult)
{
if(message == CBN_DROPDOWN)
{
int nCurSel = GetCurSel();
if(nCurSel != CB_ERR)
{
ShowDropDown();
SetCurSel(nCurSel);
return TRUE;
}
}
return CComboBox::OnWndMsg(message, wParam, lParam, pResult);
}
This kind of works but when I kill focus, or click on the dropdown arrow again to hide the dropdown, the wrong item is displayed in the text box. Is this a valid method, or am I completely off base here?
What message is sent when the drop down is collapsed?
EDIT 2:
I have implemented the case-sensitive combobox from code project and it works great.
Further to my comment. I think you will find that the internal mechanics is using SelectString to set the index when it is a dropdown style.
The side effect is that it may not pick the right entry for you from the list. Therefore, given the nature of the content in your combo, please try this:
int iIndex = m_cbData.FindStringExact(-1, "On");
m_cbData.SetCurSel(iIndex);
Or
int iIndex = m_cbData.FindStringExact(-1, "OFF");
m_cbData.SetCurSel(iIndex);
However, be warned, the document for FindStringExact says the search is not case sensitive. But SelectString (default behaviour) is even worse.
An alternative, which may resolve all of this, is to use SetWindowText and do it that way. This way, it does not matter what is in the listbox component. Eg:
m_cbData.SetWindowText("On");
m_cbData.SetWindowText("ON");
And get the value for the variable by either mapping to a string, or directly using GetWindowText.
UPDATE: Someone has done the work already! Here is a Case Sensitive ComboBox class:
http://www.codeproject.com/Articles/1363/Case-sensitive-ComboBox
I'm not sure how to call Dialog Procedures of certain classes and was looking for some help.
I'm aware for a normal Message Procedure you can do this in the MessageProc:
case WM_CREATE:
{
CREATESTRUCT* cs = (CREATESTRUCT*)_lParam;
pApplication = (CApplication*)cs->lpCreateParams;
return pApplication->MessageProc(_Msg, _wParam, _lParam);
}
which will allow you to create a Message Proc independent to the class.
However due to the fact I don't know exactly how the first two lines work (just the definition that they return the 'this' pointer of the application), I can't work out what to do to get my Dialog Procedures to do a similar thing
case WM_INITDIALOG:
{
//How can I get the pointer to the inspector that
//I want to call the dialog proc on?
return pInspector->DlgProc(_hWndDlg, _Msg, _wParam, _lParam);
}
Any help to both get the pointer to the inspector working and also clarify exactly what the other two lines are doing in WM_CREATE would be appreciated
When a window is created, it receives a WM_CREATE message with a pointer to a CREATESTRUCT structure, and in there is a pointer-sized userdata field (lpCreateParams). This value comes from the lpParam argument passed to the CreateWindowEx() function.
This is the general mechanism which lets you associate your own class or data structure with an instance of a window.
This pointer generally needs to be saved somewhere in order to use it later on. One common way of doing this is to store it in a window property:
case WM_CREATE:
{
CREATESTRUCT* cs = (CREATESTRUCT*)_lParam;
pApplication = (CApplication*)cs->lpCreateParams;
SetProp(hWnd, L"my.property", (HANDLE)pApplication);
}
Then to retrieve the value when handling other messages:
pApplication = (CApplication*)GetProp(hWnd, L"my.property");
Dialogs are not exactly like normal windows, so although a similar mechanism exists, it is implemented differently. When a dialog procedure receives the WM_INITDIALOG message, the lParam value is equivalent to the lpCreateParams value in a WM_CREATE message.
In order to save your pInspector value it would need to have been provided as the dwInitParam value when the dialog was created, but assuming that it was, you can handle this in a similar way:
case WM_INITDIALOG:
{
pInspector = (CInspector*)lParam;
SetProp(hWnd, L"my.property", (HANDLE)pInspector);
}
And to retrieve the value when handling other messages:
pInspector = (CInspector*)GetProp(hWnd, L"my.property");
According to: http://msdn.microsoft.com/en-us/library/windows/desktop/bb762102(v=vs.85).aspx
the SetWindowSubclass prototype is:
BOOL SetWindowSubclass(
__in HWND hWnd,
__in SUBCLASSPROC pfnSubclass,
__in UINT_PTR uIdSubclass,
__in DWORD_PTR dwRefData
);
Ok, I understand hWnd, pfnSubclass, and dwRefData.
What I can not find good information on is, what do I set uIdSubclass to?
MSDN says:
The subclass ID. This ID together with the subclass procedure uniquely identify
a subclass. To remove a subclass, pass the subclass procedure and this value to the
RemoveWindowSubclass function. This value is passed to the subclass procedure in the
uIdSubclass parameter.
Ok, understood, but still, where do I get this ID? Is it something I create or do I get it someplace? If it something I create, what should it look like?
I am doing this in C++ and Win32 API, nothing else.
Thanks.
Karl E. Peterson writes about the purpose of the uIdSubclass parameter in Visual Studio Magazine 07/16/2009 Subclassing the XP Way and provides an example named HookXP
Topics discussed in Karl E. Peterson's article:
Use uIdSubclass to store a this pointer [or HWND].
Use dwRefData to pass optional data, e.g. pointer to create data.
It is important to handle WM_NCDESTROY in pfnSubclass and call
RemoveWindowSubclass() passing uIdSubclass used in call to SetWindowSubclass().
Quotes from Karl E. Peterson's article
Each subclass is identified two pieces of data along with the window's hWnd -- those being a pointer to the new message-handling procedure and a unique ID that's up to you to generate. You may also select to pass one Long value along as an extra parameter to each callback, for whatever purpose you may desire. The thought that immediately struck me was to use an ObjPtr() as the unique ID for each subclass. What object? The one that will be handling the callback!
About the only undocumented caveat I've run into is that you must be sure to unhook your subclass before a window is destroyed. If you use my technique of embedding the handler in a form or class that's destroyed during the Form_Unload method, this should never be a problem. If you really don't want to take any chances, you can insert a simple branch in your handling routine:
Select Case uiMsg
Case WM_THIS
'
Case WM_THAT
'
Case WM_NCDESTROY
Call Unhook ' !!!
End Select
The Explorer Browser Search Sample also has a example of using uIdSubclass to store a pointer to this.
void CExplorerBrowserSearchApp::_OnInitializeDialog()
{
...
// Register the searchbox icon to receive hover and mouseclick events
SetWindowSubclass(GetDlgItem(_hdlg, IDC_SEARCHIMG), s_SearchIconProc, (UINT_PTR)this, 0);
...
}
Raymond Chen's blog "The Old New Thing" article Safer subclassing also mentions the importance handling WM_NCDESTROY in pfnSubclass for the purpose of calling RemoveWindowSubclass().
Quote from Raymond Chen's article
One gotcha that isn’t explained clearly in the documentation is that you must remove your window subclass before the window being subclassed is destroyed. This is typically done either by removing the subclass once your temporary need has passed, or if you are installing a permanent subclass, by inserting a call to RemoveWindowSubclass inside the subclass procedure itself:
case WM_NCDESTROY:
RemoveWindowSubclass(hwnd, thisfunctionname, uIdSubclass);
return DefSubclassProc(...);
As a side note the Microsoft SetWindowSubclass documentation has the caveat of SetWindowSubclass() being limited to a per thread basis.
Warning You cannot use the subclassing helper functions to subclass a window across threads.
I'm coding an MFC application in which i have a dialog box with multiple CListCtrls in report view. I want one of them to be sortable.
So i handled the HDM_ITEMCLICK event, and everything works just fine .. Except that if i click on the headers of another CListCtrl, it does sort the OTHER CListCtrl, which does look kind of dumb.
This is apparently due to the fact that headers have an ID of 0, which make the entry in the message map look like this :
ON_NOTIFY(HDN_ITEMCLICK, 0, &Ccreationprogramme::OnHdnItemclickList5)
But since all the headers have an id of zero, apparently every header of my dialog sends the message.
Is there an easy way around this problem ?
EDIT: Maybe i wasn't clear, but i did check the values inside the NMHDR structure. The HwndFrom pointer is different depending on which header is clicked, which doesn't help me a lot since it's value is obviously different at each runtime. The idFrom value is 0, for the very reasons i explained above, because that's the id of every header. Thanks
EDIT2: The hwnd pointer values do also not correspond to the CListCtrl, probably because it's coming from a different object entirely.
Check the values of the NMHDR structure.
http://msdn.microsoft.com/en-us/library/bb775514%28VS.85%29.aspx
Ok i found a solution, though i find it a bit dirty but it works, so i'll post it for future reference.
You can get the Header through the GetHeaderCtrl member function of CListCtrl. You can then get it's handler thru m_hWnd. So all you got to do is to test if that handler is the same as the one in the NMHDR structure, so the code looks like this :
void Ccreationprogramme::OnHdnItemclickList5(NMHDR *pNMHDR, LRESULT *pResult)
{
if (pNMHDR->hwndFrom == LC_gen_schedules.GetHeaderCtrl()->mhWnd)
{
// Code goes here
}
*pResult = 0;
}
Thanks all for the help
The LPARAM passed to your message handler is actually a pointer to an NMHEADER structure, which contains an NMHDR structure, which in turn contains the HWND and control ID of the control which sent the message. You may be able to compare that to your list controls' HWNDs to determine which window's header control was clicked.
Alternatively, you could derive a class from CListCtrl and reflect the HDN_ITEMCLICK messages back to the list control. That way, each list control object handles its own header's notifications.
I'm writing an (unmanaged) C++ class to wrap the Windows PropertySheet. Essentially, something like this:
class PropSheet {
PROPSHEETHEADER d_header;
public:
PropSheet(/* parameters */);
INT_PTR show();
private:
static int CALLBACK *propSheetProc(HWND hwnd, UINT msg, LPARAM lParam);
};
The constructor just initializes the d_header member:
PropSheet::PropSheet(/* parameters */) {
d_header.dwSize = sizeof(PROPSHEETHEADER);
d_header.dwFlags = PSH_USECALLBACK;
// ...
d_header.pfnCallback = &propSheetProc;
// ...
}
After which I can show it, modally, with:
INT_PTR PropSheet::show() {
return PropertySheet(&d_header);
}
Now the problem is, because the callback is static, that it cannot access the wrapper class. If this were a normal window, with a WindowProc instead of a PropSheetProc, I could attach some extra data to the window using cbWndExtra in WNDCLASS, in which I could store a pointer back to the wrapper, like in this article. But property sheets do not offer this functionality.
Furthermore, because the property sheet is shown modally, I can execute no code between the creation and destruction of the actual window, except when that code is executed through the callback or one of the sheets's window procedures.
The best solution I've come up with so far is to, right before showing the property sheet, store a pointer to the wrapper class inside a global variable. But this assumes that I'll only be showing one property sheet at a time, and is quite ugly anyway.
Does anyone have a better idea how to work around this?
As you are showing the property sheet modally, you should be able to use the parent window (i.e. its handle) of the property sheet to map to an instance, using ::GetParent() on the hwndDlg parameter of PropSheetProc().
Awesome, yet another Win32 API that uses callbacks without a user-defined context parameter. It is not the only one, alas. e.g. CreateWindow is bad (it gives you user-defined context, but that context isn't available for the first few window messages), SetWindowsHookEx is even worse (no context at all).
The only "solution" that is general-purpose and effective is to emit a small piece of executable code with a 'this' pointer hardcoded. Something like this: http://episteme.arstechnica.com/eve/forums/a/tpc/f/6330927813/m/848000817831?r=848000817831#848000817831
It's horrible.
The PROPSHEETPAGE structure has an lParam field available for callbacks. In your PROPSHEETHEADER, you can include the PSH_PROPSHEETPAGE flag to pass an array of PROPSHEETPAGE items describing your pages, or omit the flag to pass an array of preallocated HPROPSHEETPAGE handles instead (which means using CreatePropertySheetPage(), and thus using PROPSHEETPAGE anyway).
You've already admitted "I can execute no code between the creation and destruction of the actual window". It seems that a global variable wouldn't be a terrible hack.
I've found another option: using SetProp to add a property that stores the pointer to the wrapper. Only requires the global variable once, to be able call SetProp from the property sheet callback.