create toolbar with my bitmap images - winapi

this is an example code of msdn to create toolbar, but this example use the standard images of the system.
What do I need to change in this code to use my images from resource file, for example: IDB_COPY BITMAP "copy.bmp", and IDB_CUT BITMAP "cut.bmp", and IDB_PASTE BITMAP "paste.bmp".
HIMAGELIST g_hImageList = NULL;
HWND CreateSimpleToolbar(HWND hWndParent)
{
// Declare and initialize local constants.
const int ImageListID = 0;
const int numButtons = 3;
const int bitmapSize = 16;
const DWORD buttonStyles = BTNS_AUTOSIZE;
// Create the toolbar.
HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | TBSTYLE_WRAPABLE, 0, 0, 0, 0,
hWndParent, NULL, g_hInst, NULL);
if (hWndToolbar == NULL)
return NULL;
// Create the image list.
g_hImageList = ImageList_Create(bitmapSize, bitmapSize, // Dimensions of individual bitmaps.
ILC_COLOR16 | ILC_MASK, // Ensures transparent background.
numButtons, 0);
// Set the image list.
SendMessage(hWndToolbar, TB_SETIMAGELIST,
(WPARAM)ImageListID,
(LPARAM)g_hImageList);
// Load the button images.
SendMessage(hWndToolbar, TB_LOADIMAGES,
(WPARAM)IDB_STD_SMALL_COLOR,
(LPARAM)HINST_COMMCTRL);
// Initialize button info.
// IDM_NEW, IDM_OPEN, and IDM_SAVE are application-defined command constants.
TBBUTTON tbButtons[numButtons] =
{
{ MAKELONG(STD_FILENEW, ImageListID), IDM_NEW, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)L"New" },
{ MAKELONG(STD_FILEOPEN, ImageListID), IDM_OPEN, TBSTATE_ENABLED, buttonStyles, {0}, 0, (INT_PTR)L"Open"},
{ MAKELONG(STD_FILESAVE, ImageListID), IDM_SAVE, 0, buttonStyles, {0}, 0, (INT_PTR)L"Save"}
};
// Add buttons.
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
SendMessage(hWndToolbar, TB_ADDBUTTONS, (WPARAM)numButtons, (LPARAM)&tbButtons);
// Resize the toolbar, and then show it.
SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0);
ShowWindow(hWndToolbar, TRUE);
return hWndToolbar;
}

I found this solution:
const int ID_TB_STANDARD = 0;
const int ID_IL_STANDARD = 0;
HWND hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
WS_CHILD | TBSTYLE_TOOLTIPS, 0, 0, 0, 0, hWnd, (HMENU)ID_TB_STANDARD, hInstance, NULL);
HIMAGELIST hImageList = ImageList_LoadBitmap(hInstance, MAKEINTRESOURCEW(IDB_CUT), 16, 0, RGB(255, 0, 255));
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_COPY)), NULL);
ImageList_Add(hImageList, LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_PASTE)), NULL);
SendMessage(hWndToolbar, TB_SETIMAGELIST, (WPARAM)ID_IL_STANDARD, (LPARAM)hImageList);
SendMessage(hWndToolbar, (UINT) TB_SETHOTIMAGELIST, 0, (LPARAM)hHotImageList);
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
TBBUTTON tbb[3] =
{
{0,ID_CUT,TBSTATE_ENABLED,TBSTYLE_BUTTON},
{1,ID_COPY,TBSTATE_ENABLED,TBSTYLE_BUTTON},
{2,ID_PASTE,TBSTATE_ENABLED,TBSTYLE_BUTTON},
};
SendMessage(hWndToolbar, (UINT) TB_ADDBUTTONS, 3, (LPARAM)&tbb);
SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0);
ShowWindow(hWndToolbar , SW_SHOW);

Related

Force Win32 common controls to draw on `ID2D1HwndRenderTarget`?

I draw most of my UI using ID2D1HwndRenderTarget, but I want some classic window controls: button, edit. How to
ID2D1HwndRenderTarget * canvas = nullptr; // it's global object
HWND button = nullptr; // it's global object
HWND edit = nullptr; // it's global object
HWND custom = nullptr; // it's global object
// mainWindow WinPproc
case WM_CREATE:
button = CreateWindowExW(0, L"button", L"Send", WS_CHILD | WS_VISIBLE, 10, 10, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // win32 control
edit = CreateWindowExW(0, L"edit", L"Edit", WS_CHILD | WS_VISIBLE, 10, 50, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // win32 control
custom = CreateWindowExW(0, L"custom", L"Custom", WS_CHILD | WS_VISIBLE, 10, 90, 120, 30, hWnd, BUTTON_ID, hInstance, 0); // it's my custom class
break;
case WM_PAINT:
BeginPaint(hWnd, nullptr);
render_target->BeginPaint();
... GUI rendering stuff ....
HRESULT result = render_target->EndDraw();
if(result != S_OK)
{
// Error handling
...
}
EndPaint(hWnd, nullptr);
break;
// custom WinProc
case WM_PAINT:
BeginPaint(hWnd, nullptr);
render_target->BeginPaint();
... rendering stuff ....
HRESULT result = render_target->EndDraw();
if(result != S_OK)
{
// Error handling
...
}
EndPaint(hWnd, nullptr);
break;
Only things painted with render_target are visible. I understand why: becausebutton and edit are default win32 controls, internally drawn using PAINTSTRUCT->HDC context. I read Direct2D and GDI Interoperability Overview and get the concept, but still don't know where this HDC intrecpet should take place? I don't want touch default control WM_PAINT. I have to supclass all default win32 controls?
How to force those Win32 controls to draw onto my render_target?
If you want to share a device context (HDC) between GDI and Direct2D, you can use a ID2D1DCRenderTarget and Bind to this HDC when you want to render on it.
This is demonstrated in this official sample : GDI/Direct2D Interoperability Sample
Note, as is, it doesn't compile/work with today's Visual Studio. So, here is a similar code here with a simple button and textbox:
#include <windows.h>
#include <stdlib.h>
#include <math.h>
#include <d2d1.h>
template<class Interface>
inline void
SafeRelease(Interface** ppInterfaceToRelease)
{
if (*ppInterfaceToRelease != NULL)
{
(*ppInterfaceToRelease)->Release();
(*ppInterfaceToRelease) = NULL;
}
}
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
class DemoApp
{
public:
DemoApp();
~DemoApp();
HRESULT Initialize();
private:
HRESULT CreateDeviceIndependentResources();
HRESULT CreateDeviceResources();
void DiscardDeviceResources();
HRESULT OnRender(const PAINTSTRUCT& ps);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
private:
HWND m_hwnd;
ID2D1Factory* m_pD2DFactory;
ID2D1DCRenderTarget* m_pDCRT;
ID2D1SolidColorBrush* m_pBlackBrush;
};
int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nCmdShow*/)
{
if (SUCCEEDED(CoInitialize(NULL)))
{
DemoApp app;
if (SUCCEEDED(app.Initialize()))
{
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
CoUninitialize();
}
return 0;
}
DemoApp::DemoApp() :
m_hwnd(NULL),
m_pD2DFactory(NULL),
m_pDCRT(NULL),
m_pBlackBrush(NULL)
{
}
DemoApp::~DemoApp()
{
SafeRelease(&m_pD2DFactory);
SafeRelease(&m_pDCRT);
SafeRelease(&m_pBlackBrush);
}
HRESULT DemoApp::Initialize()
{
HRESULT hr;
hr = CreateDeviceIndependentResources();
if (SUCCEEDED(hr))
{
// Register the window class.
WNDCLASSEX wcex = { sizeof(WNDCLASSEX) };
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = DemoApp::WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = sizeof(LONG_PTR);
wcex.hInstance = (HINSTANCE)&__ImageBase;
wcex.hbrBackground = NULL;
wcex.lpszMenuName = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.lpszClassName = L"D2DDemoApp";
RegisterClassEx(&wcex);
// Create the application window.
// Because the CreateWindow function takes its size in pixels, we obtain the system DPI and use it to scale the window size.
FLOAT dpiX, dpiY;
m_pD2DFactory->GetDesktopDpi(&dpiX, &dpiY);
m_hwnd = CreateWindow(
L"D2DDemoApp",
L"Direct2D Demo App",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
static_cast<UINT>(ceil(640.f * dpiX / 96.f)),
static_cast<UINT>(ceil(480.f * dpiY / 96.f)),
NULL,
NULL,
(HINSTANCE)&__ImageBase,
this
);
hr = m_hwnd ? S_OK : E_FAIL;
if (SUCCEEDED(hr))
{
ShowWindow(m_hwnd, SW_SHOWNORMAL);
UpdateWindow(m_hwnd);
}
}
return hr;
}
HRESULT DemoApp::CreateDeviceIndependentResources()
{
// Create D2D factory
return D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pD2DFactory);
}
HRESULT DemoApp::CreateDeviceResources()
{
HRESULT hr = S_OK;
if (!m_pDCRT)
{
// Create a DC render target.
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
D2D1_RENDER_TARGET_TYPE_DEFAULT,
D2D1::PixelFormat(
DXGI_FORMAT_B8G8R8A8_UNORM,
D2D1_ALPHA_MODE_IGNORE),
0,
0,
D2D1_RENDER_TARGET_USAGE_NONE,
D2D1_FEATURE_LEVEL_DEFAULT
);
hr = m_pD2DFactory->CreateDCRenderTarget(&props, &m_pDCRT);
if (SUCCEEDED(hr))
{
// Create a black brush.
hr = m_pDCRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::Black), &m_pBlackBrush);
}
}
return hr;
}
void DemoApp::DiscardDeviceResources()
{
SafeRelease(&m_pDCRT);
SafeRelease(&m_pBlackBrush);
}
HRESULT DemoApp::OnRender(const PAINTSTRUCT& ps)
{
HRESULT hr;
RECT rc;
// Get the dimensions of the client drawing area.
GetClientRect(m_hwnd, &rc);
// Draw the pie chart with Direct2D.
// Create the DC render target.
hr = CreateDeviceResources();
if (SUCCEEDED(hr))
{
// Bind the DC to the DC render target.
hr = m_pDCRT->BindDC(ps.hdc, &rc);
m_pDCRT->BeginDraw();
m_pDCRT->SetTransform(D2D1::Matrix3x2F::Identity());
m_pDCRT->Clear(D2D1::ColorF(D2D1::ColorF::White));
m_pDCRT->DrawEllipse(D2D1::Ellipse(D2D1::Point2F(150.0f, 150.0f), 100.0f, 100.0f), m_pBlackBrush, 3.0);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F((150.0f + 100.0f * 0.15425f), (150.0f - 100.0f * 0.988f)), m_pBlackBrush, 3.0
);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F((150.0f + 100.0f * 0.525f), (150.0f + 100.0f * 0.8509f)), m_pBlackBrush, 3.0
);
m_pDCRT->DrawLine(
D2D1::Point2F(150.0f, 150.0f),
D2D1::Point2F((150.0f - 100.0f * 0.988f), (150.0f - 100.0f * 0.15425f)), m_pBlackBrush, 3.0
);
hr = m_pDCRT->EndDraw();
if (SUCCEEDED(hr))
{
// Draw the pie chart with GDI.
// Save the original object.
HGDIOBJ original = NULL;
original = SelectObject(ps.hdc, GetStockObject(DC_PEN));
HPEN blackPen = CreatePen(PS_SOLID, 3, 0);
SelectObject(ps.hdc, blackPen);
Ellipse(ps.hdc, 300, 50, 500, 250);
POINT pntArray1[2];
pntArray1[0].x = 400;
pntArray1[0].y = 150;
pntArray1[1].x = static_cast<LONG>(400 + 100 * 0.15425);
pntArray1[1].y = static_cast<LONG>(150 - 100 * 0.9885);
POINT pntArray2[2];
pntArray2[0].x = 400;
pntArray2[0].y = 150;
pntArray2[1].x = static_cast<LONG>(400 + 100 * 0.525);
pntArray2[1].y = static_cast<LONG>(150 + 100 * 0.8509);
POINT pntArray3[2];
pntArray3[0].x = 400;
pntArray3[0].y = 150;
pntArray3[1].x = static_cast<LONG>(400 - 100 * 0.988);
pntArray3[1].y = static_cast<LONG>(150 - 100 * 0.15425);
Polyline(ps.hdc, pntArray1, 2);
Polyline(ps.hdc, pntArray2, 2);
Polyline(ps.hdc, pntArray3, 2);
DeleteObject(blackPen);
// Restore the original object.
SelectObject(ps.hdc, original);
}
}
if (hr == D2DERR_RECREATE_TARGET)
{
hr = S_OK;
DiscardDeviceResources();
}
return hr;
}
LRESULT CALLBACK DemoApp::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pcs->lpCreateParams);
auto button = CreateWindowExW(0, L"button", L"Send", WS_CHILD | WS_VISIBLE, 10, 10, 120, 30, hwnd, (HMENU)1, (HINSTANCE)&__ImageBase, 0); // win32 control
auto edit = CreateWindowExW(0, L"edit", L"Edit", WS_CHILD | WS_VISIBLE, 10, 50, 120, 30, hwnd, (HMENU)2, (HINSTANCE)&__ImageBase, 0); // win32 control
return 1;
}
LRESULT result = 0;
DemoApp* pDemoApp = (DemoApp*)(GetWindowLongPtr(hwnd, GWLP_USERDATA));
bool wasHandled = false;
if (pDemoApp)
{
switch (message)
{
case WM_PAINT:
case WM_DISPLAYCHANGE:
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
pDemoApp->OnRender(ps);
EndPaint(hwnd, &ps);
}
result = 0;
wasHandled = true;
break;
case WM_DESTROY:
{
PostQuitMessage(0);
}
result = 1;
wasHandled = true;
break;
}
}
if (!wasHandled)
{
result = DefWindowProc(hwnd, message, wParam, lParam);
}
return result;
}
And here is how it renders (left circle is Direct2D, right is aliased GDI):
How to force Win32 controls to draw onto my ID2D1Bitmap1? Is that even possible?
Yes. You can write GDI content to a Direct2D GDI-compatible render target. This approach is useful for applications that primarily render with Direct2D but have an extensibility model or other legacy content that requires the ability to render with GDI.
Step:
To render GDI content to a Direct2D GDI-compatible render target, use
an ID2D1GdiInteropRenderTarget, which provides access to a device
context that can accept GDI draw calls. Unlike other interfaces, an
ID2D1GdiInteropRenderTarget object is not created directly. Instead,
use the QueryInterface method of an existing render target instance.
Refer: Draw GDI Content to a Direct2D GDI-Compatible Render Target

Draw cursor multiple display (only draw text icon) GDI

I select displays in multiple display environments and produce captured programs.
And try to draw a cursor by selecting dc among multiple displays.
I draw cursors with bitblt bitmap images and it works well.
HDC HDCC = CreateDC((L"Display"), NULL, NULL, NULL);
But When I select from createDC multuple display value,
DISPLAY_DEVICEW info = { 0 };
info.cb = sizeof(DISPLAY_DEVICEW);
EnumDisplayDevicesW(NULL, 0, &info, EDD_GET_DEVICE_INTERFACE_NAME);
HDCC = CreateDC(info.DeviceName, NULL, NULL, NULL);
I am working hard to get other display images. But there is no drawing cursor. (Other forms of cursor are not drawn, only text cursor is drawn)
This is my code.
const int d_count = GetSystemMetrics(SM_CMONITORS); //I have 3 display and count is 3.
HDC hCaptureDC;
HDC HDCC;
HBITMAP hBitmap;
HGDIOBJ hOld;
BYTE *src;
bool GetMouse() {
CURSORINFO cursor = { sizeof(cursor) };
::GetCursorInfo(&cursor);
ICONINFOEXW info = { sizeof(info) };
::GetIconInfoExW(cursor.hCursor, &info);
BITMAP bmpCursor = { 0 };
GetObject(info.hbmColor, sizeof(bmpCursor), &bmpCursor);
POINT point;
GetCursorPos(&point);
bool res = DrawIconEx(hCaptureDC, point.x, point.y, cursor.hCursor, bmpCursor.bmWidth, bmpCursor.bmHeight, 0, NULL, DI_NORMAL);
return res;
}
void screencap(){
BITMAPINFO MyBMInfo;
BITMAPINFOHEADER bmpInfoHeader;
HWND m_hWndCopy= GetDesktopWindow();
GetClientRect(m_hWndCopy, &ImageRect);
const int nWidth = ImageRect.right - ImageRect.left;
const int nHeight = ImageRect.bottom - ImageRect.top;
MyBMInfo = { 0 };
MyBMInfo.bmiHeader.biSize = sizeof(MyBMInfo.bmiHeader);
bmpInfoHeader = { sizeof(BITMAPINFOHEADER) };
bmpInfoHeader.biWidth = nWidth;
bmpInfoHeader.biHeight = nHeight;
bmpInfoHeader.biPlanes = 1;
bmpInfoHeader.biBitCount = 32;
b_size = ((nWidth * bmpInfoHeader.biBitCount + 31) / 32) * 4 * nHeight;
//HDCC = CreateDC((L"Display"), NULL, NULL, NULL); //It's good.
DISPLAY_DEVICEW info = { 0 };
info.cb = sizeof(DISPLAY_DEVICEW);
EnumDisplayDevicesW(NULL, 0, &info, EDD_GET_DEVICE_INTERFACE_NAME);
HDCC = CreateDC(info.DeviceName, NULL, NULL, NULL); // It draws only text cursor.
hCaptureDC = CreateCompatibleDC(HDCC);
hBitmap = CreateCompatibleBitmap(HDCC, nWidth, nHeight);
hOld = SelectObject(hCaptureDC, hBitmap);
BitBlt(hCaptureDC, 0, 0, nWidth, nHeight, HDCC, 0, 0, SRCCOPY);
GetMouse();
SelectObject(hCaptureDC, hOld);
src = (BYTE*)malloc(b_size);
if (GetDIBits(hCaptureDC, hBitmap, 0, nHeight, src, (BITMAPINFO*)&bmpInfoHeader, DIB_RGB_COLORS)) {
if (RGBSaveBMP(src) == true)
free(src);
}
}
I'm using windows 10.
How can I solved?
Any links, idea, Thanks.
ADD
It does not draw cursor....(except text cursor.)
HDCC = CreateDC(TEXT("\\\\.\\Display1"), NULL, NULL, NULL);
It draws cursor..
HDCC = CreateDC(TEXT("Display"), NULL, NULL, NULL);
SOLVED:
Now, I changed my algorithm.
CreateDC(L"Display", NULL, NULL, NULL) create DC for all monitors, so what is your problem? – user2120666 Apr 22 at 15:37
This comment is very helpful but It was not kind. (Or I was stupid.) :(
HDC HDCC = CreateDC((L"Display"), NULL, NULL, NULL);
HDCC has "All Virtual Screens" DC.
When I selected the monitor I needed, and accordingly selected and used the area to capture on the virtual screen.
HDC hCaptureDC = CreateCompatibleDC(HDCC);
HBITMAP hBitmap = CreateCompatibleBitmap(HDCC, nWidth, nHeight);
HDC HDCC = CreateDC((L"Display"), NULL, NULL, NULL);
DEVMODE dev;
std::string str = "\\\\.\\Display" + std::to_string(select);
std::wstring temp;
temp.assign(str.begin(), str.end());
EnumDisplaySettingsW(temp.c_str(), ENUM_CURRENT_SETTINGS, &dev);
printf("Display%d : (%d * %d) (%d, %d)\n", select, dev.dmPelsWidth, dev.dmPelsHeight, dev.dmPosition.x, dev.dmPosition.y);
nWidth = dev.dmPelsWidth;
nHeight = dev.dmPelsHeight;
nposx = dev.dmPosition.x;
nposy = dev.dmPosition.y;
hOld = SelectObject(hCaptureDC, hBitmap);
BitBlt(hCaptureDC, 0, 0, nWidth, nHeight, HDCC, nposx, nposy, SRCCOPY);
int colorcheck = GetSystemMetrics(SM_SAMEDISPLAYFORMAT);
CURSORINFO cursor = { sizeof(cursor) };
bool check = ::GetCursorInfo(&cursor);
bool check2 = ::GetCursorInfo(&cursor);
int count = ShowCursor(TRUE);
info = { sizeof(info) };
::GetIconInfoExW(cursor.hCursor, &info);
GetCursorPos(&point);
if (point.x > nWidth) {
point.x = point.x - nWidth;
}
else if (point.x < 0) {
point.x = nWidth + point.x;
}
if (point.y > nHeight) {
point.y = point.y - nHeight;
}
else if (point.y < 0) {
point.y = nHeight + point.y;
}
cursor.ptScreenPos.x = point.x;
cursor.ptScreenPos.y = point.y;
bool res = ::DrawIconEx(hCaptureDC, point.x, point.y, cursor.hCursor, 0, 0, 0, NULL, DI_NORMAL);
BYTE* src = (BYTE*)malloc(b_size);
GetDIBits(hCaptureDC, hBitmap, 0, nHeight, src, (BITMAPINFO*)&bmpInfoHeader, DIB_RGB_COLORS)
Thanks for the comment!
Your call GetClientRect(m_hWndCopy, &ImageRect); is wrong.
From documentation:
The rectangle of the desktop window returned by GetWindowRect or
GetClientRect is always equal to the rectangle of the primary monitor,
for compatibility with existing applications.
So you are capturing only primary display.
Following MSDN to get EnumDisplayMonitor may solve your problem.
Use EnumDisplayMonitors to get the device name and pass it to
CreateDC.

How do I implement double buffering in the winapi?

I can't stop the flickering. I got the advice to add dubbel-buffering. How do I do that?
#include <iostream>
#include <windows.h>
#include <string>
#include <fstream>
#include <vector>
using namespace std;
namespace {
const int ID_NEW = 1;
const int ID_QUIT = 2;
const int ID_ABOUT = 3;
const int NORTH_BUTTON_ID = 4;
const int SOUTH_BUTTON_ID = 5;
const int WEST_BUTTON_ID = 6;
const int EAST_BUTTON_ID = 7;
const int ID_FINISHED_GAME = 8;
int x = 0;
int y = 0;
int xStart = 0;
int yStart = 0;
int windowHeight = 400;
int windowWidth = 500;
char level1[20][21];
int noOfMoves = 0;
}
void readLevel(string fileName, char level[20][21]) {
char character{};
ifstream file(fileName);
int i = 0;
int j = 0;
if (file.is_open()) {
while (file >> character) {
level[j][i] = character;
if (level[j][i] == 's') {
y = yStart = j;
x = xStart = i;
}
if (++i % 20 == 0) {
i = 0;
j++;
}
}
file.close();
}
}
void restart(){
x = xStart;
y = yStart;
noOfMoves = 0;
}
LRESULT CALLBACK WinProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
HDC hdc{ 0 };
PAINTSTRUCT ps{ 0 };
switch (msg) {
case WM_CLOSE:
PostQuitMessage(0);
return 0;
case WM_COMMAND:
switch (LOWORD(wParam)){
case ID_ABOUT:
MessageBox(hwnd, L"About this program!", L"About", MB_OK);
return 0;
case ID_NEW:
restart();
return 0;
case ID_QUIT:
if (MessageBox(0, L"Do you really want to quit?", L"Are you sure?", MB_YESNO) == IDYES) {
PostQuitMessage(0);
return 0;
}
case NORTH_BUTTON_ID:
if (level1[y - 1][x] != '1') {
y -= 1;
noOfMoves++;
}
break;
case SOUTH_BUTTON_ID:
if (level1[y + 1][x] != '1'){
y += 1;
noOfMoves++;
}
break;
case WEST_BUTTON_ID:
if (level1[y][x - 1] != '1'){
x -= 1;
noOfMoves++;
}
break;
case EAST_BUTTON_ID:
if (level1[y][x + 1] != '1') {
x += 1;
noOfMoves++;
}
break;
}
if (level1[y][x] == 'e') {
wstring moves = L"Congratulations, you finished the game with " + to_wstring(noOfMoves);
MessageBox(hwnd, moves.c_str(), L"Finished game", MB_OK);
}
case WM_PAINT: {
char wall[2] = { "W" };
char floor[2] = { 'W' };
char current[2] = { "X" };
char goal[2] = { "G" };
wstring position = L"Position = [" + to_wstring(x) + L", " + to_wstring(y) + L"]";
wstring moves = L"Move = " + to_wstring(noOfMoves);
hdc = BeginPaint(hwnd, &ps);
TextOut(hdc, 20, 200, position.c_str(), position.size());
TextOut(hdc, 20, 220, moves.c_str(), moves.size());
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 20; j++) {
if (level1[j][i] == '1') {
SetTextColor(hdc, RGB(0, 0, 0));
SetBkColor(hdc, RGB(0, 0, 0));
TextOut(hdc, 14 * i + 190, 14 * j + 20, LPCTSTR(wall), strlen(wall));
}
SetBkColor(hdc, RGB(255, 255, 255));
if (level1[j][i] == '0') {
SetTextColor(hdc, RGB(255, 255, 255));
TextOut(hdc, 14 * i + 190, 14 * j + 20, LPCTSTR(floor), strlen(floor));
}
SetTextColor(hdc, RGB(0, 0, 0));
if (i == x && j == y)
TextOut(hdc, 14 * i + 190, 14 * j + 20, LPCTSTR(current), strlen(current));
if (level1[j][i] == 'e')
TextOut(hdc, 14 * i + 190, 14 * j + 20, LPCTSTR(goal), strlen(goal));
}
}
EndPaint(hwnd, &ps);
break;
}
case WM_ERASEBKGND:
return true;
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
HMENU CreateMainMenu() {
HMENU main = CreateMenu();
HMENU file = CreateMenu();
AppendMenu(file, MF_STRING, ID_NEW, L"&New");
AppendMenu(file, MF_SEPARATOR, 0, 0);
AppendMenu(file, MF_STRING, ID_QUIT, L"&Quit");
AppendMenu(main, MF_POPUP, (UINT_PTR)file, L"&File");
HMENU help = CreateMenu();
AppendMenu(help, MF_STRING, ID_ABOUT, L"&About");
AppendMenu(main, MF_POPUP, (UINT_PTR)help, L"&Help");
return main;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow) {
readLevel("level1.txt", level1);
WNDCLASS wc = { 0 };
wc.hbrBackground = NULL;
wc.lpfnWndProc = WinProc;
wc.hInstance = hInstance;
wc.lpszClassName = L"MyWindowClass";
RegisterClass(&wc);
HWND hwnd = CreateWindow(L"MyWindowClass", L"The Maze",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
windowWidth, windowHeight, 0, CreateMainMenu(), hInstance, 0);
HWND buttonNorth = CreateWindow(L"BUTTON", L"NORTH", WS_CHILD | WS_VISIBLE,
10, 20, 150, 40, hwnd, (HMENU)NORTH_BUTTON_ID, hInstance, 0);
HWND buttonSouth = CreateWindow(L"BUTTON", L"SOUTH", WS_CHILD | WS_VISIBLE,
10, 60, 150, 40, hwnd, (HMENU)SOUTH_BUTTON_ID, hInstance, 0);
HWND buttonEast = CreateWindow(L"BUTTON", L"EAST", WS_CHILD | WS_VISIBLE,
10, 140, 150, 40, hwnd, (HMENU)EAST_BUTTON_ID, hInstance, 0);
HWND buttonWest = CreateWindow(L"BUTTON", L"WEST", WS_CHILD | WS_VISIBLE,
10, 100, 150, 40, hwnd, (HMENU)WEST_BUTTON_ID, hInstance, 0);
UpdateWindow(hwnd);
ShowWindow(hwnd, nCmdShow);
MSG msg = { 0 };
BOOL isRunning = true;
while (isRunning) {
while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
if (msg.message == WM_QUIT)
isRunning = false;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
wc.hbrBackground = NULL;
InvalidateRect(hwnd, NULL, FALSE);
Sleep(10);
}
return 0;
}
As member Retired Ninja said, you make compatible device context with the original one ( hdc in your case ), and create a bitmap compatible with your original device context ( bitmap size is equal to the size of your rectangle where you paint your stuff ).
Then select this newly created bitmap into compatible device context you just created and draw everything on it.
Then you just BitBlt(...) the compatible device context into original one.
Do not forget to perform proper cleanup in order to avoid GDI leaks.
Your code should look like this:
case WM_PAINT:
{
// skipped the initialization part to preserve space
// just copy those, they are irrelevant for your problem
hdc = BeginPaint(hwnd, &ps);
// create memory DC and memory bitmap where we shall do our drawing
HDC memDC = CreateCompatibleDC( hdc );
// get window's client rectangle. We need this for bitmap creation.
RECT rcClientRectangle;
GetClientRect( hwnd, &rcClientRect );
// now we can create bitmap where we shall do our drawing
HBITMAP bmp = CreateCompatibleBitmap( hdc,
rcClientRect.right - rcClientRect.left,
rcClientRect.bottom - rcClientRect.top );
// we need to save original bitmap, and select it back when we are done,
// in order to avoid GDI leaks!
HBITMAP oldBmp = (HBITMAP)SelectObject( memDC, bmp );
// now you draw your stuff in memory dc;
// just substitute hdc with memDC in your drawing code,
// like I did below:
TextOut( memDC, //...
TextOut( memDC, //...
for (int i = 0; i < 20; i++)
{
for (int j = 0; j < 20; j++)
{
if (level1[j][i] == '1')
{
SetTextColor( memDC, //...
SetBkColor( memDC, //...
TextOut( memDC, //...
}
SetBkColor( memDC, //...
if (level1[j][i] == '0')
{
SetTextColor( memDC, //...
TextOut( memDC, //...
}
SetTextColor( memDC, //...
if (i == x && j == y)
TextOut( memDC, //...
if (level1[j][i] == 'e')
TextOut( memDC, //...
}
}
// OK, everything is drawn into memory DC,
// now is the time to draw that final result into our target DC
BitBlt( hdc, 0, 0, rcClientRect.right - rcClientRect.left,
rcClientRect.bottom - rcClientRect.top, memDC, 0, 0, SRCCOPY );
// all done, now we need to cleanup
SelectObject( memDC, oldBmp ); // select back original bitmap
DeleteObject( bmp ); // delete bitmap since it is no longer required
DeleteDC( memDC ); // delete memory DC since it is no longer required
EndPaint(hwnd, &ps);
break;
}

creating rebar control and introduction a band with toolbar into the rebar

I use this code to create a rebar control, and introduction a band with toolbar into the rebar.
But when window is shown up, I can not see the toolbar. and when I check the height of the rebar, in this line of code: int height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top; I find rebar's height is only 4 pixels.
#include <windows.h>
#include <stdlib.h>
#include <CommCtrl.h>
#pragma comment(lib, "comctl32.lib")
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HINSTANCE instance;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
instance = hInstance;
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"Example";
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
RegisterClassEx(&wcex);
HWND hWnd = CreateWindow(L"Example", L"", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
500, 500, NULL, NULL, hInstance, NULL);
// Initialize common controls.
INITCOMMONCONTROLSEX icex;
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_COOL_CLASSES | ICC_BAR_CLASSES;
InitCommonControlsEx(&icex);
HWND hwndRebar = CreateWindowEx(WS_EX_TOOLWINDOW, REBARCLASSNAME, NULL, WS_CHILD | WS_VISIBLE | WS_BORDER,
0, 0, 100, 50, hWnd, NULL, instance, NULL);
// create toolbar
HWND hWndToolbar = CreateWindowEx(0 , TOOLBARCLASSNAME, NULL, WS_CHILD | TBSTYLE_TOOLTIPS,
0, 0, 0, 0, hwndRebar, (HMENU)0, instance, NULL);
HIMAGELIST hImageList = ImageList_Create(16, 16, ILC_COLOR16 | ILC_MASK, 3, 0);
SendMessage(hWndToolbar, TB_SETIMAGELIST, (WPARAM)0, (LPARAM)hImageList);
SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
TBBUTTON tbb[4] =
{
{0,0,TBSTATE_ENABLED,TBSTYLE_BUTTON},
{1,1,TBSTATE_ENABLED,TBSTYLE_BUTTON},
{2,2,TBSTATE_ENABLED,TBSTYLE_BUTTON},
};
SendMessage(hWndToolbar, (UINT) TB_ADDBUTTONS, 3, (LPARAM)&tbb);
SendMessage(hWndToolbar, TB_AUTOSIZE, 0, 0);
ShowWindow(hWndToolbar , SW_SHOW);
// Initialize band info.
REBARBANDINFO rbBand = { sizeof(REBARBANDINFO) };
rbBand.fMask = RBBIM_STYLE | RBBIM_TEXT | RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_SIZE | RBBIM_COLORS;
rbBand.fStyle = RBBS_GRIPPERALWAYS;
// Get the height of the toolbar.
DWORD dwBtnSize = (DWORD)SendMessage(hWndToolbar, TB_GETBUTTONSIZE, 0,0);
// Set values unique to the band with the toolbar.
rbBand.clrFore = RGB(233, 233, 233);
rbBand.clrBack = RGB(200, 200, 200);
rbBand.lpText = TEXT("");
rbBand.hwndChild = hWndToolbar;
rbBand.cyChild = LOWORD(dwBtnSize);
rbBand.cyMinChild = LOWORD(dwBtnSize);
rbBand.cxMinChild = 3 * HIWORD(dwBtnSize);
// The default width is the width of the buttons.
rbBand.cx = 0;
// Add the band
SendMessage(hwndRebar, RB_INSERTBAND, (WPARAM)-1, (LPARAM)&rbBand);
// show the main window
ShowWindow(hWnd, nCmdShow);
// check the rebar size
WINDOWPLACEMENT wp;
GetWindowPlacement(hwndRebar, &wp);
int height = wp.rcNormalPosition.bottom - wp.rcNormalPosition.top;
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int) msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
return 0;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
As follows from the comments, the solution was:
REBARBANDINFO rbBand;
rbBand.cbSize = REBARBANDINFO_V3_SIZE;
// initialize the rest here
This seems to affect older versions of Windows (specifically, XP), because the original code compiles and runs fine on Windows 7.
This was mentioned in the comments to a MSDN page: http://msdn.microsoft.com/en-us/library/windows/desktop/bb774393.aspx
As was already stated, the cbSize member of the REBARBANDINFO struct needed to be set.
Note the following about the provided code as well:
The images on the toolbar buttons don't show up. Right after creating the image list you have to make the following call to load the images:
SendMessage(hWndToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);
The TBBUTTON array is declared with a size of 4 but it only gets populated with 3 items. Technically it should be declared as:
TBBUTTON tbb[3]

Add toolbar Dialogbox with Win32 API

I have a dialog box on which controls are added with resource editor. But I am trying to create a toolbar on the fly in WM_INITGDIALOG message but the toolbar is not visible. Is there something else to do make it visible(I dont think so but...). If this is not possible how can add a toolbar in resource editor.
As you guessed I use VS 2008.
CreateButtons(HWND hwnd)
{
HIMAGELIST m_hTBImageList;
HIMAGELIST m_hTBHottrack;
HWND hwndSysButtonTB = CreateWindowEx(0,
TOOLBARCLASSNAME,
_T(""),
WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT | TBSTYLE_TOOLTIPS | CCS_NORESIZE | CCS_NOPARENTALIGN,
toolbarRect.left, toolbarRect.top, toolbarRect.right-toolbarRect.left, toolbarRect.bottom-toolbarRect.top,
hwnd,
(HMENU)IDR_TOOLBAR,
(HINSTANCE)hAppInstance,
NULL);
m_hTBImageList = ImageList_LoadImage((HINSTANCE)hAppInstance,
MAKEINTRESOURCE(IDB_BITMAP_ICONS), toolbarButtonSize.cx, 1,
0, IMAGE_BITMAP, LR_CREATEDIBSECTION|LR_SHARED);
m_hTBHottrack = ImageList_LoadImage((HINSTANCE)hAppInstance,
MAKEINTRESOURCE(IDB_MOUSEOVER), toolbarButtonSize.cx, 1,
0, IMAGE_BITMAP, LR_CREATEDIBSECTION|LR_SHARED);
SendMessage(hwndSysButtonTB, (UINT) TB_SETIMAGELIST, 0, (LPARAM)m_hTBImageList);
SendMessage(hwndSysButtonTB, (UINT) TB_SETHOTIMAGELIST, 0, (LPARAM)m_hTBHottrack);
SendMessage(hwndSysButtonTB, (UINT) TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
// win2k: set color of hot tracking frame
COLORSCHEME scheme;
scheme.dwSize = sizeof(scheme);
scheme.clrBtnHighlight = RGB(175,175,175);
scheme.clrBtnShadow = RGB(175,175,175);
SendMessage(hwndSysButtonTB, (UINT) TB_SETCOLORSCHEME, 0, (LPARAM)&scheme);
TBBUTTON ButtonEnd = {0,ID_BUTTON_END,TBSTATE_ENABLED,TBSTYLE_BUTTON};
TBBUTTON ButtonRefresh = {1,ID_BUTTON_REFRESH,TBSTATE_ENABLED,TBSTYLE_BUTTON};
TBBUTTON ButtonOptions = {2,ID_BUTTON_PROPERTIES,TBSTATE_ENABLED,TBSTYLE_BUTTON};
SendMessage(hwndSysButtonTB, (UINT) TB_ADDBUTTONS, 1, (LPARAM)&ButtonEnd);
SendMessage(hwndSysButtonTB, (UINT) TB_ADDBUTTONS, 1, (LPARAM)&ButtonRefresh);
SendMessage(hwndSysButtonTB, (UINT) TB_ADDBUTTONS, 1, (LPARAM)&ButtonOptions);
}
You have to call
SendMessage(hwndSysButtonTB, TB_AUTOSIZE, 0, 0);
ShowWindow(hwndSysButtonTB , SW_SHOW);
at the end of your function.
And I think you should use an TBBUTTON array instead of three separate variables. Then you can add them all at once with
SendMessage(hwndSysButtonTB, (UINT) TB_ADDBUTTONS, 3, (LPARAM)&ButtonArray);

Resources