I created a ListView using CreateWindowEx and using the WC_LISTVIEW as class name.
I'm trying to create a smooth scrolling. Everything works perfectly except that list is not drawn correctly. See below the screenshot of the list:
The list view has the following style in CreateWindowEx:
WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_NOCOLUMNHEADER |
WS_TABSTOP | WS_BORDER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_OWNERDRAWFIXED
I'm using a timer to scroll window. It does the following:
ScrollWindowEx(
listHandle,
0,
step * linesDelta,
NULL,
NULL,
0, 0, 0
);
UpdateWindow(listHandle);
Scroll works perfectly except painting.
I tried:
UpdateWindow() - screenshoot attached
RedrawWindow with all possible options - window is painted only once
InvalidateRect + UpdateWindow = same as 2
InvalidateRect + SendMessage(hwnd, WM_PAINT, 0, 0) - same as 2
The code that paints the item for the list is below:
LRESULT drawItem(HWND hwnd, DRAWITEMSTRUCT* drawStruct) {
Item *itemData = (Item *)drawStruct->itemData;
HDC hdc = drawStruct->hDC;
COLORREF backgroundColor;
COLORREF oldColor;
if (drawStruct->itemState & ODS_SELECTED || ListView_GetHotItem(hwnd) == drawStruct->itemID) {
backgroundColor = GetSysColor(COLOR_HIGHLIGHT);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
} else {
backgroundColor = RGB(255, 255, 255);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));
}
HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor);
HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, backgroundBrush);
FillRect(hdc, &drawStruct->rcItem, backgroundBrush);
drawStruct->rcItem.left += 5;
drawStruct->rcItem.right -= 5;
drawStruct->rcItem.left += 30;
DrawText(hdc, itemData->path, -1, &drawStruct->rcItem,
DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS);
drawStruct->rcItem.left -= 30;
if (itemData->searchData && itemData->searchData->bitmap) {
HBITMAP bitmap = itemData->searchData->bitmap;
HDC hdcMem = CreateCompatibleDC(hdc);
HGDIOBJ oldBitmap = SelectObject(hdcMem, bitmap);
BITMAPINFO bi = { 0 };
bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
// Get the bitmap info header.
if (0 != GetDIBits(
hdcMem, // hdc
bitmap, // hbmp
0, // uStartScan
0, // cScanLines
NULL, // lpvBits
&bi,
DIB_RGB_COLORS
)) {
BLENDFUNCTION blendFunc;
blendFunc.BlendOp = AC_SRC_OVER;
blendFunc.BlendFlags = 0;
blendFunc.SourceConstantAlpha = 255;
blendFunc.AlphaFormat = AC_SRC_ALPHA;
AlphaBlend(hdc,
drawStruct->rcItem.left + 2, //dest X
drawStruct->rcItem.top + 3, //dest Y
bi.bmiHeader.biWidth,
bi.bmiHeader.biHeight,
hdcMem, 0, 0,
bi.bmiHeader.biWidth,
bi.bmiHeader.biHeight, blendFunc);
}
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdcMem);
}
SelectObject(hdc, hOldBrush);
DeleteObject(backgroundBrush);
SetTextColor(hdc, oldColor);
return 0;
}
Does anyone know a solution for this?
Please see below a complete example created from scratch that has exactly the same behavior:
#include "stdafx.h"
#include "TestList.h"
#include <strsafe.h>
#include <commctrl.h>
#define MAX_LOADSTRING 100
#define ID_LIST_BOX 200
#define TIMER_ID_SMOOTH_SCROLL 100
class ListData {
int scrollToDelta;
int currentScrollPos;
int numPixelsToChangeScrollPos;
int numPixelsChanged;
public:
HWND listWindow;
WNDPROC defaultListProcedure;
void startSmoothScrolling(HWND hwnd, int delta) {
if (delta < 0) {
scrollToDelta = 100;
} else {
scrollToDelta = -100;
}
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_POS;
if (GetScrollInfo(listWindow, SB_VERT, &si)) {
double count = SendMessage(listWindow, LVM_GETITEMCOUNT, 0, 0);
double totalHeight = count * 30;
currentScrollPos = (int)((totalHeight * (double)si.nPos) / (double)si.nMax);
numPixelsToChangeScrollPos = totalHeight / si.nMax;
numPixelsChanged = 0;
} else {
currentScrollPos = 0;
numPixelsChanged = 0;
numPixelsToChangeScrollPos = 30;
}
}
void smoothScroll(HWND listHandle) {
SCROLLINFO si;
si.cbSize = sizeof(si);
si.fMask = SIF_RANGE | SIF_POS;
DWORD linesDelta;
SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &linesDelta, 0);
if (scrollToDelta < 0) {
if (GetScrollInfo(listHandle, SB_VERT, &si)) {
if (si.nPos == 0) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
return;
}
}
scrollToDelta += 5;
int step = -5;
if (scrollToDelta > -80) {
step = -4;
} else if (scrollToDelta > -60) {
step = -3;
} else if (scrollToDelta > -40) {
step = -3;
} else if (scrollToDelta > -20) {
step = -2;
}
numPixelsChanged += abs(step);
if (numPixelsChanged >= numPixelsToChangeScrollPos) {
int posDelta = numPixelsChanged / numPixelsToChangeScrollPos;
numPixelsChanged -= posDelta * numPixelsToChangeScrollPos;
si.nPos = si.nPos + posDelta;
si.fMask = SIF_POS;
SetScrollInfo(listHandle, SB_VERT, &si, TRUE);
}
ScrollWindowEx(
listHandle,
0,
step * linesDelta,
NULL,
NULL,
0, 0,
SW_INVALIDATE);
if (scrollToDelta >= 0) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
}
} else {
if (GetScrollInfo(listHandle, SB_VERT, &si)) {
int pos = GetScrollPos(listHandle, SB_VERT);
if (pos == si.nMax) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
return;
}
}
scrollToDelta -= 5;
int step = 5;
if (scrollToDelta > -80) {
step = 4;
} else if (scrollToDelta > -60) {
step = 3;
} else if (scrollToDelta > -40) {
step = 3;
} else if (scrollToDelta > -20) {
step = 2;
}
numPixelsChanged += abs(step);
if (numPixelsChanged >= numPixelsToChangeScrollPos) {
int posDelta = numPixelsChanged / numPixelsToChangeScrollPos;
numPixelsChanged -= posDelta * numPixelsToChangeScrollPos;
si.nPos = si.nPos - posDelta;
si.fMask = SIF_POS;
SetScrollInfo(listHandle, SB_VERT, &si, TRUE);
}
ScrollWindowEx(
listHandle,
0,
step * linesDelta,
NULL,
NULL,
0, 0, 0
);
if (scrollToDelta <= 0) {
KillTimer(listHandle, TIMER_ID_SMOOTH_SCROLL);
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//RedrawWindow(listHandle, NULL, NULL,
// RDW_UPDATENOW | RDW_INTERNALPAINT | RDW_INVALIDATE | RDW_NOERASE | RDW_ALLCHILDREN | RDW_ERASENOW);
//InvalidateRect(listHandle, NULL, FALSE);
//SendMessage(listHandle, WM_PAINT, 0, 0);
UpdateWindow(listHandle);
//ListView_RedrawItems(listHandle, 0, 300);
////////////////////////////////////////////////////////////////////////////////////////////////////
}
};
struct Item {
WCHAR *name;
};
// Global Variables:
HINSTANCE hInst; // current instance
WCHAR szTitle[MAX_LOADSTRING]; // The title bar text
WCHAR szWindowClass[MAX_LOADSTRING]; // the main window class name
// Forward declarations of functions included in this code module:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
StringCchCopy(szTitle, MAX_LOADSTRING, L"Test");
StringCchCopy(szWindowClass, MAX_LOADSTRING, L"TestClassList");
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TESTLIST));
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// FUNCTION: MyRegisterClass()
//
// PURPOSE: Registers the window class.
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW 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_TESTLIST));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TESTLIST);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
LRESULT drawItem(HWND hwnd, DRAWITEMSTRUCT* drawStruct) {
Item *itemData = (Item *)drawStruct->itemData;
HDC hdc = drawStruct->hDC;
COLORREF backgroundColor;
//pcd->clrTextBk;
COLORREF oldColor;
if (drawStruct->itemState & ODS_SELECTED || ListView_GetHotItem(hwnd) == drawStruct->itemID) {
backgroundColor = GetSysColor(COLOR_HIGHLIGHT);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
} else {
backgroundColor = RGB(255, 255, 255);
oldColor = SetTextColor(hdc, GetSysColor(COLOR_CAPTIONTEXT));
}
HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor);
HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, backgroundBrush);
FillRect(hdc, &drawStruct->rcItem, backgroundBrush);
drawStruct->rcItem.left += 5;
drawStruct->rcItem.right -= 5;
drawStruct->rcItem.left += 30;
DrawText(hdc, itemData->name, -1, &drawStruct->rcItem,
DT_NOPREFIX | DT_SINGLELINE | DT_END_ELLIPSIS);
drawStruct->rcItem.left -= 30;
SelectObject(hdc, hOldBrush);
DeleteObject(backgroundBrush);
SetTextColor(hdc, oldColor);
return 0;
}
LRESULT CALLBACK ListViewWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
////////////////////////////////////////////////////////////////////////////////////////////////////
case WM_TIMER: {
if (wParam == TIMER_ID_SMOOTH_SCROLL) {
ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
listData->smoothScroll(hwnd);
}
break;
}
case WM_MOUSEWHEEL: {
int delta = HIWORD(wParam);
ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
listData->startSmoothScrolling(hwnd, delta);
SetTimer(hwnd, TIMER_ID_SMOOTH_SCROLL, 200, NULL);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
default:
ListData *listData = (ListData*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
return CallWindowProc(listData->defaultListProcedure, hwnd, uMsg, wParam, lParam);
}
return 0;
}
//
// FUNCTION: InitInstance(HINSTANCE, int)
//
// PURPOSE: Saves instance handle and creates main window
//
// COMMENTS:
//
// In this function, we save the instance handle in a global variable and
// create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // Store instance handle in our global variable
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
100, 100, 400, 400, nullptr, nullptr, hInstance, nullptr);
if (!hWnd) {
return FALSE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
HWND listWindow = CreateWindowEx(
0,
WC_LISTVIEW,
L"",
WS_CHILD | WS_VISIBLE | LVS_REPORT | LVS_NOCOLUMNHEADER |
WS_TABSTOP | WS_BORDER | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_OWNERDRAWFIXED,
1, //x
1, //y
400 - 20, //width
400 - 20, //height
hWnd,
(HMENU)ID_LIST_BOX,
hInstance,
NULL);
ListData *listData = new ListData();
listData->listWindow = listWindow;
SetWindowLongPtr(listWindow, GWLP_USERDATA, (LPARAM)listData);
listData->defaultListProcedure = (WNDPROC)SetWindowLongPtr(listWindow, GWLP_WNDPROC, (LONG_PTR)ListViewWindowProc);
ListView_SetExtendedListViewStyle(listWindow, LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_AUTOSIZECOLUMNS);
SendMessage(listWindow, LVM_SETTEXTBKCOLOR, 0, 0xFFFFFF);
LVCOLUMN col;
col.mask = LVCF_TEXT | LVCF_WIDTH;
col.pszText = L"";
col.cx = 390;
SendMessage(listWindow, LVM_INSERTCOLUMN, 0, (LPARAM)&col);
LVITEM item;
item.mask = LVIF_PARAM | LVIF_TEXT;
item.iSubItem = 0;
for (int i = 0; i < 300; i++) {
item.iItem = i;
Item *itemData = (Item*)malloc(sizeof(Item));
WCHAR *name = (WCHAR*)malloc(sizeof(WCHAR) * 30);;
wsprintf(name, L"Item Name %d", i);
itemData->name = name;
item.pszText = name;
item.lParam = (LPARAM)itemData;
SendMessage(listWindow, LVM_INSERTITEM, 0, (LPARAM)&item);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// PURPOSE: Processes messages for the main window.
//
// WM_COMMAND - process the application menu
// WM_PAINT - Paint the main window
// WM_DESTROY - post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_DRAWITEM: {
////////////////////////////////////////////////////////////////////////////////////////////////////
if (wParam == ID_LIST_BOX) {
DRAWITEMSTRUCT *drawStruct = (DRAWITEMSTRUCT*)lParam;
drawItem(drawStruct->hwndItem, drawStruct);
return TRUE;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
break;
}
case WM_PAINT: {
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: Add any drawing code that uses hdc here...
EndPaint(hWnd, &ps);
}
break;
case WM_MEASUREITEM: {
if (wParam == ID_LIST_BOX) {
MEASUREITEMSTRUCT *measureStruct = (MEASUREITEMSTRUCT*)lParam;
measureStruct->itemHeight = 30;
measureStruct->itemWidth = 390;
return TRUE;
}
break;
}
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
Here's an old trick: before invoking ScrollWindowEx() invalidate the entire listHandle area without erasing the background.
InvalidateRect(listHandle, NULL, FALSE);
ScrollWindowEx(...
Have a nice day!
From MSDN for ScrollWindowEx:
If the SW_INVALIDATE and SW_ERASE flags are not specified, ScrollWindowEx does not invalidate the area that is scrolled from. If either of these flags is set, ScrollWindowEx invalidates this area. The area is not updated until the application calls the UpdateWindow function, calls the RedrawWindow function (specifying the RDW_UPDATENOW or RDW_ERASENOW flag), or retrieves the WM_PAINT message from the application queue.
So let's look at what you tried:
UpdateWindow() - screenshoot attached
Nothing got invalidated, so the update region is empty, so UpdateWindow does nothing.
RedrawWindow with all possible options - window is painted only once
If called correctly, this should invalidate the client. You can cause the WM_ERASEBKGND to happen immediately, but the WM_PAINT message will come only when there's nothing else in the queue. I suspect this didn't work because the WM_TIMER takes priority over the WM_PAINT. (Both are special messages in that they are not actually posted but synthesized when you call GetMessage and nothing else is pending.)
InvalidateRect + UpdateWindow = same as 2
I would expect this to work, but it seems to make more sense to pass the flags to ScrollWindowEx to get the invalidation. I think what's happening is that the control is not designed to draw the items an non-integral positions. So you're getting invalidation, but the window is attempting to draw the items at different offsets than you expect. I don't see an straightforward way to solve this.
InvalidateRect + SendMessage(hwnd, WM_PAINT, 0, 0) - same as 2
That's not a valid WM_PAINT message. Don't do that.
The problem is that ListView is created using the LVS_REPORT flag. This means that list cannot scroll smoothly but only in lines. I.e. if line height is, for instance, 25, scrolling 20 pixels will make the list scroll 25 pixels.
One more issue is that ScrollWindowEx doesn't really scroll the list (or at least it wasn't used correctly). In order to scroll the list, the ListView_Scroll macro should be used (which again scrolls by lines not by pixels).
Related
I use extended frame to render a custom caption and window border.
hr = DwmExtendFrameIntoClientArea(hWnd, &margins);
I also use layered window to render background transparent. My COLORKEY is RGB(0,0,0)
SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 255, LWA_COLORKEY);
The reason I use layered window is, I want to make window's bottom border's corners rounded.
The issue is I want to do something beatiful and I tried to render a Anti Aliased window border in client area with GDI. However, It work as Anti Aliased with painted background (Solid) but not with TRANSPARENT background.
IMAGE: https://ibb.co/fD9GsFg
What should I do to fix this ?
If you try it please use VS debugger as I did not put functional window buttons.
#include <windows.h>
#include <tchar.h>
#include <windowsx.h>
#include <objidl.h>
#include <gdiplus.h>
#include <dwmapi.h>
#pragma comment(lib,"Dwmapi")
using namespace Gdiplus;
#pragma comment (lib,"Gdiplus.lib")
HINSTANCE hInst;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int nWidth = 600, nHeight = 400;
#define RECTWIDTH(rc) (rc.right - rc.left)
#define RECTHEIGHT(rc) (rc.bottom - rc.top)
//CHANGE THEM TO 0 when thats maximized!
const int TOPEXTENDWIDTH = 48;
const int LEFTEXTENDWIDTH = 8;
const int RIGHTEXTENDWIDTH = 8;
const int BOTTOMEXTENDWIDTH = 8;
HBRUSH RED_BRUSH = CreateSolidBrush(RGB(237, 28, 36));
HBRUSH DARKBLUE_BRUSH = CreateSolidBrush(RGB(26, 31, 96));
HBRUSH PURPLE_BRUSH = CreateSolidBrush(RGB(163, 73, 164));
HBRUSH LIGHTPURPLE_BRUSH_1 = CreateSolidBrush(RGB(189, 106, 189));
HBRUSH LIGHTPURPLE_BRUSH_2 = CreateSolidBrush(RGB(255, 174, 201));
HBRUSH DARKEST_BRUSH = CreateSolidBrush(RGB(0, 0, 0));
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam);
int WINAPI wWinMain( _In_opt_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_opt_ LPTSTR lpCmdLine, _In_opt_ int nCmdShow)
{
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
// Initialize GDI+.
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
hInst = hInstance;
WNDCLASSEX wcex =
{
sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInst, LoadIcon(NULL, IDI_APPLICATION),
LoadCursor(NULL, IDC_ARROW), (HBRUSH)GetStockObject(BLACK_BRUSH), NULL, TEXT("WindowClass"), NULL,
};
if (!RegisterClassEx(&wcex))
return MessageBox(NULL, L"Cannot register class !", L"Error", MB_ICONERROR | MB_OK);
int nX = (GetSystemMetrics(SM_CXSCREEN) - nWidth) / 2, nY = (GetSystemMetrics(SM_CYSCREEN) - nHeight) / 2;
HWND hWnd = CreateWindowEx(0, wcex.lpszClassName, TEXT("Test"), WS_OVERLAPPEDWINDOW, nX, nY, nWidth, nHeight, NULL, NULL, hInst, NULL);
if (!hWnd) return MessageBox(NULL, L"Cannot create window !", L"Error", MB_ICONERROR | MB_OK);
//NO SHADOW
SystemParametersInfoA(SPI_SETDROPSHADOW,0,(const PVOID) false,SPIF_SENDWININICHANGE);
ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}
void FillRoundRectangle(Gdiplus::Graphics* g, Brush* p, Gdiplus::Rect& rect, UINT8 radius[4])
{
if (g == NULL) return;
GraphicsPath path;
//TOP RIGHT
path.AddLine(rect.X + radius[0], rect.Y, rect.X + rect.Width - (radius[0] * 2), rect.Y);
path.AddArc(rect.X + rect.Width - (radius[0] * 2), rect.Y, radius[0] * 2, radius[0] * 2, 270, 90);
//BOTTOM RIGHT
path.AddLine(rect.X + rect.Width, rect.Y + radius[1], rect.X + rect.Width, rect.Y + rect.Height - (radius[1] * 2));
path.AddArc(rect.X + rect.Width - (radius[1] * 2), rect.Y + rect.Height - (radius[1] * 2), radius[1] * 2, radius[1] * 2, 0, 90);
//BOTTOM LEFT
path.AddLine(rect.X + rect.Width - (radius[2] * 2), rect.Y + rect.Height, rect.X + radius[2], rect.Y + rect.Height);
path.AddArc(rect.X, rect.Y + rect.Height - (radius[2] * 2), radius[2] * 2, radius[2] * 2, 90, 90);
//TOP LEFT
path.AddLine(rect.X, rect.Y + rect.Height - (radius[3] * 2), rect.X, rect.Y + radius[3]);
path.AddArc(rect.X, rect.Y, radius[3] * 2, radius[3] * 2, 180, 90);
path.CloseFigure();
g->FillPath(p, &path);
}
VOID OnPaint(HDC hdc,int width, int height)
{
Graphics graphics(hdc);
graphics.SetSmoothingMode(SmoothingMode::SmoothingModeHighQuality);
graphics.SetCompositingQuality(CompositingQuality::CompositingQualityInvalid);
graphics.SetPixelOffsetMode(PixelOffsetMode::PixelOffsetModeHighQuality);
SolidBrush mySolidBrush(Color(255, 255, 0, 0)); ;
Gdiplus::Rect rect1;
rect1.X = 0;
rect1.Y = TOPEXTENDWIDTH;
rect1.Width = width;
rect1.Height = height- TOPEXTENDWIDTH-111;
UINT8 rad[4]{ 0,12,12,0 };
FillRoundRectangle(&graphics, &mySolidBrush, rect1, rad);
SolidBrush DarkSolidBrush(Color(255, 0, 1, 0)); ;
Gdiplus::Rect rectX = {0,455,55,55};
graphics.FillEllipse(&DarkSolidBrush,rectX);
}
LRESULT CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pfCallDWP)
{
LRESULT lRet = 0;
HRESULT hr = S_OK;
bool fCallDWP = true; // Pass on to DefWindowProc?
static HICON hIcon = NULL;
fCallDWP = !DwmDefWindowProc(hWnd, message, wParam, lParam, &lRet);
if (message == WM_CREATE)
{
RECT rcClient;
GetWindowRect(hWnd, &rcClient);
// Inform the application of the frame change.
SetWindowPos(hWnd, NULL, rcClient.left, rcClient.top, RECTWIDTH(rcClient), RECTHEIGHT(rcClient), SWP_FRAMECHANGED);
HMODULE hDLL = LoadLibrary(L"Setupapi.dll");
if (hDLL)
{
hIcon = (HICON)LoadImage(hDLL, MAKEINTRESOURCE(2), IMAGE_ICON, 32, 32, LR_DEFAULTCOLOR | LR_SHARED);
}
SetWindowLong(hWnd, GWL_EXSTYLE,
GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);
/*Use pointer to function*/
SetLayeredWindowAttributes(hWnd, 0,
(255 * 70) / 100, LWA_ALPHA);
fCallDWP = true;
lRet = 0;
}
// Handle window activation.
if (message == WM_ACTIVATE)
{
// Extend the frame into the client area.
MARGINS margins;
margins.cxLeftWidth = 0;
margins.cxRightWidth = 0;
margins.cyBottomHeight = 0;
margins.cyTopHeight = 0;
hr = DwmExtendFrameIntoClientArea(hWnd, &margins);
if (!SUCCEEDED(hr))
{
// Handle error.
}
fCallDWP = true;
lRet = 0;
}
if (message == WM_PAINT)
{
PAINTSTRUCT ps;
BITMAP bm;
RECT rect, rectCaptionButtonBounds, rectText,myRect,ContentRect,ClientRect,CaptionBorderBottom, rect_EXIT_BTN, rect_RESTORE_BTN, rect_MINIMIZE_BTN;
HFONT windowTitleText;
GetClientRect(hWnd,&ClientRect);
BeginPaint(hWnd, &ps);
SetGraphicsMode(ps.hdc, GM_ADVANCED);
SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 255, LWA_COLORKEY);
SetBkMode(ps.hdc, TRANSPARENT);
if (SUCCEEDED(DwmGetWindowAttribute(hWnd, DWMWA_CAPTION_BUTTON_BOUNDS, &rectCaptionButtonBounds, sizeof(rectCaptionButtonBounds))))
{
GetClientRect(hWnd, &rect);
//HRGN hrgn_cptBtmBrdrRND = CreateRoundRectRgn(0, 0, RECTWIDTH(ClientRect), RECTHEIGHT(ClientRect), 16, 16);
//FillRgn(ps.hdc, hrgn_cptBtmBrdrRND, DARKBLUE_BRUSH);
HRGN hrgn = CreateRectRgn(0, 0, RECTWIDTH(ClientRect), TOPEXTENDWIDTH);
FillRgn(ps.hdc, hrgn, PURPLE_BRUSH);
DrawIconEx(ps.hdc, rect.right - (rectCaptionButtonBounds.right - rectCaptionButtonBounds.left) - 32, 0, hIcon, 32, 32, 0, NULL, DI_NORMAL);
SetRect(&myRect, LEFTEXTENDWIDTH, 10, RECTWIDTH(rect)-200, TOPEXTENDWIDTH);
SetTextColor(ps.hdc, RGB(1, 0, 0));
DrawText(ps.hdc,L"test",-1,&myRect, DT_SINGLELINE | DT_RIGHT);
SetTextColor(ps.hdc, RGB(255, 255, 255));
WCHAR wsText[255] = L"ARMNET";
SetRect(&rectText, LEFTEXTENDWIDTH, 0, RECTWIDTH(rect), TOPEXTENDWIDTH);
windowTitleText =
CreateFontA
(
32,
0,
GM_ADVANCED,
0,
FW_DONTCARE,
false,
false,
false,
DEFAULT_CHARSET,
OUT_OUTLINE_PRECIS,
CLIP_DEFAULT_PRECIS,
CLEARTYPE_QUALITY, //BETTER BLENDING THAN ANTIALIASED
VARIABLE_PITCH,
"RETRO COMPUTER");
SelectObject(ps.hdc, windowTitleText);
DrawText(ps.hdc, wsText, -1, &rectText, DT_SINGLELINE | DT_VCENTER);
DeleteObject(windowTitleText);
DeleteObject(hrgn);
}
//CONTENT AREA
//SetRect(&ContentRect, 0, TOPEXTENDWIDTH, RECTWIDTH(ClientRect) - 0, RECTHEIGHT(ClientRect) - 0);
//FillRect(ps.hdc, &ContentRect, DARKBLUE_BRUSH);
HRGN hrgn_cptBtmBrdr = CreateRectRgn(0, TOPEXTENDWIDTH-1, RECTWIDTH(ClientRect), TOPEXTENDWIDTH);
FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(132, 68, 133)));
hrgn_cptBtmBrdr = CreateRectRgn(0, TOPEXTENDWIDTH - 2, RECTWIDTH(ClientRect), TOPEXTENDWIDTH-1);
FillRgn(ps.hdc, hrgn_cptBtmBrdr,CreateSolidBrush(RGB(185, 91, 186)));
//BUTTONS
hrgn_cptBtmBrdr = CreateRectRgn(RECTWIDTH(ClientRect)-32, 0, RECTWIDTH(ClientRect), 32);
FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(11, 11, 111)));
hrgn_cptBtmBrdr = CreateRectRgn(RECTWIDTH(ClientRect) - 64, 0, RECTWIDTH(ClientRect)-32, 32);
FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(111, 11, 111)));
hrgn_cptBtmBrdr = CreateRectRgn(RECTWIDTH(ClientRect) - 96, 0, RECTWIDTH(ClientRect)-64, 32);
FillRgn(ps.hdc, hrgn_cptBtmBrdr, CreateSolidBrush(RGB(11, 111, 11)));
OnPaint(ps.hdc, RECTWIDTH(ClientRect), RECTHEIGHT(ClientRect));
DeleteObject(hrgn_cptBtmBrdr);
EndPaint(hWnd, &ps);
fCallDWP = true;
lRet = 0;
}
// Handle the non-client size message.
if ((message == WM_NCCALCSIZE) && (wParam == TRUE))
{
// Calculate new NCCALCSIZE_PARAMS based on custom NCA inset.
NCCALCSIZE_PARAMS *pncsp = reinterpret_cast<NCCALCSIZE_PARAMS*>(lParam);
pncsp->rgrc[0].left = pncsp->rgrc[0].left + 1;
pncsp->rgrc[0].top = pncsp->rgrc[0].top + 0;
pncsp->rgrc[0].right = pncsp->rgrc[0].right - 1;
pncsp->rgrc[0].bottom = pncsp->rgrc[0].bottom - 1;
lRet = 0;
// No need to pass the message on to the DefWindowProc.
fCallDWP = false;
}
// Handle hit testing in the NCA if not handled by DwmDefWindowProc.
if ((message == WM_NCHITTEST) && (lRet == 0))
{
lRet = HitTestNCA(hWnd, wParam, lParam);
if (lRet != HTNOWHERE)
{
fCallDWP = false;
}
}
if (message == WM_SIZE)
{
if (unsigned int(wParam) == SIZE_MAXIMIZED) {
}
else
{
}
}
if (message == WM_GETMINMAXINFO)
{
LPMINMAXINFO lpMMI = (LPMINMAXINFO)lParam;
lpMMI->ptMinTrackSize.x = 800;
lpMMI->ptMinTrackSize.y = 600;
}
if (message == WM_DESTROY)
PostQuitMessage(0);
*pfCallDWP = fCallDWP;
return lRet;
}
// Hit test the frame for resizing and moving.
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam)
{
// Get the point coordinates for the hit test.
POINT ptMouse = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
// Get the window rectangle.
RECT rcWindow;
GetWindowRect(hWnd, &rcWindow);
// Get the frame rectangle, adjusted for the style without a caption.
RECT rcFrame = { 0 };
AdjustWindowRectEx(&rcFrame, WS_OVERLAPPEDWINDOW & ~WS_CAPTION, FALSE, NULL);
// Determine if the hit test is for resizing. Default middle (1,1).
USHORT uRow = 1;
USHORT uCol = 1;
bool fOnResizeBorder = false;
// Determine if the point is at the top or bottom of the window.
if ((ptMouse.y >= rcWindow.top && ptMouse.y < rcWindow.top + TOPEXTENDWIDTH) )
{
if((ptMouse.x < rcWindow.right - 100) || (ptMouse.y > rcWindow.top + 32)){
fOnResizeBorder = (ptMouse.y < (rcWindow.top - rcFrame.top));
uRow = 0;
}
}
else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - BOTTOMEXTENDWIDTH)
{
uRow = 2;
}
// Determine if the point is at the left or right of the window.
if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + LEFTEXTENDWIDTH)
{
uCol = 0; // left side
}
else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - RIGHTEXTENDWIDTH)
{
uCol = 2; // right side
}
// Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
LRESULT hitTests[3][3] =
{
{ fOnResizeBorder ? HTTOPLEFT : HTLEFT, fOnResizeBorder ? HTTOP : HTCAPTION, fOnResizeBorder ? HTTOPRIGHT : HTRIGHT },
{ HTLEFT, HTNOWHERE, HTRIGHT },
{ HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT },
};
return hitTests[uRow][uCol];
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
bool fCallDWP = true;
BOOL fDwmEnabled = FALSE;
LRESULT lRet = 0;
HRESULT hr = S_OK;
// Winproc worker for custom frame issues.
hr = DwmIsCompositionEnabled(&fDwmEnabled);
if (SUCCEEDED(hr))
{
lRet = CustomCaptionProc(hWnd, message, wParam, lParam, &fCallDWP);
}
// Winproc worker for the rest of the application.
if (fCallDWP)
{
// lRet = AppWinProc(hWnd, message, wParam, lParam);
lRet = DefWindowProc(hWnd, message, wParam, lParam);
}
return lRet;
}
Well, after searching for solutions, in the end I think I found an answer.
SOLUTION:
A solution for Anti aliasing issue could be capturing the background of entire window bound by exluding it from capture. At this time you will not need layered window since you are painting the background with a bitmap. HDC will be able to blend it with background. Hopefully Windows 10 2004 version you have an option called:
WDA_EXCLUDEFROMCAPTURE
USAGE:
SetWindowDisplayAffinity(hWnd, WDA_EXCLUDEFROMCAPTURE); //At creation time
SOURCE: https://blogs.windows.com/windowsdeveloper/2019/09/16/new-ways-to-do-screen-capture/
After that you can paint background with bitmap and then paint everything over it. However, this produced low performance and I did not benefit. Still, that works and produces ANTI-ALIASED looking when drawn. For the performance issue Direct2D can be used for drawing from returned bitmap.
EXAMPLE:
int DsktpBkSS(HWND hWnd) {
HDC hdcScreen;
HDC hdcWindow;
HDC hdcMemDC = NULL;
HBITMAP hbmScreen = NULL;
BITMAP bmpScreen;
RECT windowPos;
SetWindowDisplayAffinity(hWnd, WDA_EXCLUDEFROMCAPTURE);
GetWindowRect(hWnd, &windowPos);
// Retrieve the handle to a display device context for the client
// area of the window.
hdcScreen = GetDC(NULL);
hdcWindow = GetDC(hWnd);
// Create a compatible DC which is used in a BitBlt from the window DC
hdcMemDC = CreateCompatibleDC(hdcWindow);
if (!hdcMemDC)
{
MessageBox(hWnd, L"CreateCompatibleDC has failed", L"Failed", MB_OK);
}
// Get the client area for size calculation
RECT rcClient;
GetClientRect(hWnd, &rcClient);
//This is the best stretch mode
SetStretchBltMode(hdcWindow, HALFTONE);
//The source DC is the entire screen and the destination DC is the current window (HWND)
if (!StretchBlt(hdcWindow,
0, 0,
GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN),
hdcScreen,
windowPos.left+1,windowPos.top,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
SRCCOPY))
{
MessageBox(hWnd, L"StretchBlt has failed", L"Failed", MB_OK);
}
// Create a compatible bitmap from the Window DC
hbmScreen = CreateCompatibleBitmap(hdcWindow, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top);
if (!hbmScreen)
{
MessageBox(hWnd, L"CreateCompatibleBitmap Failed", L"Failed", MB_OK);
}
// Select the compatible bitmap into the compatible memory DC.
if(hdcMemDC && hbmScreen){
SelectObject(hdcMemDC, hbmScreen);
}
// Bit block transfer into our compatible memory DC.
if(hdcMemDC)
if (!BitBlt(hdcMemDC,
0, 0,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
hdcWindow,
0, 0,
SRCCOPY))
{
MessageBox(hWnd, L"BitBlt has failed", L"Failed", MB_OK);
}
// Get the BITMAP from the HBITMAP
if(hbmScreen)
GetObjectW(hbmScreen, sizeof(BITMAP), &bmpScreen);
if (hbmScreen)DeleteObject(hbmScreen);
if (hdcMemDC)DeleteObject(hdcMemDC);
ReleaseDC(NULL, hdcScreen);
ReleaseDC(hWnd, hdcWindow);
return 0;
}
Later,
(...){
if(message==WM_PAINT)
{
PAINTSTRUCT ps;
BeginPaint(hWnd, &ps);
Graphics graphics(ps.hdc);
/*SET SMOOTHING (AA)*/
graphics.SetSmoothingMode(SmoothingMode::SmoothingModeHighQuality);
if(DsktpBkSS(hWnd))
{
/*DRAW ROUNDED RECTANGLE*/
}
}
//...
};
One more important thing for window drawn with "Desktop Window Manager" when resizing from left too fast and continuously that will produce "flickering" after a time. On Microsoft docs that is recommended that to use StretchBlt(...) for drawing since GDI+ cause this.
flicker mentioned for DWM: https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-stretchblt
"Use BitBlt or StretchBlt function instead of Windows GDI+ to present
your drawing for rendering. GDI+ renders one scan line at a time with
software rendering. This can cause flickering in your applications."
So basically I have this program that makes a transparent overlay for a game so I can make a kill counter. However, when I click on the game with the overlay on, nothing happens. I managed to make it so when you click on it then it sends a message to the game telling it to shoot, however, when I tried the same for moving my characters head it was just laggy and snappy. When I would move my head quickly, my cursor would also fly out of the game window. How can I fix this so when I play the game it would be like its not even there.
I have tried sending the message and setting the window active AND using setcapture. However, none of these worked. I have tried looking at other places but they didn't work either.
/* This is my while(true) loop: */
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg); // translates virtual-key messages into character messages
DispatchMessage(&msg); // dispatches a message to WindowProc
}
if (gameProcId == 0)
{
gameProcId = GetProcId(L"ac_client.exe");
}
if (gameWnd == NULL)
{
gameWnd = FindWindow(NULL, L"AssaultCube");
}
if ((gameProc == NULL) && (gameProcId != 0))
{
gameProc = OpenProcess(PROCESS_VM_READ, false, gameProcId); // opens an existing local process and returns a handle to it
}
if (gameProc != NULL)
{
if ((!init_ok) || ((loops % 20) == 0))
{
RECT client_rect;
#pragma warning (suppress: 6387)
GetClientRect(gameWnd, &client_rect); // gets a windows coordinates, upper-left corner is (0,0)
w_res.X = client_rect.right;
w_res.Y = client_rect.bottom;
RECT bounding_rect;
#pragma warning (suppress: 6387)
GetWindowRect(gameWnd, &bounding_rect); // gets dimensions of a window
if (!init_ok)
{
if ((w_pos.X != bounding_rect.left) || (w_pos.Y != bounding_rect.top))
{
MoveWindow(hWnd, bounding_rect.left, bounding_rect.top,
client_rect.right, client_rect.bottom, false);
w_pos.X = bounding_rect.left;
w_pos.Y = bounding_rect.top;
}
//SetCursorPos(w_pos.X * 4, w_pos.Y * 4);
//ClipCursor(&gameRect);
}
else
{
if ((bounding_rect.left == 0) && (bounding_rect.top == 0))
{
MoveWindow(hWnd, bounding_rect.left, bounding_rect.top, // changes both the position and dimension of a window
client_rect.right, client_rect.bottom, false);
}
MoveWindow(hWnd, bounding_rect.left, bounding_rect.top, client_rect.right,
client_rect.bottom, false);
}
init_ok = true;
}
}
if (loops % 10 == 0)
{
if (FindWindow(NULL, L"AssaultCube") == NULL)
{
SendMessage(hWnd, WM_CLOSE, NULL, NULL); // calls WindowProc() and sends the message to a window
}
}
loops++;
if (loops > 100) loops = 0;
Render();
}
}
/* This is my WindowProc() function: */
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage(0);
return 0;
}break;
case WM_LBUTTONDOWN:
{
PostMessage(gameWnd, message, wParam, lParam);
return 0;
}
case WM_MOUSEMOVE:
{
SendMessage(gameWnd, message, wParam, lParam);
return 0;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
}
You didn't post enough code to attempt to solve this problem, we don't see your call to CreateWindowEx().
Here is a working solution for a win32 overlay:
#include <iostream>
#include <windows.h>
using namespace std;
//global forward declerations
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void cleanUpObjects(HPEN pen);
int CALLBACK WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
RECT overlayWindowRect;
RECT gameWindowRect;
HWND gameWindowHandle;
gameWindowHandle = FindWindowA(0, "AssaultCube");
GetWindowRect(gameWindowHandle, &gameWindowRect);
WNDCLASSEX w;
w.cbSize = sizeof(WNDCLASSEX);
w.style = CS_HREDRAW | CS_VREDRAW;
w.lpfnWndProc = WndProc;
w.cbClsExtra = 0;
w.cbWndExtra = 0;
w.hInstance = hInstance;
w.hIcon = NULL;
w.hCursor = NULL;
w.hbrBackground = (HBRUSH)0;
w.lpszMenuName = NULL;
w.lpszClassName = "ClassName";
w.hIconSm = NULL;
if (!RegisterClassEx(&w))
{
MessageBox(NULL, "Could not Register Class", "Window Title", NULL);
return -1;
}
HWND hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED | WS_EX_TRANSPARENT, "ClassName", "Window",
WS_CAPTION,
gameWindowRect.left, //x
gameWindowRect.top, // y
gameWindowRect.right - gameWindowRect.left, // width
gameWindowRect.bottom - gameWindowRect.top, // height
NULL, NULL,
hInstance, NULL);
if (!hWnd)
{
MessageBox(NULL, "Call to create window failed", "Win32 Guided tour", NULL);
return -1;
}
else
{
GetWindowRect(hWnd, &overlayWindowRect);
}
// Remove Borders around window
SetWindowLong(hWnd, GWL_STYLE, GetWindowLong(hWnd, GWL_STYLE) & ~(WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_BORDER));
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) & ~(WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
SetWindowPos(hWnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOOWNERZORDER);
// Make the Background Transparent
SetLayeredWindowAttributes(hWnd, RGB(255, 255, 255), 255, LWA_COLORKEY); // Replaces color white with transparancey
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
HDC myHDC = GetDC(hWnd);
// Drawing Stuff
HPEN myPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0));
HPEN originalPen;
originalPen = (HPEN)SelectObject(myHDC, myPen);
//main loop waits for messages
MSG msg;
while (true)
{
//peekmessage allows for program to do multiple things at once. faster than getmessage()
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
// if msg is quit, quit
if (msg.message == WM_QUIT)
break;
}
else
{
Rectangle(myHDC, 200, 200, 300, 400);
ZeroMemory(&gameWindowRect, sizeof(gameWindowRect)); //clear out the struct
GetWindowRect(gameWindowHandle, &gameWindowRect); // retrieves the games xy and height width and stores in gamewindowrect
if (gameWindowRect.right != overlayWindowRect.right) // checks if the x coordinates are the same
{
ZeroMemory(&gameWindowRect, sizeof(gameWindowRect)); // clear out struct once again
GetWindowRect(gameWindowHandle, &gameWindowRect); // get the dimensions of the game window again
MoveWindow(hWnd, gameWindowRect.left, gameWindowRect.top, gameWindowRect.right - gameWindowRect.left, gameWindowRect.bottom - gameWindowRect.top, TRUE);
// moves window to specific spot
}
}
Sleep(5);
}
cleanUpObjects(myPen);
cleanUpObjects(originalPen);
return msg.wParam;
}
LRESULT CALLBACK WndProc(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam
)
{
PAINTSTRUCT ps;
HDC hdc;
switch (uMsg)
{
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EndPaint(hWnd, &ps);
break;
case WM_DESTROY:
DestroyWindow(hWnd);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, uMsg, wParam, lParam);
break;
}
return 0;
}
//cleans up objects
void cleanUpObjects(HPEN pen)
{
DeleteObject(pen);
}
I want to change button colors when the user click on it.
The main plan is: every month 10th one worker's button go to red and if the worker done his job then click on the button and it going to be green.
I have no idea what to do.
I already have this code. I just created a window and add a button to it:
switch (msg) {
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_CREATE:
AddButton(hWnd);
break;
default:
return DefWindowProcW(hWnd, msg, wp, lp);
}
}
void AddButton(HWND hWnd)
{
CreateWindowW(L"Button", L"Change colors", WS_VISIBLE | WS_CHILD,
350, 200,
100, 100,
hWnd,
NULL,
NULL,
NULL);
}
So I tried WM_LBUTTONDOWN. I think this is something when the user click on the button the program will do something. I put in the switch(msg) case WM_LBUTTONDOWN:.
But no idea what's next.
Here is a simple demo to show how to detect click on a button, and change the color of button when you click on it.
#pragma comment(linker, "/manifestdependency:\"type='win32' \
name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \
processorArchitecture='*' \
publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#include "stdafx.h"
#include <windows.h>
#include <commctrl.h>
static BOOL flag = false;
ATOM RegisterWndClass(HINSTANCE hInst);
BOOL CreateMainWnd(HINSTANCE hInstance, int nCmdShow);
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
HINSTANCE hInst;
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hInstPrev, LPWSTR lpszCmdLine,
int nCmdShow)
{
INITCOMMONCONTROLSEX icex = { 0 };
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
icex.dwICC = ICC_LISTVIEW_CLASSES | ICC_USEREX_CLASSES | ICC_BAR_CLASSES |
ICC_COOL_CLASSES | ICC_TAB_CLASSES | ICC_WIN95_CLASSES |
ICC_PROGRESS_CLASS | ICC_PAGESCROLLER_CLASS;
InitCommonControlsEx(&icex);
MSG msg;
hInst = hInstance;
if (!RegisterWndClass(hInstance))
return NULL;
if (!CreateMainWnd(hInstance, nCmdShow))
return NULL;
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
};
ATOM RegisterWndClass(HINSTANCE hInstance)
{
WNDCLASS wndClass = { 0 };
wndClass.style = CS_DBLCLKS;
wndClass.lpfnWndProc = MainWndProc;
wndClass.hInstance = hInstance;
wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndClass.hbrBackground = GetSysColorBrush(COLOR_BTNFACE);
wndClass.lpszMenuName = NULL;
wndClass.lpszClassName = L"MainClass";
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
return RegisterClass(&wndClass);
}
BOOL CreateMainWnd(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd = CreateWindow(L"MainClass", L"Buttons sample",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
GetSystemMetrics(SM_CXSCREEN) / 2 - 115,
GetSystemMetrics(SM_CYSCREEN) / 2 - 50,
230, 100, NULL, NULL, hInstance, NULL);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
HBITMAP hBitmap = NULL;
LRESULT CALLBACK MainWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
// Owner draw button
CreateWindow(L"BUTTON", L"", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON |
BS_OWNERDRAW, 10, 10, 60, 30, hWnd,
(HMENU)10001, hInst, NULL);
// Custom draw button
CreateWindow(L"BUTTON", L"", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 80,
10, 60, 30, hWnd, (HMENU)10002, hInst, NULL);
// Bitmap button
HWND hBitmapButton = CreateWindow(L"BUTTON", L"", WS_CHILD | WS_VISIBLE
| BS_PUSHBUTTON | BS_BITMAP,
150, 10, 60, 30, hWnd,
(HMENU)10003, hInst, NULL);
HDC hDC = GetDC(hWnd);
HDC hMemDC = CreateCompatibleDC(hDC);
hBitmap = CreateCompatibleBitmap(hDC, 55, 25);
SelectObject(hMemDC, hBitmap);
SetDCBrushColor(hMemDC, RGB(0, 0, 255));
RECT r = { 0 };
r.left = 0;
r.right = 55;
r.top = 0;
r.bottom = 25;
FillRect(hMemDC, &r, (HBRUSH)GetStockObject(DC_BRUSH));
DeleteDC(hMemDC);
ReleaseDC(hWnd, hDC);
SendMessage(hBitmapButton, BM_SETIMAGE, (WPARAM)IMAGE_BITMAP,
(LPARAM)hBitmap);
return 0;
}
case WM_COMMAND:
switch (LOWORD(wParam))
{
case 10001:
MessageBox(hWnd, L"Owner draw button clicked", L"Message", NULL);
return 0;
case 10002:
MessageBox(hWnd, L"Custom draw button clicked", L"Message", NULL);
return 0;
case 10003:
MessageBox(hWnd, L"Bitmap button clicked", L"Message", NULL);
return 0;
}
break;
// Owner draw button
case WM_DRAWITEM:
if (wParam == 10001)
{
if (flag == false)
{
LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;
SetDCBrushColor(lpDIS->hDC, RGB(255, 0, 0));
SelectObject(lpDIS->hDC, GetStockObject(DC_BRUSH));
RoundRect(lpDIS->hDC, lpDIS->rcItem.left, lpDIS->rcItem.top,
lpDIS->rcItem.right, lpDIS->rcItem.bottom, 5, 5);
}
else
{
LPDRAWITEMSTRUCT lpDIS = (LPDRAWITEMSTRUCT)lParam;
SetDCBrushColor(lpDIS->hDC, RGB(0, 255, 0));
SelectObject(lpDIS->hDC, GetStockObject(DC_BRUSH));
RoundRect(lpDIS->hDC, lpDIS->rcItem.left, lpDIS->rcItem.top,
lpDIS->rcItem.right, lpDIS->rcItem.bottom, 5, 5);
}
flag = !flag;
return TRUE;
}
break;
// Custom draw button
case WM_NOTIFY:
switch (((LPNMHDR)lParam)->code)
{
case NM_CUSTOMDRAW:
if (((LPNMHDR)lParam)->idFrom == 10002)
{
LPNMCUSTOMDRAW lpnmCD = (LPNMCUSTOMDRAW)lParam;
switch (lpnmCD->dwDrawStage)
{
case CDDS_PREPAINT:
SetDCBrushColor(lpnmCD->hdc, RGB(0, 255, 0));
SetDCPenColor(lpnmCD->hdc, RGB(0, 255, 0));
SelectObject(lpnmCD->hdc, GetStockObject(DC_BRUSH));
SelectObject(lpnmCD->hdc, GetStockObject(DC_PEN));
RoundRect(lpnmCD->hdc, lpnmCD->rc.left + 3,
lpnmCD->rc.top + 3,
lpnmCD->rc.right - 3,
lpnmCD->rc.bottom - 3, 5, 5);
return TRUE;
}
}
break;
}
break;
case WM_DESTROY:
if (hBitmap != NULL)
DeleteObject((HBITMAP)hBitmap);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
Try to make some modifications to "if" conditions to meet your needs.
Today I jumped to Visual Studio (C++) 2013, I was using Codeblocks a lot of time but I realized that codeblocks starts to fail at compiling some codes like this:
//#define _WIN32_WINNT 0x0500
#include "hMain.h"
#include <windows.h>
#include <Uxtheme.h>
int Width = 800;
int Height = 600;
const MARGINS* Margin = { 0, 0, Width , Height };
char lWindowName[256] = "TEEEST";
HWND hWnd;
char tWindowName[256] = "TEEEST";
HWND tWnd;
RECT tSize;
MSG Message;
LRESULT CALLBACK WinProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch (Message)
{
case WM_PAINT:
Render ();
break;
case WM_CREATE:
DwmExtendFrameIntoClientArea(hWnd, Margin);
break;
case WM_COMMAND:
if (wParam == VK_ESCAPE) PostQuitMessage(0);
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
default:
return DefWindowProc(hWnd, Message, wParam, lParam);
break;
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hSecInstance, LPSTR nCmdLine, INT nCmdShow)
{
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)SetWindowToTarget, 0, 0, 0);
WNDCLASSEX wClass;
wClass.cbClsExtra = NULL;
wClass.cbSize = sizeof(WNDCLASSEX);
wClass.cbWndExtra = NULL;
wClass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 0, 0));
wClass.hCursor = LoadCursor(0, IDC_ARROW);
wClass.hIcon = LoadIcon(0, IDI_APPLICATION);
wClass.hIconSm = LoadIcon(0, IDI_APPLICATION);
wClass.hInstance = hInstance;
wClass.lpfnWndProc = WinProc;
wClass.lpszClassName = (LPCWSTR)lWindowName;
wClass.lpszMenuName = (LPCWSTR)lWindowName;
wClass.style = CS_VREDRAW | CS_HREDRAW;
if(!RegisterClassEx(&wClass))
exit(1);
tWnd = FindWindow(0, (LPCWSTR)tWindowName);
if (tWnd)
{
GetWindowRect(tWnd, &tSize);
Width = tSize.right - tSize.left;
Height = tSize.bottom - tSize.top;
hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TRANSPARENT | WS_EX_LAYERED, (LPCWSTR)lWindowName, (LPCWSTR)lWindowName, WS_POPUP, 1, 1, Width, Height, 0, 0, 0, 0);
SetLayeredWindowAttributes(hWnd, 0, 1.0f, LWA_ALPHA);
SetLayeredWindowAttributes(hWnd, 0, RGB(0, 0, 0), LWA_COLORKEY);
ShowWindow( hWnd, SW_SHOW);
}
DirectXInit(hWnd);
for (;;)
{
if (GetAsyncKeyState(VK_ESCAPE)) break;
if(PeekMessage(&Message, hWnd, 0, 0, PM_REMOVE))
{
DispatchMessage(&Message);
TranslateMessage(&Message);
}
Sleep(1);
}
return 0;
}
void SetWindowToTarget()
{
while(true)
{
tWnd = FindWindow(0, (LPCWSTR)tWindowName);
if (tWnd)
{
GetWindowRect(tWnd, &tSize);
Width = tSize.right - tSize.left;
Height = tSize.bottom - tSize.top;
DWORD dwStyle = GetWindowLong(tWnd, GWL_STYLE);
if(dwStyle & WS_BORDER)
{
tSize.top += 23;
Height -= 23;
}
MoveWindow(hWnd, tSize.left, tSize.top, Width, Height, true);
}
else
{
char* ErrorMsg[125];
MessageBox(0, L"MAL", (LPCWSTR)L"Error - Cannot find the game!", MB_OK | MB_ICONERROR);
exit(1);
}
Sleep(100);
}
}
So, 2 question. First, how could I fix this compiler error: http://gyazo.com/6d7de9e0f3bad345dbbe7b9b80c90b8d And the second question. I realized that I must put an "L" and (LPCWSTR) before some chars*, this is 100% required? Is there any way to avoid that at least the (LPCWSTR)? Thanks for read.
First,
const MARGINS* Margin = { 0, 0, Width , Height };
is invalid. You probably want to create a new MARGINS object and initialize it with the given values, in which case you don't want Margin to be a pointer:
const MARGINS Margin = { 0, 0, Width , Height };
Second, DwmExtendFrameIntoClientArea() expects a const MARGINS*, which is a pointer to a MARGINS object. Just give it the address of Margin:
DwmExtendFrameIntoClientArea(hWnd, &Margin);
As to the wide character issues, I am assuming that you are compiling this with the "Unicode character set" in the project options. In this case, most strings in the Windows API will be pointers to wide characters (wchar_t*). However, you are allocating narrow character arrays (such as lWindowName) and casting them to a pointer to a wchar_t:
wClass.lpszClassName = (LPCWSTR)lWindowName;
This will give you all sorts of weird behaviours. Make sure your strings really are made of wide characters:
wchar_t lWindowName[256] = L"TEEEST";
This will allow you to drop most casts.
I have this odd behavior where my code was unable to call a event handler when i compile and run using JNI but it worked if i compile it as .exe in VS 2013.
The ones not working are listed under case ID_KRUSKAL_IMPERFECT, case ID_KRUSKAL_PERFECT, case ID_PRIM_IMPERFECT, case ID_PRIM_PERFECT. This cases are menu's (see image below for illustration ). By right it is suppose to pop up another dialog box
However, when i clicked case ID_FILE_EXIT the window closed (so it worked for this button)
Source code as attached :
#include <windows.h>
#include <iostream>
#include <string>
#include <fstream>
//#include "resource.h"
#include "maze.h"
#include <sstream>
#include <vector>
#include "HelloWorld.h"
#include "win_main.h"
#include "KruskalAlgo.h"
#include "PrimAlgo.h"
//JNI
#include <jni.h>
#include <stdio.h>
#include "Window.h"
//win32 GUI ID
#define IDR_MENU1 102
#define IDI_ICON1 103
#define IDD_DIALOG1 105
#define IDC_EDIT1 1005
#define IDC_WIDTH 1005
#define IDC_EDIT2 1006
#define IDC_EDIT3 1007
#define IDC_WALLBUTTON 1008
#define IDC_PATHBUTTON 1009
#define IDC_EDIT4 1010
#define IDC_HEIGHT 1010
#define ID_STUFF_GO 40001
#define ID_FILE_EXIT 40002
#define ID_FILE_GENERATEMAZE 40003
#define ID_FILE_UPLOADMAZE 40004
#define ID_GENERATEMAZE_KRUSKAL 40005
#define ID_GENERATEMAZE_PRIM 40006
#define ID_GENERATEMAZE_KRUSKAL40007 40007
#define ID_GENERATEMAZE_PRIM40008 40008
#define ID_GENERATEMANUALLY_KRUSKAL 40009
#define ID_GENERATEMANUALLY_PRIM 40010
#define ID_KRUSKAL_PERFECT 40011
#define ID_KRUSKAL_IMPERFECT 40012
#define ID_PRIM_PERFECT 40013
#define ID_PRIM_IMPERFECT 40014
#define ID_GENERATEMAZEAUTO 40015
//class name
LPCSTR g_szClassName = "mazeGenerator";
//2d vector to store 1s and 0s
typedef std::vector<std::vector<int>> IntMatrix;
IntMatrix vector2D;
//function header
void drawMaze(HWND hwnd);
void fileUpload(HWND hwnd);
void chooseColour(HWND hwnd);
//global height
int inRowCount;
//global width
int inColCount;
int disp;
PrimAlgo p;
PrimAlgo pi;
KruskalAlgo k;
KruskalAlgo ki;
INT_PTR CALLBACK ConfigDialog(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
//HWND hWndComboBox;
switch (Message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDC_WALLBUTTON:
chooseColour(hwnd);
break;
case IDC_PATHBUTTON:
chooseColour(hwnd);
break;
case IDOK:
inColCount = GetDlgItemInt(hwnd, IDC_WIDTH, NULL, FALSE);
inRowCount = GetDlgItemInt(hwnd, IDC_HEIGHT, NULL, FALSE);
EndDialog(hwnd, IDD_DIALOG1);
break;
case IDCANCEL:
EndDialog(hwnd, IDD_DIALOG1);
break;
default:
return FALSE;
}
default:
return FALSE;
}
return TRUE;
}
// Step 4: the Window Procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HWND temp;
HINSTANCE htemp;
switch (msg)
{
case WM_CREATE:
{
//initialization
HMENU hMenu, hSubMenu1, hSubMenu2, hSubMenu3, hSubMenu4, hSubMenu5;
HICON hIcon, hIconSm;
//menu
hMenu = CreateMenu();
hSubMenu1 = CreatePopupMenu();
hSubMenu2 = CreatePopupMenu();
hSubMenu3 = CreatePopupMenu();
hSubMenu4 = CreatePopupMenu();
hSubMenu5 = CreatePopupMenu();
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu1, "&File");
AppendMenu(hSubMenu1, MF_STRING | MF_POPUP, (UINT)hSubMenu2, "&New Game");
AppendMenu(hSubMenu2, MF_STRING, ID_GENERATEMAZEAUTO, "&Generate Automatically");
AppendMenu(hSubMenu2, MF_STRING | MF_POPUP, (UINT)hSubMenu3, "&Generate Manually");
AppendMenu(hSubMenu3, MF_STRING | MF_POPUP, (UINT)hSubMenu4, "&Kruskal");
AppendMenu(hSubMenu4, MF_STRING, ID_KRUSKAL_PERFECT, "&Perfect");
AppendMenu(hSubMenu4, MF_STRING, ID_KRUSKAL_IMPERFECT, "&Imperfect");
AppendMenu(hSubMenu3, MF_STRING | MF_POPUP, (UINT)hSubMenu5, "&Prim's");
AppendMenu(hSubMenu5, MF_STRING, ID_KRUSKAL_PERFECT, "&Perfect");
AppendMenu(hSubMenu5, MF_STRING, ID_KRUSKAL_IMPERFECT, "&Imperfect");
AppendMenu(hSubMenu1, MF_STRING, ID_FILE_EXIT, "&Exit");
/*hSubMenu = CreatePopupMenu();
AppendMenu(hSubMenu, MF_STRING, ID_STUFF_GO, "&Go");
AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, "&Stuff");*/
SetMenu(hwnd, hMenu);
hIcon = (HICON)LoadImage(NULL, "logo.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
if (hIcon)
SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
else
MessageBox(hwnd, "Could not load large icon!", "Error", MB_OK | MB_ICONERROR);
hIconSm = (HICON)LoadImage(NULL, "logo.ico", IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
if (hIconSm)
SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSm);
else
MessageBox(hwnd, "Could not load small icon!", "Error", MB_OK | MB_ICONERROR);
}
break;
case WM_PAINT:
{
drawMaze(hwnd);
}
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case ID_FILE_EXIT:
PostMessage(hwnd, WM_CLOSE, 0, 0);
break;
case ID_GENERATEMAZEAUTO:
fileUpload(hwnd);
InvalidateRect(hwnd, 0, TRUE);
SetWindowPos(hwnd, 0, 0, 0, (inColCount * 10) + 17, (inRowCount * 10) + 60, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
case ID_PRIM_PERFECT:
disp = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hwnd, ConfigDialog);
p.setDimension(inRowCount, inColCount);
p.setWallPath(0, 1);
p.generateMaze();
vector2D = p.get2DOutput();
InvalidateRect(hwnd, 0, TRUE);
SetWindowPos(hwnd, 0, 0, 0, (inColCount * 10) + 17, (inRowCount * 10) + 60, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
case ID_PRIM_IMPERFECT:
disp = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hwnd, ConfigDialog);
pi.setDimension(inRowCount, inColCount);
pi.setWallPath(0, 1);
pi.generateMaze();
pi.generateImperfect();
vector2D = pi.get2DOutput();
InvalidateRect(hwnd, 0, TRUE);
SetWindowPos(hwnd, 0, 0, 0, (inColCount * 10) + 17, (inRowCount * 10) + 60, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
case ID_KRUSKAL_PERFECT:
disp = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hwnd, ConfigDialog);
k.setDimension(inRowCount, inColCount);
k.setWallPath(0, 1);
k.generateMaze();
vector2D = k.get2DOutput();
InvalidateRect(hwnd, 0, TRUE);
SetWindowPos(hwnd, 0, 0, 0, (inColCount * 10) + 17, (inRowCount * 10) + 60, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
case ID_KRUSKAL_IMPERFECT:
disp = DialogBox(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hwnd, ConfigDialog);
ki.setDimension(inRowCount, inColCount);
ki.setWallPath(0, 1);
ki.generateMaze();
ki.generateImperfect();
vector2D = ki.get2DOutput();
InvalidateRect(hwnd, 0, TRUE);
SetWindowPos(hwnd, 0, 0, 0, (inColCount * 10) + 17, (inRowCount * 10) + 60, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
break;
}
break;
case WM_CLOSE:
{
DestroyWindow(hwnd);
break;
}
case WM_DESTROY:
{
PostQuitMessage(0);
break;
}
default:
{
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
WNDCLASSEX wc;
HWND hwnd;
MSG Msg;
//Step 1: Registering the Window Class
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
wc.lpszClassName = g_szClassName;
wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
wc.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1), IMAGE_ICON, 16, 16, 0);
if (!RegisterClassEx(&wc))
{
MessageBoxA(NULL, "Window Registration Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
// Step 2: Creating the Window
hwnd = CreateWindowEx(
WS_EX_WINDOWEDGE,
g_szClassName,
"Maze Generator 0.1",
WS_OVERLAPPEDWINDOW,
0, 0, 300, 60,
NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
MessageBoxA(NULL, "Window Creation Failed!", "Error!",
MB_ICONEXCLAMATION | MB_OK);
return 0;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
// Step 3: The Message Loop
while (GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
}
//draws maze based on vector2D
void chooseColour(HWND hwnd){
CHOOSECOLOR cc; // common dialog box structure
static COLORREF acrCustClr[16]; // array of custom colors
HBRUSH hbrush; // brush handle
static DWORD rgbCurrent; // initial color selection
ZeroMemory(&cc, sizeof(cc));
cc.lStructSize = sizeof(cc);
cc.hwndOwner = hwnd;
cc.lpCustColors = (LPDWORD)acrCustClr;
cc.rgbResult = rgbCurrent;
cc.Flags = CC_FULLOPEN | CC_RGBINIT;
if (ChooseColor(&cc) == TRUE)
{
hbrush = CreateSolidBrush(cc.rgbResult);
rgbCurrent = cc.rgbResult;
}
}
void drawMaze(HWND hwnd){
RECT rcClient;
GetClientRect(hwnd, &rcClient);
PAINTSTRUCT ps;
//starting x and y value for first maze cell
int startRow = 0;
int startCol = 0;
//displacement
int dRow = 10;
int dCol = 10;
//counter
int rowCounter = 0;
int colCounter = 0;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &rcClient, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
for (int rowId = 0; rowId < inRowCount; rowId++){
for (int colId = 0; colId < inColCount; colId++){
if (vector2D[rowId][colId] == 1){
RECT cell = { startCol, startRow, startCol + dCol, startRow + dRow };
FillRect(hdc, &cell, (HBRUSH)GetStockObject(WHITE_BRUSH));
}
if (vector2D[rowId][colId] == 0){
RECT cell = { startCol, startRow, startCol + dCol, startRow + dRow };
FillRect(hdc, &cell, (HBRUSH)GetStockObject(BLACK_BRUSH));
}
startCol += dCol;
colCounter++;
if (colCounter == inColCount)
{
startCol = 0;
colCounter = 0;
}
}
startRow += dRow;
rowCounter++;
if (rowCounter == inRowCount)
{
startRow = 0;
rowCounter = 0;
}
}
EndPaint(hwnd, &ps);
}
void fileUpload(HWND hwnd){
//file upload
OPENFILENAME ofn;
HANDLE hFile;
DWORD dwRead;
char filechar[20000] = "";
int countMaze = 0;
int countValue = 0;
int x = 0;
int y = 0;
int tempx = 0;
int tempy = 0;
char szFileName[MAX_PATH] = "";
ZeroMemory(&ofn, sizeof(ofn));
ofn.lStructSize = sizeof(ofn); // SEE NOTE BELOW
ofn.hwndOwner = hwnd;
ofn.lpstrFilter = (LPCSTR)"Text Files (*.txt)\0*.txt\0All Files (*.*)\0*.*\0";
ofn.lpstrFile = (LPSTR)szFileName;
ofn.nMaxFile = MAX_PATH;
ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
ofn.lpstrDefExt = (LPCSTR)"txt";
if (GetOpenFileName(&ofn))
{
// Load the file name for maze drawing
// Load the file name for maze drawing
//MessageBox(hWnd, (LPCWSTR)szFileName, (LPCWSTR)L"Notice",MB_OK | MB_ICONINFORMATION);
hFile = CreateFile((LPCSTR)szFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
ReadFile(hFile, filechar, sizeof(filechar), &dwRead, NULL);
int status = 1;//to track invalid input detected in the readfile
while (dwRead != countMaze)
{
char temp;
//temp in ASCII value (convert to 0 and 1)
temp = *&filechar[countMaze];
if (temp != '\r' && temp != '\n' && temp != '0' && temp != '1')
{
status = 0;
break;
}
if (temp == '\r')//For carriage return case.
{
countMaze = countMaze + 1;
continue;
}
if (temp == '\n')//For newline case.
{
y = dwRead / (x + 2);
break;
}
x = x + 1;
countMaze = countMaze + 1;
}
//return the width and height needed to print maze.
if (status == 1){
inColCount = x;
inRowCount = y;
//resize maze based on x and y values.
vector2D.resize(y);
for (int i = 0; i < y; i++){
vector2D[i].resize(x);
}
while (dwRead > countValue)
{
char temp;
//temp in ASCII value
temp = *&filechar[countValue];
if (temp)
if (temp == '\r')//For carriage return case.
{
countValue++;
continue;
}
if (temp == '\n')//For newline case.
{
countValue++;
tempx++;
tempy = 0;
continue;
}
//minus 48 to convert from ascii value to integer
vector2D[tempx][tempy] = int(temp - 48);
tempy = tempy + 1;
countValue++;
}
}
else{
MessageBox(hwnd, (LPCSTR)"Invalid maze file found ! Re-upload please with text file containing 0 and 1 only please", (LPCSTR)"Notice",
MB_OK | MB_ICONINFORMATION);
}
if (status == 1){
//close hFile handler
CloseHandle(hFile);
}
}
}
// Implementation of native method sayHello() of HelloJNI class
JNIEXPORT void JNICALL Java_Window_load(JNIEnv *env, jobject thisObj) {
WinMain(0,0,0,1);
printf("Hello World!\n");
return;
}
It doesn't work because the hInstance passed to WinMain is NULL in Java_Window_load(). You need to use the HINSTANCE of the DLL or else resources like your menus won't work.
Do something like this:
static HINSTANCE sDllInstance;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {
if (fdwReason == DLL_PROCESS_ATTACH) {
sDllInstance = hinstDLL;
}
return TRUE;
}
// Implementation of native method sayHello() of HelloJNI class
JNIEXPORT void JNICALL Java_Window_load(JNIEnv *env, jobject thisObj) {
WinMain(sDllInstance,0,0,1);
printf("Hello World!\n");
return;
}