I don't have much experience with windows programming, but can't specifically see what's wrong with this code, yet the window doesn't open. Although sometimes, not always, it will be open in task manager, so my guess is that it is registering the class and creating the window, but the problem is with the ShowWindow() function. But, I'm not positive.
To my understanding the flow of the program is:
Window is created with the registered class.
The window is shown.
Continuously looks for messages that are processed in the window Proc.
I feel like I've done all these things, so is my understanding wrong, or is my code missing something?
Thanks.
Source Code:
#include <Windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hinstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
//Variable for message loop.
MSG msg;
//Setting up the window class.
WNDCLASSEX windowClass;
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW | CS_PARENTDC;
windowClass.lpfnWndProc = WindowProc;
windowClass.hInstance = hinstance;
windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW;
windowClass.lpszClassName = "WindowClass";
RegisterClassEx(&windowClass);
HWND windowHandle = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, "WindowClass", "My Program", WS_OVERLAPPEDWINDOW, 500, 200, 800, 500, NULL, NULL, hinstance, 0);
if (!windowHandle)
return FALSE;
ShowWindow(windowHandle, nCmdShow);
// Start the message loop.
while (GetMessage(&msg, NULL, 0, 0) != 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Return the exit code to the system.
return msg.wParam;
}
Your window procedure is invoking DefWindowProc but not actually returning the result, and you have undefined behavior because of that. The return value is important, and it can control how the OS handles successive messages to your window. For example, it's important to return the correct value in response to the WM_CREATE message.
Change your window procedure to:
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_DESTROY)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Also, as Mark Ransom identified in the comments section, you should zero-initialize your WNDCLASSEX structure to ensure that you don't get garbage on any members that you didn't explicitly initialize.
Related
I have an SDL2 program that opens an SDL window. In the main I create a hook for mouse events as
hMSHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, NULL);
while outside of the main I have the custom mouse event handler
HHOOK hMSHook;
int xPosAbsolute = 0;
int yPosAbsolute = 0;
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
auto &ms = *(const MOUSEHOOKSTRUCT *)lParam;
if (wParam == WM_MOUSEMOVE)
{
xPosAbsolute = ms.pt.x;
yPosAbsolute = ms.pt.y;
}
}
return CallNextHookEx(hMSHook, nCode, wParam, lParam);
}
When the window is in focus, the mouse coordinates xPosAbsolute and yPosAbsolute are properly updated as the mouse moves. However, if another window receives focus, these variables stop being updated.
My question is, how do I make SetWindowsHookEx() continue receiving WH_MOUSE_LL events while my window is not in focus?
EDIT:
Actually, as it turns out, SetWindowsHookEx only stopped receiving event updates when the window focus was lost to another window that was launched as administrator, while the SDL2 window of this program was launched as a regular user. Once the current program is launched as administrator as well, all updates continue to come in (using the above code), even when window focus is lost. So this turned out to be a non-issue.
Focus is not required but keep pumping messages is required.
The following is a mini Win32 window application with low level hook you can refer to. It will receive WM_MOUSEMOVE message in all existing threads running in the same desktop as the calling thread regardless of which window holds the focus.
LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
auto& ms = *(const MOUSEHOOKSTRUCT*)lParam;
if (wParam == WM_MOUSEMOVE)
{
OutputDebugString(L"\n WM_MOUSEMOVE \n");
OutputDebugStringA(to_string(counter++).c_str());
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
HHOOK hMSHook = SetWindowsHookEx(WH_MOUSE_LL, MouseProc, NULL, NULL);
WNDCLASSEXW wcex = {0};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.lpszClassName = szWindowClass;
RegisterClassExW(&wcex);
HWND hWnd = CreateWindowW(szWindowClass, L"test", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
I have a crazy issue. I subclassed buttons, richedits, checkboxes,.. and all seems working fine. But after I subclassed a trackbar I’m having some trouble now. The problem is that my subclassed messagehandler don’t receive the WM_VSCROLL / WM_HSCROLL messages. They are still sent to the parent's messagehandler. WM_PAINT message and some others are successfully sent to the subclassed messagehanlder.
Someone knows what I’m doing wrong? …And maybe knows how to solve this problem? I created a clean project with the following needed code:
#include <windows.h>
#include <CommCtrl.h>
#pragma comment(lib,"comctl32.lib")
//Prototyps
HWND CreateMainWindow(HINSTANCE hInstance);
LRESULT CALLBACK MessageHandler(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
LRESULT CALLBACK SubMessageHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
WNDPROC oldWndProc;
HWND hWnd = 0;
HWND hTrackBar = 0;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
WNDCLASSEXA wndClass = {sizeof(WNDCLASSEX), CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW, MessageHandler, 0,0, hInstance, LoadIcon(NULL, IDI_WINLOGO),
LoadCursor(NULL, IDC_ARROW),(HBRUSH)GetStockObject(WHITE_BRUSH), NULL, "WindowClass", LoadIcon(NULL, IDI_WINLOGO)};
RegisterClassExA(&wndClass);
//Creat MainWindow
hWnd = CreateWindowExA(NULL, "WindowClass", "Test Windows", WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
100, 100, 400, 300, NULL, NULL, hInstance, NULL);
//Creat Trackbar
INITCOMMONCONTROLSEX initCtrlEx;
initCtrlEx.dwSize = sizeof(INITCOMMONCONTROLSEX);
initCtrlEx.dwICC = ICC_BAR_CLASSES;
if (InitCommonControlsEx(&initCtrlEx)){
hTrackBar = CreateWindowExA(NULL,TRACKBAR_CLASSA, "TrackBar_Test", WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | TBS_AUTOTICKS |
TBS_ENABLESELRANGE | TBS_VERT | TBS_BOTH, 10, 10, 50, 200, hWnd, NULL, hInstance, NULL);
oldWndProc = (WNDPROC)SetWindowLongPtrA(hTrackBar, GWLP_WNDPROC, (LONG_PTR)SubMessageHandler); //Subclassing messagehandler
}
//Message loop
MSG msg;
while (GetMessageA(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessageA(&msg);
}
return 0;
}
LRESULT CALLBACK SubMessageHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch (msg){
case WM_VSCROLL: //callback is subclassed but WM_VSCROLL is not send. why?
MessageBoxA(hWnd, "WM_VSCROLL sent (to SubMessageHandler)", "Test", MB_OK);
break;
}
if (oldWndProc != 0)
return CallWindowProcA(oldWndProc, hwnd, msg, wParam, lParam);
else
return DefWindowProcA(hwnd, msg, wParam, lParam);
}
LRESULT CALLBACK MessageHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
switch (msg){
case WM_VSCROLL: //Why the hell is the Trackbar WM_VSCROLL still sent here to the parent callback!?!?
MessageBoxA(hWnd, "WM_VSCROLL sent (to Parent)", "Test", MB_OK);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
}
return DefWindowProcA(hwnd, msg, wParam, lParam);
}
From the MSDN documentation on Trackbar controls:
A trackbar notifies its parent window of user actions by sending the
parent a WM_HSCROLL or WM_VSCROLL message.
The Trackbar's contract is to notify the parent window with WM_HSCROLL/WM_VSCROLL. The Trackbar control generates and sends those messages; it does not receive them.
Also note that the Default Trackbar Message Processing section does not list WM_HSCROLL/WM_VSCROLL (but does list WM_LBUTTONDOWN, WM_MOUSEMOVE, WM_LBUTTONUP, WM_KEYDOWN, WM_KEYUP, which are the raw messages it would need to handle to handle interaction).
As for what to do about it, it probably depends on exactly what you want to do. You could try subclassing and intercepting all of the user input messages, but that seems like a lot of work and is potentially brittle. My recommendation would be to have the parent window explicitly reflect WM_HSCROLL/WM_VSCROLL back to your custom Trackbar control.
Here is my header
#pragma once
#ifndef BASE_H
#define BASE_H
#include <Windows.h>
#include <windowsx.h>
class Base
{
HWND hWnd;
WNDCLASSEX WndCls;
HRESULT Hr;
public:
Base();
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void RegisterWnd(HINSTANCE hInstance);
void ShowWnd(int nCmdShow);
~Base();
};
#endif
Here is my base.cpp
#include "Base.h"
Base::Base()
{
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch (message)
{
// this message is read when the window is closed
case WM_DESTROY:
{
// close the application entirely
PostQuitMessage(0);
return 0;
} break;
}
// Handle any messages the switch statement didn't
return DefWindowProc(hWnd, message, wParam, lParam);
}
void Base::RegisterWnd(HINSTANCE hInstance)
{
ZeroMemory(&WndCls, sizeof(WNDCLASSEX));
WndCls.cbSize = sizeof(WNDCLASSEX);
WndCls.hbrBackground = (HBRUSH)COLOR_WINDOW;
WndCls.hCursor = LoadCursor(NULL, IDC_ARROW);
WndCls.hIcon = LoadIcon(hInstance, NULL);
WndCls.hIconSm = LoadIcon(hInstance, NULL);
WndCls.hInstance = hInstance;
WndCls.lpfnWndProc = WndProc;
WndCls.lpszClassName = "ClsName";
WndCls.style = CS_HREDRAW | CS_VREDRAW;
Hr = RegisterClassEx(&WndCls);
if (FAILED(Hr))
MessageBox(NULL, "Window Class failed to register.", "ERROR", MB_OK);
hWnd = CreateWindowEx(
NULL,
"WndClassName",
"WndName",
WS_OVERLAPPEDWINDOW,
100, 100,
480, 640,
NULL,
NULL,
hInstance,
NULL);
if (FAILED(hWnd))
MessageBox(NULL, "Window Class failed to create", "ERROR", MB_OK);
}
void Base::ShowWnd(int nCmdShow)
{
Hr = ShowWindow(hWnd, nCmdShow);
if (FAILED(Hr))
MessageBox(NULL, "Failed to display Window", "ERROR", MB_OK);
}
Base::~Base()
{
}
And here is my main.cpp
#include "Base.h"
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
Base CreateWnd;
CreateWnd.RegisterWnd(hInstance);
CreateWnd.ShowWnd(nCmdShow);
MSG Msg;
while (GetMessage(&Msg, NULL, 0, 0))
{
// translate keystroke messages into the right format
TranslateMessage(&Msg);
// send the message to the WindowProc function
DispatchMessage(&Msg);
}
// return this part of the WM_QUIT message to Windows
return Msg.wParam;
}
The problem is, I keep getting this error message that I dont understand of. Sorry for the bad explanation..Still a student in programming...
UPDATED :
The error above has been corrected by replacing
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
with
LRESULT CALLBACK Base::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
Thanks to ravi and IInspectable for the quick help.
Now I am having another error D: When i clicked on debug, everything run perfectly but nothing shows up. No window is showing. Visual studio is running perfectly as "Ready". (Sorry i do not want to make another new question because it's still related to creating window in oo
SECOND UPDATE :
My class name in CreateWindowEx is different from the RegisterWnd..My bad. Thanks to IInspectable again for the help.
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
You have to define this with class scope OR how compiler know if its global static OR static member of class. So it should be
LRESULT CALLBACK Base::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
I am working on window creation with Win32 API and I'm having a problem with this part:
GetMessage(&message, NULL, 0, 0);
My problem is that when I try to change the second parameter (hwnd) which is going to receive the messages to the window I previously made, it doesn't work; for example, when I try to close the window, it only hides and doesn't close.
Here is the full code:
#include <windows.h>
LRESULT CALLBACK WinProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASS window;
window.cbClsExtra = NULL;
window.cbWndExtra = NULL;
window.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
window.hCursor = LoadCursor(hInst, IDC_ARROW);
window.hIcon = NULL;
window.hInstance = hInst;
window.lpfnWndProc = WinProc;
window.lpszClassName = "WINDOW";
window.lpszMenuName = NULL;
window.style = CS_HREDRAW | CS_VREDRAW;
RegisterClass(&window);
HWND hwnd = CreateWindow("WINDOW", "Win32 Window Application", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, 200, 200, NULL, NULL, hInst, NULL);
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
MSG message;
while (GetMessage(&message, NULL, 0, 0))
{
TranslateMessage(&message);
DispatchMessage(&message);
}
return 1;
}
LRESULT CALLBACK WinProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CLOSE:
{
PostQuitMessage(0);
break;
}
default:
break;
}
return DefWindowProc(window, message, wParam, lParam);
}
"... when I try to change the second parameter (hwnd) which is going to receive the messages to the window I previously made, it doesn't work."
Thread messages are not sent to a window; they're posted to the thread message queue with a NULL window handle, and will NOT be picked up with a GetMessage() loop tailored to a specific window handle.
Ex: PostQuitMessage() posts a thread message; not a window message. You need the NULL. From the GetMessage() docs:
If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed.
I am working on a C++ winapi app and I am struggling to get SetFocus() to work for me. I create the Main window with WS_OVERLAPPEDWINDOW | WS_VISIBLE and then within that I create its children (a couple of buttons and the beginnings of my own version of an Edit control) with WS_CHILD | WS_VISIBLE, which are all working fine except for the issue of focus.
In my searches I have battled to find much in terms of how you should handle Focus. When the windows are all created they individually receive the WM_SETFOCUS message and I handle this message in my edit control by creaing the caret, however it seems the children never receive the WM_KILLFOCUS message and so caret is never destroyed.
This is now where my problem comes in: I would like the main parent window to initially have focus and for there to be no caret in my edit control and then when the child Edit control is clicked for it have focus and then when the main window is clicked it should then have focus again and so on.
So my initial thought was to use SetFocus() to set focus to Main Window when handling the WM_CREATE message however that didn't seem to work: the child don't received the WM_KILLFOCUS message.
My next thought was that maybe the parent has to handle passing down WM_KILLFOCUS to the appropriate children so I wrote a method to do that for me but the children still did not receive the WM_KILLFOCUS message.
So my best guess is that I am not handling the Messages correctly in my WndProc.
I have created my own Window class and distribute the messages to the appropriate classes through the following WndProc:
LRESULT CALLBACK CBaseWindow::stWinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
CBaseWindow* pWnd;
if (uMsg == WM_NCCREATE)
{
SetWindowLong(hwnd, GWL_USERDATA, (long)((LPCREATESTRUCT(lParam))->lpCreateParams));
}
pWnd = GetObjectFromWindow(hwnd);
if (pWnd)
return pWnd->WinMsgHandler(hwnd, uMsg, wParam, lParam);
else
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Then each class has its own WndProc where I handle the messages as they come in.
So does anyone have any thoughts for me?
If I am going about this is complete wrong way or if I am not following best practice please say so, I am doing this to learn so shoot away.
[UPDATE]
OK here is some code to demonstrate the problem:
Main.cpp
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "MainWnd.h"
int WINAPI WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MainWnd wnd(hInstance);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0 ) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
BaseWindow.cpp
#include "BaseWindow.h"
//...
LRESULT CALLBACK CBaseWindow::stWinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
CBaseWindow* pWnd;
if (uMsg == WM_NCCREATE)
{
SetWindowLong(hwnd,
GWL_USERDATA,
(long)((LPCREATESTRUCT(lParam))->lpCreateParams));
}
pWnd = GetObjectFromWindow(hwnd);
if (pWnd)
return pWnd->WinMsgHandler(hwnd, uMsg, wParam, lParam);
else
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
BOOL CBaseWindow::Create(DWORD dwStyles, RECT* rect)
{
m_hwnd = CreateWindow(
szClassName,
szWindowTitle,
dwStyles,
rect->left,
rect->top,
rect->right - rect->left,
rect->bottom - rect->top,
NULL,
NULL,
hInstance,
(void *)this);
return (m_hwnd != NULL);
}
MainWnd.cpp
#include "MainWnd.h"
#define WIDTH 400
#define HEIGHT 400
MainWnd::MainWnd(HINSTANCE hInst): CBaseWindow(hInst), hInstance(hInst)
{
SetWindowTitle(_T("Main Window"));
WNDCLASSEX wcx;
FillWindowClass(&wcx);
if(RegisterWindow(&wcx))
{
RECT rc;
BuildRect(&rc);
if(Create(WS_OVERLAPPEDWINDOW | WS_VISIBLE, &rc))
{
customTextBox = new CustomTextBox(hInst, m_hwnd);
}
}
}
void MainWnd::FillWindowClass(WNDCLASSEX *wcx)
{
wcx->cbSize = sizeof(WNDCLASSEX);
wcx->style = CS_HREDRAW | CS_VREDRAW | CS_DROPSHADOW;
wcx->lpfnWndProc = CBaseWindow::stWinMsgHandler;
wcx->cbClsExtra = 0;
wcx->cbWndExtra = 0;
wcx->hInstance = hInstance;
wcx->hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx->hCursor = LoadCursor(NULL, IDC_ARROW);
wcx->hbrBackground = CreateSolidBrush(RGB(255,255,255));
wcx->lpszMenuName = NULL;
wcx->lpszClassName = _T("MainWindow");
wcx->hIconSm = LoadIcon(NULL, IDI_APPLICATION);
}
LRESULT CALLBACK MainWnd::WinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
delete customTextBox;
PostQuitMessage(0);
break;
case WM_LBUTTONUP:
SetFocus(hwnd);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
CustomTextBox.cpp
#include "CustomTextBox.h"
CustomTextBox::CustomTextBox(
HINSTANCE hInst,
HWND hParent): CBaseWindow(hInst),
hParent(hParent),
{
WNDCLASSEX wcx;
CreateWndClassEX(wcx);
if(RegisterWindow(&wcx))
{
RECT clientRect;
CreateClientRect(clientRect);
CreateChild(WS_VISIBLE | WS_CHILD | WS_BORDER | WS_TABSTOP, &clientRect, hParent);
}
}
void CustomTextBox::CreateWndClassEX(WNDCLASSEX& wcx)
{
wcx.cbSize = sizeof(WNDCLASSEX);
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = CBaseWindow::stWinMsgHandler;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.hInstance = hInstance;
wcx.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wcx.hCursor = LoadCursor(NULL, IDC_ARROW);
wcx.hbrBackground = CreateSolidBrush(RGB(255,255,255));
wcx.lpszMenuName = NULL;
wcx.lpszClassName = _T("Edit Control");
wcx.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
}
LRESULT CALLBACK CustomTextBox::WinMsgHandler(
HWND hwnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
switch(uMsg)
{
/* Handling the caret */
case WM_SETFOCUS:
CreateCaret(hwnd, NULL, 0, nWindowY);
SetCaretPos(GetEndOfLinePoint(), nCaretPosY * nCharY);
ShowCaret(hwnd);
return 0;
case WM_MOUSEACTIVATE:
SetFocus(hwnd);
return MA_ACTIVATE;
case WM_KILLFOCUS:
DestroyCaret();
return 0;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
Discovery
In writing up this code I came across one of the causes of my problems: in my actual application I don't have a title bar and so to move the window I was sending WM_NCLBUTTONDOWN to my main window on WM_LBUTTONDOWN:
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
I then had my SetFocus() after this and this wasn't working but if I switch them around and handle the SetFocus() first then the clicking does change focus.
There is still a problem with initially setting the focus though. At the moment after starting the app the custom edit control still displays the caret even though it doesn't have focus and you need to click on it to give it focus, whereby it will receive keyboard input. After that the focus works as desired: if I click on the main window it has focus; if I click on the custom edit, it has focus etc.
OK so it turns out that my entire problem lied with that line SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL); Once I switched this:
case WM_LBUTTONDOWN:
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
SetFocus(hwnd);
break;
to:
case WM_LBUTTONDOWN:
SetFocus(hwnd);
SendMessage(hwnd, WM_NCLBUTTONDOWN, HTCAPTION, NULL);
break;
everything came together.