Win32 textbox control alt keypress message - winapi

I need to hookup alt+alphanumberic keypress inside a child window control.
I have written this program to test it's functionality. But it seems it does not response to keystrokes
like ALT+X , ALT+X which are necessary for my application(keyboard translator).
#include <windows.h>
#include <stdio.h>
#include <wchar.h>
#include <tchar.h>
#include <string.h>
#ifndef NULL
#define NULL 0
#endif
/* glaobal variables */
HWND hwndEdit = NULL;
HWND hwndWindow = NULL;
WCHAR* lpszClassName = L"Vijesekara Keyboard Test Application";
WCHAR* lpszAppName = L"Vijesekara Keyboard Test Application";
int main_window_width = 0;
int main_window_height = 0;
WNDPROC edit_old_wndproc;
/*
GWL_WNDPROC
*/
#ifndef GWL_WNDPROC
#define GWL_WNDPROC (-4)
#endif
/* child window ID's */
#ifndef __CHILD_WINDOW_EDIT_ID__
#define __CHILD_WINDOW_EDIT_ID__
#define ID_EDIT 2000
// :TODO: more comes from here //
#endif
/* constrains on child window positions */
#ifndef __CHILD_WINDOW_POSITION__
#define __CHILD_WINDOW_POSITION__
#define EDIT_CHILD_LEFT_PRECENTAGE 0.05
#define EDIT_CHILD_RIGHT_PRECENTAGE 0.05
#define EDIT_CHILD_TOP_PRECENTAGE 0.10
#define EDIT_CHILD_BOTTOM_PRECENTAGE 0.10
// :TODO: Other controls //
#endif
/* main window procedure */
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam,LPARAM lParam);
/* edit box procedure */
LRESULT CALLBACK EditBoxProc(HWND ,UINT , WPARAM , LPARAM );
/* resize child windows */
void ResizeChildWindows();
/* win main entry */
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW |CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.hInstance = hInstance ;
wndclass.hIcon = (HICON)LoadIcon(NULL,IDI_APPLICATION);
wndclass.hCursor = (HCURSOR)LoadCursor(NULL,IDC_ARROW);
wndclass.hbrBackground =(HBRUSH) GetStockObject( WHITE_BRUSH );
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = lpszClassName ;
/* Register class */
int result ;
result = RegisterClass( &wndclass) ;
/* if failed to register window */
if ( 0 == result )
{
MessageBox(NULL,L"Registering Window Have Been Failed" , lpszAppName, MB_OK);
exit(0);
}
/* Create the window */
hwndWindow = CreateWindow(lpszAppName , lpszAppName ,WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,\
CW_USEDEFAULT, CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
if(NULL == hwndWindow )
{
MessageBox(NULL,L"Window Creation have being failed", lpszAppName,MB_OK);
exit(0);
}
ShowWindow(hwndWindow, iCmdShow);
/* Enter message loop */
MSG msg ;
while(GetMessage(&msg,NULL, 0,0 ))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
/* window procedure */
LRESULT CALLBACK WndProc (HWND hwnd, UINT message,WPARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_CREATE:
if( hwndEdit != NULL)
{
MessageBox(NULL,L"Edit control handle have already initialized",lpszAppName , MB_OK);
exit(0);
}
hwndEdit = CreateWindow( L"edit" ,NULL, WS_CHILD|WS_VISIBLE|WS_VSCROLL\
|WS_BORDER|ES_LEFT|ES_MULTILINE|ES_AUTOVSCROLL,0,0,0,0,hwnd,(HMENU)ID_EDIT\
,GetModuleHandle(NULL),NULL);
if( NULL == hwndEdit )
{
MessageBox(hwnd, L"Edit child window control creation failed", lpszAppName, MB_OK);
exit(0);
}
edit_old_wndproc = (WNDPROC)SetWindowLongPtr(hwndEdit,GWL_WNDPROC,(LONG_PTR)EditBoxProc );
break;
case WM_SIZE:
main_window_height = HIWORD(lParam);
main_window_width = LOWORD (lParam);
ResizeChildWindows();
return 0;
case WM_COMMAND:
/*:TODO:process */
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hwnd,message,wParam,lParam);
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
/* resize window procedure */
void ResizeChildWindows()
{
MoveWindow(hwndEdit,main_window_width*EDIT_CHILD_LEFT_PRECENTAGE , main_window_height* EDIT_CHILD_TOP_PRECENTAGE , \
main_window_width * (1- EDIT_CHILD_LEFT_PRECENTAGE - EDIT_CHILD_RIGHT_PRECENTAGE) ,// height
main_window_height * (1- EDIT_CHILD_TOP_PRECENTAGE - EDIT_CHILD_BOTTOM_PRECENTAGE), TRUE // width
);
}
static int alt=0;
static int ctl=0;
static int sys =0;
WCHAR buffer[1024];
LRESULT CALLBACK EditBoxProc(HWND hwnd, UINT message, WPARAM wParam , LPARAM lParam)
{
switch ( message)
{
case WM_SYSCHAR:
sys =1;
if( lParam && (1<<29) ) { alt= 1;}
else {
alt=0;
}
case WM_KEYPRESS:
wsprintf( buffer,TEXT("KeyCode is :%d"), wParam);
MessageBox( NULL,buffer, TEXT("Vijesekara Keyboard"), MB_OK);
SetFocus(hwnd);
/* keyboard procedure */
alt =0;
if ( sys==1)
{
sys=0;
break;
}
return 0;
default:
break;
}
return CallWindowProc(edit_old_wndproc ,hwndEdit,message,wParam,
lParam);
}
// end of vijesekara_keyboard.cpp
Note: Inside the subclassed textbox , the WM_CHAR message is not working for the alt keystrokes, I it does
not sent a WM_CHAR message with alt key pressed to the for AlT+keystokes.So I log all the messages using spy++
and then I found WM_SYSCHAR have been sent instead of WM_CHAR for those messages inside a editbox child
window.
Any idea how could I do this?
--Thanks in advance--

Okay this is pretty strange,
This is my spy+ output for the Alt+A key.
So then I have hook the WM_SYSKEYUP message, and the Fixup is this,
case WM_SYSKEYUP:
if( lParam && (1<<29) ) {alt=1;}else{break;}
if( wParam == 'A' || wParam=='Z'|| wParam =='X' || wParam== 'W' || wParam == 'Y' ){}else{break;}
case WM_SYSCHAR:
sys =1;
if( lParam && (1<<29) ) { alt=1; }
else {
alt=0;
break;
}
case WM_CHAR:
wsprintf( buffer,TEXT("KeyCode is :%d"), wParam);
MessageBox( NULL,buffer, TEXT("Vijesekara Keyboard"), MB_OK);
SetFocus(hwnd);
/* keyboard procedure */
alt =0;
return 0;
And also when I break (calling the default window procedure) on WM_SYSKEYU it gives a nasty ding-dong sound
inside the Windows.I have return it then that sound disappear.
What arx claim is very strange. And Yes I have mistaken WM_KEYPRESS, there's no something called WM_KEYPRESS.
And It should be corrected to WM_CHAR. According to him without these chances MessageBox appears. That's
pretty strange. #Arx, you could please post your answer, so somebody else who digging it may benefit, and also
I could give you some credits too. And also dig why was that happened and after that we could accept that
and this answer would keep as an alternative answer (does not address the question, but have fixed the
application).

Related

Is it possible to subclass a web browser using winapi c++?

The main goal is to block maximalize web browser window using subclassing and dll.
I have 2 apps: injector and the dll.
In injector app I load that dll, find window by title, get functions from dll and execute that functions ( their names are hook and unhook ) from dll. So this is standard injector. Of course I check is something NULL and I don't get any errors.
In dll I have 5 functions:
dllMain (here I only set global hInstance variable, which is in shared memory ):
BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)
{
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
{
if (hInstance == NULL)
{
hInstance = hinstDLL;
}
break;
}
...
}
return TRUE;
}
Hook ( HandleofTarget is the HWND, which I get from FindWindow ; I use this function in injector ):
extern "C" __declspec(dllexport) bool hook( HWND HandleofTarget)
{
hTarget=HandleofTarget;
hhook=SetWindowsHookEx(WH_CBT,cbtHookProc,hInstance, GetWindowThreadProcessId(hTarget,NULL));
if(hhook==NULL)
return 0;
else
return 1;
}
Unhook ( here I unhook hooks - I use this function in injector):
extern "C" __declspec(dllexport) void unhook(void)
{
if(hhook != NULL)
UnhookWindowsHookEx( hhook );
}
cbtHookProc ( hook callback, where I change window procedure ):
LRESULT CALLBACK cbtHookProc( int code, WPARAM wParam, LPARAM lParam )
{
if( code < 0 ) return CallNextHookEx( 0, code, wParam, lParam );
if (code == HCBT_ACTIVATE)
{
if((HWND)(wParam)==hTarget)
{
if(done == FALSE)
{
g_OldWndProc =(WNDPROC)(SetWindowLongPtr ( (HWND)(wParam), GWLP_WNDPROC,reinterpret_cast<LONG_PTR>( NewWndProc )));
done = TRUE;
}
}
}
return CallNextHookEx( 0, code, wParam, lParam );
}
NewWndProc ( new Window procedure, where I would like to block maximalize ):
LRESULT CALLBACK NewWndProc( HWND hwnd, UINT mesg, WPARAM wParam, LPARAM lParam )
{
switch( mesg )
{
case WM_SYSCOMMAND:
{
if(wParam == SC_MAXIMIZE)
{
return 1;
}
}
break;
}
return CallWindowProc( g_OldWndProc, hwnd, mesg, wParam, lParam );
}
When I test this dll with other apps - it works. When I use this dll with web browser like Internet Edge and Google Chrome - it doesn't works. That web browser, which I try injected works slower, but I can still maximalize that window. When I debuq dll, in web browser after SetWindowsHookEx I see that hook is not NULL, but my code doesn't go to cbtHookProc. What is going on with web browser?
UPDATE:
One more time - thank you Strive Sun - MSFT for helping me.
I change the lines in cbtHookProc, but it still doesn't work. My cbtHookProc is don't called by webBrowser - that is problem.
When I looked at your gif I see something what I don't have and I think that is the problem. My injector app looks like this:
hDll = LoadLibrary( L"dllka10" );
hHookedWindow=FindWindow(TEXT("Chrome_WidgetWin_1"),TEXT("Nowa karta - Google Chrome"));
if( hDll && hHookedWindow)
{
qDebug()<<"hDll and hHookedWindow are not NULL!";
funHook =( MYPROC2 ) GetProcAddress( hDll, (LPCSTR) "hook" );
funUnhook = ( MYPROC ) GetProcAddress( hDll, (LPCSTR) "unhook" );
if( funHook )
{
qDebug()<<funHook(hHookedWindow);
}
}
I don't use CreateThread(). Is it important here?
UPDATED 2
LRESULT CALLBACK cbtHookProc( int code, WPARAM wParam, LPARAM lParam )
{
if( code < 0 ) return CallNextHookEx( 0, code, wParam, lParam );
std::fstream file;
file.open("C:\\Users\\tom\\Desktop\\logs.txt",std::ios::out|std::ios::app);
file<<"In cbtHook function!"<<std::endl;
file.close();
if (code == HCBT_MINMAX)
{
if (LOWORD(lParam) == SW_SHOWMAXIMIZED)
{
return 1;
}
}
return CallNextHookEx( 0, code, wParam, lParam );
}
When I run chrome application - my logs.txt is empty. When I run other app - I have logs.
UPDATED 3
In my dll I have:
#ifdef __GNUC__
HWND hTarget __attribute__((section (".shared"), shared)) =NULL;
HWND hApp __attribute__((section (".shared"), shared)) = NULL;
bool done __attribute__((section (".shared"), shared)) =FALSE;
HINSTANCE hInstance __attribute__((section (".shared"), shared)) =NULL;
HHOOK hhook __attribute__((section (".shared"), shared)) = NULL;
WNDPROC g_OldWndProc __attribute__((section (".shared"), shared)) = NULL;
#endif
#ifdef _MSC_VER
#pragma data_seg(".shared")
HWND hTarget=NULL;
HWND hApp = NULL;
bool done=FALSE;
HINSTANCE hInstance=NULL;
HHOOK hhook = NULL;
WNDPROC g_OldWndProc = NULL;
#pragma data_seg()
#pragma comment(linker, "/section:.shared,RWS")
#endif
in my injector I don't have any pragma - I have only ( QT ):
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <iostream>
#include "string.h"
#include "windows.h"
I can't reproduce your problem, but you can try another easier method.
HCBT_MINMAX : Specifies, in the low-order word, a show-window value
(SW_) specifying the operation. For a list of show-window values, see
the ShowWindow. The high-order word is undefined.
if (code == HCBT_MINMAX)
{
if (LOWORD(lParam) == SW_SHOWMAXIMIZED)
{
return 1;
}
}
No need to use HCBT_ACTIVATE to obtain window handles and compare their handles.
Updated:
Code works fine.
My code sample:
DLL:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include "stdio.h"
#include <windows.h>
HHOOK hCBT = NULL;
HWND hTarget;
HMODULE thisModule;
LRESULT CALLBACK _cbtProc(int code, WPARAM wParam, LPARAM lParam)
{
if (code < 0) return CallNextHookEx(0, code, wParam, lParam);
if (code == HCBT_MINMAX)
{
if (LOWORD(lParam) == SW_SHOWMAXIMIZED)
{
return 1;
}
}
return CallNextHookEx(0, code, wParam, lParam);
}
#ifdef __cplusplus //If used by C++ code.
extern "C" { //we need to export the C interface
#endif
__declspec(dllexport) BOOL WINAPI InstallHooks(HWND HandleofTarget)
{
hTarget = HandleofTarget;
DWORD tid = GetWindowThreadProcessId(hTarget, NULL);
hCBT = SetWindowsHookEx(WH_CBT, _cbtProc, thisModule, tid);
return (hCBT) ? TRUE : FALSE;
}
#ifdef __cplusplus
}
#endif
#ifdef __cplusplus //If used by C++ code.
extern "C" { //we need to export the C interface
#endif
__declspec(dllexport) void WINAPI RemoveHooks()
{
UnhookWindowsHookEx(hCBT);
MessageBox(NULL, L"unhook", L" ", MB_OK);
hCBT = NULL;
}
#ifdef __cplusplus
}
#endif
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
thisModule = hModule;
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
main.cpp
#include <Windows.h>
#include <stdio.h>
#include <psapi.h>
#include <shlwapi.h>
#include <tchar.h>
#pragma comment(lib,"Kernel32.lib")
#pragma comment(lib,"shlwapi.lib")
#pragma comment(linker, "/SECTION:.shared,RWS")
using namespace std;
HINSTANCE hinstDLL;
typedef void (*RemoveHooks)();
DWORD __stdcall CreateThreadFunc(LPVOID)
{
while (1)
{
if (GetAsyncKeyState(0x50) & 0x0001)
{
RemoveHooks removeHooks = (RemoveHooks)GetProcAddress(hinstDLL, "RemoveHooks");
removeHooks();
}
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
if (message == WM_DESTROY) {
PostQuitMessage(0);
}
return DefWindowProc(hwnd, message, wParam, lParam);
};
int main()
{
HWND hwnd = FindWindow(L"Chrome_WidgetWin_1", L"Google Translate - Google Chrome");
CreateThread(NULL, 0, CreateThreadFunc, 0, 0, 0);
hinstDLL = LoadLibrary(TEXT("D:\\Start from 11.2\\WM_CBT_DLL\\x64\\Debug\\WM_CBT_DLL.dll"));
BOOL(*InstallHooks)(HWND);
InstallHooks = (BOOL(*)(HWND)) GetProcAddress(hinstDLL, "InstallHooks");
BOOL l = InstallHooks(hwnd);
int err = GetLastError();
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

WM_PAINT message, BeginPaint loop

I can't understand why I got a loop on BeginPaint function. I have already read posts about this kind of loop but almost all of them recommend: "Don't forget to use BeginPaint function on WM_PAINT message, because it entails subsequent WM_PAINT messages otherwise". This isn't my case. May you give me some advices?
This is my windowclass ("CWindow"):
class CWindow {
public:
CWindow();
virtual ~CWindow();
bool RegisterClass(HINSTANCE hInstance);
bool CreateWnd(HINSTANCE hInstance);
bool Show(int nShow);
private:
HWND handleWindow;
ATOM atom;
bool isRegistered;
bool isCreated;
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void OnPaint();
void OnDestroy();
};
WndProc function.
LRESULT CALLBACK CWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CWindow* windowPtr = reinterpret_cast<CWindow*> ( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
PAINTSTRUCT ps;
HDC hdc;
switch( msg ) {
case WM_PAINT:
// There is a loop right here!
hdc = BeginPaint( windowPtr->handleWindow, &ps );
// The code below doesn't executed!
RECT rect;
(void)GetClientRect(windowPtr->handleWindow, &rect);
(void)DrawText(hdc, TEXT("Hello, Windows 98!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint( windowPtr->handleWindow, &ps );
break;
case WM_DESTROY:
windowPtr->OnDestroy();
break;
default:
return DefWindowProc( hWnd, msg, wParam, lParam );
}
return 0;
}
RegisterClass
bool CWindow::RegisterClass(HINSTANCE hInstance)
{
const TCHAR app_name[] = TEXT("HelloWin");
WNDCLASSEX windowClass;
ZeroMemory( &windowClass, sizeof(windowClass) );
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = WindowProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = hInstance;
windowClass.hIcon = 0;
windowClass.hIcon = 0;
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = 0;
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = app_name;
windowClass.hIconSm = NULL;
atom = RegisterClassEx( &windowClass );
DWORD errorCode = GetLastError();
if( errorCode ) {
isRegistered = 0;
std::wcout << L"ErrorCode: " << errorCode << std::endl;
} else {
isRegistered = 1;
}
return isRegistered;
}
CreateWindow
bool CWindow::CreateWnd(HINSTANCE hInstance)
{
handleWindow = CreateWindow((PCTSTR)atom, // window class name or atom
TEXT("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
DWORD errorCode = GetLastError();
if( !handleWindow ) {
isCreated = 0;
} else {
isCreated = 1;
}
return isCreated;
}
Show
bool CWindow::Show(int nShow)
{
if( isCreated ) {
ShowWindow( handleWindow, nShow );
return UpdateWindow( handleWindow );
} else {
return 0;
}
}
WinMain
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevINstance, LPSTR lpCmdLine, int nShow )
{
CWindow window;
window.RegisterClass( hInstance );
window.CreateWnd( hInstance );
window.Show( nShow );
int response = 0;
MSG msg;
while( GetMessage( &msg, 0, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return 0;
}
Since you never call SetWindowLongPtr,
CWindow* windowPtr = reinterpret_cast<CWindow*>( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
returns a nullptr, that you subsequently try to dereference:
BeginPaint( windowPtr->handleWindow, &ps )
That will trigger an access violation exception, causing the BeginPaint call to never even get executed, leaving the invalid region as is. As a consequence, the system keeps generating WM_PAINT messages. That's the same issue as not calling BeginPaint altogether.1
To solve this, you'll either have to attach the window handle to the window instance by calling SetWindowLongPtr, or simply use the hWnd parameter that's passed into your CWindow::WindowProc.
1 Note that the system silently handles unhandled exceptions in your WindowProc on 64-bit versions of Windows under certain conditions.

C++ win32 API Window won't update on WindowUpdate

I have a program that is supposed to display a count that increments every second. The counter is in a separate thread. I call WindowUpdate when a change has occurred but the count in the window does not update unless I hover the mouse pointer over the window or resize the window. I have tried InvalidateRect and RedrawWindow but they don't work either.
Why won't the counter updates display?
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <sdkddkver.h>
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <strsafe.h>
typedef struct DataTransfer {
BOOL Updated;
int Counter;
} TRANSFER, *PTRANSFER;
TRANSFER Payload;
PTRANSFER pPayload;
DWORD dwThreadId;
HANDLE hThread;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void DisplayData(HDC hDC);
void ErrorHandler(LPTSTR lpszFunction);
DWORD WINAPI Counter(LPVOID lpParam);
int
APIENTRY
wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd)
{
MSG msg;
WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpszClassName = TEXT("MYFIRSTWINDOWCLASS");
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.hCursor = LoadCursor(hInstance, IDC_ARROW);
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
if (!RegisterClassEx(&wcex))
return 1;
CREATESTRUCT cs;
ZeroMemory(&cs, sizeof(cs));
cs.x = 0;
cs.y = 0;
cs.cx = 200;
cs.cy = 300;
cs.hInstance = hInstance;
cs.lpszClass = wcex.lpszClassName;
cs.lpszName = TEXT("Test");
cs.style = WS_OVERLAPPEDWINDOW;
HWND hWnd = ::CreateWindowEx(
cs.dwExStyle,
cs.lpszClass,
cs.lpszName,
cs.style,
cs.x,
cs.y,
cs.cx,
cs.cy,
cs.hwndParent,
cs.hMenu,
cs.hInstance,
cs.lpCreateParams);
if (!hWnd)
return 1;
DWORD dwThreadId;
HANDLE hThread;
pPayload = (PTRANSFER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TRANSFER));
if (pPayload == NULL)
ExitProcess(2);
pPayload->Updated = FALSE;
pPayload->Counter = 0;
// Display the window.
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
hThread = CreateThread(
NULL,
0,
Counter,
pPayload,
0,
&dwThreadId);
if (hThread == NULL)
ExitProcess(2);
while (1)
{
if (pPayload->Updated == TRUE)
{
// InvalidateRect(hWnd, NULL, FALSE);
// RedrawWindow(hWnd, NULL, NULL, RDW_INVALIDATE | RDW_UPDATENOW);
UpdateWindow(hWnd);
}
if (GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
::UnregisterClass(wcex.lpszClassName, hInstance);
return (int)msg.wParam;
}
LRESULT
CALLBACK
WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT paintStruct;
HDC hDC;
switch (uMsg)
{
case WM_DESTROY:
::PostQuitMessage(0);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &paintStruct);
DisplayData(hDC);
EndPaint(hWnd, &paintStruct);
return 0;
break;
default:
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
void DisplayData(HDC hDC)
{
char OutputStr[32];
sprintf_s(OutputStr, sizeof(OutputStr) - 1, "%d", pPayload->Counter);
TextOut(hDC, 100, 100, OutputStr, strlen(OutputStr));
}
DWORD WINAPI Counter(LPVOID lpParam)
{
PTRANSFER pTransfer;
pTransfer = (PTRANSFER)lpParam;
while (1)
{
pTransfer->Counter++;
pTransfer->Updated = TRUE;
Sleep(1000);
}
}
void ErrorHandler(LPTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}
(Disclaimer: This answer was written by the seat of my pants. Please correct me if I made an error somewhere.)
You are not doing what you are trying to do properly.
First, as Jonathan Potter has noted, UpdateWindow() by itself will not update the window unless it has an invalid region. The InvalidateRect() call will invalidate a rectangle. So you need both...
...but in reality you don't really want to call UpdateWindow() directly, because it bypasses the Windows drawing model. After calling InvalidateRect(), if there are no pending messages next time GetMessage() is called, and Windows itself decides it's time to refresh the screen contents with new data, you'll get a WM_PAINT message. The system knows when it's best to paint; you'll make your life easier by using it. Only use UpdateWindow() when it is vital that you want the window to redraw right now.
(The point of the invalid region is also due to the Windows drawing model: drawing can be very expensive, so Windows tries to optimize drawing by only redrawing what is needed. Invalidate only the part of your window that needs to be updated, and you'll get better performance.)
But there is a deeper issue: you do not implement multithreading properly.
Your worker thread is generating data every second and overwriting a shared structure. Your window is reading from that shared structure. There is nothing in place to ensure that only one thread is accessing that shared structure at a time. As a result, you'll wind up with ptential mixed state if your worker thread ever grows from a simple integer to a large and complex data structure.
You need to synchronize your data accesses. Communicate between threads.
How? The obvious method is to use a synchronization object, like a mutex. And you can totally do that.
But there's a better way: since one of your threads has a window, just use a window message! Window messages sent using SendMessage() will be received on the window's thread (with the calling thread blocked); messages posted using PostMessage() will be placed on the window's thread's message queue.
Plus you can pass not one but two pieces of information in that message! Both wParam and lParam are pointer-sized integers, so just stuff a pointer in one of them and use SendMessage() or PostMessage()!
But what message do you use? Every* message in the range [WM_USER, WM_APP) is available to the window class to decide what to use it for, and every message in the range [WM_APP, 0xC000) is for the application to decide what to use it for. So just pick one and use it!
Just remember how SendMessage() and PostMessage() work. If the data is allocated on the heap, and each piece of data is allocated separately, then it doesn't matter; if the data is a pointer to a local variable, SendMessage() is the correct answer; if the data is a numeric value that can fit in a pointer-sized integer, either will work. If you need a response, your worker thread will either need a window of its own or the use of SendMessage() to get a value back from the window.
There are lots of ways to do this. It all depends on what data you need to communicate. As the Go programming language's designers say (altered somewhat): don't communicate by sharing data; share data by communicating.
*until you call IsDialogMessage(), at which point you lose WM_USER and WM_USER + 1 (and possibly WM_USER + 2 as well?). But that's only three messages out of over 32,000.
Your problem is in your message loop:
while (1)
{
if (pPayload->Updated == TRUE) {
UpdateWindow(hWnd);
}
if (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
GetMessage is synchronous, and doesn't return until a message is available for retrieval. The code sits there, waiting for a message to arrive most of the time, and will not evaluate pPayload->Updated. Consequently, no updates happen, until a message arrives. But even then, nothing happens, since UpdateWindow sends "a WM_PAINT message to the window if the window's update region is not empty." UpdateWindow by itself doesn't do anything useful.1)
You could solve this by reworking your message loop. The following issues need to be addressed:
A call to InvalidateRect must precede the call to UpdateWindow.
Access to the shared data must be synchronized.
The message loop needs to terminate, when GetMessage returns 0 or -1.
None of this is necessary, though. You don't need a background thread. A single-threaded application will do just fine. Just use a Timer to trigger updates to the stored value, and the visual representation. Here is a rough sketch for a window procedure implementing a timer-based solution:
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
static unsigned __int64 startTime = ::GetTickCount64();
const UINT_PTR timerId = 1;
switch (uMsg)
{
case WM_CREATE:
// Start timer when window is created
::SetTimer(hWnd, timerId, 1000, nullptr);
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
case WM_TIMER: {
unsigned __int64 currTime = ::GetTickCount64();
Counter = (currTime - startTime) / 1000;
// Value changed -> initiate window update
::InvalidateRect(hWnd, nullptr, FALSE);
// Re-start timer
::SetTimer(hWnd, timerId, 1000, nullptr);
}
break;
case WM_DESTROY:
::KillTimer(hWnd, timerId);
::PostQuitMessage(0);
break;
case WM_PAINT: {
PAINTSTRUCT paintStruct = {0};
HDC hDC = BeginPaint(hWnd, &paintStruct);
DisplayData(hDC);
EndPaint(hWnd, &paintStruct);
}
return 0;
default:
return ::DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
A few notes on the implementation:
The timer uses a fixed time-out value of 1000 milliseconds. This will occasionally skip an update. The error doesn't accumulate, though, as the Counter is re-evaluated whenever the timer expires. If you more steady updates, calculate the remaining time-out value based on the Counter, startTime, and currTime.
Counter is the variable that holds the current time in seconds. It is a replacement for TRANSFER::Counter, that needs to be accessible from both the window procedure and the rendering code.
1) There is another issue with your message loop: It never terminates, and neither does your process. The GUI may disappear, but the process will still show up in Task Manager.
i marked my alterations with comment <<updated
instead of setting flag Updated, i changed it to be hWnd, and then update the window instantly and directly from the thread
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <sdkddkver.h>
#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <tchar.h>
#include <strsafe.h>
typedef struct DataTransfer {
//BOOL Updated; // << updated
int Counter;
HWND hWnd; // << updated
} TRANSFER, *PTRANSFER;
TRANSFER Payload;
PTRANSFER pPayload;
DWORD dwThreadId;
HANDLE hThread;
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void DisplayData(HDC hDC);
void ErrorHandler(LPTSTR lpszFunction);
DWORD WINAPI Counter(LPVOID lpParam);
int
APIENTRY
wWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,
int nShowCmd)
{
MSG msg;
WNDCLASSEX wcex;
ZeroMemory(&wcex, sizeof(wcex));
wcex.cbSize = sizeof(wcex);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpszClassName = TEXT("MYFIRSTWINDOWCLASS");
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.hCursor = LoadCursor(NULL, IDC_ARROW); //<< updated
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
if (!RegisterClassEx(&wcex))
return 1;
CREATESTRUCT cs;
ZeroMemory(&cs, sizeof(cs));
cs.x = 0;
cs.y = 0;
cs.cx = 200;
cs.cy = 300;
cs.hInstance = hInstance;
cs.lpszClass = wcex.lpszClassName;
cs.lpszName = TEXT("Test");
cs.style = WS_OVERLAPPEDWINDOW;
HWND hWnd = CreateWindowEx(
cs.dwExStyle,
cs.lpszClass,
cs.lpszName,
cs.style,
cs.x,
cs.y,
cs.cx,
cs.cy,
cs.hwndParent,
cs.hMenu,
cs.hInstance,
cs.lpCreateParams);
if (!hWnd)
return 1;
DWORD dwThreadId;
HANDLE hThread;
pPayload = (PTRANSFER)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(TRANSFER));
if (pPayload == NULL)
ExitProcess(2);
//pPayload->Updated = FALSE; //<< updated
pPayload->hWnd=hWnd;
pPayload->Counter = 0;
// Display the window.
ShowWindow(hWnd, SW_SHOWDEFAULT);
UpdateWindow(hWnd);
hThread = CreateThread( NULL, 0, Counter, pPayload, 0, &dwThreadId);
if (hThread == NULL)
ExitProcess(2);
while (1)
{
// ____[ updated ]_____
/*
if (pPayload->Updated == TRUE)
{
UpdateWindow(hWnd);
pPayload->Updated=FALSE;
}
*/
int ret=GetMessage(&msg, NULL, 0, 0);//<< updated
if(ret==0 || ret==-1)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterClass(wcex.lpszClassName, hInstance);
return (int)msg.wParam;
}
LRESULT
CALLBACK
WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT paintStruct;
HDC hDC;
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
hDC = BeginPaint(hWnd, &paintStruct);
DisplayData(hDC);
EndPaint(hWnd, &paintStruct);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return 0;
}
void DisplayData(HDC hDC)
{
char OutputStr[32];
sprintf_s(OutputStr, sizeof(OutputStr) - 1, "%d", pPayload->Counter);
TextOut(hDC, 100, 100, OutputStr, strlen(OutputStr));
}
DWORD WINAPI Counter(LPVOID lpParam)
{
PTRANSFER pTransfer;
pTransfer = (PTRANSFER)lpParam;
while (1)
{
pTransfer->Counter++;
InvalidateRect(pTransfer->hWnd,NULL,1);
Sleep(1000);
}
}
void ErrorHandler(LPTSTR lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}

Add Controls To Specific Tab Page in TabControl in C++ Win32

I want to add some controls to the tab page in tabcontrol but it seems that it will be added to all pages and there is not tab page in tabcontrol by default.
I have read these links below but they did not help me and in some parts of them, confused me.
How to add controls to a Tab control
http://www.cplusplus.com/forum/windows/37161/
https://msdn.microsoft.com/en-us/library/bb760551.aspx
https://msdn.microsoft.com/en-us/library/hh298366.aspx
https://msdn.microsoft.com/en-us/library/ms645398.aspx
Here is my code :
[Code]:
#define ID_LBL 500
#define ID_BTN 501
#define ID_TBC 502
HWND hWnd;
void InserTabItem(HWND handle, LPWSTR text, int id)
{
TCITEM tci = { 0 };
tci.mask = TCIF_TEXT;
tci.pszText = text;
tci.cchTextMax = wcslen(text);
SendMessage(handle, TCM_INSERTITEM, id, LPARAM(&tci));
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_CREATE:
{
HWND button_handle = 0;
HWND label_handle = 0;
HWND tab_handle = 0;
tab_handle = CreateWindowEx(WS_EX_CONTROLPARENT, WC_TABCONTROL, 0, WS_VISIBLE | WS_CHILD, 10, 10, 200, 150, hWnd, HMENU(ID_TBC), 0, 0);
InserTabItem(tab_handle, L"page1", 0);
InserTabItem(tab_handle, L"page2", 1);
button_handle = CreateWindowEx(0, WC_BUTTON, L"test-button-page2", WS_VISIBLE | WS_CHILD, 10, 50, 150, 30, tab_handle, HMENU(ID_BTN), 0, 0);
label_handle = CreateWindowEx(0, WC_STATIC, L"test-label-page1", WS_VISIBLE | WS_CHILD, 10, 100, 150, 30, tab_handle, HMENU(ID_LBL), 0, 0);
}
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
break;
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreviewInstance, LPSTR lpcmdline, int ncmdshow)
{
WNDCLASSEX wndexcls;
wndexcls.lpszClassName = L"win";
wndexcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndexcls.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wndexcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndexcls.hbrBackground = (HBRUSH)(COLOR_3DSHADOW + 1);
wndexcls.lpszMenuName = NULL;
wndexcls.style = NULL;
wndexcls.hInstance = hInstance;
wndexcls.cbSize = sizeof(WNDCLASSEX);
wndexcls.cbClsExtra = 0;
wndexcls.cbWndExtra = 0;
wndexcls.lpfnWndProc = WndProc;
RegisterClassEx(&wndexcls);
hWnd = CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_CONTROLPARENT, L"win", L"TestApp", WS_OVERLAPPEDWINDOW, 100, 100, 640, 380, 0, 0, hInstance, 0);
ShowWindow(hWnd, ncmdshow);
UpdateWindow(hWnd);
MSG wnd_msg;
while (GetMessage(&wnd_msg, NULL, 0, 0)>0)
{
TranslateMessage(&wnd_msg);
DispatchMessage(&wnd_msg);
}
return (int)wnd_msg.wParam;
}
I am looking for a safe and proper implementation.
Thanks for any help
========================================================
[Update]:
Thanks for comments but no answer in detail :(
Although that is not the implementation I am looking for(NotDialogBased), But from the forth link I mentioned :
How to Create a Tabbed Dialog Box :
https://msdn.microsoft.com/en-us/library/hh298366.aspx
Here is my code of that page :
[resource.h]:
#define IDD_Page1 101
#define IDD_Page2 102
#define IDD_Page3 103
#define IDD_Main_Dialog 104
#define IDC_BTN_Page1 1001
#define IDC_BTN2_Page1 1002
#define IDC_BTN_Page2 1013
#define IDC_BTN_Page3 1014
[Resource.rc]:
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
//////////////////////////////////////////////////
//
// Dialog
//
IDD_Page1 DIALOGEX 0, 0, 313, 178
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Button2-Page1",IDC_BTN2_Page1,129,107,67,22
PUSHBUTTON "Button-Page1",IDC_BTN_Page1,127,77,67,22
END
IDD_Page2 DIALOGEX 0, 0, 309, 177
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Button-Page2",IDC_BTN_Page2,120,77,60,18
END
IDD_Page3 DIALOGEX 0, 0, 309, 177
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
PUSHBUTTON "Button-Page3",IDC_BTN_Page3,120,73,64,25
END
IDD_Main_Dialog DIALOGEX 0, 0, 309, 177
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_CHILD
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
END
////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO
BEGIN
IDD_Page1, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 306
TOPMARGIN, 7
BOTTOMMARGIN, 171
END
IDD_Page2, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 302
TOPMARGIN, 7
BOTTOMMARGIN, 170
END
IDD_Page3, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 302
TOPMARGIN, 7
BOTTOMMARGIN, 170
END
IDD_Main_Dialog, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 302
TOPMARGIN, 7
BOTTOMMARGIN, 170
END
END
#endif // APSTUDIO_INVOKED
#endif
[Main.cpp]:
#include <windows.h>
#include <CommCtrl.h>
#include "resource.h"
#pragma comment(lib, "ComCtl32.lib")
#define C_PAGES 3
INT_PTR CALLBACK DialogProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);
HWND Win_Handle;
HWND Dailog_Handle;
HINSTANCE hInstance_Win_Global;
typedef struct {
WORD dlgVer;
WORD signature;
DWORD helpID;
DWORD exStyle;
DWORD style;
WORD cDlgItems;
short x;
short y;
short cx;
short cy;
WORD pointsize;
WORD weight;
BYTE italic;
BYTE charset;
} DLGTEMPLATEEX;
typedef struct tag_dlghdr {
HWND hwndTab; // tab control
HWND hwndDisplay; // current child dialog box
RECT rcDisplay; // display rectangle for the tab control
DLGTEMPLATEEX *apRes[C_PAGES];
} DLGHDR;
void InserTabItem(HWND handle, LPWSTR text, int id)
{
TCITEM tci = { 0 };
tci.mask = TCIF_TEXT;
tci.pszText = text;
tci.cchTextMax = wcslen(text);
SendMessage(handle, TCM_INSERTITEM, id, LPARAM(&tci));
}
DLGTEMPLATEEX* DoLockDlgRes(LPCTSTR lpszResName)
{
HRSRC hrsrc = FindResource(NULL, lpszResName, RT_DIALOG);
HGLOBAL hglb = LoadResource(hInstance_Win_Global, hrsrc);
return (DLGTEMPLATEEX *)LockResource(hglb);
}
VOID WINAPI OnChildDialogInit(HWND hwndDlg)
{
HWND hwndParent = GetParent(hwndDlg);
DLGHDR *pHdr = (DLGHDR *)GetWindowLong(
hwndParent, GWL_USERDATA);
SetWindowPos(hwndDlg, NULL, pHdr->rcDisplay.left,
pHdr->rcDisplay.top,//-2,
(pHdr->rcDisplay.right - pHdr->rcDisplay.left),
(pHdr->rcDisplay.bottom - pHdr->rcDisplay.top),
SWP_SHOWWINDOW);
return;
}
VOID OnSelChanged(HWND hwndDlg)
{
DLGHDR *pHdr = (DLGHDR *)GetWindowLong(hwndDlg, GWL_USERDATA);
int iSel = TabCtrl_GetCurSel(pHdr->hwndTab);
if (pHdr->hwndDisplay != NULL)
DestroyWindow(pHdr->hwndDisplay);
pHdr->hwndDisplay = CreateDialogIndirect(hInstance_Win_Global,
(DLGTEMPLATE *)pHdr->apRes[iSel], hwndDlg,DialogProc);
}
HRESULT OnTabbedDialogInit(HWND hwndDlg)
{
INITCOMMONCONTROLSEX iccex;
DWORD dwDlgBase = GetDialogBaseUnits();
int cxMargin = LOWORD(dwDlgBase) / 4;
int cyMargin = HIWORD(dwDlgBase) / 8;
TCITEM tie;
RECT rcTab;
HWND hwndButton;
RECT rcButton;
int i;
iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
iccex.dwICC = ICC_TAB_CLASSES;
InitCommonControlsEx(&iccex);
DLGHDR *pHdr = (DLGHDR *)LocalAlloc(LPTR, sizeof(DLGHDR));
SetWindowLong(hwndDlg, GWL_USERDATA, (LONG)pHdr);
pHdr->hwndTab = CreateWindow(
WC_TABCONTROL, L"",
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
0, 0, 300, 200,
hwndDlg, NULL, hInstance_Win_Global, NULL
);
if (pHdr->hwndTab == NULL)
{
return HRESULT_FROM_WIN32(GetLastError());
}
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = L"First";
TabCtrl_InsertItem(pHdr->hwndTab, 0, &tie);
tie.pszText = L"Second";
TabCtrl_InsertItem(pHdr->hwndTab, 1, &tie);
tie.pszText = L"Third";
TabCtrl_InsertItem(pHdr->hwndTab, 2, &tie);
pHdr->apRes[0] = DoLockDlgRes(MAKEINTRESOURCE(IDD_Page1));
pHdr->apRes[1] = DoLockDlgRes(MAKEINTRESOURCE(IDD_Page2));
pHdr->apRes[2] = DoLockDlgRes(MAKEINTRESOURCE(IDD_Page3));
SetRectEmpty(&rcTab);
for (i = 0; i < C_PAGES; i++)
{
if (pHdr->apRes[i]->cx > rcTab.right)
rcTab.right = pHdr->apRes[i]->cx;
if (pHdr->apRes[i]->cy > rcTab.bottom)
rcTab.bottom = pHdr->apRes[i]->cy;
}
MapDialogRect(hwndDlg, &rcTab);
TabCtrl_AdjustRect(pHdr->hwndTab, TRUE, &rcTab);
OffsetRect(&rcTab, cxMargin - rcTab.left, cyMargin - rcTab.top);
CopyRect(&pHdr->rcDisplay, &rcTab);
TabCtrl_AdjustRect(pHdr->hwndTab, FALSE, &pHdr->rcDisplay);
SetWindowPos(pHdr->hwndTab, NULL, rcTab.left, rcTab.top,
rcTab.right - rcTab.left, rcTab.bottom - rcTab.top,
SWP_NOZORDER);
hwndButton = GetDlgItem(hwndDlg, IDC_BTN_Page1);
SetWindowPos(hwndButton, NULL,
rcTab.left, rcTab.bottom + cyMargin, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
GetWindowRect(hwndButton, &rcButton);
rcButton.right -= rcButton.left;
rcButton.bottom -= rcButton.top;
hwndButton = GetDlgItem(hwndDlg, IDC_BTN2_Page1);
SetWindowPos(hwndButton, NULL,
rcTab.left + rcButton.right + cxMargin,
rcTab.bottom + cyMargin, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
SetWindowPos(hwndDlg, NULL, 0, 0,
rcTab.right + cyMargin + (2 * GetSystemMetrics(SM_CXDLGFRAME)),
rcTab.bottom + rcButton.bottom + (2 * cyMargin)
+ (2 * GetSystemMetrics(SM_CYDLGFRAME))
+ GetSystemMetrics(SM_CYCAPTION),
SWP_NOMOVE | SWP_NOZORDER);
OnSelChanged(hwndDlg);
return S_OK;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_CREATE:
{
Dailog_Handle = CreateDialogParam(hInstance_Win_Global, MAKEINTRESOURCE(IDD_Main_Dialog), hWnd, DialogProc, 0);
ShowWindow(Dailog_Handle, SW_SHOWDEFAULT);
UpdateWindow(Dailog_Handle);
SetWindowPos(Dailog_Handle, 0, 10, 10, 500, 300, SWP_NOZORDER);
}
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, Msg, wParam, lParam);
break;
}
return 0;
}
INT_PTR CALLBACK DialogProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch (Msg)
{
case WM_INITDIALOG:
{
OnTabbedDialogInit(hWnd);
OnChildDialogInit(hWnd);
return (INT_PTR)TRUE;
}
break;
case WM_NOTIFY:
{
switch (((LPNMHDR)lParam)->code)
{
case TCN_SELCHANGE:
{
OnSelChanged(hWnd);
}
break;
default:
break;
}
}
break;
case WM_CLOSE:
DestroyWindow(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
}
return (INT_PTR)FALSE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreviewInstance, LPSTR lpcmdline, int ncmdshow)
{
WNDCLASSEX wndexcls;
wndexcls.lpszClassName = L"win";
wndexcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndexcls.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
wndexcls.hCursor = LoadCursor(NULL, IDC_ARROW);
wndexcls.hbrBackground = (HBRUSH)(COLOR_3DSHADOW + 1);
wndexcls.lpszMenuName = NULL;
wndexcls.style = NULL;
wndexcls.hInstance = hInstance;
wndexcls.cbSize = sizeof(WNDCLASSEX);
wndexcls.cbClsExtra = 0;
wndexcls.cbWndExtra = 0;
wndexcls.lpfnWndProc = WndProc;
RegisterClassEx(&wndexcls);
Win_Handle = CreateWindowEx(WS_EX_CLIENTEDGE | WS_EX_CONTROLPARENT, L"win", L"TestApp", WS_OVERLAPPEDWINDOW, 100, 100, 640, 380, 0, 0, hInstance, 0);
hInstance_Win_Global = hInstance;
ShowWindow(Win_Handle, SW_SHOWDEFAULT);
UpdateWindow(Win_Handle);
MSG wnd_msg;
while (GetMessage(&wnd_msg, NULL, 0, 0)>0)
{
TranslateMessage(&wnd_msg);
DispatchMessage(&wnd_msg);
}
return (int)wnd_msg.wParam;
}
The Problem is that , after debugging , the application exites and does not show anything. if I comment the OnSelChanged(hWnd) and OnChildDialogInit(hWnd)
the application starts normally and shows the tabcontrol but not the controls on the pages. it seems that the problem is here.
the output log :
First-chance exception at 0x00BE1886 in testcppapp.exe: 0xC0000005: Access violation reading location 0x00000014.
The program '[16220] testcppapp.exe' has exited with code 0 (0x0).
I have Read the link below about Access violation reading location:
http://www.cplusplus.com/forum/general/17094/
But I can not fix the problem.
Please Post your Answer and Explain about it , not just brief in comments !
Thanks for any help.
One problem is here:
pHdr->hwndDisplay = CreateDialogIndirect(hInstance_Win_Global,
(DLGTEMPLATE*)pHdr->apRes[iSel], hwndDlg, DialogProc);
You are reusing the same dialog procedure for both main dialog and child dialogs. Main dialog creates child dialogs, child dialogs use the same procedure to create child dialogs... Also there are no error checks.
Beyond that, this code is too complicated. Just use a dialog box for main window. Create a new dialog IDD_DIALOG1 and drag/drop a tab control in it. Assign IDC_TAB1 for tab control ID. Try starting with this code instead:
#include <Windows.h>
#include <CommCtrl.h>
#include "Resource.h"
#pragma comment(lib,"comctl32.lib")
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
HINSTANCE g_hinst;
struct TData {
HWND page1, page2, page3;
HWND tab;
} data;
BOOL CALLBACK DialogPage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch(msg) {
case WM_COMMAND:
switch (wp) {
//...
}
}
return FALSE;
}
void OnSelChange() {
int sel = TabCtrl_GetCurSel(data.tab);
ShowWindow(data.page1, (sel == 0) ? SW_SHOW : SW_HIDE);
ShowWindow(data.page2, (sel == 1) ? SW_SHOW : SW_HIDE);
}
BOOL CALLBACK DialogProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
switch (msg) {
case WM_INITDIALOG: {
data.page1 = CreateDialog(g_hinst, MAKEINTRESOURCE(IDD_Page1), hwnd, DialogPage);
data.page2 = CreateDialog(g_hinst, MAKEINTRESOURCE(IDD_Page2), hwnd, DialogPage);
data.tab = GetDlgItem(hwnd, IDC_TAB1);
if (data.tab)
{
TCITEM tci = { 0 };
tci.mask = TCIF_TEXT;
tci.pszText = L"Page1";
TabCtrl_InsertItem(data.tab, 0, &tci);
tci.pszText = L"Page2";
TabCtrl_InsertItem(data.tab, 1, &tci);
RECT rc;//find tab control's rectangle
GetWindowRect(data.tab, &rc);
POINT offset = { 0 };
ScreenToClient(hwnd, &offset);
OffsetRect(&rc, offset.x, offset.y); //convert to client coordinates
rc.top += 50;
SetWindowPos(data.page1, 0, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_HIDEWINDOW);
SetWindowPos(data.page2, 0, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_HIDEWINDOW);
OnSelChange();
}
break;
}
case WM_NOTIFY: {
switch (((LPNMHDR)lp)->code) {
case TCN_SELCHANGE:
OnSelChange();
break;
}
}
break;
case WM_COMMAND:
switch (wp) {
case IDOK: EndDialog(hwnd, wp); break;
case IDCANCEL: EndDialog(hwnd, wp); break;
}
}
return FALSE;
}
int WINAPI wWinMain(HINSTANCE hinst, HINSTANCE, LPWSTR, int)
{
g_hinst = hinst;
DialogBox(hinst, MAKEINTRESOURCE(IDD_DIALOG1), 0, DialogProc);
return 0;
}
An approach that I am testing and looks promising is to use a dialog box template for each pane of the tab control within the modal dialog box.
For testing, I generated a starting Win32 GUI application and then hijacked the About box display triggered by IDM_ABOUT to display my own dialog box with a tab control rather than a standard About dialog.
case IDM_ABOUT:
// DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
{
CDialogTabTest x;
x.CreateModal (hWnd, hInst);
}
break;
After some research and refactoring and development I have the following approach.
I have created three classes for implementation: CDialogTest (handles the dialog box functionality), CDialogTabTest (handles the tab control within the dialog box), and CDialogTabPane (handles the individual tab panes that are displayed in the tab control).
The CDialogTest class provides the functionality for basic dialog box behavior. It allows for two kinds of dialog boxes, modal used to create the actual modal dialog box and non-modal used to create the tab control panes.
The CDialogTest class is designed to be used as a subclass and be extended for a particular dialog box or tab control pane.
The CDialogTabTest class extends CDialogTest to implement a modal dialog box. The CDialogTabPane class extends CDialogTest to implement a non-modal dialog box that is used as a tab control tab pane.
For MFC programmers some of the names may sound vaguely familiar as while working this out, I now understand some of the mechanics of the MFC classes for dialog boxes.
I put added some additional include files in the the stdafx.h file as that was a central place to put them for what I needed.
// TODO: reference additional headers your program requires here
#include "commctrl.h"
#include "windowsx.h"
#include <vector>
DialogTest.h
#pragma once
class CDialogTest
{
private:
static INT_PTR CALLBACK DlgProc (HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
public:
HINSTANCE m_hInst;
HWND m_hParent;
HWND m_hDlg;
CDialogTest();
CDialogTest(HINSTANCE hInst, HWND hParent = NULL);
virtual ~CDialogTest(void);
HWND CreateThing (HWND hWnd, LPCWSTR lpTemplateName, HINSTANCE hInst);
INT_PTR WINAPI CreateModal (HWND hWnd, LPCWSTR lpTemplateName, HINSTANCE hInst);
virtual int DoDataExchange (int iDir) { return 0; }
virtual int OnWmCommandInit () { return 0; }
virtual int OnWmNotify (LPNMHDR pNmhdr) { return 0; }
virtual int OnWmCommand (WPARAM wParam, LPARAM lParam) { return 0; }
};
and DialogTest.cpp
#include "StdAfx.h"
#include "DialogTest.h"
CDialogTest::CDialogTest(void)
{
}
CDialogTest::CDialogTest(HINSTANCE hInst, HWND hParent /* = NULL */) :
m_hInst (hInst), m_hParent(hParent)
{
}
CDialogTest::~CDialogTest(void)
{
}
INT_PTR CALLBACK CDialogTest::DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
{
// Set the provided object pointer into the dialog box user area
// so that we can use it to provide the necessary notifications
// to what ever object which has derived from this class.
::SetWindowLongPtr (hDlg, DWLP_USER, lParam);
// ensure that the object knows its dialog handle.
CDialogTest *pObj = (CDialogTest *)lParam;
pObj->m_hDlg = hDlg;
// call the objects handlers to initialize the dialog further
// and to then populate the data displayed in the dialog.
pObj->OnWmCommandInit ();
pObj->DoDataExchange (1);
}
return (INT_PTR)TRUE;
case WM_COMMAND:
// if this command is an Ok or Cancel button press then we are
// done. other command messages are routed to the derived object's
// how command message handler for further processing.
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
if (LOWORD(wParam) == IDOK) {
LPARAM lP = ::GetWindowLongPtr (hDlg, DWLP_USER);
CDialogTest *pObj = (CDialogTest *)lP;
pObj->DoDataExchange (0);
}
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
} else {
LPARAM lP = ::GetWindowLongPtr (hDlg, DWLP_USER);
CDialogTest *pObj = (CDialogTest *)lP;
pObj->OnWmCommand (wParam, lParam);
}
break;
case WM_NOTIFY:
{
LPARAM lP = ::GetWindowLongPtr (hDlg, DWLP_USER);
CDialogTest *pObj = (CDialogTest *)lP;
LPNMHDR pNmhdr = ((LPNMHDR)lParam);
pObj->OnWmNotify (pNmhdr);
}
break;
}
return (INT_PTR)FALSE;
}
// We need two different kinds of dialog create functions as we support
// both a modal dialog and a modeless dialog. The modal dialog is used to
// display an actual dialog box. The modeless dialog is used to display
// a tab control pane using a dialog template.
INT_PTR WINAPI CDialogTest::CreateModal (HWND hWnd, LPCWSTR lpTemplateName, HINSTANCE hInst)
{
m_hInst = hInst;
m_hParent = hWnd;
// display the modal dialog box and return the identifier for the button clicked.
return DialogBoxParam(hInst, lpTemplateName, hWnd, &CDialogTest::DlgProc, (LPARAM)this);
}
HWND CDialogTest::CreateThing (HWND hWnd, LPCWSTR lpTemplateName, HINSTANCE hInst)
{
m_hInst = hInst;
m_hParent = hWnd;
// create a modeless dialog box to be used as a tab control pane or similar
// non-modal dialog box use.
m_hDlg = ::CreateDialogParam(hInst, lpTemplateName, hWnd, &CDialogTest::DlgProc, (LPARAM)this);
return m_hDlg;
}
The class CDialogTabTest creates the modal dialog box that is being displayed as a result of the IDM_ABOUT menu selection message in the main application message handler.
DialogTabTest.h
#pragma once
#include "stdafx.h"
#include "DialogTest.h"
#include "DialogTabPane.h"
#include "resource.h"
struct CCheckBoxData
{
UINT iCntrlId;
int iCheck;
std::wstring prompt;
};
typedef std::vector<CCheckBoxData> CCheckBoxVector;
class CDialogTabTest : public CDialogTest
{
protected:
CDialogTabPane m_hTabPane1;
CDialogTabPane m_hTabPane2;
CDialogTabPane m_hTabPane3;
enum {IDD_DIALOG = IDD_DIALOG_TAB00};
public:
CDialogTabTest(void);
virtual ~CDialogTabTest(void);
INT_PTR WINAPI CreateModal (HWND hWnd, HINSTANCE hInst);
static int SetValues (CCheckBoxData &x, HWND hDlg);
static int GetValues (CCheckBoxData &x, HWND hDlg);
virtual int DoDataExchange (int iDir);
virtual int OnWmCommandInit ();
virtual int OnWmNotify (LPNMHDR pNmhdr);
};
and DialogTabTest.cpp which creates the modal dialog box and initializes it.
#include "StdAfx.h"
#include "DialogTabTest.h"
CDialogTabTest::CDialogTabTest(void)
{
}
CDialogTabTest::~CDialogTabTest(void)
{
}
int CDialogTabTest::SetValues (CCheckBoxData &x, HWND hDlg)
{
HWND hWnd = GetDlgItem (hDlg, x.iCntrlId);
::SetWindowText (hWnd, x.prompt.c_str());
Button_SetCheck (hWnd, x.iCheck);
return 0;
}
int CDialogTabTest::GetValues (CCheckBoxData &x, HWND hDlg)
{
HWND hWnd = GetDlgItem (hDlg, x.iCntrlId);
x.iCheck = Button_GetCheck (hWnd);
return 0;
}
int CDialogTabTest::OnWmCommandInit ()
{
// get the window handle for the tab control. This is going to
// be the parent window for all of the tab panes that are inserted
// into the tab control. By making the tab control the parent,
// the tab panes will be contained within the tab control and
// will flow with it should it be moved about.
HWND hTabCntrl = GetDlgItem (m_hDlg, IDC_TAB1);
TCITEM tie = {0};
tie.mask = TCIF_TEXT | TCIF_IMAGE;
tie.iImage = -1;
tie.pszText = L"First";
m_hTabPane1.CreateThing (&tie, 0, MAKEINTRESOURCE(IDD_DIALOG_TAB01), m_hInst, hTabCntrl);
tie.pszText = L"Second";
m_hTabPane2.CreateThing (&tie, 1, MAKEINTRESOURCE(IDD_DIALOG_TAB02), m_hInst, hTabCntrl);
tie.pszText = L"Third";
m_hTabPane3.CreateThing (&tie, 2, MAKEINTRESOURCE(IDD_DIALOG_TAB03), m_hInst, hTabCntrl);
// set the first tab to be displayed.
NMHDR Nmhdr = {hTabCntrl, 0, TCN_SELCHANGE};
m_hTabPane1.OnWmNotify (&Nmhdr);
return 0;
}
int CDialogTabTest::OnWmNotify (LPNMHDR pNmhdr)
{
// propagate the notification to the various panes in our tab control
// so that they can do what they need to do.
m_hTabPane1.OnWmNotify (pNmhdr);
m_hTabPane2.OnWmNotify (pNmhdr);
m_hTabPane3.OnWmNotify (pNmhdr);
return 0;
}
int CDialogTabTest::DoDataExchange (int iDir)
{
if (iDir) {
CCheckBoxData iVal = {IDC_CHECK1, BST_UNCHECKED, L"DoData: Check Box 1 Set and Not Checked"};
SetValues (iVal, m_hTabPane1.m_hDlg);
iVal.iCntrlId = IDC_CHECK2;
iVal.iCheck = BST_CHECKED;
iVal.prompt = L"DoData: Check Box 2 is Set and Checked.";
SetValues (iVal, m_hTabPane1.m_hDlg);
iVal.iCntrlId = IDC_CHECK4;
iVal.iCheck = BST_CHECKED;
iVal.prompt = L"DoData: Check Box 4 is Set, Checked Tab 3.";
SetValues (iVal, m_hTabPane3.m_hDlg);
} else {
CCheckBoxData iVal = {IDC_CHECK1, BST_UNCHECKED, L""};
GetValues (iVal, m_hTabPane1.m_hDlg);
}
return 0;
}
INT_PTR WINAPI CDialogTabTest::CreateModal (HWND hWnd, HINSTANCE hInst)
{
// this is a modal dialog box so we use the CreateModal() method
// in order to create the dialog box and display it.
return CDialogTest::CreateModal(hWnd, MAKEINTRESOURCE(IDD_DIALOG), hInst);
}
Finally there is the CDialogTabPane class which implements the functionality for individual tab control tab panes.
DialogTabPane.h
#pragma once
#include "stdafx.h"
#include "DialogTest.h"
class CDialogTabPane : public CDialogTest
{
protected:
int m_iTab;
public:
DWORD m_dwLastError;
CDialogTabPane(void);
virtual ~CDialogTabPane(void);
HWND CreateThing (LPTCITEM pTie, int iTab, LPCWSTR lpTemplateName, HINSTANCE hInstance, HWND hWndParent);
// virtual int DoDataExchange (int iDir);
// virtual int OnWmCommandInit ();
virtual int OnWmNotify (LPNMHDR pNmhdr);
};
and DialogTabPane.cpp
#include "StdAfx.h"
#include "DialogTabPane.h"
CDialogTabPane::CDialogTabPane(void)
{
}
CDialogTabPane::~CDialogTabPane(void)
{
}
HWND CDialogTabPane::CreateThing (LPTCITEM pTie, int iTab, LPCWSTR lpTemplateName, HINSTANCE hInstance, HWND hWndParent)
{
// insert the new tab into the tab control with the specified parameters.
TabCtrl_InsertItem(hWndParent, iTab, pTie);
m_iTab = iTab;
// We need to figure out the adjustment of the dialog window position
// within the tab control display area so that when the dialog and its
// controls are displayed for a given tab the tab selection list can
// still be seen above the top of the displayed dialog.
// The tab selection list must be visible so that the user can select
// any of the specified tabs.
RECT tabDisplay = {0};
GetWindowRect (hWndParent, &tabDisplay);
RECT dialogDisplay = tabDisplay;
// NOTE: TabCtrl_AdjustRect() - This message applies only to tab controls that
// are at the top. It does not apply to tab controls that are on the
// sides or bottom.
// But then tab controls on the side or bottom are an abomination.
TabCtrl_AdjustRect (hWndParent, FALSE, &dialogDisplay);
dialogDisplay.left -= tabDisplay.left; dialogDisplay.top -= tabDisplay.top;
m_dwLastError = 0;
// create our dialog and then position it within the tab control properly.
CDialogTest::CreateThing(hWndParent, lpTemplateName, hInstance);
if (!::SetWindowPos (m_hDlg, HWND_TOP, dialogDisplay.left, dialogDisplay.top, 0, 0, SWP_NOSIZE)) {
m_dwLastError = GetLastError ();
}
return m_hDlg;
}
int CDialogTabPane::OnWmNotify (LPNMHDR pNmhdr)
{
int currentSel = TabCtrl_GetCurSel(pNmhdr->hwndFrom);
switch (pNmhdr->code)
{
case TCN_SELCHANGING:
if (currentSel == m_iTab)
::ShowWindow (m_hDlg, SW_HIDE);
break;
case TCN_SELCHANGE:
if (currentSel == m_iTab)
::ShowWindow (m_hDlg, SW_SHOW);
break;
}
return 0;
}
The dialog box resources used for the individual tab control tab panes are similar and look like:
IDD_DIALOG_TAB01 DIALOGEX 0, 0, 186, 115
STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "First tab",IDC_STATIC,68,7,96,14
CONTROL "Check1",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,21,150,13
CONTROL "Check2",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,36,150,13
CONTROL "Check3",IDC_CHECK3,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,53,150,13
CONTROL "Check4",IDC_CHECK4,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,69,150,13
CONTROL "Check5",IDC_CHECK5,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,84,150,13
END
And the resource for the actual dialog containing the tab control looks like:
IDD_DIALOG_TAB00 DIALOGEX 0, 0, 336, 179
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Dialog Tab Test"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
DEFPUSHBUTTON "OK",IDOK,279,7,50,14
PUSHBUTTON "Cancel",IDCANCEL,279,24,50,14
CONTROL "",IDC_TAB1,"SysTabControl32",0x0,47,35,226,137
END
Displaying dialog box with tab 1 selected and then tab 3 selected.

Window Edit Control Not Displaying

Here is my code. I'm trying to create an edit control. It's not showing, however. Can some take a look at my code and point out the errors please. I can't figure out where the error is. I feel It might have something to do with the parent child relationship.
#include <cstdlib>
#include <windows.h>
#define MAINWINDOW_CLASS_ID 140;
const char* MAINWINDOW_CLASS_NAME = "Main Window";
const char* TEXTAREA_CLASS_NAME = "EDIT";
using namespace std;
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam);
/*
* Initialize the window class and register it.
*/
BOOL initInstance(HINSTANCE hInstance);
/*
* Create and show the window
*/
HWND initWindow(HINSTANCE hInstance);
HWND createTextArea(HWND hParent);
/*
*
*/
INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hprevInstance, LPSTR lpCmdline, INT cmdlShow)
{
if (!initInstance(hInstance))
return 0;
HWND hwndMain = initWindow(hInstance);
ShowWindow(hwndMain, cmdlShow);
MSG msg = {} ;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
BOOL initInstance(HINSTANCE hInstance)
{
WNDCLASS wc = {};
wc.lpfnWndProc = WinProc;
wc.hInstance = hInstance,
wc.lpszClassName = MAINWINDOW_CLASS_NAME;
return RegisterClass(&wc);
}
HWND initWindow(HINSTANCE hInstance)
{
HWND hwndMain = CreateWindow(
MAINWINDOW_CLASS_NAME, // The class name
"Text Editor", // The window name
WS_OVERLAPPEDWINDOW,// The window style
CW_USEDEFAULT, // The x pos
CW_USEDEFAULT, // The y pos
CW_USEDEFAULT, // The width
CW_USEDEFAULT, // The height
(HWND) NULL, // Handle to parent
(HMENU) NULL, // Handle to the menu
hInstance, // Handle to the instance
NULL // Window creation data
);
if (!hwndMain) {
return NULL;
}
else {
return hwndMain;
}
}
LRESULT CALLBACK WinProc(HWND hwnd, UINT uMsg,
WPARAM wParam, LPARAM lParam) {
HWND htextArea;
char sztestText[] = "I should be able to see this text.";
switch (uMsg) {
case WM_CREATE:
htextArea = createTextArea(hwnd);
SendMessage(htextArea, WM_SETTEXT, 0, (LPARAM) sztestText);
break;
case WM_PAINT:
break;
case WM_CLOSE:
break;
case WM_SIZE:
RECT rectMainWindow;
GetWindowRect(hwnd, &rectMainWindow);
INT x = rectMainWindow.right - rectMainWindow.left + 50;
INT y = rectMainWindow.bottom - rectMainWindow.top + 50;
MoveWindow(htextArea, 0, 0, x, y, TRUE);
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
/*******************************************************************/
HWND createTextArea(HWND hParent) {
return CreateWindow(
TEXTAREA_CLASS_NAME, // Class control name
NULL, // Title
WS_CHILD | WS_VISIBLE | ES_MULTILINE, // Styles
0, 0, 0, 0, // Sizing and position
hParent,
(HMENU) MAKEINTRESOURCE(100),
(HINSTANCE) GetWindowLong(hParent, GWL_HINSTANCE),
NULL
);
}
You've defined htextArea as a local variable in WinProc and so every time your window procedure is called its value will be uninitialized. You should make it static or move it to global data outside the function.
(The actual problem is that when you get a WM_SIZE message, you've lost the handle to the edit control, and so its size remains as 0,0).

Resources