WinAPI - GDI: DrawText() and ExtTextOut() ugly (AA enabled) - winapi

Im trying to make line numbering for a rich edit control using the method described here (not MFC though). While it works fine, the rendering of line numbers is really ugly compared to the rich edit.
I made a small program to reproduce the problem: (sorry that I copy paste but couldnt find a normal file hosting service -.-):
#include <Windows.h>
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch( msg )
{
case WM_CLOSE:
ShowWindow(hWnd, SW_HIDE);
DestroyWindow(hWnd);
break;
case WM_PAINT:
{
HDC hdc = GetDC(hWnd);
HDC bdc = CreateCompatibleDC(hdc);
HBITMAP bitmap = CreateCompatibleBitmap(hdc, 400, 100);
int height = -MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72);
HFONT font = CreateFont(
height, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
ANTIALIASED_QUALITY, FF_DONTCARE, "Consolas");
HGDIOBJ oldbm = SelectObject(bdc, bitmap);
HGDIOBJ oldft = SelectObject(bdc, font);
RECT rc = { 0, 0, 200, 20 };
FillRect(bdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH));
DrawText(bdc, " 1 2 3 4 5 6 7 8 9", 19, &rc, DT_LEFT|DT_EDITCONTROL);
BitBlt(hdc, 20, 20, rc.right - rc.left, rc.bottom - rc.top, bdc, 0, 0, SRCCOPY);
SelectObject(bdc, oldbm);
SelectObject(bdc, oldft);
DeleteObject(bitmap);
DeleteObject(font);
DeleteDC(bdc);
}
return 1;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_KEYUP:
switch(wParam)
{
case VK_ESCAPE:
SendMessage(hWnd, WM_CLOSE, 0, 0);
break;
}
break;
default:
break;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nShowCmd)
{
HWND hwnd = 0;
WNDCLASSEX wc =
{
sizeof(WNDCLASSEX),
CS_CLASSDC,
(WNDPROC)WndProc,
0L, 0L,
hInst,
NULL, NULL, NULL, NULL,
"TestClass", NULL
};
RegisterClassEx(&wc);
int w = GetSystemMetrics(0);
int h = GetSystemMetrics(1);
DWORD style = WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_SYSMENU|WS_BORDER|WS_CAPTION;
hwnd = CreateWindowA("TestClass", "Winapi1", style,
(w - 400) / 2, (h - 200) / 2, 400, 200,
NULL, NULL, wc.hInstance, NULL);
if( !hwnd )
{
MessageBoxA(NULL, "Could not create window!", "Error!", MB_OK);
goto _end;
}
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
MSG msg;
ZeroMemory(&msg, sizeof(msg));
while( msg.message != WM_QUIT )
{
while( PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
_end:
UnregisterClass("TestClass", wc.hInstance);
return 0;
}
A comparison of the result with typing the text into a rich edit:
(...can't post image -.- ...)
While the difference is 'subtle' if I enlarge the image it shows that rich edit uses some kind of coloring.
I tried the following things:
using ExTextOut
saving the bitmap to file to see if its ugly too (it is)
drawing directly into hdc (also ugly plus it flickers but thats not an issue right now)
What am I missing?

Related

CreateWindow “Edit” typed characters are 'invisible'?

After having researched all I could find on the proper syntax for the CreateWindow("Edit") call, I have to throw in the towel: when I run the program, all I get in the Edit box is "invisible characters". The cursor is moving right as I type, but the characters I enter are nowhere to be seen. Only when I select the box content with the mouse do I see the text. But as soon as I release the mouse, I can not longer see anything.
Here is the entire code which leads to 'text not showing' in the Edit control:
#include <windows.h>
#define FILE_MENU_NEW 1
#define FILE_MENU_OPEN 2
#define FILE_MENU_QUIT 3
#define CHANGE_TITLE 4
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM);
void AddMenus(HWND);
void AddControls(HWND);
HMENU hMenu;
HWND hEdit;
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd)
{
WNDCLASS wc = {0}; // Assign 0 to all its elements initialy
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hInstance = hInstance;
wc.lpszClassName = L"myWindowClass";
wc.lpfnWndProc = WindowProc; // this is a pointer to a function
if(!RegisterClass(&wc))
return -1;
HWND hwnd = CreateWindowEx(
WS_EX_CLIENTEDGE,
L"myWindowClass",
L"Learn to Program Windows - Roger Breton",
WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
100, 100, 800, 600,
NULL,
NULL,
hInstance,
NULL
);
if (hwnd == NULL)
{
return 0;
}
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_COMMAND:
switch(wParam)
{
case FILE_MENU_QUIT:
DestroyWindow(hWnd);
break;
case FILE_MENU_NEW:
MessageBeep(MB_ICONINFORMATION);
break;
case CHANGE_TITLE:
wchar_t text[100];
GetWindowTextW(hEdit, text, 100);
SetWindowTextW(hWnd, text);
break;
}
case WM_CREATE:
AddMenus(hWnd);
AddControls(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW));
EndPaint(hWnd, &ps);
}
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
void AddMenus(HWND hWnd)
{
hMenu = CreateMenu();
HMENU hFileMenu = CreateMenu();
HMENU hSubMenu = CreateMenu();
AppendMenu(hSubMenu, MF_STRING, NULL, L"Sub-Menu");
AppendMenu(hFileMenu, MF_STRING, FILE_MENU_NEW, L"New");
AppendMenu(hFileMenu, MF_POPUP, (UINT_PTR)hSubMenu, L"Sub-menu ");
AppendMenu(hFileMenu, MF_SEPARATOR, NULL, NULL);
AppendMenu(hFileMenu, MF_STRING, FILE_MENU_QUIT, L"Quit");
AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hFileMenu, L"File");
AppendMenu(hMenu, MF_STRING, NULL, L"Aide");
SetMenu(hWnd, hMenu);
}
void AddControls(HWND hWnd)
{
CreateWindowW(L"Static", L"Enter text here:", WS_VISIBLE | WS_CHILD | WS_BORDER | SS_CENTER , 200, 100, 150, 50, hWnd, NULL, NULL, NULL);
hEdit = CreateWindowW(L"Edit", NULL, WS_VISIBLE | WS_CHILD | WS_BORDER , 200, 152, 100, 50, hWnd, NULL, NULL, NULL);
CreateWindowW(L"Button", L"Changez title", WS_VISIBLE | WS_CHILD, 200, 204, 150, 50, hWnd, (HMENU)CHANGE_TITLE, NULL, NULL);
}
I tried to recreate the source file many times, to no avail.
You have a missing break; statement at the end of your case WM_COMMAND: block. As it stands, your code will 'fall through' to the case WM_CREATE: code after processing any WM_COMMAND.
Adding that break; statement appears to fix your code (when I test it):
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_COMMAND:
switch (wParam) {
case FILE_MENU_QUIT:
DestroyWindow(hWnd);
break;
case FILE_MENU_NEW:
MessageBeep(MB_ICONINFORMATION);
break;
case CHANGE_TITLE:
wchar_t text[100];
GetWindowTextW(hEdit, text, 100);
SetWindowTextW(hWnd, text);
break;
}
break; // ** You missed this line! **
case WM_CREATE:
AddMenus(hWnd);
AddControls(hWnd);
break;
//...
Such mistakes can be spotted if you enable all compiler warnings: The static code analyser in MSVC gives the following message:
warning C26819: Unannotated fallthrough between switch labels (es.78).

Create window without titlebar, with resizable border and without bogus 6px white stripe

I want a window without title bar but with resizable frames and shadow.
This can easily be achieved by removing WS_CAPTION and adding WS_THICKFRAME, however, since Windows 10, there's a 6px white non-client area.
With the following code I create a window and paint all the client area with black, the window gets a left, right and bottom 6px transparent margins, however the top margin is white.
#ifndef UNICODE
#define UNICODE
#endif
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"", // Window text
0,
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
ShowWindow(hwnd, nCmdShow);
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle |= WS_THICKFRAME;
lStyle = lStyle & ~WS_CAPTION;
SetWindowLong(hwnd, GWL_STYLE, lStyle);
SetWindowPos(hwnd, NULL, 0,0,0,0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
// Paint everything black
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOWTEXT));
EndPaint(hwnd, &ps);
}
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Renders:
How can I remove the white stripe ?
I also found this related Qt bug report QTBUG-47543 which was closed as not being a Qt problem, because it can be reproduced with win32 api.
That's not a bug. In Windows 10 the borders on left/right/bottom are transparent. The top border is not transparent. You should leave it as is. Probably nobody will complain.
To change it, you must modify the non-client area. This is rather difficult in Windows Vista and above. See Custom Window Frame Using DWM for reference.
Find border thickness
Use DwmExtendFrameIntoClientArea to get access to non-client area
Use BeginBufferedPaint to draw opaque color over non-client area
Windows 10 example:
(See the next example for compatibility with Windows Vista, 7, 8)
//requires Dwmapi.lib and UxTheme.lib
#include <Windows.h>
#include <Dwmapi.h>
void my_paint(HDC hdc, RECT rc)
{
HBRUSH brush = CreateSolidBrush(RGB(0, 128, 0));
FillRect(hdc, &rc, brush);
DeleteObject(brush);
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static RECT border_thickness;
switch (uMsg)
{
case WM_CREATE:
{
//find border thickness
SetRectEmpty(&border_thickness);
if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_THICKFRAME)
{
AdjustWindowRectEx(&border_thickness, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
border_thickness.left *= -1;
border_thickness.top *= -1;
}
else if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER)
{
SetRect(&border_thickness, 1, 1, 1, 1);
}
MARGINS margins = { 0 };
DwmExtendFrameIntoClientArea(hwnd, &margins);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
BP_PAINTPARAMS params = { sizeof(params), BPPF_NOCLIP | BPPF_ERASE };
HDC memdc;
HPAINTBUFFER hbuffer = BeginBufferedPaint(hdc, &rc, BPBF_TOPDOWNDIB, &params, &memdc);
my_paint(memdc, rc);
BufferedPaintSetAlpha(hbuffer, &rc, 255);
EndBufferedPaint(hbuffer, TRUE);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCACTIVATE:
return 0;
case WM_NCCALCSIZE:
if (lParam)
{
NCCALCSIZE_PARAMS* sz = (NCCALCSIZE_PARAMS*)lParam;
sz->rgrc[0].left += border_thickness.left;
sz->rgrc[0].right -= border_thickness.right;
sz->rgrc[0].bottom -= border_thickness.bottom;
return 0;
}
break;
case WM_NCHITTEST:
{
//do default processing, but allow resizing from top-border
LRESULT result = DefWindowProc(hwnd, uMsg, wParam, lParam);
if (result == HTCLIENT)
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ScreenToClient(hwnd, &pt);
if (pt.y < border_thickness.top) return HTTOP;
}
return result;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int)
{
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
CreateWindowEx(0, CLASS_NAME, NULL,
WS_VISIBLE | WS_THICKFRAME | WS_POPUP,
10, 10, 600, 400, NULL, NULL, hInstance, NULL);
MSG msg = {};
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
For compatibility with Windows Vista/7/8 use this procedure instead. This will paint over left/top/bottom borders as well as top border. This window will appear as a simple rectangle, with resizing borders:
//for Windows Vista, 7, 8, 10
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static RECT border_thickness;
switch (uMsg)
{
case WM_CREATE:
{
//find border thickness
SetRectEmpty(&border_thickness);
if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_THICKFRAME)
{
AdjustWindowRectEx(&border_thickness, GetWindowLongPtr(hwnd, GWL_STYLE) & ~WS_CAPTION, FALSE, NULL);
border_thickness.left *= -1;
border_thickness.top *= -1;
}
else if (GetWindowLongPtr(hwnd, GWL_STYLE) & WS_BORDER)
{
SetRect(&border_thickness, 1, 1, 1, 1);
}
MARGINS margins = { 0 };
DwmExtendFrameIntoClientArea(hwnd, &margins);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
BP_PAINTPARAMS params = { sizeof(params), BPPF_NOCLIP | BPPF_ERASE };
HDC memdc;
HPAINTBUFFER hbuffer = BeginBufferedPaint(hdc, &rc, BPBF_TOPDOWNDIB, &params, &memdc);
my_paint(memdc, rc);
BufferedPaintSetAlpha(hbuffer, &rc, 255);
EndBufferedPaint(hbuffer, TRUE);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCACTIVATE:
return 0;
case WM_NCCALCSIZE:
if (lParam)
return 0;
case WM_NCHITTEST:
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
ScreenToClient(hwnd, &pt);
RECT rc;
GetClientRect(hwnd, &rc);
enum {left=1, top=2, right=4, bottom=8};
int hit = 0;
if (pt.x < border_thickness.left) hit |= left;
if (pt.x > rc.right - border_thickness.right) hit |= right;
if (pt.y < border_thickness.top) hit |= top;
if (pt.y > rc.bottom - border_thickness.bottom) hit |= bottom;
if (hit & top && hit & left) return HTTOPLEFT;
if (hit & top && hit & right) return HTTOPRIGHT;
if (hit & bottom && hit & left) return HTBOTTOMLEFT;
if (hit & bottom && hit & right) return HTBOTTOMRIGHT;
if (hit & left) return HTLEFT;
if (hit & top) return HTTOP;
if (hit & right) return HTRIGHT;
if (hit & bottom) return HTBOTTOM;
return HTCLIENT;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Just to expand on this a little; in order to remove the white stripe one just has to remove the corresponding value from the first rect in NCCALCSIZE. pywin32 code would be:
if msg == WM_NCCALCSIZE:
if wParam:
res = CallWindowProc(
wndProc, hWnd, msg, wParam, lParam
)
sz = NCCALCSIZE_PARAMS.from_address(lParam)
sz.rgrc[0].top -= 6 # remove 6px top border!
return res
I think we don't need to work with DWM to remove this border. This white top resize border belongs to the non-client area of a window. So for removing it you should handle window messages related to the resizing and activating of non-client area of a window like below: ( tested only on Win 10 )
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
/* When we have a custom titlebar in the window, we don't need the non-client area of a normal window
* to be painted. In order to achieve this, we handle the "WM_NCCALCSIZE" which is responsible for the
* size of non-client area of a window and set the return value to 0. Also we have to tell the
* application to not paint this area on activate and deactivation events so we also handle
* "WM_NCACTIVATE" message. */
switch( nMsg )
{
case WM_NCACTIVATE:
{
/* Returning 0 from this message disable the window from receiving activate events which is not
desirable. However When a visual style is not active (?) for this window, "lParam" is a handle to an
optional update region for the nonclient area of the window. If this parameter is set to -1,
DefWindowProc does not repaint the nonclient area to reflect the state change. */
lParam = -1;
break;
}
/* To remove the standard window frame, you must handle the WM_NCCALCSIZE message, specifically when
its wParam value is TRUE and the return value is 0 */
case WM_NCCALCSIZE:
if( wParam )
{
/* Detect whether window is maximized or not. We don't need to change the resize border when win is
* maximized because all resize borders are gone automatically */
WINDOWPLACEMENT wPos;
// GetWindowPlacement fail if this member is not set correctly.
wPos.length = sizeof( wPos );
GetWindowPlacement( hWnd, &wPos );
if( wPos.showCmd != SW_SHOWMAXIMIZED )
{
RECT borderThickness;
SetRectEmpty( &borderThickness );
AdjustWindowRectEx( &borderThickness,
GetWindowLongPtr( hWnd, GWL_STYLE ) & ~WS_CAPTION, FALSE, NULL );
borderThickness.left *= -1;
borderThickness.top *= -1;
NCCALCSIZE_PARAMS* sz = reinterpret_cast< NCCALCSIZE_PARAMS* >( lParam );
// Add 1 pixel to the top border to make the window resizable from the top border
sz->rgrc[ 0 ].top += 1;
sz->rgrc[ 0 ].left += borderThickness.left;
sz->rgrc[ 0 ].right -= borderThickness.right;
sz->rgrc[ 0 ].bottom -= borderThickness.bottom;
return 0;
}
}
break;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
case WM_NCCALCSIZE: {
// This must always be last.
NCCALCSIZE_PARAMS* sz = reinterpret_cast<NCCALCSIZE_PARAMS*>(lparam);
// Add 8 pixel to the top border when maximized so the app isn't cut off
// on windows 10, if set to 0, there's a white line at the top
// of the app and I've yet to find a way to remove that.
sz->rgrc[0].top += 1;
sz->rgrc[0].right -= 8;
sz->rgrc[0].bottom -= 8;
sz->rgrc[0].left -= -8;
// Previously (WVR_HREDRAW | WVR_VREDRAW), but returning 0 or 1 doesn't
// actually break anything so I've set it to 0. Unless someone pointed a
// problem in the future.
return 0;
}
Change the style of dialog.
LONG lStyle = GetWindowLong(hwnd, GWL_STYLE);
lStyle |= WS_THICKFRAME; // 6px white stripe cause of this.
lStyle = lStyle & ~WS_CAPTION;

window programming API - LoadImage

I am learning window API programming.
However I am getting stuck in loading image and showing on the window.
#include <windows.h>
#include <stdio.h>
#define wname "mywin"
LRESULT CALLBACK WndProc(HWND h,UINT im,WPARAM wp,LPARAM lp);
int APIENTRY WinMain(HINSTANCE his, HINSTANCE prev,LPSTR cmd, int cshow)
{
WNDCLASS wc;
wc.cbClsExtra=0;
wc.cbWndExtra=0;
wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wc.lpszMenuName=NULL;
wc.style=CS_HREDRAW | CS_VREDRAW;
wc.hInstance = his;
wc.lpszClassName = wname;
wc.lpfnWndProc = (WNDPROC)WndProc;
RegisterClass(&wc);
HWND h = CreateWindow(wname, wname, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, (HMENU) NULL, his, NULL );
ShowWindow(h, cshow);
MSG Message;
while(GetMessage(&Message,0,0,0))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
}
LRESULT CALLBACK WndProc(HWND h, UINT im, WPARAM wp, LPARAM lp)
{
HBITMAP static hbm;
HDC static mdc;
BITMAP static bm;
switch(im)
{
case WM_CREATE:
{
hbm = (HBITMAP) LoadImage(NULL, "../img/1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
GetObject(hbm, sizeof(bm), &bm);
if(!hbm)
{
MessageBox(h, "Error loading bitmap", "msg", MB_OK);
}
break;
}
case WM_PAINT:
{
HDC hdc = GetDC(h);
mdc = CreateCompatibleDC(hdc);
SelectObject(mdc, hbm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, mdc, 0, 0, SRCCOPY);
break;
}
}
I tried this code.
There is no grammer error, but the output is wierd.
Picture appears, but without window, and the program stopped.
I think the probelm is on WM_CREATE part in 'LRESULT CALLBACK WndProc' , but cannot figure out exactly what is wrong. HELP!!!
(WinMain is ok, here is no problem)
It seems you have no return value for WndProc and probably Win32 messages are not processed by default.
(after case WM_PAINT):
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}

Weird splitter issue

I borrowed a bit of code for a splitter bar in between two edits, but I'm getting weirdness when I try to either write on the bottom one or resize them by moving the bar. Often, the bottom edit disappears completely and the scrollbar dances around.
Ultimately, my goal is to have the window divided up into 5 different edits for a look similar to this: http://www.codeproject.com/KB/tree/Win32TreeList/TreeList.gif
Am I going about it the right way?
Relevant code:
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HINSTANCE hInst;
RECT rect;
static HCURSOR hCursor;
static BOOL bSplitterMoving;
static DWORD dwSplitterPos;
static HWND hWnd1, hWnd2;
switch (uMsg)
{
case WM_CREATE:
{
hInst = ((LPCREATESTRUCT)lParam)->hInstance;
hWnd1 = CreateWindowEx(WS_EX_CLIENTEDGE,
L"edit", NULL,
WS_CHILD | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL,
0, 0, 0, 0,
hWnd, (HMENU)1,
hInst, NULL);
hWnd2 = CreateWindowEx(WS_EX_CLIENTEDGE,
L"edit", NULL,
WS_CHILD | WS_VISIBLE | ES_MULTILINE | WS_VSCROLL,
0, 0, 0, 0,
hWnd, (HMENU)2,
hInst, NULL);
hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_SIZENS));
bSplitterMoving = FALSE;
dwSplitterPos = 130;
//
case WM_SIZE:
if ((wParam != SIZE_MINIMIZED) && (HIWORD(lParam) < dwSplitterPos))
dwSplitterPos = HIWORD(lParam) - 10;
/* Adjust the children's size and position */
MoveWindow(hWnd1, 0, 0, LOWORD(lParam), dwSplitterPos - 1, TRUE);
MoveWindow(hWnd2, 0, dwSplitterPos + 2, LOWORD(lParam), HIWORD(lParam) - dwSplitterPos - 2, TRUE);
return 0;
case WM_MOUSEMOVE:
if (HIWORD(lParam) > 10) // do not allow above this mark
{
SetCursor(hCursor);
if ((wParam == MK_LBUTTON) && bSplitterMoving)
{
GetClientRect(hWnd, &rect);
if (HIWORD(lParam) > rect.bottom)
return 0;
dwSplitterPos = HIWORD(lParam);
SendMessage(hWnd, WM_SIZE, 0, MAKELPARAM(rect.right, rect.bottom));
}
}
return 0;
case WM_LBUTTONDOWN:
SetCursor(hCursor);
bSplitterMoving = TRUE;
SetCapture(hWnd);
return 0;
case WM_LBUTTONUP:
ReleaseCapture();
bSplitterMoving = FALSE;
return 0;
//
Images showing what's happening:
http://imgur.com/a/OcdSx
A simple WNDCLASSEX wcx declaration will work sometimes, but sometimes it crashes the program.
You should always initialize structures with zero: WNDCLASSEX wcx = { 0 };
Another problem, you didn't handle WM_COMMAND properly, you have to return 0 or break when it is done:
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDM_EXIT:
PostQuitMessage(0);
break;
case IDM_ABOUT:
break;
default:
break;
}
return 0;//** THIS WAS MISSING
}

Why does it take so long to hear the beep when dragging the app window side a little bit faster?

This is a very simple code using the Windows API, where a child window is painted in the central part of the app window. By clicking with the mouse left button on the main window client area, the child window assumes a null height. By clicking with the mouse right button, the child window has its height increased by 10 pixels, at each mouse click. One can hear a beep every time a left, or right mouse click occurs in the window's app, due to the MessageBeep(-1) call made while painting the parent window.
The curious thing is that when you drag one of the sides of the parent window, changing its width or height, very slowly, you can hear the beeps, at each move. But if you drag the window's side a little bit faster, you'll hear just one beep, many many seconds after the mouse button was released. Why is that?
This is the code:
#include <windows.h>
LRESULT CALLBACK WindowProc(HWND, UINT, UINT, LONG);
LRESULT CALLBACK ChildProc(HWND, UINT, UINT, LONG);
/****************************************************************************************************************************
WinMain()
****************************************************************************************************************************/
int APIENTRY WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASSEX wndclassx;
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = 0;
wndclassx.lpfnWndProc = WindowProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = 0;
wndclassx.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclassx.lpszMenuName = NULL;
wndclassx.lpszClassName = L"ParentWindow";
wndclassx.hIconSm = NULL;
if( !RegisterClassEx(&wndclassx) ) return 0;
wndclassx.cbSize = sizeof(WNDCLASSEX);
wndclassx.style = 0;
wndclassx.lpfnWndProc = ChildProc;
wndclassx.cbClsExtra = 0;
wndclassx.cbWndExtra = 0;
wndclassx.hInstance = hInstance;
wndclassx.hIcon = 0;
wndclassx.hCursor = 0;
wndclassx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wndclassx.lpszMenuName = NULL;
wndclassx.lpszClassName = L"ChildWindow";
wndclassx.hIconSm = NULL;
if( !RegisterClassEx(&wndclassx) ) return 0;
HWND hWnd;
if( !(hWnd = CreateWindow(L"ParentWindow", L"Parent Window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL)) ) return 0;
ShowWindow(hWnd, SW_SHOW);
UpdateWindow(hWnd);
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
/****************************************************************************************************************************
WindowProc()
****************************************************************************************************************************/
LRESULT CALLBACK WindowProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
PAINTSTRUCT ps;
switch ( message )
{
RECT rect;
HWND hChild;
case WM_CREATE:
GetClientRect(hwnd, &rect);
if( !CreateWindow(L"ChildWindow", NULL, WS_CHILD | WS_VISIBLE | WS_BORDER, rect.right / 3, rect.bottom / 3, rect.right / 3,
rect.bottom / 3, hwnd, (HMENU)0, ((LPCREATESTRUCT)lParam)->hInstance, NULL) ) return -1;
break;
case WM_SIZE:
MoveWindow(GetDlgItem(hwnd, 0), LOWORD(lParam) / 3, HIWORD(lParam) / 3, LOWORD(lParam) / 3, HIWORD(lParam) / 3,
true);
break;
case WM_LBUTTONDOWN:
hChild = GetDlgItem(hwnd, 0);
GetWindowRect(hChild, &rect);
ScreenToClient(hwnd, (LPPOINT)&rect);
ScreenToClient(hwnd, (LPPOINT)&rect.right);
MoveWindow(hChild, rect.left, rect.top, rect.right - rect.left, 0, true);
break;
case WM_RBUTTONDOWN:
hChild = GetDlgItem(hwnd, 0);
GetWindowRect(hChild, &rect);
ScreenToClient(hwnd, (LPPOINT)&rect);
ScreenToClient(hwnd, (LPPOINT)&rect.right);
MoveWindow(hChild, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top + 10, true);
break;
case WM_PAINT:
BeginPaint(hwnd, &ps);
MessageBeep(-1);
EndPaint(hwnd, &ps);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return 0;
}
/****************************************************************************************************************************
ChildProc()
****************************************************************************************************************************/
LRESULT CALLBACK ChildProc (HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
switch( message )
{
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
}
To quote from this KB article on MSDN:
With normal use of GetMessage() (passing zeros for all arguments
except the LPMSG parameter) or PeekMessage(), any message on the
application queue is processed before user input messages. And input
messages are processed before WM_TIMER and WM_PAINT "messages."
In other words, because you call MessageBeep during WM_PAINT, it won't happen until after the window stops processing user input, e.g., when the user stops moving the window around.

Resources