window without wndproc - c++

I am curious it is possible to create a window without using WndProc.
So I would like to register the window-class with the lpfnWndProc field set to NULL;
And using the msg ( that is given by TranslateMessage(&msg) ) in my own way.
Is there any disadvantages of this?
Thanks ahead, and sorry for my grammar.
Edit #1:
Okay, I have a window, but I am wrong somewhere.
MSG msg;
while(PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE)) {
if (msg.message == WM_QUIT) return false;
else {
TranslateMessage(&msg);
switch (msg.message) {
case WM_CREATE:
createContext();
break;
default:
DispatchMessage(&msg);
break;
}
}
}
return true;
The createContext is not getting called.
Why? Where I am wrong?
Yeah, maybe the WM_CREATE message is to be sent to WndProc (DefWindowProc now), but are there any way to get it outside from the WndProc?

AFAIK, you can't set it to NULL. However, you'll notice that DefWindowProc's signature matches the WindowProc callback's signature. Simply give it DefWindowProc if you'd like a reasonable default.

Sorry that makes no sense. Without the wndproc how can you even get hold of the message?
A window without a window proc is not a window!

No, you'll break SendMessage(). Only PostMessage() can work. Supporting SendMessage is required.

Related

win32 C++ Custom Color for Trackbar thumb

I have spent today searching everywhere for a concrete explanation or example of coloring the thumb on a trackbar (slider) in win32 C++. Everything I've found has been partially explained, and in trying every conceivable variation I have come up blank.
The control I have been focused on is defined in my rc file as
CONTROL "",IDC_PLAYSLIDER,"msctls_trackbar32",TBS_NOTICKS | WS_TABSTOP,5,22,187,15
Essentially, my message handling of NM_CUSTOMDRAW comes down to the following. I have no confidence on my color/hdc handling, but the lack of messages is my primary problem.
INT_PTR CALLBACK dialogproc(HWND h, UINT m, WPARAM w, LPARAM l)
{
switch (m) {
case WM_NOTIFY:
{
switch (((LPNMHDR)l)->code) {
case NM_CUSTOMDRAW:
{
LPNMCUSTOMDRAW lpNMCD = (LPNMCUSTOMDRAW)l;
UINT idc = lpNMCD->hdr.idFrom;
switch (lpNMCD->dwDrawStage) {
case CDDS_PREPAINT:
return CDRF_NOTIFYSUBITEMDRAW;
break;
case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
{
switch (lpNMCD->dwItemSpec)
{
case TBCD_THUMB:
HGDIOBJ old_pen = SelectObject(lpNMCD->hdc, penSlider);
HGDIOBJ old_brush = SelectObject(lpNMCD->hdc, brushSlider);
return CDRF_NEWFONT;
}
}
break;
}
What I am getting at runtime is a CDDS_PREPAINT on the correct control, but no matter what I have tried, I have had no further CDDS_ drawStage messages.
If anyone has done this on a trackbar (most examples are list controls) and is willing to share their message handler code, or can otherwise shed light on my confusion, that would be greatly appreciated.
From the docs for NM_CUSTOMDRAW:
If this message is handled in a dialog procedure, you must set the
return value as part of the window data before returning TRUE. For
more information, see DialogProc.
The DialogProc docs say:
If the dialog box procedure processes a message that requires a
specific return value, the dialog box procedure should set the desired
return value by calling SetWindowLong(hwndDlg, DWL_MSGRESULT, lResult)
immediately before returning TRUE
Note that with the advent of 64-bit windows it is better practice to use SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, lResult).

Creating Responsive Windows winapi c++

I am just learning to create a gui using the winapi, but i have run into an issue. I can create a window like this
#include "stdafx.h"
#include <windows.h>
int main()
{
HWND hwnd = CreateWindow(L"STATIC",NULL,WS_VISIBLE|WS_SYSMENU|WS_CAPTION,0,0,600,600,NULL,NULL,NULL,NULL);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
_gettch();
}
But the windows will not close when the close button is clicked, and the window can not be dragged around or moved. I was wondering how i would enable these features of the window.
Static windows are not there for normal windows, you should try and look up how to register and handle your own class with RegisterWindowEx then use the same class name to create a window. You have to have your own window procedure in order to handle messages.
All window classes registered by the system run their own default window procudure and as far as I know none of them handle WM_CLOSE ( that is the close button ) this is why you can't close it.
For you main windows always use something like WS_OVERLAPPEDWINDOW so it'll be clear if it's okay or not and from that eliminate the flags you don't need.
How you set it up :
WNDCLASSEX wndcls;
HWND hMainWnd;
// Register your own window class
ZeroMemory(&wndcls,sizeof(WNDCLASSEX));
wndcls.cbSize=sizeof(WNDCLASSEX);
wndcls.style=CS_VREDRAW+CS_HREDRAW;
wndcls.lpfnWndProc=&appWndFunc;
wndcls.hInstance=hInstance;
wndcls.hIcon=hMainIcon; // or just LoadIcon(hInstance,MAKEINTRESOURCE(IDI_MAIN_ICON))
wndcls.hIconSm=hMainIcon;
wndcls.hCursor=LoadCursor((HINSTANCE)NULL,IDC_ARROW);
wndcls.hbrBackground=(HBRUSH)COLOR_APPWORKSPACE;
wndcls.lpszClassName="myWndClass";
if (RegisterClassEx(&wndcls)==0)
{
// failed to register class name
return false;
}
// Create window with your own class
hMainWnd=CreateWindowEx(0,\
"myWndClass","widnow title",\
WS_OVERLAPPEDWINDOW|WS_VISIBLE,\
0,\
0,\
250,\
250,\
hMainWnd,NULL,hInstance,NULL);
if (hMainWnd==(HWND)NULL)
{
// failed to create main window
return false;
}
Then your main loop :
bool bAppMainLoop=false
while(!bAppMainLoop)
{
WaitMessage();
while(PeekMessage(&emsg,NULL,0,0,PM_NOREMOVE))
{
if(GetMessage(&emsg,NULL,0,0)==0)
{
bAppMainLoop=true;
break;
}
TranslateMessage(&emsg);
DispatchMessage(&emsg);
}
}
This is a bit more than usual setup, so let me explain , in order to not burn CPU, you wait for a message with WaitMessage, it'll block until something happens, like move window, click, paint etc. PeekMessage will return true if there is a message so calling it in a while loop will make sure it drains the message quene, GetMessage will obtain the message if it returns 0 it means that your app called the PostQuitMessage(0) so a WM_QUIT arrived was found in the message loop that means it's time to break out from the message loop. The rest Translate and Dispatch does what it name says.
Finally you need your own window procedure :
LRESULT CALLBACK appWndFunc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
if (uMsg==WM_CLOSE)
{
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd,uMsg,wParam,lParam);
}
DefWindowProc is essential that handles all commonly occurring messages from the system, thus you don't need to handle those here. You simply respond to the WM_CLOSE message which is sent when you want to close the window and post a quit message into the message loop that you will catch and exit.
Additional info :
It's not required to release your stuff since windows does that for you so it wont be locked the next time you start your program , but it's a good practice to at least Unregister your window class after your main loop.
Btw that is the wrong main function : WinMain that is the correct one. Plus to avoid even more bugs make sure you compile a windows GUI application.

Why doesn't my window get WM_SIZE command when handling WM_KEYDOWN myself?

I am unable to get WM_SIZE command if i use this method (or WM_KEYUP with same return 0 in end):
case WM_KEYDOWN: {
keys[wParam] = 1;
return 0;
}
But it gives me WM_SIZE command when i use this:
case WM_KEYDOWN: {
keys[wParam] = 1;
break;
}
Could someone explain why is this happening?
Without seeing the entire code, my guess is that the return statement is preventing the WM_KEYDOWN message from being passed to a default message handler, such as DefWindowProc(), so the window does not actually process the keystroke and take whatever action it needs to generate WM_KEYUP and WM_SIZE messages.

Switch statement use

Should i use this form of switch statement:
switch(msg)
{
case WM_LBUTTONDOWN:
{
char szFileName[MAX_PATH];
HINSTANCE hInstance = GetModuleHandle(NULL);
GetModuleFileName(hInstance, (LPWCH)szFileName, MAX_PATH);
MessageBox(hwnd, (LPCWSTR)szFileName, L"This program is:", MB_OK | MB_ICONINFORMATION);
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
or make a function for the first case constant ?
There's nothing wrong with how you have it, but it's probably cleaner code to call a function so you can keep your functions a reasonable size.
Also, take a look at message crackers
What you going to do when will hadle 20 or 50 window messages?
Maybe it right time for create map - events on functions ( fuctors ) and call them?
Or start to use rule - one message = one function call.
char szFileName[MAX_PATH];
HINSTANCE hInstance = GetModuleHandle(NULL);
GetModuleFileName(hInstance, (LPWCH)szFileName, MAX_PATH);
MessageBox(hwnd, (LPCWSTR)szFileName, L"This program is:", MB_OK | MB_ICONINFORMATION);
Could you explain this strange trick with convetation (LPCWSTR)szFileName. Why you don't use array wchar_t instead casting? - you will have big problem with long paths ( path_length > MAX_PATH / sizeof( wchar_t ) )
One reccomendation - avoid to use casts in general and C-style casts in particulary.
If you are asking if you should turn the code in the first case into a function, then yes, definitely.
Well, it would depend on how many other cases you would have.
Something as small as that, I would say it is not worth it to make it a function, but if your switch statement contains more cases, it will just get ugly, especially if a lot of the cases has multiple lines like that. Putting it into a function would clean it up and make your code look nicer.
One of the most important things I'd say would be consistency. If you create a function for LBUTTONDOWN then create a function for everything. This way there is a predictable pattern for where to find stuff if it breaks.
Related to the topic at hand:
I personally find an if / else if pattern to work better, as it eliminates the problem of a forgotten break:
if (msg == WM_LBUTTONDOWN) {
// your code here
return 0;
} else if (msg == WM_DESTROY) {
PostQuitMessage(0);
return;
} else if (msg == WM_KEYDOWN) {
if (wp == VK_F1) {
DoSomething();
return;
}
}
return DefWindowProc(hWnd, msg, wp, lp);
It's really up to you, in the end.
I would probably declare a map and use functors for every message:
typedef std::map<UINT, boost::function<int (HWND, WPARAM, LPARAM) > > messageFuncs_t;
messageFuncs_t messageFuncs;
Then, when the window class is created, just add a new function for each message:
messageFuncs[WM_LBUTTONDOWN] = &onMouseDownEvent;
... And then implement the message loop thus:
messageFuncs_t::iterator fun = messageFuncs.find(msg);
if(fun != messageFuncs.end())
return (*fun)(hWnd, wparam, lparam);
else
return DefWindowProc(hWnd, msg, wp, lp);
... Or whatever works. Then it's easy to add new messages, and the work for each is delegated to a function. Clean, concise, and makes sense.
You are missing a break on the first case. Other than that, I would definitely put that code on a separate function.
It's fine, but I generally don't like mixing styles and indentations. If I needed to bracket one case I'd probably bracket them all and keep the indentation consistent.
Also bb is right, you should use wchar_t array instead of char in that case.
I'm writing fairly many Win32 message crackers such as these switches.
My rule of thumb is: Wiring up the behavior goes into the switch, behavior into a separate function. That usually means the switch contains the decision whether this command should be handled (e.g. testing for sender ID), and "prettyfying" the parameters.
So in that particular case, a separate function.
The original motivation is that I often end up triggering the behavior in other circumstances (e.g. "when no file name has been specified and the dialog is called with the moon parameter set to full, show the SaveAs dialog immediately").

How do I destroy a Window correctly?

I'm programming a little game, and I set the lpfnWndProc to DefWindowProc
and after that, I made a loop in that way:
MSG lastMessage;
while (true)
{
if (PeekMessage(
&lastMessage,
this->getWindow(),
0, 0,
PM_REMOVE))
{
TranslateMessage(&lastMessage);
DispatchMessage(&lastMessage);
}
}
So how do I handle the Close Window event in that case?
First of all, this is not how you write a message loop: it will take 100% CPU while waiting for messages, and won't remove messages for other windows from the queue. It will also never terminate. See here for an example of a message loop.
About closing windows: DefWindowProc will handle WM_CLOSE automatically and destroy your window. If you want your application to terminate when the window is closed, you need to handle WM_DESTROY and call PostQuitMessage(0) from it. This means you will need your own window procedure instead of DefWindowProc.
If you want WindowProc to be handled by a class, you do something like
class CWindow
{
static LRESULT WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
CWindow* self;
if(uMsg == WM_CREATE)
{
self = (CWindow*)((LPCREATESTRUCT)lParam)->lplpCreateParams;
}
else
self = GetWindowLong(hwnd,GWL_USERDATA);
if(self){
switch(uMsg){
case WM_CREATE:
return self->OnCreate(hwnd,(LPCREATESTRUCT)lParam);
case WM_CLOSE:
self->OnClose();
return 0;
// etc.
}
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
int OnCreate(HWND hwnd,LPCREATESTRUCT lpcs)
{
m_hwnd = hwnd;
SetWindowLong(m_hwnd,GWL_USERDATA,this);
return 0;
}
}
Making sure of course to pass 'this' as the last parameter to CreateWindow(Ex).
Next, In your message loop, you MUST check for WM_QUIT messages and use that as a cue to exit the loop. Also, NEVER do a filter on hwnd as that will prevent your application loop from dispatching messages for other windows on your thread. And many windows libraries create message windows on threads to facilitate inter process (and thread) comms. If you dont process all windows messages then (a) your game will eventually run out of memory, and (b) the entire system may start to act funny as your application will make IPC messages deadlock, or time out.
Also, WM_CLOSE is (usually) sent via SendMessage, not PostMessage. Sent messages are delivered straight to the window proc and can't be filtered in the app loop.