I'm writing a plug-in for another application. In my plug-in, I can create a settings window, when handed a parent window HWND.
What I do is create an "empty" window (WS_CHILD style) to sit inside the parent HWND, and then create my controls (a ListView and a Button) inside this empty window. So far so good; this works.
However, I can't get mouse clicking to dispatch to the controls. Clicking the windows does nothing. Keyboard input works; I can keyboard navigate and activate the button with spacebar (and scroll the list view); I just can't click.
So, what's causing the mouse messages to not get to the children? The sad thing is that I recall having had this problem a few years back, but I'm drawing a blank on what the cause was at the time. Googling doesn't work, because all the words I can think of are too common.
Here's some code; the host is setting up the parent window:
t.style = WS_POPUPWINDOW|WS_DLGFRAME|DS_MODALFRAME|DS_CENTER;
t.cx = 100;
t.cy = 100;
DialogBoxIndirectParam (GetModuleHandle (0), &t, 0, (DLGPROC)EditorProc, (LPARAM)plugin);
INT_PTR CALLBACK EditorProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_INITDIALOG :
{
SetWindowText (hwnd, "The Title");
SetTimer (hwnd, 1, 20, 0);
if(plugin)
{
plugin->dispatcheropen(hwnd);
ERect* eRect = 0;
effect->getRect(&eRect);
if(eRect)
{
int width = eRect->right - eRect->left;
int height = eRect->bottom - eRect->top;
if(width < 100)
width = 100;
if(height < 100)
height = 100;
RECT wRect;
SetRect (&wRect, 0, 0, width, height);
AdjustWindowRectEx (&wRect, GetWindowLong (hwnd, GWL_STYLE), FALSE, GetWindowLong (hwnd, GWL_EXSTYLE));
width = wRect.right - wRect.left;
height = wRect.bottom - wRect.top;
SetWindowPos (hwnd, HWND_TOP, 0, 0, width, height, SWP_NOMOVE);
}
}
}
break;
// removed CLOSE and TIMER handlers...
}
return 0;
}
Here is how my plug-in handles the open() call:
// this is slightly paraphrased, as there's really a wrapper class involved
bool Editor::open(void *ptr)
{
PluginBase::open(ptr);
// ptr is a HWND
window_ = ::CreateWindowEx(
WS_EX_LEFT | WS_EX_CONTROLPARENT,
myClass_,
"Editor",
WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN,
0,
0,
500,
300,
(HWND)ptr,
0,
hInstance,
someWrapper_);
// listView styles are WS_VISIBLE | WS_CHILD | LVS_REPORT | LVS_NOSORTHEADER |
// LVS_SHOWSELALWAYS | LVS_SINGLESEL | WS_TABSTOP;
// listView exStyles are WS_EX_LEFT | WS_EX_ACCEPTFILES;
listView_ = ... wrapper function ...
listView_->enableExtendedStyles(LVS_EX_FULLROWSELECT | LVS_EX_FLATSB |
LVS_EX_TWOCLICKACTIVATE | LVS_EX_DOUBLEBUFFER | LVS_EX_GRIDLINES);
listView_->addColumn("A", 40); // wrappers for ListView_InsertColumn()
listView_->addColumn("B", 40);
listView_->addColumn("C", 40);
listView_->addColumn("D", 40);
listView_->addColumn("File", 400);
for (int i = 0; i < 128; ++i) {
listView_->addRow(i); // wrapper for ListView_InsertRow()
}
button_ = ::CreateWindowEx(
WS_EX_LEFT,
WC_BUTTON,
"Push Me!",
WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON | BS_CENTER | WS_TABSTOP,
10,
270,
100,
25,
window_,
0,
hInstance,
0);
HCURSOR hc = (HCURSOR)::LoadImage(0, (LPCTSTR)OCR_NORMAL, IMAGE_CURSOR, 0, 0, LR_SHARED);
::SetCursor(hc);
return true;
}
Window class registration for the top window is:
// registering class on load-up
INITCOMMONCONTROLSEX iccex;
memset(&iccex, 0, sizeof(iccex));
iccex.dwSize = sizeof(iccex);
iccex.dwICC = ICC_BAR_CLASSES | ICC_LINK_CLASS | ICC_STANDARD_CLASSES |
ICC_USEREX_CLASSES | ICC_LISTVIEW_CLASSES;
::InitCommonControlsEx(&iccex);
WNDCLASSEX wcex;
memset(&wcex, 0, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_DROPSHADOW | CS_OWNDC;
wcex.lpfnWndProc = &Win32Window::wndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = (HINSTANCE)hInstance;
wcex.hIcon = 0;
wcex.hCursor = (HCURSOR)::LoadImage(0, (LPCTSTR)OCR_NORMAL, IMAGE_CURSOR, 0, 0, LR_SHARED);
wcex.hbrBackground = (HBRUSH)::GetStockObject(COLOR_BACKGROUND+1);
wcex.lpszMenuName = 0;
wcex.lpszClassName = "jwSP-class";
wcex.hIconSm = 0;
myClass_ = ::RegisterClassEx(&wcex);
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LONG_PTR lp = ::GetWindowLongPtr(hWnd, GWL_USERDATA);
if (!lp && msg == WM_NCCREATE) {
lp = reinterpret_cast<LONG_PTR>(((CREATESTRUCT const *)lParam)->lpCreateParams);
}
Win32Window * wp = reinterpret_cast<Win32Window *>(lp);
LRESULT ret = 0;
if (wp != NULL) {
if (msg == WM_NCCREATE) {
wp->window_->setWindow(wp);
}
bool x = wp->window_->wndProc(msg, wParam, lParam, &ret);
if (msg == WM_DESTROY) {
wp->destroyed_ = true;
if (!wp->deleted_) {
delete wp;
}
}
// Warning! Window may have been deleted at this point!
if (x) {
// the wrapper wndProc just returns false, so this path is never taken
// (confirmed with a breakpoint)
return ret;
}
}
// Warning! Window may have been deleted at this point!
return ::DefWindowProc(hWnd, msg, wParam, lParam);
}