Weird splitter issue - winapi

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
}

Related

CreateSwapChainForHwnd with DXGI_SCALING_STRETCH : erroneous stretch

I'm using a SwapChain created with CreateSwapChainForHwnd with DXGI_SCALING_STRETCH scaling. When the sizes of the swapchain and the window client area differ, the SwapChain's associated bitmap is correctly scaled, but its origin is moved as if the window borders were accounted in the stretch process although there sizes are constant.
Is there a way to fix or circumvent this issue ?
Thank you for the help !
(More precisely : for a client area larger that swapchain, the origin of the bitmap in shifted down, right proportionally respectively to title-bar height and left border width, the part of the bitmap thus moved outside the client area being invisible; a reverse shift is produced with client area smaller than swapchain).
Below a minimal code to reproduce this issue based on visual studio desktop app template. I put everything in InitInstance() (I know it's bad), appart from classical DX #includes and declarations. It corresponds to https://learn.microsoft.com/en-us/windows/win32/direct2d/devices-and-device-contexts except I create the swapchain with CreateSwapChainForHwnd() instead of CreateSwapChainForCoreWindow() :
The pb appears fror MDI childs, as well as for SDI windows with a menu. The way the buffer bitmap is drawn, CopyFromMemory, DrawBitmap or even D2D drawing doesn't matter, as shown in images with the two diagonals made with DrawLine().226707-stretchedout.png
Actually the white bands are flickering during resizing.
Example, starting from Visual Studios's destop app template, replace InitInstance() below and add D2D includes and declarations...
...
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
ATOM aMainClass;
ATOM aChildClass;
const TCHAR lpszMainClassName[] = TEXT("FCLAYERMAIN");
const TCHAR lpszChildClassName[] = TEXT("FCLAYERCHILD");
hInst = hInstance;
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
//prepare MDI windows (main + MDI client + one child)
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = 0;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = lpszMainClassName;
wcex.hIconSm = NULL;
aMainClass = RegisterClassEx(&wcex);
wcex.lpfnWndProc = ChildWndProc;
wcex.lpszClassName = lpszChildClassName;
wcex.hCursor = LoadCursor(NULL, IDC_CROSS);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
aChildClass = RegisterClassEx(&wcex);
hWndMDI = CreateWindow((LPCTSTR)aMainClass,
szTitle,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
CW_USEDEFAULT,
0,
CW_USEDEFAULT,
0,
NULL,
NULL,
hInst,
NULL);
CLIENTCREATESTRUCT ccs;
ccs.idFirstChild = 5000;
hwndClient = CreateWindow(TEXT("mdiclient"),
NULL,
WS_CLIPCHILDREN | WS_CHILD | WS_HSCROLL | WS_VSCROLL | MDIS_ALLCHILDSTYLES,
0,
0,
0,
0,
hWndMDI,
0,
hInst,
(LPSTR)&ccs);
ShowWindow(hWndMDI, SW_SHOW);
ShowWindow(hwndClient, SW_SHOW);
//so as the initial child's client are fits the D2D swapchain (256*256px)
RECT Rect;
Rect.left = 0;
Rect.top = 0;
Rect.right = 256;
Rect.bottom = 256;
AdjustWindowRect(&Rect,
WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU| WS_SIZEBOX, FALSE);
HWND hWnd = CreateMDIWindow(
(LPCTSTR)aChildClass,
szTitle,
WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU| WS_SIZEBOX,
CW_USEDEFAULT,
CW_USEDEFAULT,
Rect.right - Rect.left,
Rect.bottom - Rect.top,
hwndClient,
hInst,
NULL);
ShowWindow(hWnd, SW_SHOW);
//create D2D + D3D + DXGI machinery
D2D1_FACTORY_OPTIONS debugOptions;
debugOptions.debugLevel = D2D1_DEBUG_LEVEL_NONE;
HRESULT hr = D2D1CreateFactory(
D2D1_FACTORY_TYPE_SINGLE_THREADED,
__uuidof(ID2D1Factory1),
&debugOptions,
(void*)&m_D2DFactory
);
if (SUCCEEDED(hr))
{
UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
D3D_FEATURE_LEVEL featureLevels[] =
{
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
hr = D3D11CreateDevice(
nullptr,
D3D_DRIVER_TYPE_HARDWARE,
0,
creationFlags,
featureLevels,
ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&m_pD3D11device,
NULL,
NULL
);
}
if (SUCCEEDED(hr))
{
IDXGIDevice1 dxgiDevice;
hr = m_pD3D11device->QueryInterface(__uuidof(IDXGIDevice), (void*)&dxgiDevice);
if (SUCCEEDED(hr))
{
hr = m_D2DFactory->CreateDevice(
dxgiDevice,
&m_d2dDevice
);
if (SUCCEEDED(hr))
{
m_d2dDevice->CreateDeviceContext(
D2D1_DEVICE_CONTEXT_OPTIONS_NONE,
&m_d2dContext
);
}
}
IDXGIAdapter dxgiAdapter;
if (SUCCEEDED(hr))
{
hr = dxgiDevice->GetAdapter(&dxgiAdapter);
if (SUCCEEDED(hr))
{
hr = dxgiAdapter->GetParent(IID_PPV_ARGS(&m_dxgiFactory));
SafeRelease(&dxgiAdapter);
dxgiDevice->SetMaximumFrameLatency(1);
SafeRelease(&dxgiDevice);
}
}
}
if (SUCCEEDED(hr))
{
DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 };
swapChainDesc.Width = 256;
swapChainDesc.Height = 256;
swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
swapChainDesc.Stereo = FALSE;
swapChainDesc.SampleDesc.Count = 1;
swapChainDesc.SampleDesc.Quality = 0;
swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
swapChainDesc.BufferCount = 2;
swapChainDesc.Scaling = DXGI_SCALING_STRETCH; //DXGI_SCALING_ASPECT_RATIO_STRETCH seems not supported
swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE;
swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;
swapChainDesc.Flags = 0;
hr = m_dxgiFactory->CreateSwapChainForHwnd(
m_pD3D11device,
hWnd,
&swapChainDesc,
nullptr,
nullptr,
&(m_swapChain)
);
}
if (SUCCEEDED(hr))
{
hr = m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_dxgiBackBuffer));
if (SUCCEEDED(hr))
{
D2D1_BITMAP_PROPERTIES1 bitmapProperties = D2D1::BitmapProperties1(
D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW,
D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE),
0,
0,
NULL
);
/
//just in case dpi=0 doesn't work:
bitmapProperties.dpiX = GetDpiForWindow(hWnd);
bitmapProperties.dpiY = GetDpiForWindow(hWnd);
/
hr = m_d2dContext->CreateBitmapFromDxgiSurface(
m_dxgiBackBuffer,
&bitmapProperties,
&(m_pBitmap)
);
}
}
if (SUCCEEDED(hr))
{
D2D1_SIZE_U bitmapSize = m_pBitmap->GetPixelSize();
m_d2dContext->BeginDraw();
m_d2dContext->SetTarget(m_pBitmap);
ID2D1SolidColorBrush* m_pBlackBrush;
hr = m_d2dContext->CreateSolidColorBrush(
D2D1::ColorF(D2D1::ColorF::Red, 1.0f),
&m_pBlackBrush
);
m_d2dContext->DrawLine(
D2D1::Point2F(0.0f, 0.0f),
D2D1::Point2F(bitmapSize.height, bitmapSize.width),
m_pBlackBrush,
2.0f,
NULL);
m_d2dContext->DrawLine(
D2D1::Point2F(0.0f, bitmapSize.width),
D2D1::Point2F(bitmapSize.height, 0.0f),
m_pBlackBrush,
2.0f,
NULL);
}
hr = m_d2dContext->EndDraw();
hr = m_swapChain->Present(1, 0);
return TRUE;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// Analyse les sélections de menu :
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefFrameProc(hWnd, hwndClient, message, wParam, lParam);
}
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefFrameProc(hWnd, hwndClient, message, wParam, lParam);
}
return 0;
}
LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefMDIChildProc(hWnd, message, wParam, lParam);
}
...
Replace your AdjustWindowRect with AdjustWindowRectEx and specify WS_EX_MDICHILD extended window style.
Another thing, you’re rendering and even presenting some graphics from within InitInstance method. That’s not the right time for that, too early. Instead, you should move rendering into WM_PAINT handler of the window for which you have created the swap chain.
P.S. This sample might help.
The streching error only occurs for windows with borders and/or menu bar. A possible workaround is thus to add a borderless child window (hWndPic) to the MDI child (hWnd) itself:
//initialization :
hWnd = CreateMDIWindow(
(LPCTSTR)aChildClass,
szTitle,
WS_SIZEBOX|WS_OVERLAPPED | WS_VISIBLE | WS_MINIMIZEBOX | WS_CAPTION | WS_SYSMENU ,
CW_USEDEFAULT,
CW_USEDEFAULT,
Rect.right - Rect.left,
Rect.bottom - Rect.top,
hwndClient,
hInst,
NULL);
hWndPic = CreateWindow(
(LPCTSTR)aPicClass,
NULL,
WS_CHILD,
0,
0,
256,
256,
hWnd,
0,
hInst,
NULL);
Then resize this picture window in response to WM_SIZE sent to the MDI child hWnd; that's all! then there is no need to repaint the picture when the window is resized and the stretch process is correctly handled by D2D, benefitting from hardware acceleration.
// windows procedures :
LRESULT CALLBACK ChildWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_SIZE:
{
RECT Rect;
GetClientRect(hWnd,&Rect);
SetWindowPos(
hWndPic,
HWND_TOP,
0,
0,
Rect.right - Rect.left,
Rect.bottom - Rect.top,
0
);
}
}
return DefMDIChildProc(hWnd, message, wParam, lParam);
}
LRESULT CALLBACK PicWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}

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;

How to implement "teamviewer quickconnect"-like button into foreign window?

I'd like to put a button in a foreign windows' title bar, much like Teamviewer does with the Quickconnect feature, or like Chrome has one in the top-right for switching users.
I know this is a repeat of How is Teamviewers Quickconnect button accomplished?
I'm just wondering if it would be possible to get a working example or a link to an open-source program that implements this. The answers given there are rather advanced for me. As in, how am I supposed to "hook" and "intercept" WM_NCPAINT message and so on.
This is the most simple example i can develop:
You need Visual Studio, add 2 project to the solution:
first project (HookDLL) is a dll project, second (Running app) is a win32 console project
in main.cpp (at project Running app) add this:
__declspec(dllimport) void RunHook();
int _tmain(int argc, _TCHAR* argv[])
{
RunHook();
return 0;
}
in dllmain button.cpp (at HookDLL project) add this code:
#include <Windows.h>
#include <stdio.h>
HINSTANCE hinstDLL;
HHOOK hhook_wndproc;
HWND b = NULL;
HBRUSH blue_brush = NULL, yellow_brush, red_brush;
int button_status = 0;
LRESULT CALLBACK DefaultWindowProc(HWND hwnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
switch(Msg)
{
case WM_CREATE:
if(!blue_brush)
{
blue_brush = CreateSolidBrush(RGB(0, 0, 255));
yellow_brush = CreateSolidBrush(RGB(255, 255, 0));
red_brush = CreateSolidBrush(RGB(255, 0, 0));
}
break;
case WM_PAINT:
{
HBRUSH b;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
switch(button_status)
{
case 0:
b = blue_brush;
break;
case 1:
b = yellow_brush;
break;
default:
b = red_brush;
}
FillRect(hdc, &ps.rcPaint, b);
EndPaint(hwnd, &ps);
}
return 0;
case WM_MOUSEMOVE:
if(button_status == 0)
{
SetTimer(hwnd, 1, 100, NULL);
button_status = 1;
InvalidateRect(hwnd, NULL, false);
}
return 0;
case WM_TIMER:
{
POINT pt;
GetCursorPos(&pt);
if(button_status == 1 && WindowFromPoint(pt) != hwnd)
{
KillTimer(hwnd, 1);
button_status = 0;
InvalidateRect(hwnd, NULL, false);
}
}
return 0;
case WM_MOUSELEAVE:
button_status = 0;
InvalidateRect(hwnd, NULL, false);
return 0;
case WM_LBUTTONDOWN:
button_status = 2;
InvalidateRect(hwnd, NULL, false);
return 0;
case WM_LBUTTONUP:
if(button_status == 2) MessageBox(GetParent(hwnd), "teamviewer like button clicked", "Message", MB_OK);
button_status = 1;
InvalidateRect(hwnd, NULL, false);
return 0;
}
return DefWindowProc(hwnd, Msg, wParam, lParam);
}
void InitButton(HWND parent, int xPos, int yPos)
{
WNDCLASS wc;
wc.style = 0;
wc.lpfnWndProc = DefaultWindowProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hinstDLL;
wc.hIcon = NULL;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpszMenuName = NULL;
wc.lpszClassName = "DEFAULT_CLASS";
RegisterClass(&wc);
b = CreateWindowEx(WS_EX_TOOLWINDOW, "DEFAULT_CLASS", NULL, WS_BORDER | WS_POPUP | WS_VISIBLE, xPos, yPos, 20, 20, parent, NULL, hinstDLL, NULL);
}
LRESULT WINAPI HookCallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode >= 0 && lParam != 0)
{
CWPRETSTRUCT *msg = (CWPRETSTRUCT*)lParam;
if(!IsWindow(msg->hwnd) || (GetWindowLong(msg->hwnd, GWL_STYLE) & WS_CHILD) != 0) return CallNextHookEx(hhook_wndproc, nCode, wParam, lParam);
switch(msg->message)
{
case WM_SHOWWINDOW:
if(!b && msg->wParam != 0)
{
b = (HWND)1;// see NOTES 5
RECT a;
GetWindowRect(msg->hwnd, &a);
InitButton(msg->hwnd, a.right - 150, a.top);
}
break;
case WM_SIZE:
if(GetParent(b) == msg->hwnd)
{
RECT a;
GetWindowRect(msg->hwnd, &a);
SetWindowPos(b, 0, a.right - 150, a.top, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOZORDER);
}
break;
case WM_SIZING:
case WM_MOVING:
if(GetParent(b) == msg->hwnd)
{
RECT* lprc = (LPRECT) msg->lParam;
SetWindowPos(b, 0, lprc->right - 150, lprc->top, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOZORDER);
}
}
}
return CallNextHookEx(hhook_wndproc, nCode, wParam, lParam);
}
__declspec(dllexport) void RunHook()
{
hhook_wndproc = SetWindowsHookEx(WH_CALLWNDPROCRET, HookCallWndProc, hinstDLL, 0);
char aux[10];
gets_s(aux);
UnhookWindowsHookEx(hhook_wndproc);
}
BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
hinstDLL = hModule;
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Now, make dll project depedent of running app project in project->project dependencies:
NOTES:
1) i dont use NC paint code, because not always works, if windows buffer non client region, erases customized NC paint buttons
2) in 64 bits enviroment, you need to run a 32 bits hook for 32 bits apps, and other hook for 64 bits apps
3) YOU CAN NOT DEBUG YOUR HOOK WHEN IS CONNECTED TO ANOTHER PROCCESS, i suggest you debug it with a windows in your app and thread, and test it late in another proccess when is working
4) i use a button like approach for simplicity
5) this line
b = (HWND)1;
I use it for "solve" a multi thread problem, i suggest you make better code (syncronization) this case
HOW THIS WORKS:
run the app
when it start install a hook
open any other app (same 32/64 bits, see NOTE 2)
you must see a blue button at left side of title bar
click it and see a message box
for finish hook: just press ENTER at console window
CODE FLOW:
Running app just calls RunHook() procedure in dll, and dll do the work
RunHook() in dll starts a hook HookCallWndProc (global)
HookCallWndProc captures required message and creates the window button using InitButton() procedure
DefaultWindowProc handles button message

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.

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

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?

Resources