Board index » cppbuilder » Subclassing WndProc and CM_RECREATEWND

Subclassing WndProc and CM_RECREATEWND


2006-04-15 06:21:10 AM
cppbuilder38
I'm subclassing the WndProc for a TListView's Header:
HWND hHandle;
FARPROC NewHeaderWndProc;
FARPROC OldHeaderWndProc;
void __fastcall HeaderWndProc( TMessage &Message );
hHandle = reinterpret_cast<HWND>( ::SendMessage(ListView1->Handle, LVM_GETHEADER, 0, 0) );
NewHeaderWndProc = reinterpret_cast<FARPROC>( MakeObjectInstance(HeaderWndProc) );
OldHeaderWndProc = reinterpret_cast<FARPROC>( ::SetWindowLong(hHandle, GWL_WNDPROC, reinterpret_cast<LONG>(NewHeaderWndProc)) );
My questions are about how to handle if and when the HWND is
ever recreated.
Do I simply just make another call to SetWindowLong as I did
above?
Will the Header be sent CM_RECREATEWND as well as the ListView?
~ JD
 
 

Re:Subclassing WndProc and CM_RECREATEWND

"JD" < XXXX@XXXXX.COM >wrote in message
Quote
Will the Header be sent CM_RECREATEWND as well as the ListView?
No, because the header is not mananged by the VCL at all. It is managed
only by the Win32 API directly, as an internal child of the ListView window.
If the ListView HWND is created, it will automatically free the header
window as well. You have to catch the CM_RECREATEWND message by subclassing
the TListView itself instead.
Gambit
 

Re:Subclassing WndProc and CM_RECREATEWND

"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >wrote:
Quote

[...] If the ListView HWND is created, it will automatically
free the header window as well. You have to catch the
CM_RECREATEWND message by subclassing the TListView itself
instead.
I can't get the logic down. At what point do I need to restore
the Header's WndProc and then when can I subclass it again?
~ JD
 

{smallsort}

Re:Subclassing WndProc and CM_RECREATEWND

"JD" < XXXX@XXXXX.COM >wrote:
Quote

I can't get the logic down. [...]
This is what I did. Are there any problems with it?
//-------------------------------------------------------------
void __fastcall TForm1::HeaderWndProc( TMessage &Message )
{
if( Message.Msg == WM_DESTROY )
{
::SetWindowLong( hHeader, GWL_WNDPROC, reinterpret_cast<LONG>(OldHeaderWndProc) );
FreeObjectInstance( NewHeaderWndProc );
}
Message.Result = ::CallWindowProc( OldHeaderWndProc, hHeader, Message.Msg, Message.WParam, Message.LParam );
}
//-------------------------------------------------------------
void __fastcall TForm1::NewListViewWndProc( TMessage &Message )
{
if( Message.Msg == WM_DESTROY )
{
Destroying = true;
ListView1->WindowProc = OldListViewWndProc;
}
else if( Message.Msg == CM_RECREATEWND )
{
hHeader = reinterpret_cast<HWND>( ::SendMessage(ListView1->Handle, LVM_GETHEADER, 0, 0) );
NewHeaderWndProc = reinterpret_cast<FARPROC>( MakeObjectInstance(HeaderWndProc) );
OldHeaderWndProc = reinterpret_cast<FARPROC>( ::SetWindowLong(hHeader, GWL_WNDPROC, reinterpret_cast<LONG>(NewHeaderWndProc)) );
}
OldListViewWndProc( Message );
}
//-------------------------------------------------------------
~ JD
 

Re:Subclassing WndProc and CM_RECREATEWND

"JD" < XXXX@XXXXX.COM >wrote in message
Quote
This is what I did. Are there any problems with it?
Yes. Your handling of the CM_RECREATEWND message is wrong. You need to
pass the CM_RECREATEWND message to the default handler *before* you can
subclass the new header HWND. It is during TListView's default handling of
that message that the current HWNDs get destroyed and reallocated. You are
calling the default handler *after* subclassing the header HWND, so you are
going to subclass the HWND that you already subclassed (and thus lose your
pointer to the original window procedure) and ignore the new HWND
completely.
Try this instead:
#ifdef STRICT
#define MYWNDPROC WNDPROC
#else
#define MYWNDPROC FARPROC
#endif
class TForm1 : public TForm
{
private:
HWND hHeader;
LONG HeaderWndProcPtr;
MYWNDPROC OldHeaderWndProc;
TWndMethod OldListViewWndProc;
void __fastcall HeaderWndProc(TMessage &Message);
void __fastcall ListViewWndProc(TMessage &Message);
//...
};
__fastcall TForm1::TForm1(TComponent *Owner)
: TForm(Owner)
{
HeaderWndProcPtr =
reinterpret_cast<LONG>(MakeObjectInstance(HeaderWndProc));
OldListViewWndProc = ListView1->WindowProc;
ListView1->WindowProc = ListViewWndProc;
}
__fastcall TForm1::~TForm1()
{
if( hHeader )
{
::SetWindowLong(hHeader, GWL_WNDPROC,
reinterpret_cast<LONG>(OldHeaderWndProc));
hHeader = NULL;
}
ListView1->WindowProc = OldListViewWndProc;
FreeObjectInstance(reinterpret_cast<void*>(HeaderWndProcPtr));
}
void __fastcall TForm1::HeaderWndProc(TMessage &Message)
{
Message.Result = ::CallWindowProc(OldHeaderWndProc, hHeader,
Message.Msg, Message.WParam, Message.LParam);
if( Message.Msg == WM_DESTROY )
{
::SetWindowLong(hHeader, GWL_WNDPROC,
reinterpret_cast<LONG>(OldHeaderWndProc));
hHeader = NULL;
}
}
void __fastcall TForm1::ListViewWndProc(TMessage &Message)
{
OldListViewWndProc(Message);
if( Message.Msg == CM_RECREATEWND )
{
hHeader = ListView_GetHeader(ListView1->Handle);
if( hHeader )
OldHeaderWndProc =
reinterpret_cast<MYWNDPROC>(::SetWindowLong(hHeader, GWL_WNDPROC,
HeaderWndProcPtr));
}
}
Gambit
 

Re:Subclassing WndProc and CM_RECREATEWND

"Remy Lebeau \(TeamB\)" < XXXX@XXXXX.COM >wrote:
Quote

Your handling of the CM_RECREATEWND message is wrong.
You need to pass the CM_RECREATEWND message to the default
handler *before* you can subclass the new header HWND. [...]
Makes perfect sense now! Thanks.
~ JD