Painting gives unusual results, black portions of window - c++

I'm creating a win32 application in C++ and trying (unsuccessfully) to keep things somewhat object-orientated while still respecting the winapi message loop etc.
The problem I'm having is that my window seems to be drawing inconsistently and/or in a weird way that I have not encountered before. Eg:
This occurs when the window is resized. If I minimize the window and open it again, everything is black, apart from the buttons. Also, when moving my mouse from outside the window into the window, whatever my cursor was before getting inside the window is retained. For example, if there is a textbox behind the window, and I move my cursor from the textbox and into this window, the cursor remains the caret cursor.
I'm using this code to handle messages inside my WindowMain class:
std::map<HWND, WindowMain*> WindowMain::HWNDMap;
LRESULT CALLBACK WindowMain::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (HWNDMap.find(hWnd) == HWNDMap.end()) { return DefWindowProc(hWnd, msg, wParam, lParam); }
return HWNDMap[hWnd]->InstWndProc(hWnd, msg, wParam, lParam);
}
LRESULT CALLBACK WindowMain::InstWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_CLOSE:
PostQuitMessage(0);
break;
case WM_DESTROY:
DeleteObject(_hFont);
break;
default:
return DefWindowProc(hWnd, msg, wParam, lParam);
}
return 0;
}
The WindowMain::WndProc function is a static member of WindowMain. I use a std::map to map HWND to WindowMain* so that I can pass messages from the static method to the relevant WindowMain::InstWndProc. I feel like the problem might be here but I can't spot it.
In case it's relevant, this is the code I'm using to create the window:
WindowMain::WindowMain(HINSTANCE hInst) {
_isReady = false;
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = WndProc;
wc.lpszClassName = ClassName.c_str();
wc.hInstance = hInst;
wc.hbrBackground = (HBRUSH)(0);
if (!RegisterClass(&wc)) { return; }
_hWnd = CreateWindow(ClassName.c_str(), "AlchemyAC", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 400, 300, NULL, NULL, hInst, NULL);
if (!_hWnd) { return; }
_hBtnStart = CreateWindow("BUTTON", "Start", WS_TABSTOP | WS_VISIBLE | WS_CHILD, 10, 10, 100, 24, _hWnd, NULL, hInst, NULL);
_hBtnStop = CreateWindow("BUTTON", "Stop", WS_TABSTOP | WS_VISIBLE | WS_CHILD, 10, 39, 100, 24, _hWnd, NULL, hInst, NULL);
if (!_hBtnStart || !_hBtnStop) { return; }
GetObject(GetStockObject(DEFAULT_GUI_FONT), sizeof(LOGFONT), &_lf);
_hFont = CreateFont(_lf.lfHeight, _lf.lfWidth,
_lf.lfEscapement, _lf.lfOrientation, _lf.lfWeight,
_lf.lfItalic, _lf.lfUnderline, _lf.lfStrikeOut, _lf.lfCharSet,
_lf.lfOutPrecision, _lf.lfClipPrecision, _lf.lfQuality,
_lf.lfPitchAndFamily, _lf.lfFaceName);
SendMessage(_hBtnStart, WM_SETFONT, (WPARAM)_hFont, true);
SendMessage(_hBtnStop, WM_SETFONT, (WPARAM)_hFont, true);
EnableWindow(_hBtnStop, false);
HWNDMap[_hWnd] = this;
_isReady = true;
}
My question is, why is this strange painting issue occurring? I'm mainly interested in the odd thing that is happening with the cursor.
EDIT: I am using a while loop to translate and dispatch messages.

Related

window fails to initiate when specifying a custom class name registered with RegisterClass to CreateWindowEx but only the second time I do it

I'm creating a window that's supposed to contain buttons.
When I create the first window (the container) I use a WNDCLASS to specify a WindowProc callback function to lpfnWndProc and it works as intended. When I do it the second time with a window, specifying a callback used to detect when the button is clicked, it doesn't work; the CreateWindowEx call there returns NULL (comment below shows where).
When I don't create WNDCLAS wB below and specify some other valid lpClassName to CreateWindowEx instead, such as Button it works fine.
code used
#pragma comment(linker, "/entry:wWinMainCRTStartup")
#include <windows.h>
#include <iostream>
HWND button;
LRESULT CALLBACK ButtonWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine,
int nCmdShow) {
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"textedit";
RegisterClass(&wc);
HWND hwnd = CreateWindowEx(
0, wc.lpszClassName, L"textedit", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 340, 240, NULL, NULL, hInstance, NULL);
if (hwnd == NULL) {
return 0;
}
HWND hWndEdit = CreateWindowEx(WS_EX_CONTROLPARENT, L"Edit", L"id here",
WS_BORDER | WS_CHILD | WS_VISIBLE, 94, 20, 140,
40, hwnd, NULL, NULL, NULL);
// if I leave wcB unused/remove it and
// specify L"Button" as the lpClassName to CreateWindowEx below, the button displays
WNDCLASS wcB = {};
wcB.lpfnWndProc = ButtonWindowProc;
wcB.hInstance = hInstance;
wcB.lpszClassName = L"modify";
RegisterClass(&wcB);
ShowWindow(hwnd, nCmdShow);
// doing the exact same thing I did when creating the first window
// but it fails to create the window
HWND hButton =
CreateWindowEx(0L, L"modify", L"modify", BS_FLAT | WS_VISIBLE | WS_CHILD,
94, 70, 140, 50, hwnd, NULL, hInstance, NULL);
ShowWindow(hButton, nCmdShow);
// here hButton is NULL <----------------------
if (hButton == NULL) {
return 0;
}
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK ButtonWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case BN_CLICKED:
std::cout << "clicked";
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
return 0;
}
return 0;
return DefWindowProc(hwnd, uMsg, wParam, lParam);
};
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam,
LPARAM lParam) {
switch (uMsg) {
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
GetLastError() returns 1400, but I don't understand how the handle hwnd could possible be invalid because as I mentioned before, when I just change the lpClassName I pass to CreateWindowEx, it works without errors.
I would appreciate any help in finding out why the window (the HWND of which is hButton) does not initiate, and possible workarounds.
The solution to the problem in the title was solved by the comment by Igor Tandetnik:
ButtonWindowProc returns 0 for all messages it doesn't handle, and doesn't pass them to DefWindowProc. In particular, it returns 0 in response to WM_NCCREATE, which is a signal that the window creation has failed. You probably didn't mean to put return 0; right before return DefWindowProc(...);
After fixing that, the window was created but was invisible.
To fix that I used FillRect with a valid brush handle
SetDCBrushColor(hdc, bkcolor);
SetBkColor(hdc, RGB(187, 189, 189));
FillRect(hdc, &ps.rcPaint, (HBRUSH)GetStockObject(DC_BRUSH));
To detect left mouse button clicks you need to check for the WM_LBUTTONDOWN message.

WM_COMMAND unabled to fulfil request unless clicked outside of application window,

Currently creating an application that just outputs Physics equations + some historical and mathematical context. I've ran into this problem where I'm using WS_EX_CLIENTEDGE to sort out a list of these terms and when I click said term on the screen, expecting a definition to appear, it doesn't output anything until I've clicked outside of the application window(Visual Studio, Debug Terminal, etc)
Cursed gif
And I want to make it clear that I DON'T want to use a button due to GUI aesthetics.
Mundane WINAPI application creation
int main(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
const wchar_t CLASS_NAME[] = L"Application";
WNDCLASS window = { 0 };
MSG msg = { 0 };
window.lpfnWndProc = WindowProc;
window.lpszClassName = CLASS_NAME;
window.hInstance = hInstance;
RegisterClass(&window);
HWND hWnd = CreateWindow(CLASS_NAME, L"PhysicsBox", WS_VISIBLE | WS_OVERLAPPEDWINDOW, 100, 100, 600, 500, NULL, NULL, NULL, NULL);
ShowWindow(hWnd, nCmdShow);
//aMessageBox = CreateWindowW(L"edit", L" ", WS_VISIBLE | WS_CHILD | WS_BORDER, 165, 150, 400, 300, hWnd, NULL, NULL, NULL);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
The Juicy Window Procedure
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
aListBox(hWnd);
break;
case WM_COMMAND:
if (SendMessage(hList, LB_GETSEL, FORCE, 0))
{
CreateWindowW(L"static", L"Simple Text", WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER, 200, 100, 100, 100, hWnd, NULL, NULL, NULL);
}
break;
case WM_DESTROY:
PostQuitMessage(0); break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
}
The void function that creates the list
void aListBox(HWND hWnd)
{
hList = CreateWindowEx(WS_EX_CLIENTEDGE, L"ListBox", L" ", WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_AUTOVSCROLL, 3, 4, 150, 300, hWnd, (HMENU)ID_LISTBOX, 0, 0);
SendMessageW(hList, LB_ADDSTRING, FORCE, (LPARAM)L"Force");
SendMessageW(hList, LB_ADDSTRING, WORK, (LPARAM)L"Work");
SendMessageW(hList, LB_ADDSTRING, POWER, (LPARAM)L"Power");
SendMessageW(hList, LB_ADDSTRING, EFFICENCY, (LPARAM)L"Efficency");
SendMessageW(hList, LB_ADDSTRING, POTENTIALENERGY, (LPARAM)L"Potential Energy");
SendMessageW(hList, LB_ADDSTRING, HOOKESLAW, (LPARAM)L"Hooke's Law");
}
Note: the MSG('s) placed in the "aListBox(HWND hWnd)" SendMessageW parameters are numerically ordered(from 0). So FORCE = 0, WORK = 1, and so on. Also hList is a global variable if that helps :P
The listbox is created without the LBS_NOTIFY style and will therefore not send LBN_SELCHANGE notifications to the parent window. Changing the first line in aListBox to the following fixes this.
hList = CreateWindowEx(WS_EX_CLIENTEDGE, L"ListBox", L" ", WS_CHILD | LBS_NOTIFY | WS_VISIBLE | WS_VSCROLL | ES_AUTOVSCROLL, 3, 4, 150, 300, hWnd, (HMENU)ID_LISTBOX, 0, 0);
Also, the WindowProc does not return a result on all code paths, and does not call DefWindowProc for all messages that it does not handle. The following corrects those issues.
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
aListBox(hWnd);
return 0;
case WM_COMMAND:
if (SendMessage(hList, LB_GETSEL, FORCE, 0))
{
HWND hText = CreateWindowW(L"static", L"Simple Text", WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER, 200, 100, 100, 100, hWnd, NULL, NULL, NULL);
ShowWindow(hText, SW_SHOW);
return 0;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
Other problems remain with the code, for example the WindowProc does not check the WM_COMMAND notification code and will happily create new windows over and over, but the assumption was that this was minimal code only meant to showcase the particular issue in the question.
[ EDIT ]   The behavior described by the OP when running without the LBS_NOTIFY style bit is mostly accidental, due to WM_COMMAND not checking the WPARAM notification code. What happens is that clicking outside the app window causes the listbox to send a LBN_KILLFOCUS notification (which is being sent regardless of LBS_NOTIFY). That notification is the one which triggers the WM_COMMAND code in WindowProc that creates the static control when the FORCE item is selected.

Why is C++ ShowWindow() not working properly when hiding a window and showing another?

I am writing my first Win32 app in C++ and I am trying to create 3 windows between which to redirect based on what buttons the user clicks on. I initialised the windows (window1,window2,window3) as children of the main window hwnd and only set window1 as visible. window1 also has two buttons, each of which is supposed to direct either to window2 or window3.
I tried to hide window1 and show which window I want to switch to using the ShowWindow() function. However, it is not working (clicking the buttons does nothing). Could you help me understand why?
On another piece of code I had before, where I had not created window1, and the buttons and the other two windows were just children of hwnd, pressing the button did show the right window, but the button remained there, even if it did not belong on that window.
Also, is there a more efficient way of switching between windows (without deleting and creating them again and again?
Thanks!!!
EDIT: I managed to solve my problem. It arose from the fact that I had declared windows 1,2 and 3 as static. By declaring them under the same class as hwnd, I was able to process the messages from the buttons under the main WindowProcedure(). Since my program is gonna be quite simple, I do not need to create a different procedure for the new windows, but thanks to the comments, now I also know how to do that!
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
window1 = CreateWindow("STATIC","Window 1",WS_VISIBLE|WS_BORDER|WS_CHILD,0,0,600,600,hwnd,NULL,NULL,NULL);
window2 = CreateWindow("STATIC","Window 2",WS_BORDER|WS_CHILD,0,0,600,600,hwnd,NULL,NULL,NULL);
button2 = CreateWindow(
"BUTTON",
"SECOND WINDOW",
WS_CHILD | WS_VISIBLE | WS_BORDER,
350, 480,
200, 20,
window1, (HMENU) 2, NULL, NULL);
window3 = CreateWindow("STATIC","Window 3",WS_BORDER|WS_CHILD,0,0,600,600,hwnd,NULL,NULL,NULL);
button3 = CreateWindow(
"BUTTON",
"THIRD WINDOW",
WS_CHILD | WS_VISIBLE | WS_BORDER,
50, 480,
200, 20,
window1, (HMENU) 3, NULL, NULL);
break;
case WM_COMMAND:
switch(LOWORD(wParam))
{
case 2:
ShowWindow(window1,SW_HIDE);
ShowWindow(window2,SW_SHOW);
break;
case 3:
ShowWindow(window1,SW_HIDE);
ShowWindow(window3,SW_SHOW);
break;
}
break;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
First, create a new window1 as in the steps of creating the main form.
Then create a windowprocessforwindow1 for window1, process the WM_COMMAND message in this function.
Here is the sample:
#include <Windows.h>
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK windowprocessforwindow1(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static HWND window1, window2, button2, window3, button3;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("hello windows");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WindowProcedure;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
}
hwnd = CreateWindow(szAppName,
TEXT("the hello program"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessageW(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
static HINSTANCE hInstance;
static WNDCLASSEX win1;
case WM_CREATE:
hInstance = (HINSTANCE)::GetWindowLong(hwnd, GWL_HINSTANCE);
win1.hInstance = hInstance;
win1.lpszClassName = L"Window 1";
win1.lpfnWndProc = (WNDPROC)windowprocessforwindow1; /* This function is called by windows */
win1.style = CS_DBLCLKS; /* Catch double-clicks */
win1.cbSize = sizeof(WNDCLASSEX);
win1.hIcon = LoadIcon(NULL, IDI_APPLICATION);
win1.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
win1.hCursor = LoadCursor(NULL, IDC_ARROW);
win1.lpszMenuName = NULL; /* No menu */
win1.cbClsExtra = 0; /* No extra bytes after the window class */
win1.cbWndExtra = 0; /* structure or the window instance */
win1.hbrBackground = (HBRUSH)COLOR_BACKGROUND;
if (!RegisterClassEx(&win1))
return 0;
window1 = CreateWindowEx(
0, /* Extended possibilites for variation */
L"Window 1", /* Classname */
L"Window 1", /* Title Text */
WS_VISIBLE | WS_BORDER | WS_CHILD, /* default window */
0, /* Windows decides the position */
0, /* where the window ends up on the screen */
600, /* The programs width */
600, /* and height in pixels */
hwnd, /* The window is a child-window to desktop */
NULL, /* No menu */
hInstance, /* Program Instance handler */
NULL /* No Window Creation data */
);
window2 = CreateWindow(L"STATIC", L"Window 2", WS_BORDER | WS_CHILD, 0, 0, 600, 600, hwnd, NULL, NULL, NULL);
button2 = CreateWindow(
L"BUTTON",
L"SECOND WINDOW",
WS_CHILD | WS_VISIBLE | WS_BORDER,
350, 480,
200, 20,
window1, (HMENU)2, NULL, NULL);
window3 = CreateWindow(L"STATIC", L"Window 3", WS_BORDER | WS_CHILD, 0, 0, 600, 600, hwnd, NULL, NULL, NULL);
button3 = CreateWindow(
L"BUTTON",
L"THIRD WINDOW",
WS_CHILD | WS_VISIBLE | WS_BORDER,
50, 480,
200, 20,
window1, (HMENU)3, NULL, NULL);
ShowWindow(window1, SW_SHOW);
break;
case WM_DESTROY:
PostQuitMessage(0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK windowprocessforwindow1(HWND handleforwindow1, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
{
switch (wParam) {
case 2:
ShowWindow(window1, SW_HIDE);
ShowWindow(window2, SW_SHOW);
break;
case 3:
ShowWindow(window1, SW_HIDE);
ShowWindow(window3, SW_SHOW);
break;
}
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(handleforwindow1, msg, wParam, lParam);
}
return 0;
}

Drag&drop event (WM_DROPFILES) in C++ GUI

I am trying to create a simple application where the user can drag and drop files from outside of the window (usually Explorer) into an area inside of the window. My ultimate purpose is to get the file path to later on process it.
Currently I can drag and drop files into the area but I never receive the WM_DROPFILES event. I have tried with some related functions (DoDragDrop, RegisterDragDrop, CDropSource), but they all have been either impossible to compile or unsuccessful.
Could anyone tell me if I am missing setting any property?
Many thanks in advance
LRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_CREATE:
{
CreateWindowEx(
WS_EX_ACCEPTFILES,
TEXT("static"),
TEXT("Drag and drop your file to this area"),
WS_VISIBLE | WS_CHILD,
20, // x
20, // y
120, // w
60, // h
hwnd, // parent window
(HMENU) 1, // unique label
NULL, NULL);
}
case WM_DROPFILES:
{
MessageBox(hwnd, "Dragged!", "Title", MB_OK | MB_ICONINFORMATION);
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
{
return DefWindowProc(hwnd, Message, wParam, lParam);
}
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG msg;
memset(&wc,0,sizeof(wc));
wc.cbSize = sizeof(WNDCLASSEX);
wc.lpfnWndProc = WndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszClassName = "WindowClass";
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, "Window Registration Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
"WindowClass",
"MySimpleApp",
WS_VISIBLE | WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
WINDOW_W,
WINDOW_H,
NULL,NULL,hInstance,NULL);
if (hwnd == NULL)
{
MessageBox(NULL, "Window Creation Failed!","Error!",MB_ICONEXCLAMATION|MB_OK);
return 0;
}
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
You are not receiving the WM_DROPFILES message because you are not subclassing the STATIC control you create to receive messages that are sent to it. You are assuming you can catch the message in the control's parent window, but that is not where the message goes. It is sent to the window that you actually drop onto - the STATIC control.
Try this instead:
LRESULT CALLBACK StaticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg) {
case WM_NCDESTROY: {
RemoveWindowSubclass(hwnd, &StaticWndProc, uIdSubclass);
break;
}
case WM_DROPFILES: {
MessageBox(hwnd, "Dragged!", "Title", MB_OK | MB_ICONINFORMATION);
break;
}
}
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE: {
HWND hStatic = CreateWindowEx(
WS_EX_ACCEPTFILES,
TEXT("static"),
TEXT("Drag and drop your file to this area"),
WS_VISIBLE | WS_CHILD,
20, // x
20, // y
120, // w
60, // h
hwnd, // parent window
(HMENU) 1, // unique label
NULL, NULL);
SetWindowSubclass(hStatic, &StaticWndProc, 0, 0);
break;
}
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
default: {
return DefWindowProc(hwnd, Message, wParam, lParam);
}
}
return 0;
}
DoDragDrop() and RegisterDragDrop() (which you should be using instead of WM_DROPFILES) have nothing to do with receiving WM_DROPFILES.
You are missing
DragAcceptFiles( hwnd, TRUE );
Put it just before message loop.
WM_DROPFILES fails to correctly transfer data from 32-bit application to 64-bit one. Could be remedied by implementing IDropTarget and removing WM_DROPFILES handling.

winapi C++ Handling Focus

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.