PlaySound() keeps playing my .wav after WM_DESTROY and a DrawText Issue - c

I am experimenting on Windows 7 64 bit OS with win32 API.When i run my Program on debug mode my window appears but i have 2 bugs. Firt things first here is my api code modified from Charles Petzold book Ch3:
#include<Windows.h>
#include<mmsystem.h>
LRESULT CALLBACK HandleMyWindowsClassMessages(HWND WindowHandle, UINT Message, WPARAM wParam, LPARAM lParam);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreviousInstance, PSTR szCmdLine, int iCmdShow)
{
WNDCLASSEX MyWindowsClass;
static wchar_t szMyWindowsClassName[] = TEXT("The Name of My Window Class");
static wchar_t szAppName[] = TEXT("AppName");
HWND myWindowHandle;
MSG msg;
MyWindowsClass.hInstance = hInstance;
MyWindowsClass.style = CS_HREDRAW | CS_VREDRAW;
MyWindowsClass.lpfnWndProc = HandleMyWindowsClassMessages;
MyWindowsClass.lpszClassName = szMyWindowsClassName;
MyWindowsClass.cbSize = sizeof(WNDCLASSEX);
MyWindowsClass.cbClsExtra = 0;
MyWindowsClass.cbWndExtra = 0;
MyWindowsClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
MyWindowsClass.hCursor = LoadCursor(NULL, IDC_ARROW);
MyWindowsClass.hIcon = LoadIcon(NULL, IDI_SHIELD);
MyWindowsClass.hIconSm = NULL;
MyWindowsClass.lpszMenuName = NULL;
if (!RegisterClassEx(&MyWindowsClass))
{
MessageBoxEx(0, TEXT("This Programm Requires WINNT!"), szMyWindowsClassName, MB_ICONERROR, 0);
return(0);
}
myWindowHandle = CreateWindowEx(
WS_EX_OVERLAPPEDWINDOW,
szMyWindowsClassName,
szAppName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(myWindowHandle, iCmdShow);
UpdateWindow(myWindowHandle);
while (GetMessage(&msg, myWindowHandle, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return(msg.wParam);
}
LRESULT CALLBACK HandleMyWindowsClassMessages(HWND WindowHandle, UINT Message, WPARAM wParam, LPARAM lParam)
{
//wchar_t szGreeting[] = TEXT("Heeeey");
HDC hdc;
PAINTSTRUCT ps;
switch (Message)
{
case WM_CREATE:
{
PlaySound(TEXT("D:\\mp3\\aywy._&_EphRem_-_Adderall.wav"), NULL, SND_FILENAME | SND_ASYNC);
return(0);
} break;
case WM_PAINT:
{
hdc = BeginPaint(WindowHandle, &ps);
DrawText(hdc, TEXT("Hello Win 7!!!"), -1, &ps.rcPaint, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
//TextOut(hdc, 0, 0, szGreeting, (int)wcslen(szGreeting));
EndPaint(WindowHandle, &ps);
return(0);
}break;
case WM_DESTROY:
{
PostQuitMessage(0);
return(0);
}break;
}
return(DefWindowProc(WindowHandle, Message, wParam, lParam));
}
Bug1:PlaySound keeps playing the wav even when i have closed-destroyed the Window.When I used the original example with plain WNDCLASS the bug dissapeared!
So I must be doing something wrong using WNDCLASSEX ???.
Also in this bug to end execution of debbuging I have to press Shift+F5.
Bug2:TextOut vs DrawText
Info:I have only 1 screen.I program on my laptop.
When I use drawText and the text in my window goes out of my desktop screen borders the text just doesnt update correclty.
When I use TextOut the bug dissapears!!!
Why???
Below i have some pitures ordered to explain this.
The Program starts in debug mode
Moving the window out of Desktop borders(including the displayed text)
Moving the window back inside my desktop area(the text has been mutated)
Window is destroyed and sound will keep playing even when the wav ends.
Any help in any of the 2 bugs would be greatly appreciated.
2nd version of my WndProc:
LRESULT CALLBACK HandleMyWindowsClassMessages(HWND WindowHandle, UINT Message, WPARAM wParam, LPARAM lParam)
{
//wchar_t szGreeting[] = TEXT("Heeeey");
HDC hdc;
PAINTSTRUCT ps;
RECT rc;
rc.left = 50; rc.top = 100; rc.right = 200; rc.bottom = 200;
switch (Message)
{
case WM_CREATE:
{
PlaySound(TEXT("D:\\mp3\\aywy._&_EphRem_-_Adderall.wav"), NULL, SND_FILENAME | SND_ASYNC);
return(0);
} break;
case WM_PAINT:
{
hdc = BeginPaint(WindowHandle, &ps);
DrawText(hdc, TEXT("Hello Win 7!!!"), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
//TextOut(hdc, 0, 0, szGreeting, (int)wcslen(szGreeting));
EndPaint(WindowHandle, &ps);
return(0);
}break;
case WM_DESTROY:
{
PlaySound(NULL, NULL, SND_FILENAME | SND_ASYNC);
PostQuitMessage(0);
return(0);
}break;
}
return(DefWindowProc(WindowHandle, Message, wParam, lParam));
}

Call PlaySound( NULL, NULL, SND_FILENAME | SND_ASYNC) before PostQuitMessage to stop the sound.
About DrawText - note that BeginPaint returns rectangle rcPaint for the region which needs redraw. If you are using it for start point of DrawText, the text will appear at different places. For example - when first created, the region will start at 0,0. Then you can move another window over DrawText application window and it can require repaint from point 80,100 to lower right corner. Try with absolute (client) coordinates, e.g. define RECT rc and set rc.left = 50; rc.top = 100; rc.right = 200; rc.bottom = 200; (always the same position).

Related

Make only part of a window scrollable in win32 api

I have created a working application with pure win32 APIs and C. It has a shared "status area" on top. Rest of the area displays device information (multiple fields per device).
How do I make the device information area only scrollable?
All my widgets are currently attached to single main window handle. See here the picture about what I want :
There must be a simple way to like group the widgets of bottom page and attach scrollbar to only the group, but I cannot seem to find a working technique with google. I guess my problem is I do not know hot to create groups of widgets or something. Attaching scrollbar to to the full window works fine but I want only partial.
For those interested: I'm using Dev-C++ 5.11 with TDM-GCC 4.9.2 . I don't have and won't have a resource editor. It is all in C code.
Help!
Edit1: Ok, I believe the correct term I need to search for is "child windows" per https://learn.microsoft.com/en-us/windows/win32/winmsg/window-features#child-windows
Edi2: This old scavenged code worked somewhat: https://cboard.cprogramming.com/windows-programming/94197-creating-child-window-parent-window-post676608.html#post676608 . It creates ugly window inside window but it is what I need to get started.
Ok, i reduced the code into a minimum: https://gist.github.com/usvi/b39713e270f1f75880698baacd1774b4 or
#include <windows.h>
LRESULT CALLBACK HelloWndProc (HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCMLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT ("HelloApplication");
HWND hwnd,hwnd2;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = HelloWndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = sizeof(long);
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass (&wndclass))
{
MessageBox (NULL, TEXT ("This program requires Windows 95/98/NT"),
szAppName, MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName,
TEXT("Hello World for Windows"),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
400,
400,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
hwnd2 = CreateWindow(szAppName,
TEXT("Hello World"),
WS_CHILD | WS_VISIBLE | WS_VSCROLL,
CW_USEDEFAULT,
CW_USEDEFAULT,
300,
300,
hwnd,
NULL,
hInstance,
NULL);
ShowWindow(hwnd2, iCmdShow);
UpdateWindow(hwnd2);
while (GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK HelloWndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message){
case WM_CREATE:
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, TEXT("Hello, Windows"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
And it works, see:
My problem basically was that child window creation CreateWindow() first parameter was omitted or otherwise wrong. It needs to match named and registered WNDCLASS.
NOTE: This is a rough rough rough sketch, I might need to make 2 classes and second message handler. But this is a good start.

DestroyWindow() is not destroying child window

WndProc is the window procedure of the main window. ChildProc is the window procedure of the child window. ChildProc is not receiving WM_DESTROY. What am I doing wrong?
EDIT: If I remove the WS_CHILD window style from hChild = CreateWindowExW(...); so it's hChild = CreateWindowExW(..., WS_VISIBLE, ...); I do get WM_DESTROY in ChildProc.
Also, I'm using Windows 10 and Visual Studio 2008
#include <windows.h>
HINSTANCE g_hInst;
LRESULT CALLBACK ChildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_PAINT:
{
HDC hdc;
PAINTSTRUCT ps;
hdc = BeginPaint(hwnd, &ps);
if(hdc)
{
RECT rc;
GetClientRect(hwnd, &rc);
SetBkMode(hdc, TRANSPARENT);
FillRect(hdc, &rc, GetSysColorBrush(COLOR_GRAYTEXT));
TextOut(hdc, 0, 0, TEXT("Child"), 5);
EndPaint(hwnd, &ps);
}
}
break;
case WM_DESTROY:
MessageBoxW(0, L"Child WM_DESTROY", 0, MB_OK);
break;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
static HWND hChild;
switch(msg)
{
case WM_CREATE:
{
WNDCLASSEXW wc;
SecureZeroMemory(&wc, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
wc.hCursor = LoadCursorW(0, IDC_ARROW);
wc.hInstance = g_hInst;
wc.lpfnWndProc = ChildProc;
wc.lpszClassName = L"Childclass////";
if(!RegisterClassExW(&wc)) return -1;
hChild = CreateWindowExW(0, L"Childclass////", 0, WS_VISIBLE | WS_CHILD,
0, 0, 200, 100, hwnd, 0, g_hInst, 0);
if(!hChild) return -1;
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wParam, lParam);
}
return 0;
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEXW wc;
HWND hwnd;
MSG msg;
SecureZeroMemory(&wc, sizeof(WNDCLASSEXW));
wc.cbSize = sizeof(WNDCLASSEXW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.hCursor = LoadCursorW(0, IDC_ARROW);
wc.hIcon = LoadIconW(0, IDI_APPLICATION);
wc.hInstance = hInstance;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = L"Mainclass";
if(!RegisterClassExW(&wc)) return 0;
g_hInst = hInstance;
hwnd = CreateWindowExW(0, L"Mainclass", L"Main window", WS_OVERLAPPEDWINDOW, 240, 240, 400, 200, 0, 0, hInstance, 0);
if(!hwnd) return 0;
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
while(GetMessageW(&msg, 0, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
return (int)msg.wParam;
}
when you call DestroyWindow (assume with valid window handle) - all child windows of course will be destroyed. and all child windows of course received WM_DESTROY
ChildProc is not receiving WM_DESTROY.
this is false. i absolute sure it receive it.
What am I doing wrong?
debug diagnostic and call PostQuitMessage from wrong place.
you decide that ChildProc is "not receiving" WM_DESTROY only because you not view message box. but it will be just closed, even before shown, if you call PostQuitMessage(0); before it.
when a window is being destroyed WM_DESTROY is sent first to the owned windows (if any), then to window being destroyed and finally to the child windows (if any).
so in case you use child window - first parent window received WM_DESTROY and you call PostQuitMessage then child window call MessageBox which just returned without show due previous PostQuitMessage call.
if you use owned window - it receive WM_DESTROY first and show MessageBox normal. and only after you close it parent window receive WM_DESTROY finally and you call PostQuitMessage
for fix this, at first need call PostQuitMessage from WM_NCDESTROY - the parent window receive this message after all owned and child windows.
at second the MessageBox not the best for debug diagnostic. much better use DbgPrint, OutputDebugString or breakpoints in debugger
thank #RemyLebeau for link to Raymond Chen blog - why MessageBox() does not show anything if PostQuitMessage() was already called beforehand:
The other important thing about modality is that a WM_QUIT message
always breaks the modal loop.
So, if PostQuitMessage() is called before MessageBox(), the latter will receive the WM_QUIT message, cancel its UI, re-post WM_QUIT, and exit.

Why Drawtext is still drawing text when I haven`t validated my RECT?

I am replacing BeginPaint-EndPaint in a simple window with GetDC-ReleaseDC.
I am reading Charles Petzold Programming Windows 5th Edition.
Here is my code with the changes and the lines changed as comments:
#include<Windows.h>
#include<mmsystem.h>
LRESULT CALLBACK myWndProc(HWND windowHandle, UINT winMessage, WPARAM wParam, LPARAM lParam);
int
WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
WNDCLASSEX myWndClass;
MSG msg;
HWND myWndHandle;
wchar_t szmyWndClassName[] = TEXT("SotoWindClass");
wchar_t szmyWndowName[] = TEXT("SotoWindow");
myWndClass.cbClsExtra = 0;
myWndClass.cbSize = sizeof(WNDCLASSEX);
myWndClass.cbWndExtra = 0;
myWndClass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
myWndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
myWndClass.hIcon = LoadIcon(NULL, IDI_HAND);
myWndClass.hIconSm = NULL;
myWndClass.hInstance = hInstance;
myWndClass.lpfnWndProc = myWndProc;
myWndClass.lpszClassName = szmyWndClassName;
myWndClass.lpszMenuName = NULL;
myWndClass.style = CS_HREDRAW | CS_VREDRAW;
if (!RegisterClassEx(&myWndClass))
{
MessageBoxEx(NULL, TEXT("I need at least WINNT"), szmyWndClassName, MB_ICONERROR, 0);
}
myWndHandle = CreateWindowEx(
WS_EX_LEFT,
szmyWndClassName,
szmyWndowName,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(myWndHandle, iCmdShow);
UpdateWindow(myWndHandle);
while (GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK myWndProc(HWND windowHandle, UINT winMessage, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
//PAINTSTRUCT ps;
RECT rc;
wchar_t displayText[] = TEXT("Display My Text!!!!");
switch (winMessage)
{
case WM_CREATE:
{
PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC);
return(0);
}break;
case WM_PAINT:
{
/*
hdc = BeginPaint(WindowHandle, &ps);
DrawText(hdc, TEXT("Hello Win 7!!!"), -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(WindowHandle, &ps);
return(0);
*/
hdc = GetDC(windowHandle);
GetClientRect(windowHandle, &rc);
//ValidateRect(windowHandle, &rc);
DrawText(hdc, displayText, -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
ReleaseDC(windowHandle, hdc);
return(0);
}
case WM_DESTROY:
{
PlaySound(NULL, NULL, SND_FILENAME | SND_ASYNC);
PostQuitMessage(0);
return(0);
}
}
return(DefWindowProc(windowHandle, winMessage, wParam, lParam));
}
My question is:
Why DrawText is still displaying the message when i haven't called ValidateRect?
From what i understand(which is obviously incorrect) the text inside RECT rc shouldn't appear, unless I call ValidateRect.
When the window is displayed the text drawn is flickering which i assume happens because Windows are calling WM_PAINT and are trying to validate rc (my client area) but DrawText still manages to display the text line every time.
I am a bit confused.
Your text is rendered again and again, because you haven't called ValidateRect. EndPaint calls ValidateRect to mark the area rendered during this paint cycle as valid, i.e. doesn't need rendering.
Leaving an area marked as invalid doesn't stop you drawing to it, it just means the system won't think you have drawn to it and will keep asking you to.
(posted as community wiki, since the question was answered in the comments)

Loosing mouse cursor when changing GetStockObject(WHITE_BRUSH) to GetStockObject(GREY_BRUSH)

I am trying to learn win32 API using Programming Windows fifth Edition.
As I was experimenting with some Identifiers I noticed something that I am not able to understand why is happening.I` ll be more specific, here is my code:
#include<Windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int
WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
static TCHAR szAppName[] = TEXT("HELLOWIN");
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_SHIELD);
wndclass.hCursor = LoadCursor(NULL, IDC_CROSS);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(0, TEXT("This Programm Requires WINNT!"), szAppName, MB_ICONERROR);
return(0);
}
hwnd = CreateWindow(szAppName, //window class name
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(we have top-level window)
NULL, //window menu handle
hInstance, //programm instances handle
NULL); //creation parameters
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
switch (message)
{
case WM_CREATE:
{
PlaySound(TEXT("D:\\mp3\\aywy._&_EphRem_-_Adderall.wav"), NULL, SND_FILENAME | SND_ASYNC);
return 0;
} break;
case WM_PAINT:
{
hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rect);
DrawText(hdc, TEXT("Hello, Windows 98!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint(hwnd, &ps);
return 0;
} break;
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
} break;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
With this code everything works great and as expected but...
when i change:
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
to
wndclass.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
the cursor icon is lost on the background and is only visisble in the small line
in which i use drawText().What confuses me is that this doesnt happen when my background is white(WHITE_BRUSH).
Could someone explain why?
PS:If this behaviour is explained later in the book (I am finishing chapter 3 currently) just type Read more so i don`t waste you time.
Thank you in advance.
What is probably happening is that the 'cross' cursor that you are using is a very thin cursor implemented (either by windows or by the hardware) by NEGating the underlying pixels instead of painting above them. This works fine for all colors except the 0x808080 gray, because negating 0x808080 still gives 0x808080, so the cursor is invisible. Try using light gray, dark gray, or another cursor which is not so thin.

TabControl clips client area when displaying overflow arrows

This bug appears only with common control v6 (theme enabled) on XP (seems to work on 7 and 2008). I wonder if someone else might have seen this bug feature.
When you have a single-line TabControl with lots of tabs, a pairs of arrows should appear if there is not enough space to display all the tabs. This is all nice except that the client area is also clipped, which is not nice at all.
Have I miss something ? I played with tabcontrol's window style, but no luck so far.
To illustrate this, it's actually best to see it in action:
#define UNICODE
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
HWND htab, hbut;
int WINAPI WinMain (HINSTANCE instance,
HINSTANCE previnst,
LPSTR args,
int wndState)
{
int i;
MSG messages;
WNDCLASSEX wincl = {
.hInstance = instance, .lpszClassName = L"WindowsApp",
.lpfnWndProc = WindowProcedure, .style = CS_DBLCLKS, .cbSize = sizeof wincl,
.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1)
};
InitCommonControls();
wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClassEx (&wincl))
return 0;
HWND hwnd = CreateWindow(L"WindowsApp", L"Windows App", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 544, 375, HWND_DESKTOP, NULL, instance, NULL);
htab = CreateWindowEx(WS_EX_CONTROLPARENT, WC_TABCONTROL,
L"MyTab", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, 10, 10, 514, 325, hwnd,
(HMENU) 10, instance, NULL);
hbut = CreateWindow(
WC_BUTTON, L"My nice button that is clipped", WS_CHILD | WS_VISIBLE | WS_TABSTOP,
10, 30, 494, 285, htab, (HMENU) IDOK, instance, NULL
);
for (i = 0; i < 10; i ++)
{
WCHAR myBuf[100];
TCITEM tc = {.mask = TCIF_TEXT, .pszText = myBuf};
wsprintf(myBuf, L"My super tab %d", i + 1);
TabCtrl_InsertItem(htab, i, &tc);
}
SendMessage(hbut, WM_SETFONT, (LPARAM) GetStockObject(DEFAULT_GUI_FONT), FALSE);
SendMessage(htab, WM_SETFONT, (LPARAM) GetStockObject(DEFAULT_GUI_FONT), FALSE);
ShowWindow(hwnd, wndState);
while (GetMessage (&messages, NULL, 0, 0))
{
TranslateMessage(&messages);
DispatchMessage(&messages);
}
return messages.wParam;
}
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
RECT r;
switch (message) {
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_SIZE:
GetClientRect(hwnd, &r);
MoveWindow(htab, 10, 10, r.right-20, r.bottom - 20, TRUE);
MoveWindow(hbut, 10, 30, r.right-40, r.bottom - 60, TRUE);
break;
case WM_COMMAND:
break;
default:
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
Unfortunately I can't test it (I have Win7) but according to http://msdn.microsoft.com/en-us/library/hh298367%28v=VS.85%29.aspx you need to have clip siblings on the tab control and also the parent window. You could also try using the tab control's adjust rect message to get the positions for your button.

Resources