Tab through Edit boxes doesn't work C++ - c++

Well hello,
I have a problem and I didn't find any solution to this by searching; basically I want to make a simple login where you can switch through the controls ( username, password, login button ) with tabbing.
The only thing happening when pressing TAB is that it just completely selects the text from the current edit box.
Here's my code:
HWND g_hLogin1, g_hLogin2, g_hLogin3;
#define ID_BUTTON_LOGIN 201
LRESULT CALLBACK LoginProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_CREATE:
{
g_hLogin1 = CreateWindow( "EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER, 50, 40, 200, 20, hWnd, 0, 0, 0 );
g_hLogin2 = CreateWindow( "EDIT", "", WS_CHILD | WS_VISIBLE | WS_BORDER | ES_PASSWORD, 50, 80, 200, 20, hWnd, 0, 0, 0 );
g_hLogin3 = CreateWindow( "BUTTON", "Login", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 100, 120, 100, 22, hWnd, (HMENU)ID_BUTTON_LOGIN, 0, 0 );
HFONT font = CreateFont( 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Arial" );
SendMessage( g_hLogin1, WM_SETFONT, (WPARAM)font, 0 );
SendMessage( g_hLogin2, WM_SETFONT, (WPARAM)font, 0 );
SendMessage( g_hLogin3, WM_SETFONT, (WPARAM)font, 0 );
break;
}
case WM_DESTROY:
{
PostQuitMessage( 0 );
return 0;
}
case WM_KEYDOWN:
{
if( wParam == VK_TAB )
{
HWND hNext = GetWindow( hWnd, GW_HWNDNEXT );
if( !hNext )
hNext = GetWindow( hWnd, GW_HWNDLAST );
SendMessage( hNext, EM_SETSEL, (WPARAM)0, (LPARAM)-1 );
SetFocus( hNext );
}
break;
}
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint( hWnd, &ps );
HFONT font = CreateFont( 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Arial" );
SelectObject( hDC, font );
RECT rc[] =
{
{ 50, 22, 0, 0 },
{ 50, 62, 0, 0 }
};
DrawText( hDC, "Username:", -1, &rc[ 0 ], DT_NOCLIP );
DrawText( hDC, "Password:", -1, &rc[ 1 ], DT_NOCLIP );
EndPaint( hWnd, &ps );
break;
}
case WM_COMMAND:
{
if( LOWORD( wParam ) == ID_BUTTON_LOGIN )
{
int len1 = GetWindowTextLength( g_hLogin1 );
int len2 = GetWindowTextLength( g_hLogin2 );
char* username = new char[ len1 + 1 ];
char* password = new char[ len2 + 1 ];
GetWindowText( g_hLogin1, username, len1 + 1 );
GetWindowText( g_hLogin2, password, len2 + 1 );
if( g_pServer->Login( username, password ) )
{
ShowWindow( g_hLogin, SW_HIDE );
ShowWindow( g_hMainWnd, SW_SHOW );
}
}
break;
}
default:
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
return DefWindowProc( hWnd, uMsg, wParam, lParam );
}
bool SetupClass( HINSTANCE hInstance, char* szClassName, WNDPROC wndProc )
{
WNDCLASSEX wc = { 0 };
wc.cbClsExtra = 0;
wc.cbSize = sizeof( WNDCLASSEX );
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
wc.hCursor = LoadCursor( 0, IDC_ARROW );
wc.hIcon = LoadIcon( hInstance, MAKEINTRESOURCE( IDI_ICON1 ) );
wc.hIconSm = LoadIcon( hInstance, MAKEINTRESOURCE( IDI_ICON1 ) );
wc.hInstance = hInstance;
wc.lpfnWndProc = wndProc;
wc.lpszClassName = szClassName;
wc.lpszMenuName = "";
wc.style = CS_HREDRAW | CS_VREDRAW;
return RegisterClassEx( &wc );
}
int APIENTRY WinMain( HINSTANCE hThis, HINSTANCE hPrev, LPSTR lpCmd, int iCmd )
{
if( !SetupClass( hThis, "zcfw001", LoginProc ) )
return 1;
g_hLogin = CreateWindowEx( WS_EX_CONTROLPARENT, "zcfw001", "Login", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 200, 0, 0, hThis, 0 );
if( !g_hLogin )
return 1;
ShowWindow( g_hLogin, SW_SHOW );
MSG msg;
while( 1 )
{
if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) )
{
if( msg.message == WM_QUIT )
break;
if( !IsDialogMessage( g_hLogin, &msg ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
}
return msg.wParam;
}

You should use WS_TABSTOP style to make tab stops working.

Related

How to create custom slider control in win32 in C++?

I am learning WIN32 API in C/C++ and want to know how to create custom slider/trackbar control with custom shaped thumb that is a child of a dialog. An example would be of benefit on how to do it as there is very low information about WIN32 programming on the internet, especially for customizing your own child controls.
Please do not post MFC examples.
Here is the code example I am trying to complete:
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#include <string>
#include <Windows.h>
#include <CommCtrl.h>
using namespace std;
HWND window = nullptr;
HWND trackBar = nullptr;
HWND progressBar = nullptr;
HWND staticText = nullptr;
WNDPROC defWndProc = nullptr;
static HBITMAP hBitmapThumb, hBitmapBar;
static BITMAP bm;
LRESULT OnWindowClose(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
PostQuitMessage(0);
return CallWindowProc(defWndProc, hwnd, message, wParam, lParam);
}
LRESULT OnTrackBarChanged(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
LRESULT value = SendMessage(trackBar, TBM_GETPOS, 0, 0);
SendMessage(progressBar, PBM_SETPOS, value, 0);
SendMessage(staticText, WM_SETTEXT, 0, reinterpret_cast<LPARAM>(to_wstring(value).c_str()));
return CallWindowProc(defWndProc, hwnd, message, wParam, lParam);
}
void DrawBitmapTransparent(HDC hDCDest, int nXDest, int nYDest, int nBitmapWidth, int nBitmapHeight, HBITMAP hBitmap, int nXSrc, int nYSrc, int nTransparentColor)
{
HDC hDCSrc;
HBITMAP hBitmapOld;
HDC hDCMask;
HBITMAP hBitmapMask;
HBITMAP hBitmapMaskOld;
HDC hDCMem;
HBITMAP hBitmapMem;
HBITMAP hBitmapMemOld;
int nBkColorOld;
int nTextColorOld;
BITMAP bm;
GetObject( hBitmap, sizeof( BITMAP ), &bm );
if (!nBitmapWidth) {
nBitmapWidth = bm.bmWidth;
}
if (!nBitmapHeight) {
nBitmapHeight = bm.bmHeight;
}
hDCSrc = CreateCompatibleDC( hDCDest );
hBitmapOld = reinterpret_cast<HBITMAP>(SelectObject( hDCSrc, hBitmap ));
hDCMask = CreateCompatibleDC( hDCDest );
hBitmapMask = CreateBitmap( nBitmapWidth, nBitmapHeight, 1, 1, 0 );
hBitmapMaskOld = reinterpret_cast<HBITMAP>(SelectObject( hDCMask, hBitmapMask ));
hDCMem = CreateCompatibleDC( hDCDest );
hBitmapMem = CreateCompatibleBitmap( hDCDest, nBitmapWidth, nBitmapHeight );
hBitmapMemOld = reinterpret_cast<HBITMAP>(SelectObject( hDCMem, hBitmapMem ));
nBkColorOld = SetBkColor( hDCSrc, nTransparentColor );
BitBlt( hDCMask, 0, 0, nBitmapWidth, nBitmapHeight, hDCSrc, nXSrc, nYSrc, SRCCOPY );
SetBkColor( hDCSrc, nBkColorOld );
nBkColorOld = SetBkColor( hDCDest, RGB(255,255,255) );
nTextColorOld = SetTextColor( hDCDest, RGB(0,0,0) );
BitBlt( hDCMem, 0, 0, nBitmapWidth, nBitmapHeight, hDCDest, nXDest, nYDest, SRCCOPY );
BitBlt( hDCMem, 0, 0, nBitmapWidth, nBitmapHeight, hDCSrc, nXSrc, nYSrc, SRCINVERT );
BitBlt( hDCMem, 0, 0, nBitmapWidth, nBitmapHeight, hDCMask, 0, 0, SRCAND );
BitBlt( hDCMem, 0, 0, nBitmapWidth, nBitmapHeight, hDCSrc, nXSrc, nYSrc, SRCINVERT );
BitBlt( hDCDest, nXDest, nYDest, nBitmapWidth, nBitmapHeight, hDCMem, 0, 0, SRCCOPY );
SetBkColor( hDCDest, nBkColorOld );
SetTextColor( hDCDest, nTextColorOld );
SelectObject( hDCMem, hBitmapMemOld );
DeleteDC( hDCMem );
DeleteObject( hBitmapMem );
SelectObject( hDCMask, hBitmapMaskOld );
DeleteDC( hDCMask );
DeleteObject( hBitmapMask );
SelectObject( hDCSrc, hBitmapOld );
DeleteDC( hDCSrc );
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
switch(message) {
case WM_NOTIFY :
{
LPNMHDR lpNmhdr = reinterpret_cast<LPNMHDR>(lParam);
if (lpNmhdr->code == NM_CUSTOMDRAW)
{
LPNMCUSTOMDRAW lpNMCustomDraw = reinterpret_cast<LPNMCUSTOMDRAW>(lParam);
if (lpNMCustomDraw->dwDrawStage == CDDS_PREPAINT) {
return CDRF_NOTIFYITEMDRAW;
}
else if (lpNMCustomDraw->dwDrawStage == CDDS_ITEMPREPAINT)
{
long nLeft = lpNMCustomDraw->rc.left;
long nTop = lpNMCustomDraw->rc.top;
long nRight = lpNMCustomDraw->rc.right;
long nBottom = lpNMCustomDraw->rc.bottom;
if (lpNMCustomDraw->dwItemSpec == TBCD_THUMB && hBitmapThumb)
{
long nWidth = nRight - nLeft;
long nHeight = nBottom - nTop;
if (nWidth - bm.bmWidth > 0)
{
nLeft += (nWidth - bm.bmWidth)/2;
nWidth = bm.bmWidth;
}
if (nHeight - bm.bmHeight > 0)
{
nTop += (nHeight - bm.bmHeight) / 2;
nHeight = bm.bmHeight;
}
DrawBitmapTransparent(lpNMCustomDraw->hdc , nLeft, nTop, nWidth, nHeight, hBitmapThumb, 0, 0, RGB( 255, 0, 255 ));
return CDRF_SKIPDEFAULT ;
}
}
}
}
break;
}
if (message == WM_CLOSE && hwnd == window) return OnWindowClose(hwnd, message, wParam, lParam);
if (message == WM_HSCROLL && hwnd == window && reinterpret_cast<HWND>(lParam) == trackBar) return OnTrackBarChanged(hwnd, message, wParam, lParam);
return CallWindowProc(defWndProc, hwnd, message, wParam, lParam);
}
int main() {
window = CreateWindowEx(0, WC_DIALOG, L"TrackBar example", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, nullptr, nullptr, nullptr, nullptr);
trackBar = CreateWindowEx(0, TRACKBAR_CLASS, nullptr, WS_CHILD | TBS_HORZ | TBS_BOTTOM | WS_VISIBLE | TBS_FIXEDLENGTH, 150, 10, 250, 70, window, nullptr, nullptr, nullptr);
progressBar = CreateWindowEx(0, PROGRESS_CLASS, nullptr, WS_CHILD | PBS_SMOOTH | WS_VISIBLE, 20, 100, 200, 23, window, nullptr, nullptr, nullptr);
staticText = CreateWindowEx(0, WC_STATIC, L"100", WS_CHILD | WS_VISIBLE, 20, 150, 100, 23, window, nullptr, nullptr, nullptr);
defWndProc = reinterpret_cast<WNDPROC>(SetWindowLongPtr(window, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(WndProc)));
hBitmapThumb = reinterpret_cast<HBITMAP>(LoadImage(NULL, reinterpret_cast<LPCWSTR>("pink.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
GetObject( hBitmapThumb, sizeof( BITMAP ), &bm );
SendMessage(trackBar, TBM_SETRANGEMIN, 1, 0);
SendMessage(trackBar, TBM_SETRANGEMAX, 1, 200);
SendMessage(trackBar, TBM_SETTHUMBLENGTH, bm.bmWidth*2, 0);
SendMessage(progressBar, PBM_SETRANGE32, 0, 200);
SendMessage(progressBar, PBM_SETPOS, 100, 0);
ShowWindow(window, SW_SHOW);
MSG message = { 0 };
while (GetMessage(&message, nullptr, 0, 0))
DispatchMessage(&message);
}
In the example I am trying to replace the trackbar thumb with a bitmap image but obviously nothing is happening, it compiles but the trackbar is very small and not replaced by the image. Please I need advice on how to solve this problem.
I copy/pasted your code and ran it and saw the missing thumb too. Started tracing it and found that hbitmapThumb was null.... of course, because I didn't have pink.bmp! I changed that to load it from an absolute path... and noticed you missed the L on the thing:
hBitmapThumb = reinterpret_cast<HBITMAP>(LoadImage(NULL, reinterpret_cast<LPCWSTR>(L"c:\\users\\me\\Documents\\pink.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE));
You might not need the absolute path, depends on where you have the files on your computer.
But you absolutely do need that L before the quote - otherwise you are reinterpret casting a narrow string as a wide string, which will be an invalid filename!
So yeah, I think you did the harder part of getting the custom draw etc right, but just made a char vs w_char mistake there. Let me know if fixing that L fixes the issue for you too.

BeginDraw() paints over statusbar - resizing of renderTarget not respected

I have a windowed Direct2D app and added a statusbar to the window from common controls:
InitCommonControls();
HWND hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
hWnd, (HMENU)ID_STATUSBAR, GetModuleHandle(NULL), NULL);
The statusbar is showing up just fine, but as soon as I activate the BeginDraw()&EndDRaw() functions in my message loop, the statusbar is painted over, despite the fact I defined the height of the renderTarget when initialising it
res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
res = factory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(windowHandle, rectRender),
&renderTarget);
I also created a resize function
RECT rectWindow{ 0 }, rectStatus{ 0 };
D2D1_SIZE_U rectRender{ 0 };
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
renderTarget->Resize(rectRender);
InvalidateRect(windowHandle, NULL, FALSE);
and called in in WM_SIZING and WM_SIZE:
case WM_SIZE:
case WM_SIZING:
hStatus = GetDlgItem(hWnd, ID_STATUSBAR);
gfx->Resize(hWnd, hStatus);
SendMessage(hStatus, WM_SIZE, 0, 0);
Doesn't BeginDraw() respect the dimensions of the rendertarget and just take the entire window? And if so, should I consider using layers or is there something wrong in my code?
EDIT: I received some downvotes for this question. If there's something wrong with my post, do let me know and I'll try to improve. I'm still fresh in the win32 world, but I've learned a lot from this platform. I would love to contribute with interesting questions and answers, but a simple -1 doesn't give me a clue what to improve. I've read 2 evenings about the subject on MSDN and various forums but didn't see what I do wrong. I tried be as complete as possible by writing an complete example code that illustrates the issue.
For reference the entire code
#include <windows.h>
#include <CommCtrl.h>
#include <d2d1.h>
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "d2d1.lib")
#define ID_STATUSBAR 1000
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class Graphics
{
ID2D1Factory* factory;
ID2D1HwndRenderTarget* renderTarget;
ID2D1SolidColorBrush* brush;
public:
Graphics()
{
factory = NULL;
renderTarget = NULL;
brush = NULL;
}
~Graphics()
{
if (factory) factory->Release();
if (renderTarget) renderTarget->Release();
if (brush) brush->Release();
}
bool Init(HWND windowHandle, HWND statusHandle);
void BeginDraw() { renderTarget->BeginDraw(); }
void EndDraw() { renderTarget->EndDraw(); }
void Resize(HWND windowHandle, HWND statusHandle);
void DrawCircle(float x, float y, float r);
};
HINSTANCE hInstance;
Graphics* gfx;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
InitCommonControls();
WNDCLASS wc = { };
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = TEXT("mainwindow");
RegisterClass(&wc);
HWND hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, TEXT("mainwindow"),
TEXT("MainWindow"), WS_OVERLAPPEDWINDOW, 100, 100, 800, 600,
NULL, NULL, hInstance, NULL);
if (!hWnd) return -1;
HWND hStatus = CreateWindowEx(0, STATUSCLASSNAME, NULL,
WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
hWnd, (HMENU)ID_STATUSBAR, GetModuleHandle(NULL), NULL);
gfx = new Graphics;
if (!gfx->Init(hWnd, hStatus))
{
delete gfx;
return -1;
}
ShowWindow(hWnd, nCmdShow);
MSG message{ 0 };
bool runGame = true;
while (runGame)
{
while (PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&message);
DispatchMessage(&message);
if (message.message == WM_QUIT)
runGame = false;
}
gfx->BeginDraw();
gfx->DrawCircle(400.0f, 100.0f, 100.0f);
gfx->DrawCircle(400.0f, 300.0f, 100.0f);
gfx->DrawCircle(400.0f, 500.0f, 100.0f);
gfx->EndDraw();
}
return 0;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HWND hStatus;
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_SIZE:
case WM_SIZING:
hStatus = GetDlgItem(hWnd, ID_STATUSBAR);
gfx->Resize(hWnd, hStatus);
SendMessage(hStatus, WM_SIZE, 0, 0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
bool Graphics::Init(HWND windowHandle, HWND statusHandle)
{
RECT rectWindow{ 0 }, rectStatus{ 0 };
D2D1_SIZE_U rectRender{ 0 };
HRESULT res = D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &factory);
if (res != S_OK) return false;
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
res = factory->CreateHwndRenderTarget(
D2D1::RenderTargetProperties(),
D2D1::HwndRenderTargetProperties(windowHandle, rectRender),
&renderTarget);
if (res != S_OK) return false;
res = renderTarget->CreateSolidColorBrush(D2D1::ColorF(1, 0, 0, 0), &brush);
if (res != S_OK) return false;
return true;
}
void Graphics::Resize(HWND windowHandle, HWND statusHandle)
{
if (renderTarget != NULL)
{
RECT rectWindow{ 0 }, rectStatus{ 0 };
D2D1_SIZE_U rectRender{ 0 };
GetClientRect(windowHandle, &rectWindow);
GetClientRect(statusHandle, &rectStatus);
rectRender.width = rectWindow.right;
rectRender.height = rectWindow.bottom - (rectStatus.bottom - rectStatus.top);
renderTarget->Resize(rectRender);
}
}
void Graphics::DrawCircle(float x, float y, float r)
{
brush->SetColor(D2D1::ColorF(1.0f, 0.0f, 0.0f, 1.0f));
renderTarget->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(x, y), r, r), brush, 1.0f);
}
You can add WS_CLIPCHILDREN window style to your MainWindow:
HWND hWnd = CreateWindowEx(WS_EX_OVERLAPPEDWINDOW, TEXT("mainwindow"),
TEXT("MainWindow"), WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, 100, 100, 800, 600,
NULL, NULL, hInstance, NULL);
Alternatively, you can create another child window (sibling to status bar) and use that for your Direct2D target.

OpenGL context creation in Windows

Here's my code. I don't want a lib for device context/rc creation.
Nothing will draw at all. Maybe I set up DC or RC up wrong?
I'm trying to make a game engine from scratch.
#pragma once
#include <cstdlib>
#include <windows.h>
#include <GL/GL.h>
#pragma comment (lib, "opengl32.lib")
#include <iostream>
using namespace std;
class shadoeGE
{
public:
HWND s_Window = NULL;
int __cdecl Init(const char*, int, int);
private:
};
LRESULT __stdcall s_WindowEvent(HWND hWnd, unsigned int message, unsigned int wParam, long lParam)
{
switch (message)
{
default:
return DefWindowProc(hWnd, message, wParam, lParam);
};
};
int shadoeGE::Init(const char* title, int x, int y)
{
MSG msg = { 0 };
WNDCLASS wc = { 0 };
wc.lpfnWndProc = s_WindowEvent;
wc.hInstance = GetModuleHandle(NULL);
wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND);
wc.lpszClassName = "sWindowClass";
wc.style = CS_OWNDC;
if (!RegisterClass(&wc))
{
return 0;
};
this->s_Window = CreateWindowA(wc.lpszClassName, title, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 0, 0, x, y, 0, 0, GetModuleHandle(NULL), 0);
while (GetMessage(&msg, NULL, 0, 0) > 0)
{
DispatchMessage(&msg);
};
PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
32,
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
24,
8,
0,
PFD_MAIN_PLANE,
0,
0, 0, 0
};
HDC deviceContextHandle = GetDC(this->s_Window);
int windowsChosen = ChoosePixelFormat(deviceContextHandle, &pfd);
SetPixelFormat(deviceContextHandle, windowsChosen, &pfd);
HGLRC ourOpenGLRenderingContext = wglCreateContext(deviceContextHandle);
wglMakeCurrent(deviceContextHandle, ourOpenGLRenderingContext);
glViewport(0, 0, 640, 480);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_TRIANGLES);
glColor3f(0.1, 0.2, 0.3);
glVertex3f(0, 0, 0);
glVertex3f(1, 0, 0);
glVertex3f(0, 1, 0);
glEnd();
SwapBuffers(deviceContextHandle);
return 1;
};
You have to implement a message loop:
MSG msg;
while( ::GetMessage( &msg, 0, 0, 0 ) )
::DispatchMessage( &msg );
You have to draw the scene in the message loop, when the WM_PAINT message is sent:
LRESULT CALLBACK shadoeGE::s_WindowEvent( HWND hWnd, unsigned int msg, WPARAM wparam, LPARAM lparam )
{
switch(msg)
{
// [...]
case WM_PAINT:
wnd.Display();
break;
}
return DefWindowProc( hWnd, msg, wparam, lparam );
}
void shadoeGE::Display( void )
{
RECT clientRect;
::GetClientRect( hOGLWnd, &clientRect );
glViewport( 0, 0, clientRect.right-clientRect.left, clientRect.bottom-clientRect.top );
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
// [...]
HDC hDC = ::GetDC( hOGLWnd );
::SwapBuffers( hDC );
::ReleaseDC( hOGLWnd, hDC );
}
See the example:
#include <windows.h>
#include <GL/gl.h>
#include <vector>
#include <stdexcept>
class shadoeGE
{
public:
static LRESULT CALLBACK s_WindowEvent( HWND hWnd, unsigned int msg, WPARAM wparam, LPARAM lparam );
HWND Init( int width, int height );
void DestroyWindow( void );
void MessageLoop( void );
void Display( void );
private:
HWND hOGLWnd = NULL;
HGLRC hOGLRenderContext = NULL;
};
std::wstring wnd_class( L"my_wnd_class" );
shadoeGE wnd;
int main()
{
int w = 800;
int h = 600;
HWND hWnd = wnd.Init( w, h );
if ( hWnd == 0 )
throw std::runtime_error( "error initializing window" );
wnd.MessageLoop();
wnd.DestroyWindow();
return 0;
}
HWND shadoeGE::Init( int width, int height )
{
// Get module handle
HMODULE hModule = ::GetModuleHandle( 0 );
if (!hModule)
return NULL;
// Create window class
WNDCLASSEX wndClassData;
memset( &wndClassData, 0, sizeof( WNDCLASSEX ) );
wndClassData.cbSize = sizeof( WNDCLASSEX );
wndClassData.style = CS_DBLCLKS;
wndClassData.lpfnWndProc = s_WindowEvent;
wndClassData.cbClsExtra = 0;
wndClassData.cbWndExtra = 0;
wndClassData.hInstance = hModule;
wndClassData.hIcon = ::LoadIcon(0,IDI_APPLICATION);
wndClassData.hCursor = ::LoadCursor(0,IDC_ARROW);
wndClassData.hbrBackground = ::CreateSolidBrush(COLOR_WINDOW+1);
wndClassData.lpszMenuName = 0;
wndClassData.lpszClassName = wnd_class.c_str();
wndClassData.hIconSm = 0;
if ( !::RegisterClassEx( &wndClassData ) )
return false;
// Creaate Window
hOGLWnd = ::CreateWindow( wnd_class.c_str(), NULL, WS_OVERLAPPEDWINDOW, 0, 0, width, height, NULL, NULL, hModule, NULL);
if ( hOGLWnd == NULL )
return NULL;
// Get device context
HDC hDC = ::GetDC( hOGLWnd );
// Create OpenGL context
DWORD pixelFormatFlags = PFD_SUPPORT_OPENGL | PFD_SUPPORT_COMPOSITION | PFD_GENERIC_ACCELERATED | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
PIXELFORMATDESCRIPTOR pfd =
{
sizeof(PIXELFORMATDESCRIPTOR),
1,
pixelFormatFlags, //Flags
PFD_TYPE_RGBA, //The kind of framebuffer. RGBA or palette.
32, //Colordepth of the framebuffer.
0, 0, 0, 0, 0, 0,
0,
0,
0,
0, 0, 0, 0,
24, //Number of bits for the depthbuffer
8, //Number of bits for the stencilbuffer
0, //Number of Aux buffers in the framebuffer.
PFD_MAIN_PLANE,
0,
0, 0, 0
};
int pixelFormat = ::ChoosePixelFormat( hDC, &pfd );
::SetPixelFormat( hDC, pixelFormat, &pfd );
hOGLRenderContext = ::wglCreateContext( hDC );
// make OpenGL context the current context
::wglMakeCurrent( hDC, hOGLRenderContext );
// release device context
::ReleaseDC( hOGLWnd, hDC );
// show the window
::ShowWindow( hOGLWnd, SW_SHOWDEFAULT );
return hOGLWnd;
}
void shadoeGE::MessageLoop( void )
{
MSG msg;
while( ::GetMessage( &msg, 0, 0, 0 ) )
::DispatchMessage( &msg );
}
void shadoeGE::DestroyWindow(void)
{
::DestroyWindow( hOGLWnd );
::wglMakeCurrent( NULL, NULL );
::wglDeleteContext( hOGLRenderContext );
HMODULE hModule = ::GetModuleHandle( 0 );
if (!hModule)
return;
::UnregisterClass( wnd_class.c_str(), hModule );
}
LRESULT CALLBACK shadoeGE::s_WindowEvent( HWND hWnd, unsigned int msg, WPARAM wparam, LPARAM lparam )
{
switch(msg)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
wnd.Display();
break;
}
return DefWindowProc( hWnd, msg, wparam, lparam );
}
void shadoeGE::Display( void )
{
RECT clientRect;
::GetClientRect( hOGLWnd, &clientRect );
glViewport( 0, 0, clientRect.right-clientRect.left, clientRect.bottom-clientRect.top );
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glBegin(GL_TRIANGLES);
glColor3f(0.1, 0.2, 0.3);
glVertex3f(0, 0, 0);
glVertex3f(1, 0, 0);
glVertex3f(0, 1, 0);
glEnd();
HDC hDC = ::GetDC( hOGLWnd );
::SwapBuffers( hDC );
::ReleaseDC( hOGLWnd, hDC );
}

Draw border on top of another application window

I'm need to draw border on top another application's window (the main purpose is to highlight window user chooses from running applications list). I'm trying to draw border on top of native window border, but the border isn't drawn. Here is the code:
HPEN framePen = ::CreatePen(PS_SOLID, 5, RGB(255, 0, 0));
HWND handle = FindWindow(L"ConsoleWindowClass", L"C:\\WINDOWS\\system32\\cmd.exe");
WINDOWPLACEMENT winPlacement;
GetWindowPlacement(handle, &winPlacement);
if (winPlacement.showCmd == SW_SHOWMINIMIZED)
{
ShowWindow(handle, SW_RESTORE);
}
SetWindowPos(handle, HWND_TOP, 0, 0, 0, 0, SWP_ASYNCWINDOWPOS | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE);
SetForegroundWindow(handle);
PAINTSTRUCT ps;
RECT rect = {};
::GetClientRect(handle, &rect);
HDC hdc = ::BeginPaint(handle, &ps);
::SelectObject(hdc, framePen);
::Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom);
::EndPaint(handle, &ps);
In this example used handle of cmd window, but in fact it doesn't matter.
Could you please tell why border isn't drawn and how to draw it?
Thanks.
You can't draw directly on another window, because the system may refresh the window at any time, overwriting your drawing.
To make your drawing persistent, create a layered window, positioned on top of the other window.
Create the window with WS_EX_LAYERED flag.
Pass a color key to SetLayeredWindowAttributes().
In your WM_PAINT handler, draw the inside of the rectangle with the color key (by using it for the brush). Everything you draw with the color key will become transparent. Draw the border of the rectangle with the desired color (by using it for the pen).
Here is a minimal example to get you started. The frame can be moved around by drag-n-drop.
Note there is no error handling to keep the sample code concise. You should check the return value of each Windows API call.
#include <windows.h>
const COLORREF MY_COLOR_KEY = RGB( 255, 0, 255 );
int APIENTRY wWinMain(
HINSTANCE hInstance, HINSTANCE /*hPrevInst*/, LPWSTR /*lpCmdLine*/, int nCmdShow )
{
WNDCLASSW wc{ sizeof( wc ) };
wc.hInstance = hInstance;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.hCursor = LoadCursor( nullptr, IDC_ARROW );
wc.hbrBackground = reinterpret_cast<HBRUSH>( COLOR_BTNFACE + 1 );
wc.lpszClassName = L"MyTransparentFrame";
wc.lpfnWndProc = []( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp ) -> LRESULT
{
switch( msg )
{
case WM_PAINT:
{
PAINTSTRUCT ps{};
HDC hdc = BeginPaint( hwnd, &ps );
RECT rc{}; GetClientRect( hwnd, &rc );
HPEN hPen = CreatePen( PS_SOLID, 20, GetSysColor( COLOR_HIGHLIGHT ) );
HBRUSH hBrush = CreateSolidBrush( MY_COLOR_KEY );
HGDIOBJ hOldPen = SelectObject( hdc, hPen );
HGDIOBJ hOldBrush = SelectObject( hdc, hBrush );
Rectangle( hdc, rc.left, rc.top, rc.right, rc.bottom );
if( hOldPen )
SelectObject( hdc, hOldPen );
if( hOldBrush )
SelectObject( hdc, hOldBrush );
if( hPen )
DeleteObject( hPen );
if( hBrush )
DeleteObject( hBrush );
EndPaint( hwnd, &ps );
}
break;
case WM_DESTROY:
PostQuitMessage( 0 );
break;
case WM_NCHITTEST:
return HTCAPTION; // to be able to drag the window around
break;
default:
return DefWindowProcW( hwnd, msg, wp, lp );
}
return 0;
};
RegisterClassW( &wc );
HWND hwnd = CreateWindowExW( WS_EX_LAYERED, wc.lpszClassName, L"", WS_POPUP,
200, 200, 800, 600, nullptr, nullptr, hInstance, nullptr );
SetLayeredWindowAttributes( hwnd, MY_COLOR_KEY, 255, LWA_COLORKEY );
ShowWindow( hwnd, nCmdShow );
MSG msg;
while( GetMessage( &msg, nullptr, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return (int) msg.wParam;
}
Finally I managed to solve the problem with the following code:
const COLORREF MY_COLOR_KEY = RGB(255, 128, 0);
HWND cmdHanlde = NULL;
constexpr unsigned int timerIdWindowUpdate = 1;
constexpr unsigned int timerIdFrameColor = 2;
bool tick = false;
bool minimized = false;
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
WNDCLASSEX wc = {};
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpszClassName = L"MyTransparentFrame";
wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = NULL;
wc.lpfnWndProc = [](HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) -> LRESULT
{
switch (msg)
{
case WM_PAINT:
{
PAINTSTRUCT ps{};
HDC hdc = BeginPaint(hwnd, &ps);
RECT rc{}; GetClientRect(hwnd, &rc);
HPEN hPen = CreatePen(PS_SOLID, 5, tick ? RGB(255, 128, 1) : RGB(255, 201, 14));
HBRUSH hBrush = CreateSolidBrush(MY_COLOR_KEY);
HGDIOBJ hOldPen = SelectObject(hdc, hPen);
HGDIOBJ hOldBrush = SelectObject(hdc, hBrush);
Rectangle(hdc, rc.left, rc.top, rc.right, rc.bottom);
if (hOldPen)
SelectObject(hdc, hOldPen);
if (hOldBrush)
SelectObject(hdc, hOldBrush);
if (hPen)
DeleteObject(hPen);
if (hBrush)
DeleteObject(hBrush);
EndPaint(hwnd, &ps);
}
break;
case WM_TIMER:
{
if (wp == timerIdWindowUpdate)
{
WINDOWPLACEMENT windowPlacement = { sizeof(WINDOWPLACEMENT), };
if (::GetWindowPlacement(cmdHanlde, &windowPlacement))
{
if (windowPlacement.showCmd == SW_SHOWMINIMIZED
|| !IsWindowVisible(cmdHanlde))
{
ShowWindow(hwnd, SW_HIDE);
minimized = true;
}
else
{
RECT rect = {};
DwmGetWindowAttribute(cmdHanlde, DWMWA_EXTENDED_FRAME_BOUNDS, &rect, sizeof(rect));
MONITORINFO monInfo;
monInfo.cbSize = sizeof(MONITORINFO);
GetMonitorInfoW(MonitorFromWindow(cmdHanlde, MONITOR_DEFAULTTONEAREST), &monInfo);
if (cmdHanlde != NULL && ::IsZoomed(cmdHanlde))
{
rect.left = monInfo.rcWork.left;
rect.top = monInfo.rcWork.top;
rect.bottom = monInfo.rcWork.bottom > rect.bottom ? rect.bottom : monInfo.rcWork.bottom;
rect.right = monInfo.rcWork.right > rect.right ? rect.right : monInfo.rcWork.right;
}
if (minimized)
{
::SetWindowPos(hwnd, cmdHanlde, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
minimized = false;
}
else
{
::SetWindowPos(cmdHanlde, hwnd, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
::SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top,
SWP_SHOWWINDOW);
}
}
}
}
else if (wp == timerIdFrameColor)
{
tick = !tick;
::RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE);
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProcW(hwnd, msg, wp, lp);
}
return 0;
};
RegisterClassEx(&wc);
HWND hwnd = CreateWindowExW(WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE | WS_EX_LAYERED | WS_EX_TRANSPARENT, wc.lpszClassName, L"", WS_POPUP | WS_VISIBLE | WS_DISABLED,
0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
::SetTimer(hwnd, timerIdWindowUpdate, 50, NULL);
::SetTimer(hwnd, timerIdFrameColor, 500, NULL);
SetLayeredWindowAttributes(hwnd, MY_COLOR_KEY, 255, LWA_COLORKEY);
ShowWindow(hwnd, SW_SHOW);
cmdHanlde = FindWindow(L"ConsoleWindowClass", L"C:\\WINDOWS\\system32\\cmd.exe");
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
May be it is not the best solution, but it works for me. Could you please take a look on it and tell if there something to improve?

Show menu on Window with Opengl

I made an Edit Box in my window, but when I try to draw something with OpenGl in dissapears. What should I change?
Here is the code:
#include <windows.h>
#include <gl/gl.h>
#include <math.h>
#define IDC_MAIN_EDIT 102
HWND hEdit;
// Function Declarations
HMENU men;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void EnableOpenGL(HWND hWnd, HDC * hDC, HGLRC * hRC);
void DisableOpenGL(HWND hWnd, HDC hDC, HGLRC hRC);
// WinMain
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int iCmdShow)
{
WNDCLASS wc;
HWND hWnd;
HDC hDC;
HGLRC hRC;
MSG msg;
BOOL quit = FALSE;
float theta = 0.0f;
// register window class
wc.style = CS_OWNDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
wc.hCursor = LoadCursor( NULL, IDC_ARROW );
wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
wc.lpszMenuName = NULL;
wc.lpszClassName = "GLSample";
RegisterClass( &wc );
// create main window
hWnd = CreateWindow(
"GLSample", "OpenGL Sample",
WS_CAPTION | WS_POPUPWINDOW | WS_VISIBLE,
0, 0, 800, 800,
NULL, NULL, hInstance, NULL );
// enable OpenGL for the window
EnableOpenGL( hWnd, &hDC, &hRC );
// program main loop
while ( !quit )
{
// check for messages
if ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
// handle or dispatch messages
if ( msg.message == WM_QUIT )
{
quit = TRUE;
}
else
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
}
else
{
// OpenGL animation code goes here
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glPushMatrix();
glRotatef( theta, 0.0f, 0.0f, 1.0f );
glBegin( GL_TRIANGLES );
glColor3f( 1.0f, 0.0f, 0.0f ); glVertex2f( 0.0f, 0.25f );
glColor3f( 0.0f, 1.0f, 0.0f ); glVertex2f( 0.21f, -0.12f );
glColor3f( 0.0f, 0.0f, 1.0f ); glVertex2f( -0.21f, -0.12f );
glEnd();
glPopMatrix();
SwapBuffers( hDC );
theta += 1.0f;
}
}
// shutdown OpenGL
DisableOpenGL( hWnd, hDC, hRC );
// destroy the window explicitly
DestroyWindow( hWnd );
return msg.wParam;
}
// Window Procedure
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
hEdit=CreateWindowEx(WS_EX_CLIENTEDGE,
"EDIT",
"",
WS_CHILD|WS_VISIBLE|
ES_MULTILINE|ES_AUTOVSCROLL|ES_AUTOHSCROLL,
50,
20,
60,
30,
hWnd,
(HMENU)IDC_MAIN_EDIT,
GetModuleHandle(NULL),
NULL);
HGDIOBJ hfDefault=GetStockObject(DEFAULT_GUI_FONT);
SendMessage(hEdit,
WM_SETFONT,
(WPARAM)hfDefault,
MAKELPARAM(FALSE,0));
SendMessage(hEdit,
WM_SETTEXT,
NULL,
(LPARAM)"Length");
return 0;
}
case WM_CLOSE:
PostQuitMessage( 0 );
return 0;
case WM_DESTROY:
return 0;
case WM_KEYDOWN:
switch ( wParam )
{
case VK_ESCAPE:
PostQuitMessage(0);
return 0;
}
return 0;
default:
return DefWindowProc( hWnd, message, wParam, lParam );
}
}
// Enable OpenGL
void EnableOpenGL(HWND hWnd, HDC * hDC, HGLRC * hRC)
{
PIXELFORMATDESCRIPTOR pfd;
int format;
// get the device context (DC)
*hDC = GetDC( hWnd );
// set the pixel format for the DC
ZeroMemory( &pfd, sizeof( pfd ) );
pfd.nSize = sizeof( pfd );
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 24;
pfd.cDepthBits = 16;
pfd.iLayerType = PFD_MAIN_PLANE;
format = ChoosePixelFormat( *hDC, &pfd );
SetPixelFormat( *hDC, format, &pfd );
// create and enable the render context (RC)
*hRC = wglCreateContext( *hDC );
wglMakeCurrent( *hDC, *hRC );
}
// Disable OpenGL
void DisableOpenGL(HWND hWnd, HDC hDC, HGLRC hRC)
{
wglMakeCurrent( NULL, NULL );
wglDeleteContext( hRC );
ReleaseDC( hWnd, hDC );
}
The answer is very simple - I just changed the parametrs of function CreateWindow to
hWnd = CreateWindow(
"GLSample", "OpenGL Sample",
WS_SYSMENU|WS_VISIBLE|WS_MINIMIZEBOX|WS_OVERLAPPED|WS_BORDER|WS_CAPTION|
WS_CLIPCHILDREN|WS_CLIPSIBLINGS,
0, 0, 800, 800,
NULL, NULL, hInstance, NULL );
this and voila- it all works^_^
It could be overwriting your painting in the default WM_ERASEBKGND code. One way to fix it is to move your OpenGL code to WM_PAINT handler, and then invalidate your window each time you want the animation to advance.