Getting app object pointer in Dialog Message Proc - c++

We can use this as the last argument to CreateWindow and get a pointer to the app object in the WndProc like this:
if(message == WM_CREATE)
{
CREATESTRUCT* cs = (CREATESTRUCT*)lParam;
pApp = (DemoApp*)cs->lpCreateParams;
return 0;
}
What is the best way to access this pointer in a Dialog Message Proc? Is the solution to make a global pointer?

You get additional initialization data with WM_INITDIALOG, see WM_INITDIALOG message :
lParam
Additional initialization data. This data is passed to the system
as the lParam parameter in a call to the CreateDialogIndirectParam,
CreateDialogParam, DialogBoxIndirectParam, or DialogBoxParam function
used to create the dialog box. For property sheets, this parameter is
a pointer to the PROPSHEETPAGE structure used to create the page. This
parameter is zero if any other dialog box creation function is used.
That is, you can pass lParam as an argument with CreateDialogParam and the dialog proc will receive it with WM_INITDIALOG message.

Related

Best practice for accessing a specific child window from the parent's wndproc [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
I keep running into scenarios where, from a parent window's wndproc function, I must modify a child window (ex: clicking a child button adds to a sibling combobox; sending a message to the parent which it then forwards to the appropriate child). As far as I can tell, this means somehow obtaining the child's HWND reference and calling the child's wndproc with that reference.
In such a situation, I have the child's child-window-identifier, and I have the parent window's HWND reference, but nothing else.
From various tutorials, it looks like my only options are to either save the child's HWND reference as a global so I can access it from the parent's wndproc function, or call EnumChildWindows() and have a switch statement at the beginning which returns immediately if the current child is not the target child: The former doesn't seem to scale well because I'm saving a global for every child I will ever need to speak too; The latter seems to be intended for broadcasting to all children, instead of speaking to a single, specific child.
I feel like there's gotta be a function which takes the parent's HWND and the child's child-windows-identifier and returns the child's HWND, but no matter how I google, no such function appears to exist.
In short, I know a couple of ways to solve my problem, but I got a gut feeling I'm doing something wrong. What is the best-practice way to perform actions on a child window given the HWND reference for its parent?
Most notifications based on WM_NOTIFY or WM_COMMAND, which includes BN_CLICKED, carry the child's HWND as well as the child's ID, eg:
WM_NOTIFY
wParam
The identifier of the common control sending the message. This identifier is not guaranteed to be unique. An application should use the hwndFrom or idFrom member of the NMHDR structure (passed as the lParam parameter) to identify the control.
lParam
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.
WM_COMMAND
wParam
The LOWORD contains the button's control identifier. The HIWORD specifies the notification code.
lParam
A handle to the button.
So, in a parent window's message procedure, it knows exactly which child is sending a notification to it, and can access that child directly.
In the case where you want a parent's message handler to act on another child (ie, to add a string to a ComboBox from within a Button's click handler), should already have that other child's HWND or ID from when you initially created the child.
In the former case, you can use the saved HWND as-is, eg:
HWND hwndCB;
HWND hwndBtn;
...
case WM_CREATE:
hwndCB = CreateWindow(WC_COMBOBOX, ..., hWnd, ...);
hwndBtn = CreateWindow(WC_BUTTON, ..., hWnd, ...);
break;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) &&
HWND(lParam) == hwndBtn)
{
SendMessage(hwndCB, CB_ADDSTRING, 0, ...);
}
break;
In the latter case, you can use GetDlgItem() to get a child's HWND given its ID within the parent window, eg:
case WM_CREATE:
CreateWindow(WC_COMBOBOX, ..., hWnd, (MENU)ID_MY_COMBO, ...);
CreateWindow(WC_BUTTON, ..., hWnd, (HMENU)ID_MY_BTN, ...);
break;
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) &&
LOWORD(wParam) == ID_MY_BTN)
{
HWND hwndCB = GetDlgItem(hWnd, ID_MY_COMBO);
SendMessage(hwndCB, CB_ADDSTRING, 0, ...);
}
break;

CreateDialog in BHO always fails with error 1813 (resource not found)

I'm working on a BHO written a long time ago in C++, without the use of any of the VS wizards. As a result, this project deviates from the COM conventions and the boilerplate for a COM product. I worked with COM long ago, but never really did any Windows GUI/dialog stuff...
I'm trying to add a dialog box to allow the user to set the values of some new settings:
// serverDialog will be NULL
HWND serverDialog = CreateDialog(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_PROPPAGE_SETTINGS), NULL, DialogProc);
id (!serverDialog)
{
int error = GetLastError(); //1813
...
}
....
1813 means that the resource cannot be found. The IDD used there is in resource.h, which I manually included where needed.
DialogProc is defined as:
INT_PTR CALLBACK DialogProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
return FALSE;
}
Which I know I will have to change later if I want the dialog to actually process messages, but I haven't gotten that far yet. The 1813 error suggests failure before the dialog is even created as does the NULL dialog handle returned.
To add the dialog I used the Add Resource wizard and added a small property page.
I've tried to follow advice here, but to no avail.
Thanks!
You are passing GetModuleHandle(NULL) as the instance of the module that contains the resource. But GetModuleHandle(NULL) defines the executable file module. You need to pass the instance of the module containing your code. This question covers that topic: How do I get the HMODULE for the currently executing code?
You probably ought to pass a window handle to the hWndParent parameter so that the dialog is owned.

Nested Dialog Procedures

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");

Retrieve WTL object from handle

I had to rewrite a custom file dialog (derived from MFC's CFileDialog) to WTL's CFileDialog. I have a bit of a problem to retrieve data when I don't have access to the dialog object itself. Imagine the following.
I have a member in the class
static WNDPROC m_wndProc;
I initialize it in the following static member fnct.
void CMyFileDialog::OnInitDone(LPOFNOTIFY lpon)
{
m_wndProc = (WNDPROC)::SetWindowLong(thisHWND, GWL_WNDPROC, reinterpret_cast<long>
(&CMyFileDialog::WndProcSelect));
}
The handle comes into the callback method with no problem and I can "connect" to it with CWindow
LRESULT CALLBACK CMyFileDialog::WndProcSelect(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// ...
CWindow callerWnd(hwnd);
}
And here, I don't know the real methodology to convert the CWindow to my CMyFileDialog. As I think, this CWindow class is just connected somehow to the handle itself, but does not the same object as it was created before. So for example, if I have a CString or other members in my CMyFileDialog, it won't access its state, because it was created in another object.
I think you are doing something wrong here. You have access to the message map without having to modify the WndProc (that is something that the CFileDialogImpl will have already done).
See for example http://www.codeproject.com/Articles/12999/WTL-for-MFC-Programmers-Part-IX-GDI-Classes-Common#usingcfiledialog, where they simply
BEGIN_MSG_MAP(CMyFileDialog)
CHAIN_MSG_MAP(CFileDialogImpl<CMyFileDialog>)
END_MSG_MAP()
You could always use SetWindowLongPtr with your "this" pointer, then it would be fairly easy to extract the pointer to your CMyFileDialog.

How to access Mainfrm member variable status without using ((CMainFrame*) AfxGetMainWnd ())->...?

I have a MDI appliation in MFC to modify. I want to check the value of a flag which is a member variable of MainFrm from a lower level class. But I don't want to access it using
'((CMainFrame*) AfxGetMainWnd ())->IsFlagOn()' kind of function because for that i have to give the mainfrm.h in a lower level class. I somehow feel this will create some circular reference later, after reading this Why are circular references considered harmful?
what are the other ways to get the flag value from mainfrm class.Please guide !
note: here class hierarchy is mainfrm->CTestExplorerFrame->CTestExplorerView->CTestExplorerTreeCtrl
I want to check from the lowest level about a flag that is only accessed by mainfrm
AfxGetMainWnd() returns a CWnd* that you can use to communicate with the mainframe via the Windows message system. Define a custom message and send this message to the CWnd*
#define UWM_MYREQUEST (WM_APP + 2)
int datatoget;
CWnd* pMainframe = AfxGetMainWnd();
pMainframe->SendMessage(UWM_MYREQUEST, (WPARAM)&datatoget, 0);
The mainframe needs code like this to receive and handle the custom message:
ON_MESSAGE(UWM_MYREQUEST, OnMyRequest)
LRESULT CMainFrame::OnMyRequest(WPARAM wparam, LPARAM lparam)
{
int* ptoget = (int*)wparam;
*ptoget = m_datarequested;
return 0;
}
I would declare an (pure virtual) interface class where you have a pure virtual call to get the value of the flag you are interested in at CTestExplorerTreeCtrl. Then the MainFrame implements this interface class and passes a pointer to CTestExplorerTreeCtrl. This way you can avoid any references to the MainFrame class.