c++ - Win32 window freezes after the first draw (directx 11) - c++

I have a standard win32 window, that I draw on with D2D1. Everything is responsive and runs smoothly. My problem is as follows: After the window is created, it calls WM_PAINT once and then gets "stuck" waiting for any user input (ex. mouse move or click in the window area). As it receives the input, it runs without any further problems. While this doesn't necessarily render the program nonfunctional, it seems highly unprofessional and... ugly. Oh, and what I mean by "stuck" is that the background processes still run without problems, so the loop runs as intended - however, the WM_PAINT isn't called for some reason.
Here is the current code:
header file:
#define PROGRAM_FPS 30
#define GWINDOW_STYLE (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_CLIPCHILDREN | WS_CLIPSIBLINGS)
class Application
{
public:
Application();
~Application();
// Register the window class and call methods for instantiating drawing resources
HRESULT Initialize();
// Process and dispatch messages
void RunMessageLoop();
static inline void SafeRelease(IUnknown * _X)
{
if(_X != NULL)
_X->Release();
};
HRESULT SetFullscreen(bool);
private:
// Time (ms) between frames of the application
// Initiation: m_AppFPS_Div( (DWORD)1000.0 / (DWORD)PROGRAM_FPS )
const DWORD m_AppFPS_Div;
// Initialize device-independent resources.
HRESULT CreateDeviceIndependentResources();
// Initialize device-dependent resources.
HRESULT CreateDeviceResources();
// Release device-dependent resource.
void DiscardDeviceResources();
// Draw content.
HRESULT OnRender();
HRESULT LoadBitmapFromFile(
ID2D1RenderTarget*,
PCWSTR,
UINT,
UINT,
ID2D1Bitmap **
);
// The windows procedure.
static LRESULT CALLBACK WndProc(
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam
);
HWND m_hwnd;
ID2D1Factory * m_pDirect2dFactory;
ID2D1HwndRenderTarget * m_pRenderTarget;
IWICImagingFactory *m_pIWICFactory;
ID2D1SolidColorBrush * m_pWhiteBrush;
ID2D1SolidColorBrush * m_pGrayBrush;
ID2D1LinearGradientBrush * m_pLinearGradientBrush;
ID2D1Bitmap *m_LoadingPicture;
};
parts of the .cpp file
void Application::RunMessageLoop()
{
HACCEL hAccelTable = LoadAccelerators(hInst, MAKEINTRESOURCE(IDC_WIN32APP));
DWORD screen_last_refresh = 0;
MSG msg;
for(bool applicationRunning = true; applicationRunning;)
{
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if(msg.message == WM_QUIT)
{
applicationRunning = false;
}
if(!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
/*** HERE I RUN VARIOUS BACKGROUND PROCESSES ***/
while((GetTickCount() - screen_last_refresh) < m_AppFPS_Div)
Sleep(2);
InvalidateRect(msg.hwnd, NULL, false);
screen_last_refresh = GetTickCount();
}
}
LRESULT CALLBACK Application::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
LRESULT result = 0;
if (message == WM_CREATE)
{
LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
Application *pApp = (Application *)pcs->lpCreateParams;
::SetWindowLongPtrW(hwnd, GWLP_USERDATA, PtrToUlong(pApp) );
InvalidateRect(hwnd, NULL, false);
result = 1;
}
else
{
Application *pApp = reinterpret_cast<Application *>(static_cast<LONG_PTR>(
::GetWindowLongPtrW(
hwnd,
GWLP_USERDATA
)));
bool wasHandled = false;
if (pApp)
{
switch (message)
{
case WM_SIZE:
{
UINT width = LOWORD(lParam);
UINT height = HIWORD(lParam);
pApp->OnResize(width, height);
}
result = 0;
wasHandled = true;
break;
case WM_DISPLAYCHANGE:
{
InvalidateRect(hwnd, NULL, FALSE);
}
result = 0;
wasHandled = true;
break;
case WM_PAINT:
{
pApp->OnRender();
ValidateRect(hwnd, NULL);
}
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;
}
HRESULT Application::OnRender()
{
HRESULT hr = S_OK;
hr = CreateDeviceResources();
if (SUCCEEDED(hr))
{
m_pRenderTarget->BeginDraw();
m_pRenderTarget->SetTransform(D2D1::Matrix3x2F::Identity());
/** HERE I HANDLE THE OPENING ANIMATION **/
hr = m_pRenderTarget->EndDraw();
}
if (hr == D2DERR_RECREATE_TARGET)
{
hr = S_OK;
DiscardDeviceResources();
}
return hr;
}
So, I've tested it on 2 computers (both using Win 8.1) with VS 2012 Ultimate and VS 2013 Professional, run debug tests back and forth, stripping certain parts of the program, searched MDSN, Google and StackExchange - to no avail. This freeze also happens when I handle WM_COMMAND of a child window. There was also no such issue when I tried implementing GDI+, but it proved to be highly ineffective, hence the switch to DirectX. Aside from that, the program runs flawlessly. I hope I've provided enough information.

You have not been clear about what you are doing to run "various background processes." That is probably where the "stuck" issue comes from. A suggestion is to move that out of the message loop. Instead, call SetTimer during window initialization and then do a bit of your background process work each time the WM_TIMER message comes in.
And, when you want a WM_PAINT to come in call InvalidateRect().

Related

ITfLanguageProfileNotifySink::OnLanguageChange only reporting when the window is active

I would like to know when the system language has changed in my application, even when the application is not active. So I created an implementation using ITfLanguageProfileNotifySink. But, ITfLanguageProfileNotifySink::OnLanguageChange seems to only be executing when the window is active. How can I have this execute when the window is not the top active window?
Sample Code
#include <windows.h>
#include <msctf.h>
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
class NotifyMe : protected ITfLanguageProfileNotifySink {
public:
ITfSource *m_tfSource;
DWORD m_dwCookie;
void Init();
virtual HRESULT STDMETHODCALLTYPE OnLanguageChange(LANGID langid, __RPC__out BOOL *pfAccept);
virtual HRESULT STDMETHODCALLTYPE OnLanguageChanged();
// IUnknown implementation
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef();
virtual ULONG STDMETHODCALLTYPE Release();
ULONG m_ulRefCount; ///< COM object reference count
};
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
// Register the window class.
const wchar_t CLASS_NAME[] = L"Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);
// Create the window.
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
NULL // Additional application data
);
if(hwnd == NULL)
{
return 0;
}
CoInitialize(nullptr);
NotifyMe notify;
notify.Init();
ShowWindow(hwnd, nCmdShow);
MSG msg = {};
while(GetMessage(&msg, NULL, 0, 0) > 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
void NotifyMe::Init() {
m_tfSource = NULL;
ITfInputProcessorProfiles *pProfiles;
HRESULT hr = CoCreateInstance(CLSID_TF_InputProcessorProfiles, NULL, CLSCTX_INPROC_SERVER, IID_ITfInputProcessorProfiles, (LPVOID*)&pProfiles);
if(SUCCEEDED(hr)) {
hr = pProfiles->QueryInterface(IID_ITfSource, (LPVOID*)&m_tfSource);
if(SUCCEEDED(hr)) {
hr = m_tfSource->AdviseSink(IID_ITfLanguageProfileNotifySink, (ITfLanguageProfileNotifySink*)this, &m_dwCookie);
if(FAILED(hr) || m_dwCookie == -1) {
m_tfSource->Release();
m_tfSource = NULL;
}
}
pProfiles->Release();
}
}
HRESULT STDMETHODCALLTYPE NotifyMe::OnLanguageChange(LANGID langid, __RPC__out BOOL *pfAccept)
{
if(pfAccept) *pfAccept = TRUE;
return S_OK;
}
HRESULT STDMETHODCALLTYPE NotifyMe::OnLanguageChanged()
{
OutputDebugStringA("Language Changed");
return S_OK;
}
HRESULT STDMETHODCALLTYPE NotifyMe::QueryInterface(REFIID riid, __RPC__deref_out void __RPC_FAR *__RPC_FAR *ppvObject)
{
if(!ppvObject)
return E_INVALIDARG;
if(riid == IID_IUnknown)
*ppvObject = static_cast<IUnknown*>(this);
else if(riid == IID_ITfLanguageProfileNotifySink)
*ppvObject = static_cast<ITfLanguageProfileNotifySink*>(this);
else {
*ppvObject = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
ULONG STDMETHODCALLTYPE NotifyMe::AddRef()
{
InterlockedIncrement(&m_ulRefCount);
return m_ulRefCount;
}
ULONG STDMETHODCALLTYPE NotifyMe::Release()
{
// Decrement the object's internal counter.
ULONG ulRefCount = InterlockedDecrement(&m_ulRefCount);
if(m_ulRefCount == 0)
delete this;
return ulRefCount;
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH)(COLOR_WINDOW + 1));
EndPaint(hwnd, &ps);
return 0;
}
// i want to listen to these events when the window is not in focus
case WM_SETFONT:
OutputDebugStringA("Font Changed");
break;
case WM_INPUTLANGCHANGE:
OutputDebugStringA("Language Changed - WndProc");
break;
// -- along with paint
//case WM_PAINT:
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
That's not how the ITfLanguageProfileNotifySink work. It is only for YOUR application.
What you need to understand is that every application got its own language just like yours. So when you switch to an app, the language there could be English, and when you switch back to any other app, it could be French, Japanese, Russian, or whatever language you set there.
What you want to achieve is to detect that change for "every" application, right? Then ITfLanguageProfileNotifySink is not the way.
You have to use GetKeyboardLayout instead because it allows you to get the language of any thread. But looks like you only want to detect the "active" thread, right? So you'd also want to pair it with GetWindowThreadProcessId(GetForegroundWindow(), NULL) because this would get the thread of the currently active window for you.
And for that to be integrated correctly with your application, you also have to modify your loop so that it doesn't "wait" for UI messages and block your detection scheme by using PeekMessage().
MSG msg = {};
const int period_ms = 10;
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
static LANGID currentLangId = LOWORD(::GetKeyboardLayout(0));
// Get active window thred
DWORD threadId = ::GetWindowThreadProcessId(::GetForegroundWindow(), NULL);
LANGID newLangId = LOWORD(::GetKeyboardLayout(threadId));
if (newLangId != currentLangId)
{
currentLangId = newLangId;
wchar_t szLangName[256];
GetLocaleInfo(MAKELCID(newLangId, SORT_DEFAULT), LOCALE_SENGLANGUAGE, szLangName, 256);
OutputDebugString(szLangName);
OutputDebugString(L"\n");
}
Sleep(period_ms);
}
Note that if the language changed in an app from X -> Y, it'll print 'Y', then when you switch to another app, it'll mostly print 'X' first and then in the next iteration, it'd print 'Y' right after. That's because "most" apps change their language accordingly to the previous window after you open them.
And some other applications would keep their language changes only inside the application no matter what how you change it outside of it. It is different from one app to another. But with that loop, you'll be able to detect all changes.

Struggling to create a functional custom Windows 10 frame decoration following 'Custom Window Frame Using DWM'

I'm going to risk being shot down for being an idiot but I've sunk hours into trying to follow this windows tutorial with no luck: Custom Window Frame Using DWM
My main goal is to create a custom window frame that behaves in the same was as the default one (for example, can be maximised by being dragged to the top of the screen, has the normal minimise, maximise, exit buttons) but one that sports a different colour and perhaps some menu items within the frame. I'm fairly comfortable using wxWidgets but I am an absolute beginner (if even a beginner) using the Windows API. Unfortunately I've been led here anyway because it looks like the only way to achieve what I'm after.
Going back to the abovementioned linked tutorial, I've able to extend the frame using DwmExtendFrameIntoClientArea. This yields a result that looks like this on Windows 10 using a grey brush:
My first point of confusion happens when trying to follow the instructions under "Removing the Standard Frame". This is what I get when I try and follow the code example:
This looks similar enough to the example image in the tutorial (below) but all of the window functionality is now gone! I.e. clicking any of the top-right three buttons doesn't nothing and the window cannot be clicked, moved or resized.
I had hoped that this functionality would return once I'd added in the rest of the example code, which I won't reproduce as they are contained in the page I linked under Appendix B and C (titled 'Painting the Caption Title' and 'HitTestNCA Function' respectively).
Not only did the functionality not return, the example code didn't seem to do anything... and I ended up with the window as it was after my previous edit (pictured below - to be clear):
I haven't reproduced by code here because it's exactly the same as the code found in the link except the background colour is changed to grey and I've added a static 'Test widget' to give myself a point of reference for what the coordinates were doing.
If any kind soul could please tell me what I am doing wrong, or whether my goals are even achievable using the methods I've reluctantly chosen, I would greatly appreciate some advice.
Many thanks!
I created a project according to the documentation and did not reproduce this issue, but you can compare it with your project:
#include <windows.h>
#include <stdio.h>
#include <uxtheme.h>
#include <dwmapi.h>
#include <vssym32.h>
#include <windowsx.h>
#pragma comment(lib, "dwmapi.lib")
#pragma comment(lib, "uxtheme.lib")
#define RECTWIDTH(rc) (rc.right - rc.left)
#define RECTHEIGHT(rc) (rc.bottom - rc.top)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT AppWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
LRESULT CustomCaptionProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, bool* pfCallDWP);
void PaintCustomCaption(HWND hWnd, HDC hdc);
LRESULT HitTestNCA(HWND hWnd, WPARAM wParam, LPARAM lParam);
HWND createmainwindow()
{
WNDCLASSEXW wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = GetModuleHandle(NULL);
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)GetStockObject(GRAY_BRUSH);
wcex.lpszClassName = L"My_Class";
RegisterClassExW(&wcex);
HWND hWnd = CreateWindowW(wcex.lpszClassName, L"Test", WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, wcex.hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
HWND staticctrl = CreateWindowW(L"STATIC", L"SETTINGS", SS_LEFT | WS_CHILD,
8, 27, 500, 300, hWnd, NULL, wcex.hInstance, NULL);
if (!staticctrl)
{
return FALSE;
}
ShowWindow(hWnd, SW_NORMAL);
UpdateWindow(hWnd);
ShowWindow(staticctrl, SW_NORMAL);
UpdateWindow(staticctrl);
}
void main()
{
HWND hWnd = createmainwindow();
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, 0, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
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);
}
return lRet;
}
//
// Message handler for the application.
//
LRESULT AppWinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
HRESULT hr;
LRESULT result = 0;
switch (message)
{
case WM_CREATE:
{}
break;
case WM_COMMAND:
wmId = LOWORD(wParam);
wmEvent = HIWORD(wParam);
// Parse the menu selections:
switch (wmId)
{
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;
case WM_PAINT:
{
hdc = BeginPaint(hWnd, &ps);
PaintCustomCaption(hWnd, hdc);
// Add any drawing code here...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// Message handler for handling the custom caption messages.
//
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?
fCallDWP = !DwmDefWindowProc(hWnd, message, wParam, lParam, &lRet);
// Handle window creation.
if (message == WM_CREATE)
{
RECT rcClient;
GetWindowRect(hWnd, &rcClient);
// Inform application of the frame change.
SetWindowPos(hWnd,
NULL,
rcClient.left, rcClient.top,
RECTWIDTH(rcClient), RECTHEIGHT(rcClient),
SWP_FRAMECHANGED);
fCallDWP = true;
lRet = 0;
}
// Handle window activation.
if (message == WM_ACTIVATE)
{
// Extend the frame into the client area.
MARGINS margins;
margins.cxLeftWidth = 8; // 8
margins.cxRightWidth = 8; // 8
margins.cyBottomHeight = 20; // 20
margins.cyTopHeight = 27; // 27
hr = DwmExtendFrameIntoClientArea(hWnd, &margins);
if (!SUCCEEDED(hr))
{
// Handle error.
}
fCallDWP = true;
lRet = 0;
}
if (message == WM_PAINT)
{
HDC hdc;
{
PAINTSTRUCT ps;
hdc = BeginPaint(hWnd, &ps);
PaintCustomCaption(hWnd, hdc);
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 + 0;
pncsp->rgrc[0].top = pncsp->rgrc[0].top + 0;
pncsp->rgrc[0].right = pncsp->rgrc[0].right - 0;
pncsp->rgrc[0].bottom = pncsp->rgrc[0].bottom - 0;
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;
}
}
*pfCallDWP = fCallDWP;
return lRet;
}
// Paint the title on the custom frame.
void PaintCustomCaption(HWND hWnd, HDC hdc)
{
RECT rcClient;
GetClientRect(hWnd, &rcClient);
HTHEME hTheme = OpenThemeData(NULL, L"CompositedWindow::Window");
if (hTheme)
{
HDC hdcPaint = CreateCompatibleDC(hdc);
if (hdcPaint)
{
int cx = RECTWIDTH(rcClient);
int cy = RECTHEIGHT(rcClient);
// Define the BITMAPINFO structure used to draw text.
// Note that biHeight is negative. This is done because
// DrawThemeTextEx() needs the bitmap to be in top-to-bottom
// order.
BITMAPINFO dib = { 0 };
dib.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
dib.bmiHeader.biWidth = cx;
dib.bmiHeader.biHeight = -cy;
dib.bmiHeader.biPlanes = 1;
dib.bmiHeader.biBitCount = 32;
dib.bmiHeader.biCompression = BI_RGB;
HBITMAP hbm = CreateDIBSection(hdc, &dib, DIB_RGB_COLORS, NULL, NULL, 0);
if (hbm)
{
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcPaint, hbm);
// Setup the theme drawing options.
DTTOPTS DttOpts = { sizeof(DTTOPTS) };
DttOpts.dwFlags = DTT_COMPOSITED | DTT_GLOWSIZE;
DttOpts.iGlowSize = 15;
// Select a font.
LOGFONT lgFont;
HFONT hFontOld = NULL;
if (SUCCEEDED(GetThemeSysFont(hTheme, TMT_CAPTIONFONT, &lgFont)))
{
HFONT hFont = CreateFontIndirect(&lgFont);
hFontOld = (HFONT)SelectObject(hdcPaint, hFont);
}
// Draw the title.
RECT rcPaint = rcClient;
rcPaint.top += 8;
rcPaint.right -= 125;
rcPaint.left += 8;
rcPaint.bottom = 50;
DrawThemeTextEx(hTheme,
hdcPaint,
0, 0,
L"Test",
-1,
DT_LEFT | DT_WORD_ELLIPSIS,
&rcPaint,
&DttOpts);
// Blit text to the frame.
BitBlt(hdc, 0, 0, cx, cy, hdcPaint, 0, 0, SRCCOPY);
SelectObject(hdcPaint, hbmOld);
if (hFontOld)
{
SelectObject(hdcPaint, hFontOld);
}
DeleteObject(hbm);
}
DeleteDC(hdcPaint);
}
CloseThemeData(hTheme);
}
}
// 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 + 27)
{
fOnResizeBorder = (ptMouse.y < (rcWindow.top - rcFrame.top));
uRow = 0;
}
else if (ptMouse.y < rcWindow.bottom && ptMouse.y >= rcWindow.bottom - 20)
{
uRow = 2;
}
// Determine if the point is at the left or right of the window.
if (ptMouse.x >= rcWindow.left && ptMouse.x < rcWindow.left + 8)
{
uCol = 0; // left side
}
else if (ptMouse.x < rcWindow.right && ptMouse.x >= rcWindow.right - 8)
{
uCol = 2; // right side
}
// Hit test (HTTOPLEFT, ... HTBOTTOMRIGHT)
LRESULT hitTests[3][3] =
{
{ HTTOPLEFT, fOnResizeBorder ? HTTOP : HTCAPTION, HTTOPRIGHT },
{ HTLEFT, HTNOWHERE, HTRIGHT },
{ HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT },
};
return hitTests[uRow][uCol];
}
Then,
the window cannot be clicked, moved or resized.
We cannot expect that without implementing logic to handle caption button hit testing and frame resizing/moving.
You will get the black of the background color default(BITMAPINFO dib = { 0 };). But the screen shot was still gray. (This may be because the function didn't work (failed?). Or, when you test the code before, you've commented B, C , and then when you add B, C, didn't to uncomment it).

Properly using AddClipboardFormatListener and subscribing to WM_CLIPBOARDUPDATE message

I am currently attempting to use the Windows clipboard and its notifications in my application. Specifically, I am attempting to subscribe to the WM_CLIPBOARDUPDATE window message by using the AddClipboardFormatListener() function. Previously, I had been using the SetClipboardViewer() function in order to add my window directly into the clipboard viewer chain. This had worked just fine, and I had received the relevant messages WM_DRAWCLIPBOARD and WM_DESTROYCLIPBOARD when expected. However, I would like to avoid continuing to use the clipboard chain because of how volatile it can be.
My understanding was that I would be perfectly able to receive WM_CLIPBOARDUPDATE after calling AddClipboardFormatListener(). Is there another step here that I am missing? What do I need to do to make sure that I receive this message properly? As it stands currently, I am not receiving it when performing a copy operation.
Here is an abridged example of what my code looks like:
WNDPROC override:
LRESULT CALLBACK ClipboardService::CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
switch ( pMsg->message )
{
case WM_DRAWCLIPBOARD:
// Handle clipboard available event and forward message
break;
case WM_CLIPBOARDUPDATE:
// This is never triggered
break;
case WM_DESTROYCLIPBOARD:
// Handle clipboard cleared event and forward message
break;
}
return ::CallNextHookEx( g_Hook, nCode, wParam, lParam );
}
Called by Constructor:
HRESULT ClipboardService::SetOrRefreshWindowsHook()
{
HRESULT hr = S_OK;
try
{
if (!m_bHookSet)
{
g_hwndCurrent = ::CreateWindowEx(0, "Message", "ClipboardMessageWindow", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL);
m_dwThreadID = ::GetWindowThreadProcessId(g_hwndCurrent, &m_dwProcessID);
_Module.Lock();
SetLastError(0);
g_Hook = ::SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, 0, m_dwThreadID);
//g_hwndNext = ::SetClipboardViewer(g_hwndCurrent); old way to subscribe
// This is what I expect should subscribe me to WM_CLIPBOARDUPDATE messages
if (!::AddClipboardFormatListener(g_hwndCurrent))
hr_exit(E_UNEXPECTED);
DWORD dwLastError = ::GetLastError();
g_This = this;
m_bHookSet = true;
}
}
catch (...)
{
hr_exit(E_UNEXPECTED);
}
wrapup:
return hr;
}
This is a COM interface that is called by a .NET wrapper, but I don't think that either of those two things are relevant to my problem in this case (figured I would add just in case).
You should not be using SetWindowsHookEx(WH_CALLWNDPROC) to receive messages to your own window. Use RegisterClass/Ex() instead to register your own custom window class that has your WndProc assigned to it, and then CreateWindowEx() can create an instance of that window class. No hooking needed.
HINSTANCE g_hThisInst = NULL;
HWND g_hwndCurrent = NULL;
//HWND g_hwndNext = NULL;
bool g_AddedListener = false;
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
g_hThisInst = hinstDLL;
return TRUE;
}
LRESULT CALLBACK ClipboardService::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
//g_hwndNext = ::SetClipboardViewer(hwnd);
g_AddedListener = ::AddClipboardFormatListener(hwnd);
return g_AddedListener ? 0 : -1;
case WM_DESTROY:
/*
ChangeClipboardChain(hwnd, g_hwndNext);
g_hwndNext = NULL;
*/
if (g_AddedListener)
{
RemoveClipboardFormatListener(hwnd);
g_AddedListener = false;
}
return 0;
/*
case WM_CHANGECBCHAIN:
if (g_hwndNext == (HWND)wParam)
g_hwndNext = (HWND)lParam;
else if (g_hwndNext)
SendMessage(g_hwndNext, uMsg, wParam, lParam);
break;
case WM_DRAWCLIPBOARD:
// Handle clipboard available event
if (g_hwndNext)
SendMessage(g_hwndNext, uMsg, wParam, lParam);
break;
*/
case WM_CLIPBOARDUPDATE:
// Handle clipboard updated event
return 0;
case WM_DESTROYCLIPBOARD:
// Handle clipboard cleared event and forward message
break;
}
return ::DefWindowProc(hwnd, uMsg, wParam, lParam);
}
HRESULT ClipboardService::SetOrRefreshWindowsHook()
{
try
{
if (!g_hwndCurrent)
{
WNDCLASS wndClass = {};
wndClass.lpfnWndProc = &ClipboardService::WndProc;
wndClass.hInstance = g_hThisInst;
wndClass.lpszClassName = TEXT("ClipboardMessageWindow");
if (!::RegisterClass(&wndClass))
{
DWORD dwLastError = ::GetLastError();
if (dwLastError != ERROR_CLASS_ALREADY_EXISTS)
return HRESULT_FROM_WIN32(dwLastError);
}
g_hwndCurrent = ::CreateWindowEx(0, wndClass.lpszClassName, "", 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, g_hThisInst, NULL);
if (!g_hwndCurrent)
{
DWORD dwLastError = ::GetLastError();
return HRESULT_FROM_WIN32(dwLastError);
}
g_This = this;
}
}
catch (...)
{
return E_UNEXPECTED;
}
return S_OK;
}

c++ Win32 Api GetMessage closing program inside thread

I'm building a interface using the win32 api and I wanted to manage all the suff in another class with a thread to keep doing work in the main.
I have this code:
WindowManager.h
class UIManager::WindowManager {
private:
//Class data
HINSTANCE hInstance;
WNDCLASSEX winClass; /* Data structure for the windowclass */
MSG msg;
HWND hwnd;
//Window Data
LPCSTR wiName;
int startX = 250;
int startY = 150;
int endX = 544;
int endY = 375;
//Private managers
void makeWindow();
public:
WindowManager(HINSTANCE & hInstance, std::string wiName = "Window Name");
void show();
};
WindowManager.c (I use bind because I have the class inside a namespace and that is the only way I found that thread allows me to compile instead of throwing an error)
#include "WindowManager.h"
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
//Private
//
void UIManager::WindowManager::makeWindow()
{
int ms = GetMessage(&msg, 0, 0, 0); //I do this to see if it gets to
while (GetMessage(&msg, NULL, 0, 0)) { //this part, but never happens
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
//Public
//
UIManager::WindowManager::WindowManager(HINSTANCE & hInstance, std::string wiName)
{
this->wiName = wiName.c_str();
this->hInstance = hInstance;
winClass.cbSize = sizeof(winClass);
winClass.hInstance = hInstance;
winClass.lpszClassName = this->wiName;
winClass.lpfnWndProc = WndProc; //Execution callback
//Load default editable ellements
winClass.hCursor = LoadCursor(0, IDC_ARROW); /*Default*/
winClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); /*Default*/ //Alt+Tab Dialog
winClass.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
winClass.lpszMenuName = NULL; /* No menu */
RegisterClassEx(&winClass);
//Create Window
hwnd = CreateWindowEx(
0,
this->wiName, /* Title Class */
this->wiName, /* Title Text */
WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU | WS_VISIBLE | WS_OVERLAPPEDWINDOW,
startX, /* X Start */
startY, /* Y Start */
endX, /* The programs width */
endY, /* and height in pixels */
HWND_DESKTOP, /* The window is a child-window to desktop */
NULL, /* No menu */
hInstance, /* Program Instance handler */
NULL
);
SetWindowPos(hwnd, 0, 0, 0, 20, 20, 0);
}
void UIManager::WindowManager::show()
{
std::thread listener(std::bind(&WindowManager::makeWindow, this));
listener.detach();
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{ //This part is executed
//...blablabla
break;
}
case WM_COMMAND:
{//This part is never executed
//...blablabla
break;
}
case WM_DESTROY:
{//This part is never executed
//...blabla
PostQuitMessage(0);
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}
The window execute and show properly and even execute WM_CREATE, but when the "GetMessage" is executed it instantly end the program without throwing any error or something like that.
I added an int with the value of the GetMessage to see if it gets to the "while" in debug mode but it happens.
Is there a way to see the error that may be throwing or prevent it from closing the program? Or is it that I'm doing wrong calling "GetMessage" from inside a thread?
your code has several problems:
You create window in one thread, but try to run meesage loop in another thread. GetMessage only handles messages of windows belonging to the calling thread.
You never wait for background thread processing messages and instead detach from it and continue execution in main thread probably ending application.
You don't inspect the value returned by GetMessage and use it as boolean.
If you want to make background ui thread you need to move all window creation code there and call join on thread. And message loop should look like this:
::MSG msg;
for(;;)
{
auto const res{::GetMessage(&msg, NULL, 0, 0))};
if(0 < res)
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
else if(0 == res)
{ // PostQuitMessage was called...
break;
}
else
{ // an error occurred...
break;
}
}

C++ Windows Not Getting Any Messages

I am creating a simple window but when I see the window being created and closes it, not WM_QUIT message is ever gotten. Here's some code:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR cmdLine, int cmdShow)
{
cWindowApplication app(hInstance);
const long width = 1024L;
const long height = 768L;
if (app.CreateWindowApplication(width, height) == false)
{
MessageBox(NULL, "Unable to create OpenGL Window", "An error occurred", MB_ICONERROR | MB_OK);
app.DestroyWindowApplication();
return 1;
}
return app.MainLoop();
}
Here's the CreateWindowApplication(int, int) function:
bool cWindowApplication::CreateWindowApplication(long width, long height, bool full_screen /*= false*/)
{
DWORD dwExStyle; // Window Extended Style
DWORD dwStyle; // Window Style
mWindowRect.left = 0L; // Set Left Value To 0
mWindowRect.right = width; // Set Right Value To Requested Width
mWindowRect.top = 0L; // Set Top Value To 0
mWindowRect.bottom = height; // Set Bottom Value To Requested Height
mFullScreen = full_screen;
// fill out the window class structure
const char* class_name = "MyClass";
mWindowClass.cbSize = sizeof(WNDCLASSEX);
mWindowClass.style = CS_HREDRAW | CS_VREDRAW;
mWindowClass.lpfnWndProc = cWindowApplication::StaticWindowsProcessCallback;
mWindowClass.cbClsExtra = 0;
mWindowClass.cbWndExtra = 0;
mWindowClass.hInstance = mhInstance;
mWindowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); // default icon
mWindowClass.hCursor = LoadCursor(NULL, IDC_ARROW); // default arrow
mWindowClass.hbrBackground = NULL; // don't need background
mWindowClass.lpszMenuName = NULL; // no menu
mWindowClass.lpszClassName = class_name;
mWindowClass.hIconSm = LoadIcon(NULL, IDI_WINLOGO); // windows logo small icon
// register the windows class
if (!RegisterClassEx(&mWindowClass))
{
return false;
}
if (mFullScreen == true) //If we are Full Screen, we need to change the display mode
{
DEVMODE dmScreenSettings; // device mode
memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmPelsWidth = width; // screen width
dmScreenSettings.dmPelsHeight = height; // screen height
dmScreenSettings.dmBitsPerPel = BITS_PER_PIXEL; // bits per pixel
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
// setting display mode failed, switch to windowed
MessageBox(NULL, "Display mode failed", NULL, MB_OK);
mFullScreen = false;
}
}
if (mFullScreen == true) // Are We Still In Full Screen Mode?
{
dwExStyle = WS_EX_APPWINDOW; // Window Extended Style
dwStyle = WS_POPUP; // Windows Style
//ShowCursor(false); // Hide Mouse Pointer
}
else
{
dwExStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // Window Extended Style
dwStyle = WS_OVERLAPPEDWINDOW; // Windows Style
}
AdjustWindowRectEx(&mWindowRect, dwStyle, false, dwExStyle); // Adjust Window To True Requested Size
// class registered, and create our window
mHWND = CreateWindowEx(NULL, // extended style
class_name, // class name
"My Windows", // application name
dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, // x,y coordinate
mWindowRect.right - mWindowRect.left,
mWindowRect.bottom - mWindowRect.top, // width, height
NULL, // handle to parent
NULL, // handle to menu
mhInstance, // application instance
this); // this pointer to call member functions
// check if window creation failed (hwnd would equal NULL)
if (mHWND == false)
{
return false;
}
mHDC = GetDC(mHWND);
ShowWindow(mHWND, SW_SHOW); // display the window
UpdateWindow(mHWND); // update the window
return true;
}
Basically after this function call, the CreateWindowEx() function will call StaticWindowProcessCallback() that looks like this:
LRESULT cWindowApplication::StaticWindowsProcessCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
cWindowApplication* win_app = NULL;
if (msg == WM_CREATE)
{
//Creation event
//Get the pointer we pass during CreateWindowApplication() call
win_app = (cWindowApplication*)((LPCREATESTRUCT)lParam)->lpCreateParams;
//Associate window pointer with the hwnd for the other events to access
SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)win_app);
}
else
{
//Non-creation event
win_app = (cWindowApplication*)GetWindowLongPtr(wnd, GWLP_USERDATA);
if (win_app != NULL)
{
return DefWindowProc(wnd, msg, wParam, lParam);
}
}
//call member
return win_app->WindowsProcessCallback(wnd, msg, wParam, lParam);
}
Finally, the last line of this function calls the member function WindowProcessCallback() that looks like this:
LRESULT cWindowApplication::WindowsProcessCallback(HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
mHDC = GetDC(wnd);
SetupPixelFormat();
//Set the version that we want, in this case 3.0
int attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, 0 }; //zero indicates the end of the array
//Create temporary context so we can get a pointer to the function
HGLRC tmp_context = wglCreateContext(mHDC);
//Make it current
wglMakeCurrent(mHDC, tmp_context);
PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribsARB = NULL;
wglCreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
if (wglCreateContextAttribsARB == NULL)
{
//No OpenGL 3.0, back to 2.0
mHGLRC = tmp_context;
}
else
{
//Create OpenGL 3.0
mHGLRC = wglCreateContextAttribsARB(mHDC, 0, attribs);
//Delete the temp context
wglDeleteContext(tmp_context);
}
//Make OpenGL 3.0
wglMakeCurrent(mHDC, mHGLRC);
mIsRunning = true;
}
break;
case WM_QUIT:
case WM_DESTROY:
case WM_CLOSE:
wglMakeCurrent(mHDC, NULL);
wglDeleteContext(mHGLRC);
mIsRunning = false;
PostQuitMessage(0); //Send a WM_QUIT message
return 0;
default:
break;
}
return DefWindowProc(wnd, msg, wParam, lParam);
}
As you can see, there are some message processing code there ... but other than the WM_CREATE, no other cases are being hit. After the WM_CREATE message being sent, the function MainLoop() is being called that looks like this:
int cWindowApplication::MainLoop()
{
while (mIsRunning == true)
{
ProcessWindowsMessages();
}
DestroyWindowApplication();
return 0;
}
Basically the ProcessWindowsMessages() function does not get any message after the window closes ... I have to press stop VS from running in order to kill the process. The ProcessWindowsMessages() function looks like this:
void cWindowApplication::ProcessWindowsMessages()
{
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
This logic in StaticWindowsProcessCallback looks backwards:
if (win_app != NULL)
{
return DefWindowProc(wnd, msg, wParam, lParam);
}
If you don't have a pointer to the window wrapper object, you'll need to call DefWindowProc. So that should happen if (win_app == NULL). This is to handle a handful of messages that are sent before WM_CREATE. As a result of this, your code has undefined behavior on messages processed before WM_CREATE, and discards (by applying default processing) all messages after WM_CREATE.
It would be even better to use WM_NCCREATE for setting up the link, though. As well, win_app is not a very good name for this, maybe win_obj or something.
You also shouldn't handle WM_QUIT in your window procedure, since it isn't sent to a window. And the default behavior of WM_CLOSE should be fine, it will call DestroyWindow which will trigger WM_DESTROY.
But the first, failure to forward any messages after WM_CREATE to your window procedure, likely explains your lack of WM_QUIT in the main message loop.