Windows API: UpdateLayeredWindow return values - c++

I have a layered window in my program and it seems(visually) to work fine, but the return code from UpdateLayeredWindow is supposed to be a non-zero value on success. In my case, it is 0, and GetLastError returns 87, that is for an incorrect parameter. Can someone please tell me if there is anything wrong with my setup? Here is the complete function, window styles are WS_EX_LAYERED|WS_EX_TOPMOST and WS_POPUP.
bool SplashScreen(HWND hwnd, HINSTANCE m_hinstance)
{
HBITMAP hBitmap = (HBITMAP)LoadImage(m_hinstance, "splash.bmp", IMAGE_BITMAP, 640, 640, LR_LOADFROMFILE);
PAINTSTRUCT ps;
HDC hdc;
BITMAP bitmap;
HDC hdcMem;
HGDIOBJ oldBitmap;
int result=0;
if(!SetLayeredWindowAttributes(hwnd, 0, (255 * 100) / 100, LWA_ALPHA))
{
char msg[255];
sprintf(msg,"Error SetLayeredWindowAttributes: %d",GetLastError());
MessageBox(hwnd,msg,"System",MB_OK);
return false;
}
hdc = BeginPaint(hwnd, &ps);
if(!hdc)
{
char msg[255];
sprintf(msg,"Error BeginPaint: %d",GetLastError());
MessageBox(hwnd,msg,"System",MB_OK);
return false;
}
hdcMem = CreateCompatibleDC(hdc);
if(!hdcMem)
{
char msg[255];
sprintf(msg,"Error CreateCompatibleDC: %d",GetLastError());
MessageBox(hwnd,msg,"System",MB_OK);
return false;
}
oldBitmap = SelectObject(hdcMem, hBitmap);
GetObject(hBitmap, sizeof(bitmap), &bitmap);
result=BitBlt(hdc, 0, 0, 640, 640, hdcMem, 0, 0, SRCCOPY);
if(result==0)
{
char msg[255];
sprintf(msg,"Error BitBlt: %d",GetLastError());
MessageBox(hwnd,msg,"System",MB_OK);
return false;
}
BLENDFUNCTION blend = { 0 };
blend.BlendOp = AC_SRC_OVER;
blend.SourceConstantAlpha = 255;
blend.AlphaFormat = AC_SRC_ALPHA;
result=UpdateLayeredWindow(hwnd,GetDC(NULL),NULL,NULL,hdcMem,NULL, RGB(0,0,0),&blend, ULW_ALPHA);// Returns non-zero on success(!), in reality works when 0 is returned.
if(result==0)
{
char msg[255];
sprintf(msg,"Error UpdateLayeredWindow: %d",GetLastError());// Error UpdateLayeredWindow: 87
MessageBox(hwnd,msg,"System",MB_OK);
return false;
}
result=SetLayeredWindowAttributes(hwnd, RGB(255,255,255), 0, ULW_COLORKEY);
if(result==0)
{
char msg[255];
sprintf(msg,"Error SetLayeredWindowAttributes: %d",GetLastError());
MessageBox(hwnd,msg,"System",MB_OK);
return false;
}
result=EndPaint(hwnd, &ps);
if(result==0)
{
char msg[255];
sprintf(msg,"Error EndPaint: %d",GetLastError());
MessageBox(hwnd,msg,"System",MB_OK);
return false;
}
SelectObject(hdcMem, oldBitmap);
DeleteDC(hdc);
DeleteObject(hdcMem);
return true;
}

You are calling SetLayeredWindowAttributes() and UpdateLayeredWindow() on the same HWND. That will not work, and the documentation is very clear on that:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633540(v=vs.85).aspx
Note that once SetLayeredWindowAttributes has been called for a layered window, subsequent UpdateLayeredWindow calls will fail until the layering style bit is cleared and set again.
Do not use SetLayeredWindowAttributes() and UpdateLayeredWindow() together. They are very different methodologies. Either use SetLayeredWindowAttributes() with traditional WM_PAINT drawing, or use UpdateLayeredWindow() with an in-memory bitmap. Do not use both. Based on what you have shown, you should be using UpdateLayeredWindow() by itself. It will set a bitmap as the window contents and set the window's transparency/alpha at the same time.
And do not use Begin/EndPaint() outside of a WM_PAINT handler.

Related

WINAPI redrawing bitmaps in client area

I am attempting to make image viewer in winapi,
so basically it loads and draws my first image, but then i click button to navigate through .bmp images in given directory and they are not being drawn.
I would appreciate if you could help me and tell me where I am making mistake.
Here are my WndProc variables:
WIN32_FIND_DATA file;
HBITMAP img;
std::vector<WIN32_FIND_DATA> vFiles;
std::vector<WIN32_FIND_DATA>::iterator it;
HANDLE hFile = FindFirstFile(L"C:\\Users\\eclip7e\\Pictures\\*.bmp", &file);
if (hFile != INVALID_HANDLE_VALUE)
{
it = vFiles.begin();
it = vFiles.insert(it, file);
while (FindNextFile(hFile, &file))
{
it = vFiles.insert(it, file);
}
it = vFiles.begin();
}
Here is my button click:
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDC_RIGHT_BUTTON:
{
if (it<vFiles.end())
{
it++;
InvalidateRect(hWnd, NULL, TRUE);
}
else if (it == vFiles.end())
{
it = vFiles.begin();
InvalidateRect(hWnd, NULL, TRUE);
}
}
break;
}
}
break;
Here is my WM_PAINT:
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HDC hdcMem;
LPCWSTR d = L"C:\\Users\\eclip7e\\Pictures\\";
WIN32_FIND_DATA filex;
filex = *(it);
std::wstring df = std::wstring(d) + filex.cFileName;
LPCWSTR dfc = df.c_str();
img = (HBITMAP)LoadImage(NULL, dfc, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
hdcMem = CreateCompatibleDC(hdc);
// Get the bitmap's parameters and verify the get
BITMAP qBitmap;
int iReturn = GetObject(reinterpret_cast<HGDIOBJ>(img), sizeof(BITMAP),
reinterpret_cast<LPVOID>(&qBitmap));
SelectObject(hdcMem, img);
BOOL bb =BitBlt(hdc, 0, 0, qBitmap.bmWidth, qBitmap.bmHeight, hdcMem, 0, 0, SRCCOPY);
DeleteDC(hdcMem);
DeleteObject(img);
EndPaint(hWnd, &ps);
}
break;

Splash Screen Make Wait box move

I have the following code that is supposed to paint a small box located on the bottom-left of a window, move 50 pixels to the right every 200 milliseconds, then reappear on the left once it reaches the right side.
Why doesn't my little rectangle move? It is painted in the same location all the time.
case WM_PAINT:
if (hBitmap != NULL)
{
// Paint the bitmap.
PAINTSTRUCT ps;
HDC hdc;
HDC hdcMem;
HGDIOBJ oldBitmap;
//
hdc = BeginPaint(hwnd, &ps);
// Create a dc in memory to paint on.
hdcMem = CreateCompatibleDC(hdc);
// Select the bitmap.
oldBitmap = SelectObject(hdcMem, hBitmap);
// Copy bitmap to splash screen window.
BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
// Fill rectangle.
HBRUSH hbr = CreateSolidBrush(RGB(42, 59, 87));
SelectObject(hdc, hbr);
FillRect(hdc, &rc, hbr);
// Cleanup.
SelectObject(hdcMem, oldBitmap);
DeleteObject(hbr);
DeleteObject(oldBitmap);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
}
break;
case WM_TIMER:
timeCount++;
addLeft += 50;
if (addLeft == 300)
{
addLeft = 0;
}
// Move rectangle.
rc.left += addLeft;
rc.right += addLeft;
// Refresh the window.
UpdateWindow(hwnd);
// Timer and RECT from the top of the code page, and WinMain:
UINT_PTR ptrTimer;
const int TIMER_INTERVAL = 200;
const int MAX_TIME_COUNT = 100;
int timeCount;
// the timer works, but here's the code anyway.
ptrTimer = SetTimer(hwnd, 1, TIMER_INTERVAL, (TIMERPROC)NULL);
RECT rc;
rc.left = 141;
rc.top = 232;
rc.right = rc.left + 15;
rc.bottom = rc.top + 15;
Thanks for any replies,
Matt
Edit: Thanks hf.enigma, for the reply. This is what I ended up doing before I read your post. This works if anyone else wants to do this, but there are a couple more handles and GDI objects to clear. I am new to C++, so if anyone sees a memory leak here, please let me know. Thanks.
case WM_PAINT:
if (hBitmap != NULL)
{
// Paint the bitmap.
PAINTSTRUCT ps;
HDC hdc;
HDC hdcMem;
HGDIOBJ oldBitmap;
//
hdc = BeginPaint(hwnd, &ps);
hdcMem = CreateCompatibleDC(hdc); // a device context (dc) in memory to paint on.
oldBitmap = SelectObject(hdcMem, hBitmap);
// Copy bitmap to splash screen window.
BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
// Cleanup.
SelectObject(hdcMem, oldBitmap);
DeleteObject(oldBitmap);
DeleteDC(hdcMem);
HBRUSH hbr ;
// Fill rectangle.
RECT f;
GetClientRect(hwndBox, &f);
hdc = BeginPaint(hwndBox, &ps);
hdcMem = CreateCompatibleDC(hdc);
hbr = CreateSolidBrush(RGB(42, 59, 87));
SelectObject(hdc, hbr);
FillRect(hdc, &f, hbr);
EndPaint(hwnd, &ps);
// Cleanup.
SelectObject(hdcMem, oldBitmap);
DeleteObject(oldBitmap);
DeleteDC(hdcMem);
DeleteObject(hbr);
}
case WM_TIMER:
timeCount++;
if (addLeft == 60)
{
addLeft = 10000;
ival = 2;
}
// 'Hide' the box for 2/3 of timer interval when
// it reaches the right side.
if (addLeft == 10000)
{
addLeft = 0;
ival = 1;
}
//
switch (ival)
{
case 2:
addLeft += 12;
break;
case 3:
ival = 0;
break;
}
ival++;
if (ival == 2)
{
// Move rectangle.
MoveWindow(hwndBox, rcleft + addLeft, rctop, 12, 12, true);
}
if (timeCount == MAX_TIME_COUNT)
{
DestroyWindow(hwnd);
}
break;
You have to copy the bitmap after you have finished the painting, and you should paint with hdcMem, like this:
...
// Fill rectangle.
HBRUSH hbr = CreateSolidBrush(RGB(42, 59, 87));
SelectObject(hdcMem, hbr);
FillRect(hdcMem, &rc, hbr);
// Copy bitmap to splash screen window.
BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
// Cleanup.
....
This can be painted like hf.enigma has stated, but FillRect() must be called twice for each paint event. Without 'erasing' the previous block, the window ended up having a long rectangle along the bottom, similar to a progress bar.
I ended up making a sub-window the size of the block I wanted, and calling the MoveWindow() function. For me, this is the simplest and most maintainable way to accomplish this without having to use MFC or CLI.
If this can help anyone else, Here is all of the code that I am using for this.
//This displays a splash screen, and starts
//another application that takes a long time
//to load.
#pragma once
#include "resource.h"
#include "PathStatus.h"
#include "StartApp.h"
#include "CenterWindow.h"
#include <string>
#include <stdlib.h>
#include <iostream>
using namespace std;
const LPWSTR CLASS_NAME(L"TMS_Logs");
const LPWSTR PATH_SUFFIX (L"\\bin\\tl.exe");
const int LEN_APP_NAME = 12; // length of "TMS_Logs.exe".
bool b;
int ival;
char *appPath;
HWND hwnd; // Main window.
HBITMAP hBitmap = NULL; // Bitmap painted in window.
HWND hwndBox; // Moving box shows app. is loading.
HWND hwndButton; // Close box.
int bmWidth;
int bmHeight;
UINT_PTR ptrTimer;
const int TIMER_INTERVAL = 50;
const int MAX_TIME_COUNT = 650;
int timeCount;
int addLeft;
int rcleft, rctop, rcright, rcbottom;
RECT rcMover;
RECT rcMover2;
bool exitApp = false;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
rcleft = 157;
rctop = 220;
rcright = rcleft + 15;
rcbottom = rctop + 15;
//
rcMover.left = rcleft;
rcMover.bottom = rcbottom;
rcMover.top = rctop;
rcMover.right = rcright;
//
rcMover2.bottom = rcbottom;
rcMover2.top = rctop;
rcMover.left + rcleft + 50;
rcMover.right = rcleft + 50 + 15;
// Get the commandline.
const wchar_t *args = GetCommandLineW();
wstring s(args);
// With no external arguments, GetCommandLineW() returns the
// full path with quotes around it, plus one space after the
// end qoute, e.g., '"c:\fullpath.exe" '.
// Remove the starting and ending quote chars, the space at
// the end, and and the exe path.
//
int len = s.length();
s = s.substr(1, len - 3 - LEN_APP_NAME) + PATH_SUFFIX;
// Convert the wide string to a regular char array.
// Convert the wstring to c-string.
const wchar_t *path = s.c_str();
// Convert the WCHAR to char* and store it in the
// 'appPath' variable.
const size_t BUFFER_SIZE = 200;
size_t i;
appPath = (char*)malloc(BUFFER_SIZE);
wcstombs_s(&i, appPath, BUFFER_SIZE, path, BUFFER_SIZE);
// Create window class objects.
WNDCLASSEX wc;
MSG msg;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = 0;
wc.lpfnWndProc = WndProc; // Sets callback function.
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDICN_WSDOT));
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wc.lpszMenuName = NULL;
wc.lpszClassName = CLASS_NAME;
wc.hIconSm = wc.hIcon;
if (!RegisterClassEx(&wc))
{
MessageBox(NULL, L"Window Registration Failed!", L"Error!",
MB_ICONEXCLAMATION | MB_OK);
return -1;
}
// Load the resource bitmap.
hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDBMP_TL7));
// Make a bitmap to hold the returned width and height.
// This bitmap will be deleted when DeleteObject is called
// for the hBitmap handle.
BITMAP bm;
if (hBitmap != NULL && GetObject(hBitmap, sizeof(bm), &bm))
{
bmWidth = bm.bmWidth;
bmHeight = bm.bmHeight;
}
else
{
MessageBox(hwnd, L"Error loading splash screen bitmap.",
L"Error", MB_ICONEXCLAMATION | MB_OK);
return -1;
}
// Center the window on the monitor
// where the mouse is.
RECT rc;
CenterRectToMonitor(&rc, bmWidth, bmHeight);
//
hwnd = CreateWindowEx(WS_EX_PALETTEWINDOW, wc.lpszClassName, // Uses the properties set in Window Class wc.
L"TMS Logs", WS_POPUP, rc.left, rc.top, bmWidth, bmHeight, NULL, NULL, hInstance, NULL);
if (hwnd == NULL)
{
MessageBox(NULL, L"Error creating Window.", L"Error",
MB_ICONEXCLAMATION | MB_OK);
return -1;
}
ShowWindow(hwnd, nCmdShow);
UpdateWindow(hwnd);
//bool started = true;
bool started = false;
bool ret;
int x, w, y, h;
x = 369;
y = 10;
w = 19;
h = 19;
// Make close box in upper-right corner.
hwndButton = CreateWindow(
L"BUTTON", // Predefined class.
L"X", // Button text
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON, // Styles
369, // x position
10, // y position
19, // Button width
19, // Button height
hwnd, // Parent window
NULL, // No menu.
(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE),
NULL); // Pointer not needed.
// Make a box that moves along the bottom of
// the window while the other app. loads.
x = rcleft;
y = rctop;
w = 10;
h = 10;
hwndBox = CreateWindow(L"static", 0, WS_CHILD | WS_VISIBLE, x, y, w, h, hwnd, NULL,
(HINSTANCE) GetWindowLong (hwnd, GWL_HINSTANCE), NULL);
// Create a timer to close this window after
// several seconds, in case the main app. does
// not send the close window message to this app.
// the WM_TIMER message is handled in the WndProc
// callback function.
ptrTimer = SetTimer(hwnd, 1, TIMER_INTERVAL, (TIMERPROC)NULL);
// Start message Loop.
while ((ret = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (ret == -1)
{
MessageBox(NULL,
L"Error in Splash screen window message loop, at:\n\n'GetMessage(&msg, NULL, 0, 0))'.\n\nMessage return value was -1.",
L"TMS Logs", MB_ICONEXCLAMATION | MB_OK);
// Exit this app.
return -1;
}
// TranslateMessage(&Msg); // Not needed with what I have here.
DispatchMessage(&msg);
// Check app. path and start the other app.
if (!started)
{
started = true;
if (Path_Accessible(appPath))
{
// Start the main app.
if (!StartApplication(appPath))
{
// The folder or file is missing, or
// the user does not have security
// permissions to the sub-folder.
MessageBox(hwnd, L"Unexpected error at: StartApplication().",
L"Error", MB_ICONEXCLAMATION | MB_OK);
if (appPath)
{
free(appPath);
}
return -1;
}
}
else
{
// Display an error message.
char msg[350];
char* prefix = "Error: Can't find 'tl.exe'.\n\n It's missing from: '";
//char* suffix = " '\ncannot be accessed, or does not exist.";
int count = sizeof(msg);
strncpy(msg, prefix, count);
strncat(msg, appPath, count);
//strncat(msg, suffix, count);
// Convert c-string to wide char. string.
count = strlen(msg) + 1;
wchar_t* wMsg = new wchar_t[count];
size_t returnCount;
int ret = mbstowcs_s(&returnCount, wMsg, count, msg, _TRUNCATE);
if (ret == 0 && returnCount == count)
{
MessageBox(hwnd, wMsg, L"Error", MB_ICONEXCLAMATION | MB_OK);
}
else
{
// Error
MessageBox(hwnd, L"Error: The application path cannot be accessd, or does not exist.", L"Error", MB_ICONEXCLAMATION | MB_OK);
}
delete[] wMsg;
if (appPath)
{
free(appPath);
}
return -1;
}
}
}
if (appPath)
{
free(appPath);
}
return msg.wParam;
}
// Process messages.
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_COMMAND:
// The close button is the only child window,
// so no need to check wparam.
DestroyWindow(hwnd);
break;
case WM_CLOSE:
DestroyWindow(hwnd);
break;
case WM_DESTROY:
DestroyWindow(hwndButton);
DestroyWindow(hwndBox);
if (hBitmap != NULL)
{
if (!DeleteObject(hBitmap))
{
MessageBox(hwnd, L"Error at: WndProc(). Failed to delete the application bitmap.",
L"Error", MB_OK);
}
}
if (ptrTimer)
{
if (!KillTimer(hwnd, ptrTimer))
{
MessageBox(hwnd, L"Error at :WndProc(). Failed to free the application timer.",
L"Error", MB_OK);
}
}
PostQuitMessage(0);
break;
case WM_PAINT:
if (hBitmap != NULL)
{
// Paint the bitmap.
PAINTSTRUCT ps;
HDC hdc;
HDC hdcMem;
HGDIOBJ oldBitmap;
//
hdc = BeginPaint(hwnd, &ps);
hdcMem = CreateCompatibleDC(hdc); // a device context (dc) in memory to paint on.
oldBitmap = SelectObject(hdcMem, hBitmap);
// Copy bitmap to splash screen window.
BitBlt(hdc, 0, 0, bmWidth, bmHeight, hdcMem, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
// Cleanup.
SelectObject(hdcMem, oldBitmap);
DeleteObject(oldBitmap);
DeleteDC(hdcMem);
HBRUSH hbr ;
// Fill rectangle.
RECT f;
GetClientRect(hwndBox, &f);
hdc = BeginPaint(hwndBox, &ps);
hdcMem = CreateCompatibleDC(hdc);
hbr = CreateSolidBrush(RGB(42, 59, 87));
SelectObject(hdc, hbr);
FillRect(hdc, &f, hbr);
EndPaint(hwnd, &ps);
// Cleanup.
SelectObject(hdcMem, oldBitmap);
DeleteObject(oldBitmap);
DeleteDC(hdcMem);
DeleteObject(hbr);
}
break;
case WM_TIMER:
timeCount++;
if (addLeft == 60)
{
addLeft = 10000;
ival = 2;
}
// 'Hide' the box for 2/3 of timer interval when
// it reaches the right side.
if (addLeft == 10000)
{
addLeft = 0;
ival = 1;
}
//
switch (ival)
{
case 2:
addLeft += 12;
break;
case 3:
ival = 0;
break;
}
ival++;
if (ival == 2)
{
// Move rectangle.
MoveWindow(hwndBox, rcleft + addLeft, rctop, 12, 12, true);
}
if (timeCount == MAX_TIME_COUNT)
{
DestroyWindow(hwnd);
}
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
Here is the code for referenced H files.
StartApp.H
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude DDE, RPC, Shell, Sockets, etc.
#define NOCOMM // Exclude serial communication APIs.
#include <Windows.h>
#include <cstdlib>
// Calls the CreateProcess function to start another app.
// ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682512(v=vs.85).aspx
bool StartApplication(const char* appPath)
{
// The CreateProcess function requires
// wide char. array, so convert appPath.
size_t count = strlen(appPath) + 1;
wchar_t* path = new wchar_t[count];
size_t returnCount;
int ret = mbstowcs_s(&returnCount, path, count, appPath, _TRUNCATE);
if (ret != 0 || returnCount != count)
{
// Error converting C-string.
delete[] path;
return false;
}
// Required objects.
STARTUPINFO si;
PROCESS_INFORMATION pi;
// Set the size of the objects.
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// Start the program at 'path'.
bool ok = CreateProcess(
path, // module name/app. path.
NULL, // Command line/path, if null app. path is used.
NULL, // Process handle, use null.
NULL, // Thread handle, use null.
FALSE, // Set handle inheritance to FALSE.
0, // No creation flags.
NULL, // Use parent's environment block.
NULL, // Use parent's starting directory.
&si, // Pointer to STARTUPINFO structure.
&pi); // Pointer to PROCESS_INFORMATION structure.
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
delete[] path;
return ok;
}
CenterWindow.H
void CenterRectToMonitor(LPRECT prc, int rcWidth, int rcHeight)
{
POINT pt;
GetCursorPos(&pt);
HMONITOR mon;
mon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(mon, &mi);
*prc = mi.rcMonitor;
//
// Center the window rectangle to the monitor rectangle.
prc->left = prc->left + (prc->right - prc->left - rcWidth) / 2;
prc->top = prc->top + (prc->bottom - prc->top - rcHeight) / 2;
prc->right = prc->left + rcWidth;
prc->bottom = prc->top + rcHeight;
}
PathStatus.H
#include <sys/stat.h>
const int SUCCESS = 0; // Indicates stat buffer was successfully set.
// Method to check if a file or folder
// exists and is accessible, using the stat
// structure and stat function, ref:
// http://pubs.opengroup.org/onlinepubs/009695399/functions/stat.html
bool Path_Accessible(const char* path)
{
if (path == 0)
{
return false;
}
struct stat path_status;
int result = stat(path, &path_status);
//
// The _S_IFREG flag indicates the path is a
// regular file and not a directory.
//
// To check for a directory, use '_S_IFDIR'.
return result == SUCCESS &&
(path_status.st_mode & _S_IFREG);
}
This is what my splash screen looks like. Note that the small box starts a little to the left of where it is now, moves a little to the right, and this repeats until the other app. this one started sends the WM_CLOSE message to this window.

Include mouse cursor in screen capture

I use CreateDC / BitBlt / GetDIBits etc. to capture the screen, but the cursor is not captured. Is there some simple argument or something to have it included?
#include <Windows.h>
#include <stdio.h>
#include <assert.h>
void scrshot() {
HWND hwnd = GetDesktopWindow();
HDC hdc = GetWindowDC(hwnd);
HDC hdcMem = CreateCompatibleDC(hdc);
int cx = GetDeviceCaps(hdc, HORZRES);
int cy = GetDeviceCaps(hdc, VERTRES);
HBITMAP hbitmap(NULL);
hbitmap = CreateCompatibleBitmap(hdc, cx, cy);
SelectObject(hdcMem, hbitmap);
BitBlt(hdcMem, 0, 0, cx, cy, hdc, 0, 0, SRCCOPY);
CURSORINFO cursor = { sizeof(cursor) };
GetCursorInfo(&cursor);
if (cursor.flags == CURSOR_SHOWING) {
RECT rect;
GetWindowRect(hwnd, &rect);
ICONINFO info = { sizeof(info) };
GetIconInfo(cursor.hCursor, &info);
const int x = cursor.ptScreenPos.x - rect.left - rect.left - info.xHotspot;
const int y = cursor.ptScreenPos.y - rect.top - rect.top - info.yHotspot;
BITMAP bmpCursor = { 0 };
GetObject(info.hbmColor, sizeof(bmpCursor), &bmpCursor);
DrawIconEx(hdcMem, x, y, cursor.hCursor, bmpCursor.bmWidth, bmpCursor.bmHeight,
0, NULL, DI_NORMAL);
}
}
int main(){
scrshot();
return 0;
}
Further to the discussion that occurred in the comments, I had the chance to further investigate the question. As a result, I came up with the following code that will grab the current cursor's HBITMAP and draw it to the screen.
Since the cursor is actually an HICON, it comes with a mask. Initially, I just did a simple BitBlt - however, I got a 32x32 black sqaure with the cursor in the top left 1/4 or so.
I then investigated using MaskBlt. Depending on where the cursor is when the app is started, I get either the wait cursor, a NS resize cursor, or the standard pointer. I guess you could start a timer and add a WM_TIMER handler to fire a couple of times a second in order to get a real-time update of the cursor as it was used in other windows in the system. It seemed like a mere curiosity to do something like that so I didn't bother.
EDIT: I actually did start a timer in WM_INITDIALOG and handle it in WM_TIMER. You can now see the image updated 10 times a second. For some reason, the I-beam cursor doesn't seem to be displayed at all - a case for further investigation as needed, I guess.
Here's the complete listing (except for resource.rc and resource.h - just create a dialog app and make sure the dialog's resource ID is used inside Main in the call to DialogBox)
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include "resource.h"
HINSTANCE hInst;
HBITMAP getCursorHBITMAP(HBITMAP *maskBmp)
{
CURSORINFO pci;
ICONINFO iconinfo;
HBITMAP result;
pci.cbSize = sizeof(pci);
GetCursorInfo(&pci);
if (GetIconInfo(pci.hCursor,&iconinfo))
{
result = iconinfo.hbmColor;
if (maskBmp)
*maskBmp = iconinfo.hbmMask;
}
else
result = NULL;
return result;
}
BOOL CALLBACK DlgMain(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_INITDIALOG:
{
SetTimer(hwndDlg, 1, 100, NULL);
}
return TRUE;
case WM_TIMER:
{
InvalidateRect(hwndDlg, NULL, true);
}
return 0;
case WM_ERASEBKGND:
{
HDC hdc = (HDC)wParam;
RECT mRect;
GetClientRect(hwndDlg, &mRect);
FillRect(hdc, &mRect, (HBRUSH)GetStockObject(GRAY_BRUSH));
}
return 1;
case WM_PAINT:
{
HBITMAP oldBm, cursorBmp, maskBmp;
cursorBmp = getCursorHBITMAP(&maskBmp);
if (cursorBmp)
{
HDC hdc;
PAINTSTRUCT ps;
HDC memDC;
BITMAP bm;
hdc = BeginPaint(hwndDlg, &ps);
memDC = CreateCompatibleDC(hdc);
oldBm = (HBITMAP) SelectObject(memDC, cursorBmp);
GetObject(cursorBmp, sizeof(bm), &bm);
// printf("Cursor size: %d x %d\n", bm.bmWidth, bm.bmHeight);
// BitBlt(hdc, 10,10, 32,32, memDC, 0,0, SRCCOPY);
MaskBlt(hdc, 10,10, bm.bmWidth, bm.bmHeight, memDC, 0,0, maskBmp, 0,0, MAKEROP4(SRCPAINT,SRCCOPY) );
SelectObject(memDC, oldBm);
DeleteDC(memDC);
EndPaint(hwndDlg, &ps);
}
}
return 0;
case WM_CLOSE:
{
EndDialog(hwndDlg, 0);
}
return TRUE;
case WM_COMMAND:
{
switch(LOWORD(wParam))
{
}
}
return TRUE;
}
return FALSE;
}
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hInst=hInstance;
InitCommonControls();
return DialogBox(hInst, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgMain);
}

Can't seem to make double-buffered painting with multiple monitors

I'm trying to remake my Windows screensaver written with C++ and WinAPIs to work on multiple monitors. I found this article that gives the basics. But when I implement it in my own code, I get a weird result. Take a look at this code:
case WM_PAINT:
{
PAINTSTRUCT ps = {0};
HDC hdcE = BeginPaint(hWnd, &ps );
EnumDisplayMonitors(hdcE, NULL, MyPaintEnumProc, 0);
EndPaint(hWnd, &ps);
}
break;
BOOL CALLBACK MyPaintEnumProc(
HMONITOR hMonitor, // handle to display monitor
HDC hdc1, // handle to monitor DC
LPRECT lprcMonitor, // monitor intersection rectangle
LPARAM data // data
)
{
MONITORINFO mi = {0};
mi.cbSize = sizeof(mi);
if(GetMonitorInfo(hMonitor, &mi))
{
//Is it a primary monitor?
BOOL bPrimary = mi.dwFlags & MONITORINFOF_PRIMARY;
DoDrawing(bPrimary, hdc1, &mi.rcMonitor);
}
return 1;
}
void DoDrawing(BOOL bPrimaryMonitor, HDC hDC, RECT* pRcMonitor)
{
//#define DIRECT_PAINT //Comment out for double-buffering
int nMonitorW = abs(pRcMonitor->right - pRcMonitor->left);
int nMonitorH = abs(pRcMonitor->bottom - pRcMonitor->top);
HDC hMemDC = ::CreateCompatibleDC(hDC);
if(hMemDC)
{
HBITMAP hMemBmp = ::CreateCompatibleBitmap(hDC, nMonitorW, nMonitorH);
if(hMemBmp)
{
HBITMAP hOldBmp = (HBITMAP)SelectObject(hMemDC, hMemBmp);
COLORREF clr, clrBorder;
if(bPrimaryMonitor)
{
clr = RGB(0, 128, 0); //Green
clrBorder = RGB(255, 0, 0);
}
else
{
clr = RGB(128, 0, 0); //Red
clrBorder = RGB(0, 255, 0);
}
RECT rcRect;
#ifndef DIRECT_PAINT
//With double-buffering
rcRect.left = 0;
rcRect.top = 0;
rcRect.right = nMonitorW;
rcRect.bottom = nMonitorH;
#else
rcRect = *pRcMonitor;
#endif
HBRUSH hBrush = ::CreateSolidBrush(clr);
#ifndef DIRECT_PAINT
//With double-buffering
::FillRect(hMemDC, &rcRect, hBrush);
#else
::FillRect(hDC, &rcRect, hBrush);
#endif
#ifndef DIRECT_PAINT
//With double-buffering
::BitBlt(hDC, pRcMonitor->left, pRcMonitor->top, nMonitorW, nMonitorH, hMemDC, 0, 0, SRCCOPY);
#endif
//Debugging output
CString _s;
_s.Format(_T("%s\n")
_T("%s\n")
_T("hDC=0x%X\n")
_T("hMemDC=0x%X\n")
_T("RcMonitor: L=%d, T=%d, R=%d, B=%d")
,
bPrimaryMonitor ? _T("Primary") : _T("Secondary"),
#ifndef DIRECT_PAINT
_T("Double-buffering"),
#else
_T("Direct paint"),
#endif
hDC,
hMemDC,
pRcMonitor->left,
pRcMonitor->top,
pRcMonitor->right,
pRcMonitor->bottom);
::DrawText(hDC, _s, _s.GetLength(), pRcMonitor, DT_NOCLIP | DT_NOPREFIX);
SelectObject(hMemDC, hOldBmp);
::DeleteObject(hMemBmp);
}
::DeleteDC(hMemDC);
}
}
Painting always works on a primary monitor. But when I paint to the secondary monitor, I can only paint directly to its DC. When I use double-buffering technique (with DIRECT_PAINT pre-processor directive commented out) I only get a black screen on a secondary monitor when it should've been red.
I'm attaching two screenshots here.
First one with direct painting that works:
And then the one with double-buffering that fails:
Any ideas what am I doing wrong here?
Replace the code for WM_PAINT from
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
EnumDisplayMonitors(hdc, NULL, MyPaintEnumProc, 0);
EndPaint(hWnd, &ps);
with
case WM_PAINT:
hdc = GetDC(NULL);
EnumDisplayMonitors(hdc, NULL, MyPaintEnumProc, 0);
ReleaseDC(NULL, hdc);
and it will work.
See this http://msdn.microsoft.com/en-us/library/windows/desktop/dd162610(v=vs.85).aspx.

Capturing image in client window win32 c++

This code is attempting to capture the image painted on the window in a box of up to 100x100 around the cursor. BitBlt is not returning 0 in either location here, and I'm pretty sure the issue is with the first function call of BitBlt, where I am trying to copy the image from the background of the window into meta, which is an HDC declared globally. In addition to just trying to create the HDC entirely in memory, I tried to create and load a bitmap of white space and capture the new image into the handle associated with it, but all that did was act like an eraser and draw a white box around the cursor as it moved. Relevant code is below, mouseRect and clientRect are global variables relating to the box around the cursor and the client rectangle, respectively. Any help is appreciated, thanks!
case WM_CREATE:
hInstance = ((LPCREATESTRUCT) lParam)->hInstance;
GetClientRect(hWnd, &clientRect);
hdc = GetDC(hWnd);
meta = CreateCompatibleDC(hdc);
return 0;
case WM_MOUSEMOVE:
x = LOWORD(lParam);
y = HIWORD(lParam);
hdc = GetDC(hWnd);
BitBlt(hdc, mouseRect.left, mouseRect.top, mouseRect.right-mouseRect.left, mouseRect.bottom-mouseRect.top, meta, 0, 0, SRCCOPY);
ReleaseDC(hWnd, meta);
meta = CreateCompatibleDC(hdc);
if(y<50)
mouseRect.top = 0;
else
mouseRect.top = y-50;
if(x<50)
mouseRect.left = 0;
else
mouseRect.left = x-50;
if(clientRect.right-x<50)
mouseRect.right = clientRect.right;
else
mouseRect.right = x+50;
if(clientRect.bottom-y<50)
mouseRect.bottom = clientRect.bottom;
else
mouseRect.bottom = y+50;
BitBlt(meta, 0, 0, mouseRect.right-mouseRect.left, mouseRect.bottom-mouseRect.top, hdc, mouseRect.left, mouseRect.top, SRCCOPY);
ReleaseDC(hWnd, hdc);
return 0;
Fixed the code, here's the corrected WM_MOUSEMOVE code
case WM_MOUSEMOVE:
x = LOWORD(lParam);
y = HIWORD(lParam);
hdc = GetDC(hWnd);
BitBlt(hdc, mouseRect.left, mouseRect.top, mouseRect.right-mouseRect.left, mouseRect.bottom-mouseRect.top, hdcMemDC, 0, 0, SRCCOPY);
ReleaseDC(hWnd, hdcMemDC);
if(y<50)
mouseRect.top = 0;
else
mouseRect.top = y-50;
if(x<50)
mouseRect.left = 0;
else
mouseRect.left = x-50;
if(clientRect.right-x<50)
mouseRect.right = clientRect.right;
else
mouseRect.right = x+50;
if(clientRect.bottom-y<50)
mouseRect.bottom = clientRect.bottom;
else
mouseRect.bottom = y+50;
hdcMemDC = CreateCompatibleDC(hdc);
hbmScreen = CreateCompatibleBitmap(hdc, mouseRect.right-mouseRect.left, mouseRect.bottom-mouseRect.top);
SelectObject(hdcMemDC,hbmScreen);
if(!BitBlt(hdcMemDC, 0, 0, mouseRect.right-mouseRect.left, mouseRect.bottom-mouseRect.top, hdc, mouseRect.left, mouseRect.top, SRCCOPY))
{
MessageBox(hWnd, "BitBlt has failed", "Failed", MB_OK);
}
ReleaseDC(hWnd, hdc);
return 0;