Related
I tried use gdi to capture certain window, it's ok for most window.
But to some window like "visual studio code", it doesn't work.
DX can't support capturing certain window.
How should I do? THANKS
::EnumWindows(EnumWindowsProc, NULL);
if (hCapWnd == NULL)
{
return FALSE;
}
SwitchToThisWindow(hCapWnd, true);
Sleep(1000);
//HWND hDesktopWnd = GetDesktopWindow();
HDC hDesktopDC = GetWindowDC(hCapWnd);
HDC hCaptureDC = CreateCompatibleDC(hDesktopDC);
SetStretchBltMode(hCaptureDC, COLORONCOLOR);
RECT rc;
::GetWindowRect(hCapWnd, &rc);
int width = rc.right - rc.left;
int height = rc.bottom - rc.top;
HBITMAP hCaptureBitmap = CreateCompatibleBitmap(hDesktopDC, width, height);
SelectObject(hCaptureDC, hCaptureBitmap);
BOOL bRet = BitBlt(hCaptureDC, 0, 0, width, height, hDesktopDC, 0, 0, SRCCOPY);
Init(width, height);
int nRet = GetDIBits(hCaptureDC, hCaptureBitmap, 0, height, buf, (BITMAPINFO*)&m_bitmapInfo, DIB_RGB_COLORS);
static int n = 0;
//if (n++ % 10 == 0)
{
// std::string name = ustd::format_string("%s\\%d.bmp", ustd::get_program_path().c_str(), n);
SaveBitmapToFile(hCaptureBitmap);
}
ReleaseDC(hCapWnd, hDesktopDC);
DeleteDC(hCaptureDC);
DeleteObject(hCaptureBitmap);
By default a rich edit control has a "3d" border. I draw a thin border around a rich edit control this way:
if (message == WM_NCPAINT)
{
RECT rc;
HDC hdc;
HPEN pen;
HBRUSH brush;
HGDIOBJ oldP, oldB;
POINT tl, br;
::GetWindowRect(hWnd, &rc);
hdc = ::GetDC(hWnd);
tl.x = rc.left;
tl.y = rc.top;
br.x = rc.right;
br.y = rc.bottom;
::ScreenToClient(hWnd, &tl);
::ScreenToClient(hWnd, &br);
pen = ::CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
brush = (HBRUSH) ::GetStockObject(HOLLOW_BRUSH);
oldP = ::SelectObject(hdc, pen);
oldB = ::SelectObject(hdc, brush);
::Rectangle(hdc, tl.x, tl.y, br.x, br.y);
::SelectObject(hdc, oldP);
::SelectObject(hdc, oldB);
::DeleteObject(pen);
::ReleaseDC(hWnd, hdc);
return 0;
}
The border looks fine but the area under the old border is not redrawn. It looks like I have to redraw to whole content of the rich edit control. After that a text shouldn't be a litte bit cut from the bottom. Here you can see what I mean (the second rich edit control has custom border). How to achieve it ?
GetDC returns DC for client area. In this case you need GetWindowDC for the whole richedit window.
Either way, overriding border color in edit and rich-edit can be difficult, because you also have to handle scrollbar painting in WM_NCPAINT
Assuming you don't need vertical and/or horizontal scrollbar, use GetWindowDC as follows:
LRESULT CALLBACK RichEditProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp,
UINT_PTR, DWORD_PTR)
{
static int border_thickness = 1;
switch(msg)
{
case WM_NCPAINT:
{
HDC hdc = GetWindowDC(hwnd);
RECT rc;
GetClientRect(hwnd, &rc);
rc.right += 2 * border_thickness + 1;
rc.bottom += 2 * border_thickness + 1;
HBRUSH hbrush = (HBRUSH)GetStockObject(NULL_BRUSH);
HPEN hpen = CreatePen(PS_SOLID, 2 * border_thickness, RGB(255, 0, 0));
HBRUSH oldbrush = (HBRUSH)SelectObject(hdc, hbrush);
HPEN oldpen = (HPEN)SelectObject(hdc, hpen);
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
SelectObject(hdc, oldpen);
SelectObject(hdc, oldbrush);
DeleteObject(hpen);
DeleteObject(hbrush);
ReleaseDC(hwnd, hdc);
return 0;
}
case WM_NCCALCSIZE:
if(lp)
{
NCCALCSIZE_PARAMS* sz = (NCCALCSIZE_PARAMS*)lp;
InflateRect(&sz->rgrc[0], -border_thickness, -border_thickness);
return 0;
}
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hwnd, RichEditProc, 0);
break;
}
return DefSubclassProc(hwnd, msg, wp, lp);
}
By default, WM_NCCALCSIZE is not called in subclass procedure, you have to call SetWindowPos with SWP_FRAMECHANGED
HWND hrichedit = CreateWindowEx(...);
SetWindowSubclass(hrichedit, RichEditProc, 0, 0);
SetWindowPos(hrichedit, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED);
If you do need horizontal/vertical scrollbar, you might consider using a borderless richedit, then paint around the rich control in parent window's WM_PAINT procedure.
I tried to color customize menu items (pure WinAPI). But there is a line in the menu bar which does not draw with MenuInfo.hbrBack color. If the mouse cursor hover above items a part of this line is redrawn. But if I resize the window the line will return. And in the area of menu bar where no items the line drawn constantly. How can I draw over this annoying line?
#include <windows.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
struct
{
COLORREF text = RGB(200, 200, 250);
COLORREF clientBorder = RGB(120, 0, 0);
COLORREF clientBackground = RGB(100, 100, 100);
COLORREF itemBorder = RGB(0, 0, 255);
COLORREF itemBackground = RGB(0, 120, 0);
COLORREF pink = RGB(255, 0, 255);
} colorTheme;
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg;
WNDCLASSEX wc;
wc.cbSize = sizeof(wc);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.lpszMenuName = NULL;
wc.lpszClassName = "MainWindow";
wc.cbWndExtra = NULL;
wc.cbClsExtra = NULL;
wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
wc.hIconSm = LoadIcon(NULL, IDI_WINLOGO);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = CreateSolidBrush(colorTheme.clientBackground);
wc.hInstance = hInst;
RegisterClassEx(&wc);
HWND hMainWnd = CreateWindow(
"MainWindow",
"MainWindow",
WS_OVERLAPPEDWINDOW,
100, 100, 450, 120,
(HWND)NULL, NULL, HINSTANCE(hInst), NULL);
HMENU hMenu = CreateMenu();
HMENU hMenuSub1 = CreatePopupMenu();
HMENU hMenuSub2 = CreatePopupMenu();
HMENU hMenuSub3 = CreatePopupMenu();
AppendMenu(hMenu, MF_OWNERDRAW | MF_POPUP, (UINT)hMenuSub1, "SubMenu1");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item01");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item02");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item03");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item04");
AppendMenu(hMenuSub1, MF_OWNERDRAW, 0, "Item05");
AppendMenu(hMenu, MF_OWNERDRAW | MF_POPUP, (UINT)hMenuSub2, "SubMenu2");
AppendMenu(hMenu, MF_OWNERDRAW | MF_POPUP, (UINT)hMenuSub3, "SubMenu3");
MENUINFO menuInfo;
menuInfo.cbSize = sizeof(menuInfo);
menuInfo.fMask = MIM_BACKGROUND;
menuInfo.hbrBack = CreateSolidBrush(colorTheme.pink);
SetMenuInfo(hMenu, &menuInfo);
SetMenu(hMainWnd, hMenu);
ShowWindow(hMainWnd, nCmdShow);
while (GetMessage(&msg, NULL, NULL, NULL)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
HFONT hApplicationFont;
LOGFONT applicationFont;
applicationFont.lfHeight = 16;
applicationFont.lfWidth = 6;
applicationFont.lfEscapement = 0;
applicationFont.lfOrientation = 0;
applicationFont.lfWeight = FW_NORMAL;
applicationFont.lfItalic = FALSE;
applicationFont.lfUnderline = FALSE;
applicationFont.lfStrikeOut = FALSE;
applicationFont.lfCharSet = DEFAULT_CHARSET;
applicationFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
applicationFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
applicationFont.lfQuality = ANTIALIASED_QUALITY;
applicationFont.lfPitchAndFamily = DEFAULT_PITCH;
strcpy_s(applicationFont.lfFaceName, "Arial");
hApplicationFont = CreateFontIndirectA(&applicationFont);
SelectObject(hDC, hApplicationFont);
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, colorTheme.clientBorder);
SelectObject(hDC, GetStockObject(DC_BRUSH));
SetDCBrushColor(hDC, colorTheme.clientBackground);
RECT clientRect;
GetClientRect(hWnd, &clientRect);
Rectangle(hDC, 0, 0, clientRect.right, clientRect.bottom);
EndPaint(hWnd, &ps);
break;
}
case WM_MEASUREITEM:
{
LPMEASUREITEMSTRUCT itemStruct = (LPMEASUREITEMSTRUCT)lParam;
const char* str = (const char*)(itemStruct->itemData);
SIZE strSize;
HDC hDC = GetDC(hWnd);
GetTextExtentPoint32(hDC, str, lstrlen(str), &strSize);
itemStruct->itemWidth = strSize.cx;
itemStruct->itemHeight = 30;
ReleaseDC(hWnd, hDC);
return TRUE;
break;
}
case WM_DRAWITEM:
{
LPDRAWITEMSTRUCT itemStruct = (LPDRAWITEMSTRUCT)lParam;
HDC hDC = itemStruct->hDC;
SelectObject(hDC, GetStockObject(DC_PEN));
SetDCPenColor(hDC, colorTheme.itemBorder);
SelectObject(hDC, GetStockObject(DC_BRUSH));
SetDCBrushColor(hDC, colorTheme.itemBackground);
SetTextColor(hDC, colorTheme.text);
SetBkMode(hDC, TRANSPARENT);
Rectangle(hDC, itemStruct->rcItem.left,
itemStruct->rcItem.top,
itemStruct->rcItem.right,
itemStruct->rcItem.bottom + 1);
DrawText(hDC, (const char*)(itemStruct->itemData), -1, &(itemStruct->rcItem), DT_SINGLELINE | DT_CENTER | DT_VCENTER);
break;
}
case WM_DESTROY:
{
PostQuitMessage(NULL);
break;
}
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
return NULL;
}
It seems to be part of the non-client area of the window. If that's the case then to paint there you need to handle WM_NCPAINT.
It is a single pixel line above the window's client area, so for example if I add the following code to your program I can paint it in red.
// ... in the WNDPROC
case WM_NCPAINT:
{
auto result = DefWindowProc(hWnd, WM_NCPAINT, wParam, lParam);
HDC hdc = GetWindowDC(hWnd);
RECT r = GetNonclientMenuBorderRect(hWnd);
HBRUSH red = CreateSolidBrush(RGB(255, 0, 0));
FillRect(hdc, &r, red);
DeleteObject(red);
ReleaseDC(hWnd, hdc);
return result;
}
// ... elsewhere
RECT MapRectFromClientToWndCoords(HWND hwnd, const RECT& r)
{
RECT wnd_coords = r;
// map to screen
MapWindowPoints(hwnd, NULL, reinterpret_cast<POINT*>(&wnd_coords), 2);
RECT scr_coords;
GetWindowRect(hwnd, &scr_coords);
// map to window coords by substracting the window coord origin in
// screen coords.
OffsetRect(&wnd_coords, -scr_coords.left, -scr_coords.top);
return wnd_coords;
}
RECT GetNonclientMenuBorderRect(HWND hwnd)
{
RECT r;
GetClientRect(hwnd, &r);
r = MapRectFromClientToWndCoords(hwnd, r);
int y = r.top - 1;
return {
r.left,
y,
r.right,
y+1
};
}
Now an issue with the above code is that it is over-painting the rectangle after the default non-client painting is done. In theory this could flicker; in practice I don't notice a flicker. If it did flicker, however, a safer way to do this would be to modify the WPARAM you pass to DefWindowProc(hWnd, WM_NCPAINT, ... ) such that it is the handle to a region that is the region passed to WM_NCPAINT minus the rectangle you want to paint. This doesnt seem necessary to me, for whatever reason.
If you are using themes / visual styles, which pretty much everything is nowadays, you can't override a lot of the menu styling without using a workaround like https://github.com/adzm/win32-custom-menubar-aero-theme which also uses the same approach to get rid of the white line. Note that you will need to handle this in WM_NCPAINT and WM_NCACTIVATE.
On Windows, I'm trying to use CEF (Chromium Embedded Framework) to create a window application with parent window to be transparent and its child window to be opaque (I want to have a rounded corner and an arrow pointing to the status bar in the child window). Something similar to:
I tried to use SetLayeredWindowAttributes to make the parent window transparent but it also makes the child window transparent. Is there a way to make this happen on Windows?
SetLayeredWindowAttributes needs a color for transparency. Make sure the transparency color is not used by the child window. You can pick a random color, for example RGB(255, 0, 254) and assume the child window is not using it.
If you have no control over the child window, and you can't be sure what colors it might use, then SetWindowRgn is another option to create non-rectangular windows.
The example below shows how to set the region such that the corners are round with a triangle on top.
You can use GDI+ to gain more flexibility for drawing the region, and for anti-aliasing effect so that the borders look more smooth.
#include <Windows.h>
int triangle_height = 30;
int corner_size = 20;
int caption_height = 60;
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
switch(msg)
{
case WM_CREATE:
{
//create a child button for testing
CreateWindowW(L"BUTTON", L"Close", WS_CHILD | WS_VISIBLE,
10, caption_height + 10, 100, 30,
hwnd, HMENU(100), GetModuleHandle(NULL), NULL);
RECT rc;
GetClientRect(hwnd, &rc);
//create a triangle region
int w = rc.right;
int h = rc.bottom;
int z = triangle_height;
POINT pt[3];
pt[0] = { w / 2, 0 };
pt[1] = { w / 2 - z, z };
pt[2] = { w / 2 + z, z };
HRGN htri = CreatePolygonRgn(pt, 3, WINDING);
//create a round rectangle region
HRGN hrgn = CreateRoundRectRgn(0, z, w, h - z, corner_size, corner_size);
//combine the triangle with round rectangle
CombineRgn(hrgn, htri, hrgn, RGN_OR);
//set the new region
SetWindowRgn(hwnd, hrgn, TRUE);
DeleteObject(htri);
DeleteObject(hrgn);
return 0;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc = ps.rcPaint;
//we don't have a standard title bar, paint one here:
rc.bottom = caption_height;
SetDCBrushColor(hdc, RGB(80, 80, 80));
FillRect(hdc, &rc, (HBRUSH)GetStockObject(DC_BRUSH));
//paint the background
rc = ps.rcPaint;
rc.top = caption_height;
SetDCBrushColor(hdc, RGB(240, 240, 240));
FillRect(hdc, &rc, (HBRUSH)GetStockObject(DC_BRUSH));
//use FrameRgn to paint a border around the region
HRGN hrgn = CreateRectRgn(0, 0, 0, 0);
GetWindowRgn(hwnd, hrgn);
SetDCBrushColor(hdc, RGB(128, 128, 128));
FrameRgn(hdc, hrgn, (HBRUSH)GetStockObject(DC_BRUSH), 1, 1);
DeleteObject(hrgn);
EndPaint(hwnd, &ps);
return 0;
}
case WM_NCHITTEST:
{
//we don't have a standard title-bar
//respond to our custome title-bar manually:
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hwnd, &pt);
if(pt.y < caption_height)
return HTCAPTION;
break;
}
case WM_COMMAND:
if(HIWORD(wparam) == BN_CLICKED)
if(LOWORD(wparam) == 100)
SendMessage(hwnd, WM_CLOSE, 0, 0);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, msg, wparam, lparam);
}
int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR, int)
{
WNDCLASSEXW wcex = { sizeof(wcex) };
wcex.style = CS_DROPSHADOW;
wcex.lpfnWndProc = WndProc;
wcex.hInstance = hInstance;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = NULL;
wcex.lpszClassName = L"classname";
RegisterClassExW(&wcex);
CreateWindowW(wcex.lpszClassName, L"Test", WS_VISIBLE | WS_POPUP,
200, 200, 600, 400, 0, 0, hInstance, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
i'm sorry for what i did. i edited.
i'd like to use Fillrect on 32bit HBITMAP which is Created with CreateDIBSection
but i can't make rect visible in color that i want to.
(i Drawed a fillrect with CreateSolidBrush blue(RGB(0, 0, 255)) on 32bit HBITMAP(hdcbmp), but it doesn't appear blue.)
here is source code
is there anyway to show rect color that i want to?
sorry for my poor english.
void DrawAlphaBitmap(HWND hWnd, ULONG uWidth, ULONG uHeight)
{
BLENDFUNCTION bf;
HBITMAP hbitmap;
HBITMAP hOldBitmap;
BITMAPINFO bmi;
PVOID pvBits;
HDC hdcwnd = GetDC(hWnd);
HDC hdcbmp = CreateCompatibleDC(hdcwnd);
ZeroMemory(&bmi, sizeof(BITMAPINFO));
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = uWidth;
bmi.bmiHeader.biHeight = uHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
bmi.bmiHeader.biSizeImage = bmi.bmiHeader.biWidth * bmi.bmiHeader.biHeight * 4;
hbitmap = CreateDIBSection(hdcbmp, &bmi, DIB_RGB_COLORS, &pvBits, NULL, 0x0);
hOldBitmap = (HBITMAP)SelectObject(hdcbmp, hbitmap);
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = 0xff;
bf.AlphaFormat = AC_SRC_ALPHA;
RECT rc2 = { 100, 100, 200, 200 };
FillRect(hdcbmp, &rc2, CreateSolidBrush(RGB(0, 0, 255)));
AlphaBlend(hdcwnd, 0, 0, uWidth, uHeight, hdcbmp, 0, 0, uWidth, uHeight, bf);
SelectObject(hdcbmp, hOldBitmap);
DeleteObject(hbitmap);
DeleteDC(hdcbmp);
ReleaseDC(hWnd, hdcwnd);
}
From documentation for BLENDFUNCTION:
AlphaFormat:
This flag is set when the bitmap has an Alpha channel (that is, per-pixel alpha).
In this case, alpha channel is not set. CreateDIBSection initializes the alpha values to zero. When AC_SRC_ALPHA is set, AlphaBlend ignores pixels whose alpha value is zero. Modify your code as follows:
//bf.AlphaFormat = AC_SRC_ALPHA; <- remove
bf.AlphaFormat = 0; //replace with 0
Side note, you have resource leak in creation of HBRUSH handle. Change the code to
HBRUSH hbrush = CreateSolidBrush(RGB(0, 0, 255));
RECT rc2 = { 0, 0, w, h };
FillRect(memdc, &rc2, hbrush);
DeleteObject(hbrush);
Ideally, your function prototype should be void DrawAlphaBitmap(HDC hdc, ULONG uWidth, ULONG uHeight); so that HDC can be passed directly, for example from BeginPaint/EndPaint in WM_PAINT message.