I have a main window with a treeview control. I need to track certain changes of a checked item, so I have decided to make a static HTREEITEM variable to store that handle.
I do not know if setting variable to NULL in my WM_DESTROY handler will suffice, or do I need to do something else?
Here are the code snippets that illustrate my dilemma:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HTREEITEM LastCheckedItem;
// Other parts of the code that work well
case WM_DESTROY:
{
LastCheckedItem = NULL; // or should I do something else ?
// ...
Thank you.
Best regards.
The tree view should handle cleanup of the individual items when it receives the WM_DESTROY message, and you probably don't need to set LastCheckedItem to NULL unless you need to check its value later on.
You don't need to do any tidy up. When you destroy the tree view it will destroy all of its items.
Related
If I declare and initialize object within main window callback function like so:
LRESULT CALLBACK WndProc
(
HWND hWnd // handle to window of this process
, UINT msg // message constant
, WPARAM wParam // holder of message parameters
, LPARAM lParam // holder of message parameters
)
{
switch (msg)
{
case WM_CREATE:
{
std::unique_ptr<Foo> foo = std::unique_ptr<Foo>(new Foo);
}
break;
}
}
Will the object foo stay initialized after the break of WM_CREATE message? And if not, where is the best place to declare it so that it's scope is not limited by the case scope?
The foo object is creating controls in the main window by the way so I think the requirement is to let it live till the end of runtime.
I suspect it will not survive. I am thinking about declaring those as global variable (but I don't like that option for obvious reasons) or as static objects within the callback function (but outside the switch). But there might be better options I don't see, so I am seeking your advice.
Thank you for help!
Here's a workup of RbMm's suggestion, which I use myself in my own code. A smart pointer has little to offer here, so there's really not much point in using one. Please note that I have used C-style casts for brevity, but you can use reinterpret_cast if you prefer.
class MyWindowData { ... };
MyWindowData *window_data = new MyWindowData;
HWND hWnd = CreateWindow (... ... ..., (LPARAM) window_data);
LRESULT CALLBACK WndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCCREATE:
CREATESTRUCT *cs = (CREATESTRUCT *) lParam;
SetWindowLongPtr (hWnd, GWLP_USERDATA, cs->lpCreateParams);
break;
case NC_DESTROY:
MyWindowData *window_data = (MyWindowData *) GetWindowLongPtr (hWnd, GWLP_USERDATA);
SetWindowLongPtr (hWnd, GWLP_USERDATA, 0);
delete window_data;
break;
}
// return something here;
}
You can then do:
MyWindowData *window_data = (MyWindowData *) GetWindowLongPtr (hWnd, GWLP_USERDATA);
to retrieve MyWindowData any time you need it, but check for zero being returned in case WM_NCCREATE is not the first message received by your WNDPROC as per the comments.
void IronmanMap::CommandLine()
{
mode = 1;
InvalidateRect(storedhwnd, NULL, 1);
HWND hmmitem;
hmmitem = GetDlgItem(storedhwnd, ID_EDITBOX1);
backupproc = (WNDPROC)SetWindowLongPtr(hmmitem, GWLP_WNDPROC, (LONG)EditProc);
SetFocus(hmmitem);
}
When this function gets to the closing brace it says there is an unhandled exception.
That line of code should be:
backupproc = (WNDPROC)SetWindowLongPtr(hmmitem, GWLP_WNDPROC,(LONG_PTR)EditProc);
But Raymond is so fast! :)
Edit: Jonathon Potter comments above that there's a better way to do this (and it's Microsoft's recommended way). So please ignore the above and subclass your window like this:
SetWindowSubclass (hmmitem, EditProc, EditProcSubclassID, EditProcReferenceData);
Where:
EditProcSubclassID is a unique ID of your choice (just make one up).
EditProcReferenceData will be passed to EditProc whenever it is called. You can use this for anything you like (or just pass 0).
You then implement EditProc like this (note the two extra parameters on the end there):
LRESULT CALLBACK EditProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
...
}
So what happened to backupproc? Well, you don't need that anymore. Instead, to call the next WndProc in the chain, call DefSubclassProc() (or not, if you want to swallow the message) at the end of EditProc.
Finally, if you want to detach EditProc from the window, call RemoveWindowSubclass().
The main advantage of this approach is that it works correctly if somebody else subclasses the window after you do (or, indeed, before, if they want to remove their WndProc before you do). That's why you should use it.
Read the SetWindowSubclass documentation on MSDN, and see Raymond's blog on Safer subclassing.
I think I've fallen in the same trap as many before me where I try to impose a nice OO methodology on win32 API programming. No MFC, no AFX, I'm not even using VC++, I'm using C::B with gcc.
I think what I'm trying to do is impossible, but since MFC exists (although I'm not using it) there must be some way.
I've created a class to contain several window controls. It implements handlers for WM_CREATE and WM_COMMAND, and keeps track of all the associated data around my small group of controls (ID codes and HWNDs).
It works great for buttons, static controls, even light GDI methods, but it all breaks down when I try to subclass an edit control.
Really, I just want to capture the "enter" key, but as anybody who's been down that road before will attest, when an edit control has focus, the parent window doesn't receive WM_KEYDOWN or WM_COMMAND, we are left to implement our own proc. Super lame.
OK, so subclassing an edit control is fine, if the editProc is global or static. I know that is because SetWindowLongPtr needs a function address, and that concept is nebulous for a member function.
So the object of my class is declared as "static" inside the parent WndProc. But the function is not "static" because then I wouldn't have access to non-static data members (completely defeating the purpose of this exercise). I'm hoping that because the objest is itself static, I should be able to properly define the address of one of its member functions.
Readers that have tried this before will either have given up and used MFC or something else, or perhaps have found a clever work-around.
I'll let this sample code do the rest of the talking: (simplified - will not compile as such)
/**** myprogram.c ****/
#include "MyControlGroup.h"
int winMain(){ // etc... }
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// object is static becuse it only needs to be initialized once
static MyControlGroup myControl;
if (msg == WM_CREATE)
myControl.onWMCreate(hWnd);
else if (msg == WM_COMMAND)
myControl.onWMCommand( wParam, lParam );
else if (msg == WM_DESTROY)
PostQuitMessage(0);
return DefWindowProcW(l_hWnd, l_msg, l_wParam, l_lParam);
}
The header file for my class:
/**** MyControlGroup.h ****/
class MyControlGroup
{
private:
HWND m_hWndParent;
HWND m_hWndEditBox;
int m_editBoxID;
public:
MyControlGroup();
void onWMCreate(HWND);
void onWMCommand(WPARAM, LPARAM);
// want to find a way to pass the address of this function to SetWindowLongPtr
LRESULT myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
};
...and the implementation:
/**** MyControlGroup.cpp ****/
static int staticID = 1;
MyControlGroup::MyControlGroup()
{
m_editBoxID = staticID++;
}
void MyControlGroup::onWMCreate(HWND hWnd)
{
// My control group has buttons, static controls, and other stuff which are created here with CreateWindowW. It also has an edit control:
m_hWndEditBox = CreateWindowW(L"EDIT", L"initial text", WS_CHILD | WS_VISIBLE | WS_BORDER, 10, 10, 150, 20, hWnd, (HMENU)m_editBoxID, NULL, NULL);
/*
To subclass the edit control, I need a pointer to my customized proc. That means I
need a pointer-to-member-function, but SetWindowLongPtr needs a pointer to global or
static function (__stdcall or CALLBACK, but not __thiscall).
*/
// I'd like to do something like this, adapted from a great write-up at
// http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
LERSULT (MyControlGroup::*myEditProcPtr)(HWND, UINT, WPARAM, LPARAM);
myEditProcPtr = &MyControlGroup::myEditProc;
// Up to now it compiles ok, but then when I try to pass it to SetWindowLongPtr, I get
// an "invalid cast" error. Any ideas?
SetWindowLongPtr(m_hWndEditBox, GWLP_WNDPROC, (LPARAM)myEditProcPtr);
}
void MyControlGroup::onWMCommand(WPARAM wParam, LPARAM lParam){ /* process parent window messages. Editboxes don't generate WM_COMMAND or WM_KEYDOWN in the parent :''( */}
LRESULT MyControlGroup::myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// process messages like IDOK, WM_KEYDOWN and so on in the edit control
}
Even once I get this done, I'll still need to figure out a way to pass the address of the parent WndProc to myEditProc for the return value, but until I get past this there is no point in worrying about that.
Thanks in advance for reading!
myEditProc needs to be a static function.
Once you've done that you can pass the address of the function directly without going through the intermediate variable:
static LRESULT myEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
...
SetWindowLongPtr(m_hWndEditBox, GWLP_WNDPROC, (LPARAM)myEditProc);
To access your class data from the static function, you can save it in the userdata field of the edit control, e.g.:
// before sub-classing the control
SetWindowLongPtr(m_hWndEditBox, GWLP_USERDATA, (LPARAM)this);
// in the sub-class procedure
MyControlGroup* pThis = (MyControlGroup*)GetWindowLongPtr(m_hWndEditBox, GWLP_USERDATA);
But as #K-ballo suggested, SetWindowSubclass is definitely the way to do this unless you want compatibility with pre-XP. It handles the sub-classing procedure for you automatically, lets you associate a userdata pointer (e.g. this) that is automatically passed to the sub-class procedure, and safely handles removing the sub-class at the end.
I am trying to handle wm_mousewheel for my application.
Code:
BEGIN_MSG_MAP(DxWindow)
MESSAGE_HANDLER(WM_MOUSEWHEEL, KeyHandler)
END_MSG_MAP()
.
.
.
LRESULT DxWindow::KeyHandler( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandled )
{
if(uMsg==wm_mousewheel)
{
//Perform task.
}
return 0;
}
But this code doesn't work.KeyHandler doesn't receive wm_mousewheel message.
I am testing this application on vista.
If my approach is wrong how to handle wm_mousewheel properly?
Do vista is responsible for failure in handling wm_mousewheel message?
From the doc:
The WM_MOUSEWHEEL message is sent to the focus window when the mouse wheel is rotated. The DefWindowProc function propagates the message to the window's parent. There should be no internal forwarding of the message, since DefWindowProc propagates it up the parent chain until it finds a window that processes it.
Change your test to if(uMsg == WM_MOUSEWHEEL).
Check that your window or one of it's children has focus.
If this is related to your previous wtl-child-window-event-handling question, I edited my answer to not forward WM_MOUSEWHEEL.
Well, first, you don't have to somehow check uMsg in message handler, because in this situation every message handler is bound to one concrete message.
Second, these atl macroses usually mean to write something like CHAIN_MSG_MAP(CMyBaseClass)
at the end of your map.
Anyway, what you've posted here looks ok, except this part:
if(uMsg==wm_mousewheel)
{
//Perform task.
}
Try erasing it, adding a breakpoint to the handler and debugging. Also you could try adding another neutral message handler (such as WM_CLICK) and tracing it's behavior.
This is the example from MSDN, and the code block that you've posted actually follows it.
class CMyWindow : ...
{
public:
...
BEGIN_MSG_MAP(CMyWindow)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
CHAIN_MSG_MAP(CMyBaseWindow)
END_MSG_MAP()
LRESULT OnPaint(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{ ... }
LRESULT OnSetFocus(UINT uMsg, WPARAM wParam,
LPARAM lParam, BOOL& bHandled)
{ ... }
};
I'm writing a win32 wrapper classes, mainly to learn more about win32 programming.
To get around the problem of c-style callbacks, the following method stores/retrieves the pointer using SetWindowLong/GetWindowLong and passes it to the actual winproc.
LRESULT CALLBACK WinClass::WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// On window creation, WindowProc receives lParam as a LPCREATESTRUCT
// Store *this* pointer as long in GWL_USERDATA
if (msg == WM_NCCREATE)
::SetWindowLong(hwnd, GWL_USERDATA, reinterpret_cast<long>(reinterpret_cast<LPCREATESTRUCT>(lParam)->lpCreateParams));
// Retrieve the pointer
WinClass *wnd = reinterpret_cast<WinClass*>(::GetWindowLongPtr(hwnd, GWL_USERDATA));
// Call the actual winproc function
if (wnd)
return wnd->WndProc(hwnd, msg, wParam, lParam);
// Default to DefWindowProc message handler function
return ::DefWindowProc(hwnd, msg, wParam, lParam);
}
Winclass is the class wrapping the main window created by CreateWindowEx. The same WindowProc function is part of the MDlgClass wrapping the modal dialog. I'm calling the dialog like this
DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(id), hwnd, DialogProc);
If I pass NULL as the hWndParent, the dialog works okay as a modeless dialog but if I pass hwnd, the handle to the main window as hWndParent the dialog works correctly as a modal dialog. However when i close the dialog it doesnt pass control back to the main parent window? Debugging in Visual Studio shows its hanging in the message pump in WinMain.
I thought of using a hashmap to map the pointers but I'd rather do it using GetWindowLong etc. Is this possible? I've tried storing the dialog pointer in DWL_USER but it doesnt help.
Any help would be appreciated, I'm still getting my head around Win32.
EDIT: I'm destroying the dialog using EndDialog
EDIT: I'm storing the pointer in the GWL_USERDATA region of the main window, which is not used by windows and I'm only modifying it in WinClass::WindowProc when the window is first created. If I don't instantiate a dialog class, I know the pointer is being accessed correctly since the application responds to menu commands processed via WindowProc and WM_COMMAND.
You can't use a WindowProc as a DialogProc. Window Procedures call DefWindowProc when they don't handle a message and return a meaningful result when they do.
Dialog Procedures return FALSE when they don't process a message, return TRUE when they DO, (except when they handle WM_INITDIALOG) and, if they have a meaningful result that they need to return from the outer window procedure, that is placed in DWL_MSGRESULT.
When you call the modal dialog box function, DialogBox, it enters a message pumping loop - which does dispatch messages to all windows in the thread so all windows continue to paint and process input - when the dialog is closed (using EndDialog), the DialogBox procedure should return.
When making a class to wrap dialog boxes, the usual method is to pass the 'this' pointer to One of the DialogBoxParam functions - which can be directly extracted from the WM_INITDIALOG message.
How are you closing the window? Are you using DestroyWindow? While the child window is live, the parent window will be disabled.
You're saving a pointer to lpCreateParams. Is it possible that the associated memory block is being freed or otherwise destroyed?
//static method
BOOL CALLBACK WinClass::DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Store *this* pointer as long in GWL_USERDATA
if (msg == WM_INITDIALOG)
{
::SetWindowLongPtr(hwnd, DWLP_USER, reinterpret_cast(lParam));
m_hWnd = hWnd; // I assume you really don't want to keep passing the hwnd around
}
WinClass* wnd = reinterpret_cast(::GetWindowLongPtr(hwnd, GWL_USERDATA));
if (wnd)
return wnd->DlgProcImpl(umsg, wParam, lParam);
return FALSE;
}
BOOL WinClass::DlgProcImpl(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
// your code goes here
return FALSE;
}
return FALSE;
}
INT_PTR WinClass:DoModalDialog(HINSTANCE hInst, HWND hwndParent, LPCTSTR template)
{
return ::DialogBoxParam(hInst, template, hwndParent, WinClass::DlgProc, this);
}