Related
I am relatively new to C++. I have a question why the program posted below behaves the way it does when the constant DEMOMETHOD is set to 1 in main.cpp and button_class.h.
The program demonstrates subclassing a few buttons created by a single c++ class - there are 4 instances of the class (4 buttons) and two (labeled button1 and button2) are subclassed.
I'm hoping to learn:
why return 0; is needed in the handler of the wm_lbuttondown message in button_class.cpp for multiinstance subclassing to work correctly (set DEMOMETHOD = 0 for correct program operation).
why the other subclassed buttons and the non subclassed buttons work incorrectly after either one of the two subclassed buttons are clicked. Example: the quit button doesn't work if you first click on Button 2 (or 1) when DEMOMETHOD = 1.
I posted compilable (gcc compiler, Windows 7) code to make it easier to observe the behavior I saw and to, hopefully, help other new c++ programmers that stumble upon this post.
Thanks in advance.
//best way to use a defined constant in two cpp files is to use a header (.h) file that is shared by the two cpp files.
//I want to minimize how many files I post, so I define DEMOMETHOD in two separate files
//also set to 1 or 0 in button_class.h
#define DEMOMETHOD 1 //SET TO 1 to make program break, set to 0 to make program work (do this in the button_class.h file too)
#if defined(UNICODE) && !defined(_UNICODE)
#define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
#define UNICODE
#endif
#include <tchar.h> //for _T
//windows 7 flags
#define WINVER 0x0601
#define _WIN32_WINNT 0x0601 // Windows 7 https://learn.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=vs-2019
#define _WIN32_IE 0x0700 //windows 7
#define WIN32_LEAN_AND_MEAN //results in smaller exe
#include <windows.h>
#include <iostream>
#include "button_class.h"//my button class
using namespace std;
//forward declares
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM);
std::string str(long data);
//declares-pre
#define IDC_CHECKBOX101 101
#define IDC_BUTTON1 102
#define IDC_BUTTON2 103
#define IDC_STATIC 104
//globals, create 4 buttons using our button class, 2 will be subclassed
Button bt,cbt,bt1,bt2;//button and checkbox button
HWND hDlg,hstatic; //main window and static label
TCHAR szClassName[ ] = _T("MyWindowsApp");
HINSTANCE hInstance;
//main window, starts the program
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInstance, LPSTR lpszArgument,int nCmdShow)
{
HWND hwnd;
MSG messages;
std::string tmp;
hInstance = hInst;//save instance to global variable
//create and register our main window class
WNDCLASSEX wincl; // Data structure for the windowclass
wincl.hInstance = hInst;
wincl.lpszClassName = szClassName;
wincl.lpfnWndProc = WndProc; // This function is called by windows
wincl.style = CS_DBLCLKS; // Catch double-clicks
wincl.cbSize = sizeof (WNDCLASSEX);
// Use default icon and mouse-pointer
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL; // No menu
wincl.cbClsExtra = 0; // No extra bytes after the window class
wincl.cbWndExtra = 0; // structure or the window instance
wincl.hbrBackground = (HBRUSH) (COLOR_3DFACE+1);
// Register the window class, and if it fails quit the program
if (!RegisterClassEx (&wincl)) {
MessageBox(NULL,"Failed to register WNDCLASSEX.","Error",MB_OK | MB_ICONINFORMATION);
return 1;//exit program on failure
}
//class is registered, now create main window
hwnd = CreateWindowEx (
0,//extended style
szClassName,
_T("Demo Multi Instance of Button Class"),
WS_OVERLAPPEDWINDOW, // styles - default window
CW_USEDEFAULT, // Windows decides the position
CW_USEDEFAULT, // where the window ends up on the screen
777, // The program's width (hardcoded for this demo)
411, // and height in pixels (hardcoded for this demo)
HWND_DESKTOP, // The window is a child-window to desktop
NULL, // No menu
hInst, // Program Instance handler
NULL // No Window Creation data
);
if (!hwnd) {
MessageBox(NULL,"Failed to create main window.","Error",MB_OK | MB_ICONINFORMATION);
return 1;//exit program on failure
}
hDlg = hwnd;//main window
// Make the window visible on the screen
ShowWindow (hwnd, nCmdShow);
UpdateWindow (hwnd);
while (GetMessage (&messages, NULL, 0, 0)) {
// Translate virtual-key messages into character messages
TranslateMessage(&messages);
// Send message to WindowProcedure (WndProc)
DispatchMessage(&messages);
}
// The program return-value is 0 - The value that PostQuitMessage() gave
return messages.wParam;
}
//handle messages intended for main window
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
std::string tmp;
LRESULT checked;
DWORD dwstyles,dwstylesex;
switch (message) { //wndproc handle the messages
case WM_CREATE://create controls
//always on top checkbox button (not subclassed)
dwstyles = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_AUTOCHECKBOX;
dwstylesex = 0;
cbt.t.hbutton = cbt.Create(hwnd,cbt,dwstylesex,dwstyles,277,8,122,26,(long) IDC_CHECKBOX101,hInstance,"Always on top",false);
//quit button (not subclassed)
dwstyles = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
dwstylesex = 0;
bt.t.hbutton = bt.Create(hwnd,bt,dwstylesex,dwstyles,691,7,65,30,(long) IDCANCEL,hInstance,"&Quit",false);
//button1 (subclassed)
dwstyles = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
dwstylesex = 0;
bt1.t.hbutton = bt1.Create(hwnd,bt1,dwstylesex,dwstyles,50,50,65,30,(long) IDC_BUTTON1,hInstance,"Button1",true);
if (bt1.t.hbutton == NULL) {
MessageBox(NULL,"button1 failed","error",0);
}
//button2 (subclassed)
dwstyles = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
dwstylesex = 0;
bt2.t.hbutton = bt2.Create(hwnd,bt2,dwstylesex,dwstyles,50,90,65,30,(long) IDC_BUTTON2,hInstance,"Button2",true);
if (bt2.t.hbutton == NULL) {
MessageBox(NULL,"button2 failed","error",0);
}
//create a statio control and display information about DEMOMETHOD
hstatic = CreateWindowEx(0,"Static",
#if (DEMOMETHOD == 0)
"Program should work correctly since DEMOMETHOD is set to 0",
#else
"Program should mess up since DEMOMETHOD is set to 1",
#endif
WS_CHILD | WS_VISIBLE,
50, 140,
450, 30,
hwnd,
(HMENU) IDC_STATIC,
hInstance,
NULL);
break;
case WM_SIZE:
break;
case WM_DESTROY: {
PostQuitMessage(0); // send a WM_QUIT to the message queue
break;
}
case WM_CLOSE: {
DestroyWindow(hwnd);
break;
}
case WM_COMMAND: {
switch (LOWORD(wParam)) {// LOWORD ctrlid. The HIWORD specifies the notification code.
case IDC_CHECKBOX101: { // always on top
//leftover functionality from another app
if (HIWORD(wParam) == BN_CLICKED) {
checked = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0);
if (checked) { // Force the program to stay always on top
SetWindowPos(hDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
} else { // else no more topmost program state
SetWindowPos(hDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
}
break;
}
case IDCANCEL: {//quit button
if (HIWORD(wParam) == BN_CLICKED) {
PostMessage(hDlg,WM_CLOSE,0,0);
return 0;
}
break;
}//idcancel
}// loword switch
break;
}//wm_command
default: // for messages that we don't deal with
return DefWindowProc (hwnd, message, wParam, lParam);
} //for switch(msg)
return 0;
}//end wndproc function
std::string str(long data)//convert non decimnal numeric to string
{
try {
return std::to_string((long) data);
} catch(int x) {
return std::to_string((long) data);
}
return "error in str function";
}
button_class.h
#ifndef BUTTONCLASSGUARD
#define BUTTONCLASSGUARD
//best way to use a defined constant in two cpp files is to use a header (.h) file that is shared by the two cpp files.
//I want to minimize how many files I post, so I define DEMOMETHOD in two separate files
//also set to 1/0 in main.cpp
#define DEMOMETHOD 1 //SET TO 1 to make program break (do this in main.cpp file too) set to 0 to make program work
#include <iostream>
//windows 7 flags
#define WINVER 0x0601 //windows 7
#define _WIN32_WINNT 0x0601 // Windows 7 https://learn.microsoft.com/en-us/cpp/porting/modifying-winver-and-win32-winnt?view=vs-2019
#define _WIN32_IE 0x0700 //windows 7
#define WIN32_LEAN_AND_MEAN //results in smaller exe
#include <windows.h>
//#include <windowsx.h> //for get_x_lparam
#include <commctrl.h> //for subclass safer also make sure linker calls libcomctl32.a (gcc compiler)
class Button //define the Button class
{
private:
public:
struct T { //seems like a more convenient way to allow sets/gets for a class that only I will use
long x;
long xx;
long y;
long yy;
HWND hparent;
HINSTANCE hinstance;
long ctrlid;
HWND hbutton;
} t;
Button() //constructor
{
}
~Button() //destructor
{
}
//forward declares
HWND Create(HWND hparent, Button &tbt,DWORD dwstylesex,DWORD dwstyles, int x,int y, int xx, int yy, long ctrlid, HINSTANCE hinst, std::string Caption, bool SubClassTF);
//next is our callback function for safe subclassing using setwindowsubclass
// https://learn.microsoft.com/en-us/windows/win32/controls/subclassing-overview
//stackoverflow article said static is needed and my testing confirmed this
static LRESULT CALLBACK OnEvent_Button(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
};//Button class end
#endif//buttonclassguard
button_class.cpp
//custom button class
#include "button_class.h"
extern std::string str(long data);//for demo I use extern keyword to give access to str function, normally that function is in a utilities class
HWND Button::Create(HWND hparent, Button &tbt, DWORD dwstylesex, DWORD dwstyles, int x,int y, int xx, int yy, long ctrlid, HINSTANCE hinst, std::string Caption, bool SubClassTF)
{
HWND result;
HGDIOBJ hFont = GetStockObject(ANSI_VAR_FONT);
bool tbool;
static long InstCount = 0;
std::string tmp;
tbt.t.hinstance = hinst;
tbt.t.x = x;
tbt.t.xx = xx;
tbt.t.y = y;
tbt.t.yy = yy;
tbt.t.hparent = hparent;
tbt.t.ctrlid = ctrlid;
//now create a button
tbt.t.hbutton = (HWND) NULL;
if (dwstyles == 0) {
dwstyles = WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | WS_CLIPSIBLINGS | BS_NOTIFY;
}
if (Caption == "") {
Caption = "&Ok";
}
result = CreateWindowEx(dwstylesex,"Button",
Caption.c_str(),
dwstyles,
tbt.t.x,tbt.t.y,
tbt.t.xx,tbt.t.yy,
hparent,
(HMENU) ctrlid,
hinst,
NULL);
if (!result) {
MessageBox(NULL, "Button creation Failed.", "Error", MB_OK | MB_ICONERROR);
return result;
}
if (hFont > 0) {
SendMessage(result, WM_SETFONT,(WPARAM) hFont, 0);
}
if (SubClassTF == true) {
//subclass if here
InstCount++;//instantiation count
//now subclass using safer method https://devblogs.microsoft.com/oldnewthing/20110506-00/?p=10723 (raymond chen) (safer subclass)
//the 'this' keyword seems to change its stripes depending on whether onevent_button is static or not
// if static it seems to track the instance of the class &tbt)
// if not static (remove static keyword from .h forward declare) it seems to point to the class not an instance of the class
//static is correct for this exercise
tbool = SetWindowSubclass(
result,//window being subclassed
reinterpret_cast<SUBCLASSPROC>(this->OnEvent_Button), //&tbt.OnEvent_Button), //&OnEvent_Button), //or &tbt.onevent_button worked too sort of onevent_button can be outside the class
InstCount, //id of this subclass, my choice
reinterpret_cast<DWORD_PTR>(this) //&tbt)//was: (&tbt) ken suspects use of this is ng since, for mult instances this points to the class not the instance of the class imho semi tested
);//returns bool with result
if (tbool == false) { //subclass failed if false
tmp = "subclass failed for " + Caption;
MessageBox(NULL,tmp.c_str(),"error",0);
}
} //subclass
tbt.t.hbutton = result;
return result;//return hwnd to caller
}//::create funtion end
LRESULT CALLBACK Button::OnEvent_Button(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
//Button *tp = (Button *) dwRefData;//I believe this is the C way, seems to work
Button *tp = reinterpret_cast<Button *>(dwRefData);//c++ way, works
std::string tmp;
switch(uMsg) {
case WM_LBUTTONDOWN: // onevent_button message
tmp = "I got clicked: uidsubclass: " + str(uIdSubclass) + " tp->t.y: " + str(tp->t.y) + " dwrefdata: " + str((long) dwRefData) + " hwnd: " + str((long)hwnd) + " click ypos: " + str((long)HIWORD(lParam));
MessageBox(NULL,tmp.c_str(),"clicked",0);
//question: why is return 0; needed (why do I need to eat the message?)
//if I break; instead, I call defwindowproc and that messes up how the class works if another subclassed button is clicked 2nd
//the nonsubclassed buttons also mess up if DEMOMETHOD is set to 1 and a subclassed button (1 or 2) is clicked before
//a nonsubclassed button. why is that?
#if (DEMOMETHOD == 0)//set demomethod to 1 to break the program
//if here the program/class should work well
MessageBox(NULL,"return 0 next - should work when you click on another button next","message",0);
return 0;//needed else 2nd call by a different subclassed control doesn't work
#else
MessageBox(NULL,"break is next - doesn't work if you click on another button next","message",0);
#endif
break;//calls defwindowproc -> messes up the ability of the class to distinguish between a button1 or button2 click
case WM_DESTROY: {//onevent_button message
//remove the subclass
//raymond says: https://devblogs.microsoft.com/oldnewthing/20031111-00/?p=41883
RemoveWindowSubclass(hwnd,
reinterpret_cast<SUBCLASSPROC>(&tp->OnEvent_Button), //thisfunctionname
reinterpret_cast<UINT_PTR>(uIdSubclass) //uidsubclass
);
break; //or return DefSubclassProc(hwnd,wMsg,lParam,wParam); //per raymond chen
} //wm_destroy
default:
//for regular subclassing: return CallWindowProc(OldButtonWndProc, hwnd, wm, wParam, lParam);
//for safer subclassing do this per: https://learn.microsoft.com/en-us/windows/win32/controls/subclassing-overview
break;//return DefSubclassProc(hwnd, uMsg, wParam, lParam);
}
//I'm a c++ beginner. I notice that most samples by experienced c++ programmers just return 0 next, however
//I find that I was using break in the switch(umsg) section and a call to defsubclassproc was needed so I am doing it this way
//note: for regular subclassing we return CallWindowProc(OldButtonWndProc...
return DefSubclassProc(hwnd, uMsg, wParam, lParam);//need this if you plan to use break for any case item
} //onevent_button
In fact, these two problems are caused by the same problem. When you use return 0, you will return directly without calling the DefSubclassProc function outside of switch.
If you use break, you will jump out switch, call the DefSubclassProc function again. This is why the same button is triggered continuously when the DEMOMETHOD value is 1.
So the solution is very simple, you just need to modify the following code:
LRESULT CALLBACK Button::OnEvent_Button(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
//Button *tp = (Button *) dwRefData;//I believe this is the C way, seems to work
Button* tp = reinterpret_cast<Button*>(dwRefData);//c++ way, works
std::string tmp;
switch (uMsg) {
case WM_LBUTTONDOWN: // onevent_button message
tmp = "I got clicked: uidsubclass: " + str(uIdSubclass) + " tp->t.y: " + str(tp->t.y) + " dwrefdata: " + str((long)dwRefData) + " hwnd: " + str((long)hwnd) + " click ypos: " + str((long)HIWORD(lParam));
MessageBox(NULL, tmp.c_str(), "clicked", 0);
//question: why is return 0; needed (why do I need to eat the message?)
//if I break; instead, I call defwindowproc and that messes up how the class works if another subclassed button is clicked 2nd
//the nonsubclassed buttons also mess up if DEMOMETHOD is set to 1 and a subclassed button (1 or 2) is clicked before
//a nonsubclassed button. why is that?
#if (DEMOMETHOD == 0)//set demomethod to 1 to break the program
//if here the program/class should work well
MessageBox(NULL, "return 0 next - should work when you click on another button next", "message", 0);
return 0;//needed else 2nd call by a different subclassed control doesn't work
#else
MessageBox(NULL, "break is next - doesn't work if you click on another button next", "message", 0);
#endif
break;//calls defwindowproc -> messes up the ability of the class to distinguish between a button1 or button2 click
case WM_DESTROY: {//onevent_button message
//remove the subclass
//raymond says: https://devblogs.microsoft.com/oldnewthing/20031111-00/?p=41883
RemoveWindowSubclass(hwnd,
reinterpret_cast<SUBCLASSPROC>(&tp->OnEvent_Button), //thisfunctionname
uIdSubclass //uidsubclass
);
break; //or return DefSubclassProc(hwnd,wMsg,lParam,wParam); //per raymond chen
} //wm_destroy
default:
return DefSubclassProc(hwnd, uMsg, wParam, lParam);
//for regular subclassing: return CallWindowProc(OldButtonWndProc, hwnd, wm, wParam, lParam);
//for safer subclassing do this per: https://learn.microsoft.com/en-us/windows/win32/controls/subclassing-overview
}
//I'm a c++ beginner. I notice that most samples by experienced c++ programmers just return 0 next, however
//I find that I was using break in the switch(umsg) section and a call to defsubclassproc was needed so I am doing it this way
//note: for regular subclassing we return CallWindowProc(OldButtonWndProc...
return 0; //need this if you plan to use break for any case item
} //onevent_button
Running my program it runs and due to me having a menu with EXIT to Destroy the window it runs and immediately exits the window. Unsure how to fix my issue here on compiling the program to have it not run the WindowProcedure function and passing the argument EXITMENU resulting in the Window being destroyed.
*.CPP
#include <windows.h>
#define HELPMENU 1
#define HIGHSCROREMENU 2
#define EXITMENU 3
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR args, int ncmdshow) {
WNDCLASS wc = { 0 }; // WNDCLASSW is a structure
LPCWSTR title = L"Window"; // Long Pointer Constant Wide (UTF-16) String
wc.hbrBackground = (HBRUSH)COLOR_WINDOW; // Background
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_HAND); // Sets Cursor
wc.hInstance = hInst; // Instance of window
wc.lpszClassName = L"windowClass"; // Class name
wc.lpfnWndProc = WindowProcedure; // Pointer to the function // Controller of window handle
if (!RegisterClassW(&wc)) { // Registers the window class
return -1;
}
// | binary combination value, posX, posY, Width, Height
// Creates the window
CreateWindow(wc.lpszClassName, title, WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_BORDER, 100, 100, 800, 600, NULL, NULL, NULL, NULL);
MSG msg = { 0 };
while (GetMessage(&msg, NULL, NULL, NULL) > 0) { // Keeps the window running
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
/* Event Paths */
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
case WM_CREATE: // On window creation
AddControls(hWnd);
AddMenu(hWnd);
break;
case WM_LBUTTONDOWN: // Left Mouse button
break;
case WM_DESTROY: // Makes GetMessage Function return false, closing the window
PostQuitMessage(0);
return 0;
case EXITMENU:
DestroyWindow(hWnd); // This part of the code shouldn't run on creation
break;
default:
return DefWindowProc(hWnd, msg, wp, lp);
}
}
/* Creates menu */
void AddMenu(HWND hWnd) {
hMenu = CreateMenu(); // Creates menu object
// AppendMenu(Menu Instance, Usage Type, Argument, String info);
AppendMenu(hMenu, MF_STRING, HELPMENU, L"Help - F1");
AppendMenu(hMenu, MF_STRING, HIGHSCROREMENU, L"Highscores - F2"); // Menu Created
AppendMenu(hMenu, MF_STRING, EXITMENU, L"Exit - ESC");
// SetMenu(Window Handle , Menu Instance);
SetMenu(hWnd, hMenu); // Sets menu for window //
}
You are not handling the menu commands correctly in your WindowProcedure().
You have defined EXITMENU as 3, which is the same value as the WM_MOVE message. So, in your switch, you are destroying your window as soon as it receives a WM_MOVE message during window creation.
You need to instead handle the menu commands via the WM_COMMAND message, per the documentation:
About Menus: Messages Used With Menus
When the user chooses a command item from a menu, the system sends a WM_COMMAND message to the window procedure. The low-order word of the WM_COMMAND message's wParam parameter contains the identifier of the chosen item. The window procedure should examine the identifier and process the message accordingly.
Try this instead:
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
...
case WM_COMMAND:
switch (wp) {
case HELPMENU: {
...
return 0;
}
case HIGHSCROREMENU: {
...
return 0;
}
case EXITMENU: {
DestroyWindow(hWnd);
return 0;
}
}
break;
}
...
}
return DefWindowProc(hWnd, msg, wp, lp);
}
UPDATE: That being said, consider having your EXITMENU handler use SendMessage(WM_CLOSE) instead of DestroyWindow(). If your app maintains data that should be saved when the app is closed by the user, you can add a WM_CLOSE handler to perform that action regardless of how the window is being closed (your exit menu, X close button, Alt-F4, etc). DefWindowProc() destroys a window when processing WM_CLOSE.
LRESULT CALLBACK WindowProcedure(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
switch (msg) {
...
case WM_CLOSE: {
if (data has been modified) {
prompt user to save data...
if (cancelled) {
return 0;
}
if (should save) {
save data ...
}
}
break;
}
case WM_COMMAND:
switch (wp) {
...
case EXITMENU: {
SendMessage(hWnd, WM_CLOSE, 0, 0);
return 0;
}
}
break;
}
...
}
return DefWindowProc(hWnd, msg, wp, lp);
}
I'm trying to understand how does the Message Loop and generally keyboard messages works in the Windows systems. I've written a simple program featuring the Message Loop:
// include the basic windows header file
#include <windows.h>
#include <windowsx.h>
#include <stdio.h>
// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);
char MsgBuff[300];
template <typename T>
char* BinVal(T SrcVal) {
const short BitsCount = sizeof(T) * CHAR_BIT;
const short SeparatorCount = sizeof(T) * 2 - 1;
static char BinValStr[BitsCount + SeparatorCount] = { 0 };
printf("BitsCount: %d\n", BitsCount);
printf("SeparatorCount: %d\n", SeparatorCount);
printf("BinValStr size: %d\n", BitsCount + SeparatorCount);
int i = 0;
int j = 0;
for (i = BitsCount + SeparatorCount - 1; i >= 0; i--) {
if ((j + 1) % 5 == 0) {
BinValStr[i] = ' ';
}
else {
if ((SrcVal & 1) == 1)
BinValStr[i] = '1';
else
BinValStr[i] = '0';
SrcVal >>= 1;
}
j++;
//printf(": %d, %d\n", i, SrcVal&1);
}
return BinValStr;
}
// the entry point for any Windows program
int WINAPI WinMain(
_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow) {
// the handle for the window, filled by a function
HWND hWnd;
// this struct holds information for the window class
WNDCLASSEX wc;
// clear out the window class for use
ZeroMemory(&wc, sizeof(WNDCLASSEX));
// fill in the struct with the needed information
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = L"WindowClass1";
// register the window class
RegisterClassEx(&wc);
// create the window and use the result as the handle
hWnd = CreateWindowEx(NULL,
L"WindowClass1", // name of the window class
L"Our First Windowed Program", // title of the window
WS_OVERLAPPEDWINDOW, // window style
300, // x-position of the window
300, // y-position of the window
500, // width of the window
400, // height of the window
NULL, // we have no parent window, NULL
NULL, // we aren't using menus, NULL
hInstance, // application handle
NULL); // used with multiple windows, NULL
// display the window on the screen
ShowWindow(hWnd, nCmdShow);
// enter the main loop:
// this struct holds Windows event messages
MSG msg;
// Enter the infinite message loop
while (TRUE) {
// Check to see if any messages are waiting in the queue
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// translate keystroke messages into the right format
TranslateMessage(&msg);
// send the message to the WindowProc function
DispatchMessage(&msg);
}
// If the message is WM_QUIT, exit the while loop
if (msg.message == WM_QUIT)
break;
}
// return this part of the WM_QUIT message to Windows
return msg.wParam;
}
int LoopCnt = 0;
int BinMask = 0b00000000'00000000'00000000'11111111;
// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch (message) {
case WM_DESTROY: {
PostQuitMessage(0);
} break;
case WM_KEYDOWN: {
sprintf_s(MsgBuff, 300, "WM_KEYDOWN(#%i) wParam (virtual-key code): %d (%Xh), scan code: %X, lParam(bin): %s\n", LoopCnt, wParam, wParam, lParam & BinMask, BinVal(lParam));
OutputDebugStringA(MsgBuff);
} break;
case WM_CHAR: {
sprintf_s(MsgBuff, 300, "WM_CHAR(#%i) wParam (virtual-key code): %d (%Xh), scan code: %X, lParam(bin): %s\n", LoopCnt, wParam, wParam, lParam & BinMask, BinVal(lParam));
OutputDebugStringA(MsgBuff);
} break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
LoopCnt++;
}
There i examine the WM_KEYDOWN message lParam param for the scan code send.
Now i have following questions:
Why pressing the left Alt key doesn't generate a WM_KEYDOWN message while pressing right Alt does? (the debugger doesn't go into the WM_KEYDOWN: case statement when pressing the left Alt)
Why pressing the right Alt key generates TWO WM_KEYDOWN messages simultaneously - having 1D(hex) & 38 (hex) scan codes respectively?
I am creating a simple window but when I see the window being created and closes it, not WM_QUIT message is ever gotten. Here's some code:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int cmdShow)
{
cWindowApplication app(hInstance);
const long width = 1024L;
const long height = 768L;
if (app.CreateWindowApplication(width, height) == false)
{
MessageBox(NULL, "Unable to create OpenGL Window", "An error occurred", MB_ICONERROR | MB_OK);
app.DestroyWindowApplication();
return 1;
}
return app.MainLoop();
}
Here's the CreateWindowApplication(int, int) function:
bool cWindowApplication::CreateWindowApplication(long width, long height, bool full_screen /*= false*/)
{
DWORD dwExStyle; // Window Extended Style
DWORD dwStyle; // Window Style
mWindowRect.left = 0L; // Set Left Value To 0
mWindowRect.right = width; // Set Right Value To Requested Width
mWindowRect.top = 0L; // Set Top Value To 0
mWindowRect.bottom = height; // Set Bottom Value To Requested Height
mFullScreen = full_screen;
// fill out the window class structure
const char* class_name = "MyClass";
mWindowClass.cbSize = sizeof(WNDCLASSEX);
mWindowClass.style = CS_HREDRAW | CS_VREDRAW;
mWindowClass.lpfnWndProc = cWindowApplication::StaticWindowsProcessCallback;
mWindowClass.cbClsExtra = 0;
mWindowClass.cbWndExtra = 0;
mWindowClass.hInstance = mhInstance;
mWindowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // default icon
mWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW); // default arrow
mWindowClass.hbrBackground = NULL; // don't need background
mWindowClass.lpszMenuName = NULL; // no menu
mWindowClass.lpszClassName = class_name;
mWindowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); // windows logo small icon
// register the windows class
if (!RegisterClassEx(&mWindowClass))
{
return false;
}
if (mFullScreen == true) //If we are Full Screen, we need to change the display mode
{
DEVMODE dmScreenSettings; // device mode
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = width; // screen width
dmScreenSettings.dmPelsHeight = height; // screen height
dmScreenSettings.dmBitsPerPel = BITS_PER_PIXEL; // bits per pixel
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
// setting display mode failed, switch to windowed
MessageBox(NULL, "Display mode failed", NULL, MB_OK);
mFullScreen = false;
}
}
if (mFullScreen == true) // Are We Still In Full Screen Mode?
{
dwExStyle = WS_EX_APPWINDOW; // Window Extended Style
dwStyle = WS_POPUP; // Windows Style
//ShowCursor(false); // Hide Mouse Pointer
}
else
{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style
dwStyle = WS_OVERLAPPEDWINDOW; // Windows Style
}
AdjustWindowRectEx(&mWindowRect, dwStyle, false, dwExStyle); // Adjust Window To True Requested Size
// class registered, and create our window
mHWND = CreateWindowEx(NULL, // extended style
class_name, // class name
"My Windows", // application name
dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, // x,y coordinate
mWindowRect.right - mWindowRect.left,
mWindowRect.bottom - mWindowRect.top, // width, height
NULL, // handle to parent
NULL, // handle to menu
mhInstance, // application instance
this); // this pointer to call member functions
// check if window creation failed (hwnd would equal NULL)
if (mHWND == false)
{
return false;
}
mHDC = GetDC(mHWND);
ShowWindow(mHWND, SW_SHOW); // display the window
UpdateWindow(mHWND); // update the window
return true;
}
Basically after this function call, the CreateWindowEx() function will call StaticWindowProcessCallback() that looks like this:
LRESULT cWindowApplication::StaticWindowsProcessCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
cWindowApplication* win_app = NULL;
if (msg == WM_CREATE)
{
//Creation event
//Get the pointer we pass during CreateWindowApplication() call
win_app = (cWindowApplication*)((LPCREATESTRUCT)lParam)->lpCreateParams;
//Associate window pointer with the hwnd for the other events to access
SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)win_app);
}
else
{
//Non-creation event
win_app = (cWindowApplication*)GetWindowLongPtr(wnd, GWLP_USERDATA);
if (win_app != NULL)
{
return DefWindowProc(wnd, msg, wParam, lParam);
}
}
//call member
return win_app->WindowsProcessCallback(wnd, msg, wParam, lParam);
}
Finally, the last line of this function calls the member function WindowProcessCallback() that looks like this:
LRESULT cWindowApplication::WindowsProcessCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
mHDC = GetDC(wnd);
SetupPixelFormat();
//Set the version that we want, in this case 3.0
int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, 0 }; //zero indicates the end of the array
//Create temporary context so we can get a pointer to the function
HGLRC tmp_context = wglCreateContext(mHDC);
//Make it current
wglMakeCurrent(mHDC, tmp_context);
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
if (wglCreateContextAttribsARB == NULL)
{
//No OpenGL 3.0, back to 2.0
mHGLRC = tmp_context;
}
else
{
//Create OpenGL 3.0
mHGLRC = wglCreateContextAttribsARB(mHDC, 0, attribs);
//Delete the temp context
wglDeleteContext(tmp_context);
}
//Make OpenGL 3.0
wglMakeCurrent(mHDC, mHGLRC);
mIsRunning = true;
}
break;
case WM_QUIT:
case WM_DESTROY:
case WM_CLOSE:
wglMakeCurrent(mHDC, NULL);
wglDeleteContext(mHGLRC);
mIsRunning = false;
PostQuitMessage(0); //Send a WM_QUIT message
return 0;
default:
break;
}
return DefWindowProc(wnd, msg, wParam, lParam);
}
As you can see, there are some message processing code there ... but other than the WM_CREATE, no other cases are being hit. After the WM_CREATE message being sent, the function MainLoop() is being called that looks like this:
int cWindowApplication::MainLoop()
{
while (mIsRunning == true)
{
ProcessWindowsMessages();
}
DestroyWindowApplication();
return 0;
}
Basically the ProcessWindowsMessages() function does not get any message after the window closes ... I have to press stop VS from running in order to kill the process. The ProcessWindowsMessages() function looks like this:
void cWindowApplication::ProcessWindowsMessages()
{
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
This logic in StaticWindowsProcessCallback looks backwards:
if (win_app != NULL)
{
return DefWindowProc(wnd, msg, wParam, lParam);
}
If you don't have a pointer to the window wrapper object, you'll need to call DefWindowProc. So that should happen if (win_app == NULL). This is to handle a handful of messages that are sent before WM_CREATE. As a result of this, your code has undefined behavior on messages processed before WM_CREATE, and discards (by applying default processing) all messages after WM_CREATE.
It would be even better to use WM_NCCREATE for setting up the link, though. As well, win_app is not a very good name for this, maybe win_obj or something.
You also shouldn't handle WM_QUIT in your window procedure, since it isn't sent to a window. And the default behavior of WM_CLOSE should be fine, it will call DestroyWindow which will trigger WM_DESTROY.
But the first, failure to forward any messages after WM_CREATE to your window procedure, likely explains your lack of WM_QUIT in the main message loop.
I built my own c++framwork that is similar with MFC, I can create child window when WM_CREATE message was received, but I can not deal with Edit Box, I got the idea from Ivan Shcherbakov's post, I post a WM_CREATEMYWINDOW message after call OnCreate, and Create something in OnCreateMyWindow function when the message was received, and, of course, it works. but I want to know why and how to fix this problem.
my CWnd is CMyWindow, I created a CBT-hook to hold the HWND and CMyWindow* of new window, then add to a HWND-WINDOW map as WindowManager, the message loop callback function MyWndProc, got the CMyWindow* from WindowManger by hWnd parameter, then call CMyWindow's message functions OnCreate, OnSize, OnMove ..., etc. it just like the CWnd class.
CMyWindow seems work well, it can hold all messages and do something to response, but it can not create a edit box when WM_CREATE, it's so wired, because it create a new window with any style when WM_CREATE.
I built it with vs2010, win7.
the WindowManager (HWND-WINDOW map) in MyWindow.h
#define WM_CREATEMYWINDOW WM_USER + 123
//the CWindowManager is a map of HWND-CMyWindow
class CMyWindow;
//define the HWND-CMyWindow map
typedef map <HWND, CMyWindow*> CWindowMap;
typedef pair<HWND, CMyWindow*> WindowPair;
typedef map <HWND, CMyWindow*>::iterator WndIterator;
typedef pair<WndIterator, bool> IterBool;
class CWindowManager : private CWindowMap
{
private:
CWindowManager(void);
~CWindowManager(void);
public:
bool Add(CMyWindow* pwnd); //add a window to map
bool Remove(HWND hwnd); //remove a window by hwnd
void Clear(); //remove all items
CMyWindow* Find(HWND hwnd); //find the window by hwnd
public:
//get CWindowManager instance as singleton pattern
static CWindowManager * GetInstance();
};
CMyWindow class is similar with CWnd of MFC
class CMyWindow
{
public:
CMyWindow(void);
~CMyWindow(void);
inline HWND GetSafeHwnd(); //get HWND
//Create a window
bool Create( HINSTANCE hInstance,
TCHAR * szWindowName,
DWORD dwStyle,
RECT& rect,
HWND hParentWnd,
HMENU hMenu,
LPVOID lpParam);
//add the window to WindowManager
void Attach(HWND hwnd);
//remove the window from WindowManager
void Dettach();
//process the WM_CREATE message
virtual int OnCreate(LPCREATESTRUCT ps);
/*
other message function
*/
//process the WM_CREATEMYWINDOW message
virtual void OnCreateMyWindow();
protected:
HWND _hwnd;
HINSTANCE _hInst;
};
WindowManager and CBT-HOOK part in MyWindow.cpp
#include "StdAfx.h"
#include "LockEx.h"
#include "MyWindow.h"
//window class name
static TCHAR * _MY_WINDOW_CLASS_NAME_ = L"MYWINDOWCLASS";
//window-proc
LRESULT CALLBACK MyWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
///an Mutex-Lock class for keep current hwnd of new window, but you can ignore it when using single thread to crate window
static CLockEx gLockInitWnd;
///keep current hwnd of new window
static CMyWindow * gpInitWnd = 0;
///an hook to create new window
static HHOOK ghook = 0;
///set gpInitWnd when create a new window
static void SetInitWnd(CMyWindow * pWnd)
{
CLockEx::Scoped lock(gLockInitWnd);
gpInitWnd = pWnd;
}
///clear gpInitWnd after new window created
static void UnsetInitWnd()
{
CLockEx::Scoped lock(gLockInitWnd);
gpInitWnd = 0;
}
///get the HWND of new window that is creating
static CMyWindow * GetInitPwnd()
{
CLockEx::Scoped lock(gLockInitWnd);
return gpInitWnd;
}
//CBT-Proc for SetWindowsHookEx
static LRESULT CALLBACK MyCBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_CREATEWND) {
//get the new window from gpInitWnd
CMyWindow * pwnd = GetInitPwnd();
if (pwnd) {
//first time call this proc
//add this window to WindowManager
pwnd->Attach((HWND)wParam);
return (0);
} else {
//sencond time call this proc
CREATESTRUCT * cs = ((CBT_CREATEWND *)lParam)->lpcs;
if (!(cs->style & WS_CHILD)) {
//we can do something here
return (0);
} else {
return (1); //error, destroy the window
//or, maybe, CallNextHookEx
}
}
} else
return CallNextHookEx(ghook, nCode, wParam, lParam);
}
//Create a WH_CBT Hook
static bool HookCrate()
{
HANDLE hThread = GetCurrentThread();
DWORD dwThreadId = GetThreadId(hThread);
if (hThread) {
ghook = SetWindowsHookEx(
WH_CBT,
MyCBTProc, //set the CBT proc
0,
dwThreadId);
if (!ghook)
return false;
}
return (0);
}
//Destroy WH_CBT Hook
static void HookDestroy()
{
if (ghook) {
UnhookWindowsHookEx(ghook);
ghook = 0;
}
}
///////////////////////////////////////////////
//this is a vector for keep all CMyWindow*
CWindowManager::CWindowManager(void)
{
}
CWindowManager::~CWindowManager(void)
{
clear();
}
//insert new window
bool CWindowManager::Add(CMyWindow* pwnd)
{
IterBool ib = insert(WindowPair(pwnd->GetSafeHwnd(), pwnd));
return ib.second;
}
//remove a window by hwnd
bool CWindowManager::Remove(HWND hwnd)
{
WndIterator wi = find(hwnd);
if (wi == end( )) {
return false;
} else {
erase(wi);
return true;
}
}
//find a window by hwnd
CMyWindow* CWindowManager::Find(HWND hwnd)
{
WndIterator wi = find(hwnd);
if (wi == end( )) {
return (0);
} else {
return wi->second;
}
}
//remove all items
void CWindowManager::Clear()
{
clear();
}
//get instance as singleton pattern.
CWindowManager * CWindowManager::GetInstance()
{
static CWindowManager wm;
return &wm;
}
register window class, set the WndProc to MyWIndowProc
ATOM RegisteWindowClass(HINSTANCE hInstance, TCHAR * szClassName)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = MyWindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = 0;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = 0;
wcex.lpszClassName = szClassName;
wcex.hIconSm = 0;
return RegisterClassEx(&wcex);
}
CMyWindow class, please pay attention on my trouble at WM_CREATE and WM_CREATEMYWINDOW message
CMyWindow::CMyWindow(void)
: _hwnd(0)
, _hInst(0)
{
}
CMyWindow::~CMyWindow(void)
{
}
inline HWND CMyWindow::GetSafeHwnd()
{
return _hwnd;
}
//Craete a window
bool CMyWindow::Create( HINSTANCE hInstance,
TCHAR * szWindowName,
DWORD dwStyle,
RECT& rect,
HWND hParentWnd,
HMENU hMenu,
LPVOID lpParam)
{
//get safe instance
HINSTANCE hInst = hInstance;
if (!hInstance)
if (!hParentWnd)
return false;
else
hInst = (HINSTANCE) GetWindowLong(hParentWnd, GWL_HINSTANCE);
if (!hInst)
return false;
//register window class
if (!RegisteWindowClass(hInst, _MY_WINDOW_CLASS_NAME_)) {
DWORD dwErr = GetLastError();
if (dwErr != ERROR_CLASS_ALREADY_EXISTS) //0x00000582
return false;
}
//claim i am creating
SetInitWnd(this);
//create CBT hook, then this window will add to WindowManager
HookCrate();
//create window
HWND hwnd = CreateWindow(
_MY_WINDOW_CLASS_NAME_,
szWindowName,
dwStyle,
rect.left, rect.right, rect.right - rect.left, rect.bottom - rect.top,
hParentWnd,
hMenu,
hInstance,
lpParam);
//destroy CBT hook
HookDestroy();
if (!hwnd)
return false;
_hwnd = hwnd;
_hInst = hInst;
//show window
ShowWindow(_hwnd, SW_SHOW);
UpdateWindow(_hwnd);
return true;
}
//add the this window to WindowManager
void CMyWindow::Attach(HWND hwnd)
{
_hwnd = hwnd;
CWindowManager::GetInstance()->Add(this);
UnsetInitWnd();
}
//remove the this window to WindowManager
void CMyWindow::Dettach()
{
CWindowManager::GetInstance()->Remove(_hwnd);
_hwnd = 0;
}
int CMyWindow::OnCreate(LPCREATESTRUCT ps)
{
return (0);
}
void CMyWindow::OnCreateMyWindow()
{
}
//the WndProc callback function
LRESULT CALLBACK MyWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
//Get the CMyWindow instance from WindowManager by hWnd
CMyWindow * pwnd = CWindowManager::GetInstance()->Find(hWnd);
//can not find thi window in WindowManager
if (!pwnd) return DefWindowProc(hWnd, message, wParam, lParam);
switch (message)
{
case WM_CREATE:
{
//perform the OnCreate function, just like MFC's OnCreate
int r = pwnd->OnCreate(reinterpret_cast<LPCREATESTRUCT>(lParam));
if (r) //some error occurred, will destory the window
return (r);
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//I can not create any edit box in OnCreate function,
//I must do it leater, when the window was created and
//WM_CREATEMYWINDOW was received
::PostMessage(hWnd, WM_CREATEMYWINDOW, 0, 0);
}
break;
/*
case WM_.....
other message
case WM_.....
*/
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_CREATEMYWINDOW:
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//I can not create any edit box in OnCreate function,
//I must do it when WM_CREATEMYWINDOW was received
pwnd->OnCreateMyWindow();
break;
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
Now, inherit the class CMyWindow to create 2 windows, first window will create the second one that with a edit box
class CMyWnd2 : public CMyWindow
{
public:
CMyWnd2(void) : _hCmdEdit(0) {}
~CMyWnd2(void){}
protected:
HWND _hCmdEdit;
//create edit box
virtual void OnCreateMyWindow();
};
class CMyWnd1 : public CMyWindow
{
public:
CMyWnd1(void) {}
~CMyWnd1(void){}
protected:
virtual int OnCreate(LPCREATESTRUCT ps);
//create window2
virtual void OnCreateMyWindow();
CMyWnd2 _wnd2;
};
int CMyWnd1::OnCreate(LPCREATESTRUCT /*ps*/)
{
//Can create window2, but can not crate window2's edit-boxs
return (0);
}
//create window2 with edit box
void CMyWnd1::OnCreateMyWindow()
{
RECT rect = {0, 0, 400, 300};
_wnd2.Create(this->_hInst,
0,
WS_VISIBLE | WS_POPUP,
rect,
_hwnd,
0,
0);
}
//create edit box
void CMyWnd2::OnCreateMyWindow()
{
RECT rect;
GetClientRect(_hwnd, &rect);
_hCmdEdit = CreateWindowEx(
WS_EX_STATICEDGE,
L"EDIT",
NULL,
WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
0,
rect.bottom - 80,
rect.right - rect.left,
80,
_hwnd,
(HMENU) 100,
(HINSTANCE) GetWindowLong(_hwnd, GWL_HINSTANCE),
NULL);
}
Finally, WinMain function create a instance of CMyWnd1
CMyWnd1 mainwnd;
RECT rect;
rect.left = rect.top = 0, rect.right = 500, rect.bottom = 400;
mainwnd.Create(hInstance,
L"MyWindow",
WS_OVERLAPPEDWINDOW,
rect,
0,
0,
0);
while (GetMessage(&msg, NULL, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Here must have something I missed, any one can help me fix this problem?
Please note that WM_CREATE message is sent before CreateWindow() returns. This means window handle member _hwnd (note: identifiers starting with _ and __ shall not be used, they are reserved for compiler extensions) is not yet initialized and contains 0. Therefore subsequent CreateWindow() call fails because you must pass valid window handle to create child window (that has WS_CHILD style). But it is allowed for popup windows (WS_POPUP style). That is very probably the reason why Wnd2 is created, but edit box is not.
i got it, a stupid mistake occurred in CBThook callback function, i refused the creation request for a window with WS_CHILD style when this function was called by second time. it only need return a zero!! please see the error in comments. now it works! thanks Rost's hint.
static LRESULT CALLBACK MyCBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_CREATEWND) {
//get the new window from gpInitWnd
CMyWindow * pwnd = GetInitPwnd();
if (pwnd) {
//first time call this proc
//add this window to WindowManager
pwnd->Attach((HWND)wParam);
return (0);
} else {
//sencond time call this proc
return (0);
////
/// below was my stupid code,
////
/*CREATESTRUCT * cs = ((CBT_CREATEWND *)lParam)->lpcs;
if (!(cs->style & WS_CHILD)) {
//we can do something here
return (0);
} else {
return (1); //error, destroy the window
//or, maybe, CallNextHookEx
}*/
}
} else
return CallNextHookEx(ghook, nCode, wParam, lParam);
}