why adding text to RichEdit window freezes it? - c++

Until I touch the richedit window by mouse its contents are live updated, but hovering the mouse over it turns arrow into hourglass cursor. The window then doesn't react to three or four consequent tries to move it by title bar. When it finally does react to mouse dragging it moves fine but stops refreshing its contents and the title bar becomes empty. Similar effect is when I try to click the client area of the window. This time after a few clicks with no reaction window also stops updating and its title bar turns to (not responding).
When the loop eventually stops the program comes back window updates and comes back 'alive'. What to do to be able to manipulate the window (and see it's updating content) while it's client area is updated ?
#include <windows.h>
#include <sstream>
int main() {
using namespace std;
LoadLibrary("Msftedit.dll");
HWND richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
L"RICHEDIT50W",
L"window text",
WS_SYSMENU | WS_VSCROLL | ES_MULTILINE | ES_NOHIDESEL | WS_VISIBLE,
50, 50, 500, 500,
NULL, NULL, NULL, NULL
);
for (int i = 0 ; i<100000; i++) {
wstringstream wss;
wss << i << L", ";
SendMessageW(richeditWindow, EM_REPLACESEL, FALSE, (LPARAM) wss.str().c_str());
}
MSG msg;
while( GetMessageW( &msg, richeditWindow, 0, 0 ) ) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}

You are populating the rich edit window in a tight loop, and not servicing your message queue. Unless your process is regularly attending to its message queue, the system thinks that your app has stopped responding. Well, it has stopped responding!
In order to keep your application responsive, you must pump your message queue. I don't really know what your real program is trying to do. If you wanted to put that text into a rich edit, you'd do so with a single EM_REPLACESEL message.
If you really do have a long running task then it belongs on a different thread. Then you have to deal with synchronizing back to the GUI thread. If all you do is call SendMessage then the system takes care of synchronizing that.
The bottom line is that must pump your message queue in a timely fashion.

Found answer this is my revised code, look at PeekMessageW and DispatchMessageW.
#include <windows.h>
#include <iostream>
#include <sstream>
int main() {
using namespace std;
LoadLibrary("Msftedit.dll");
HWND richeditWindow = CreateWindowExW (
WS_EX_TOPMOST,
L"RICHEDIT50W",
L"window text",
WS_SYSMENU | WS_VSCROLL | ES_MULTILINE | ES_NOHIDESEL | WS_VISIBLE,
50, 50, 500, 500,
NULL, NULL, NULL, NULL
);
MSG msg;
for (int i = 0 ; i<100000; i++) {
wstringstream wss;
wss << i << L", ";
SendMessageW(richeditWindow, EM_REPLACESEL, FALSE, (LPARAM) wss.str().c_str());
if (PeekMessageW(&msg, richeditWindow, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
while( GetMessageW( &msg, richeditWindow, 0, 0 ) ) {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}

Related

C++ when pressing close: minimize to system tray and keep running

I have an app in c++ for windows, which should minimize the window of the command line when the user presses the close button. It shouldn't be in the taskbar anymore and have an icon in the system tray.
What I mean is: when user presses close button, the program should only "hide" like i described.
I can only manage to make the program have an icon in the tray while running, but can't make it stay running when x is pressed
Thanks for help!
this is my code so far:
#include <iostream>
#include <Windows.h> // needed for console window and system tray functionality
// global variables
NOTIFYICONDATA trayIcon; // structure for the tray icon
HWND hwnd = GetConsoleWindow(); // handle to the console window
// function prototypes
void minimizeToTray(); // function to minimize the console window to the system tray
int main()
{
// set up the tray icon
trayIcon.cbSize = sizeof(NOTIFYICONDATA);
trayIcon.hWnd = hwnd;
trayIcon.uID = 1;
trayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
trayIcon.hIcon = (HICON)LoadImage(NULL, "icon.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE); // specify the icon file
trayIcon.uCallbackMessage = WM_USER + 1; // message identifier for tray icon clicks
trayIcon.uVersion = NOTIFYICON_VERSION_4;
strcpy_s(trayIcon.szTip, "Program Running");
// add the tray icon to the system tray
Shell_NotifyIcon(NIM_ADD, &trayIcon);
std::cout << "Program running..." << std::endl;
// set up a message loop to handle tray icon clicks and window messages
MSG msg;
while (true) // infinite loop
{
// check for messages
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
{
// if the user clicks the close button, minimize the window to the tray
if (msg.message == WM_CLOSE)
{
minimizeToTray();
continue; // skip the rest of the message loop
}
// if the user clicks the tray icon, restore the window
if (msg.message == WM_USER + 1)
{
ShowWindow(hwnd, SW_RESTORE);
}
// pass the message to the default window procedure
DispatchMessage(&msg);
}
// do other tasks here
}
// remove the tray icon before exiting the program
Shell_NotifyIcon(NIM_DELETE, &trayIcon);
return 0;
}
// function to minimize the console window to the system tray
void minimizeToTray()
{
// hide the console window
ShowWindow(hwnd, SW_HIDE);
// update the tray icon
trayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
Shell_NotifyIcon(NIM_MODIFY, &trayIcon);
}
When the user "closes" the window it should just hide and not close entirely, like ms teams or discord do
After testing your code, it definitely fails. It cannot get the message WM_CLOSE. Just as Hans Passant said, you can use SetConsoleCtrlHandler() to attached to the console receive the signal.
Here is my code. It runs well.
It used ShellExecuteW() API to restart the program to implement minimization.
Setting SW_MINIMIZE, it will minimize in the task bar.
Setting SW_HIDE, it will minimize in the system tray. But it cannot be opened again.
#include <Windows.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <iostream>
NOTIFYICONDATA trayIcon;
HWND hwnd = GetConsoleWindow();
BOOL WINAPI ConsoleHandler(DWORD CEvent)
{
char mesg[128];
switch (CEvent)
{
case CTRL_C_EVENT:
MessageBox(NULL,
L"CTRL+C received!", L"CEvent", MB_OK);
break;
case CTRL_BREAK_EVENT:
MessageBox(NULL,
L"CTRL+BREAK received!", L"CEvent", MB_OK);
break;
case CTRL_CLOSE_EVENT:
ShellExecuteW(NULL, L"open", L"yourexe.exe", NULL, NULL, SW_MINIMIZE);
break;
case CTRL_LOGOFF_EVENT:
MessageBox(NULL,
L"User is logging off!", L"CEvent", MB_OK);
break;
case CTRL_SHUTDOWN_EVENT:
MessageBox(NULL,
L"User is logging off!", L"CEvent", MB_OK);
break;
}
return TRUE;
}
int main()
{
trayIcon.cbSize = sizeof(NOTIFYICONDATA);
trayIcon.hWnd = hwnd;
trayIcon.uID = 1;
trayIcon.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
trayIcon.hIcon = (HICON)LoadImage(NULL, L"icon1.ico", IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
trayIcon.uCallbackMessage = WM_USER + 1;
trayIcon.uVersion = NOTIFYICON_VERSION_4;
Shell_NotifyIcon(NIM_ADD, &trayIcon);
std::cout << "Program running..." << std::endl;
MSG msg;
if (SetConsoleCtrlHandler(
(PHANDLER_ROUTINE)ConsoleHandler, TRUE) == FALSE)
{
printf("Unable to install handler!\n");
return -1;
}
while (true)
{
while (PeekMessage(&msg, hwnd, 0, 0, PM_REMOVE))
{
if (msg.message == WM_CLOSE)
{
continue;
}
if (msg.message == WM_USER + 1)
{
ShowWindow(hwnd, SW_RESTORE);
}
DispatchMessage(&msg);
}
}
Shell_NotifyIcon(NIM_DELETE, &trayIcon);
return 0;
}

How do I add vertical scrollbar to a fullscreen win32 console?

When I set my win32 console to full screen, the vertical scrollbar disappears. When the text goes to the bottom of the screen, it does not scroll up. Newly written output is not presented to the user because it is below.
This is the style:
if (isFullScreen)
{
// Set the full screen window style.
style = GetWindowLong(handle, GWL_STYLE);
style &= ~(WS_BORDER | WS_CAPTION | WS_THICKFRAME | WS_OVERLAPPEDWINDOW);
SetWindowLong(handle, GWL_STYLE, style);
// Minimalize, then show maximized to avoid the cursor blink bug in conhost.exe.
ShowWindow(handle, SW_MINIMIZE);
ShowWindow(handle, SW_SHOWMAXIMIZED);
// Set the font size
setFontSize(fontSize);
}
I searched the web but it's understandably not common.
How can I add the vertical scrollbar to this while in full screen?
On my computer, I can realize the appearance of full screen console and vertical scrollbar. My system is Win10, using vs2017.
This is my code.
#include "pch.h"
#include <iostream>
#include <Windows.h>
void full_screen()
{
HWND hwnd = GetForegroundWindow();
int cx = GetSystemMetrics(SM_CXSCREEN); /* Screen width pixels */
int cy = GetSystemMetrics(SM_CYSCREEN); /* Screen Height Pixel */
LONG l_WinStyle = GetWindowLong(hwnd, GWL_STYLE); /* Get window information */
/* Set window information to maximize the removal of title bar and border*/
SetWindowLong(hwnd, GWL_STYLE, (l_WinStyle | WS_POPUP | WS_MAXIMIZE) & ~WS_CAPTION & ~WS_THICKFRAME & ~WS_BORDER);
SetWindowPos(hwnd, HWND_TOP, 0, 0, cx, cy, 0);
}
int main()
{
full_screen();
while(1)
{
std::cout << "Hello World!\n";
}
return 0;
}
Debug Result:
The official way to set a console to fullscreen is to call SetConsoleDisplayMode().
Under Windows 10 Pro Version 1803 the following code shows a vertical scrollbar without further ado:
#include <iostream>
#include <windows.h>
int main()
{
HANDLE const hConsole = ::GetStdHandle( STD_OUTPUT_HANDLE );
if( hConsole == INVALID_HANDLE_VALUE ||
! ::SetConsoleDisplayMode( hConsole, CONSOLE_FULLSCREEN_MODE, nullptr ) )
{
DWORD const err = ::GetLastError();
std::cerr << "Failed to set console fullscreen mode. System error: " << err << "\n";
return 1;
}
for( int i = 0; i < 200; ++i )
{
std::cout << "Hello World!\n";
}
return 0;
}
Note that SetConsoleDisplayMode() can fail if the process is not attached to a console (e. g. by passing the flag CREATE_NO_WINDOW to CreateProcess()) or if STDOUT is redirected to a file.

How to correctly retrieve Unicode text from a control?

Here is an example of Unicode: I avoided to use a win32 application for for brevity sake:
In main I created an edit control, a button to retrieve text from it and adds it to the listbox when pressed. So I used an object of struct MSG and blocked in a while loop peeking the messages from the message queue.
int main(){
// An edit control where I can add any unicode text to.
HWND hEdit = CreateWindowExW(WS_EX_CLIENTEDGE,
L"Edit", L"你好! 你好吗?", WS_BORDER | WS_VISIBLE | WS_SYSMENU,
200, 100, 250, 70, 0, 0, GetModuleHandle(NULL), NULL);
// A listobx to receive the content of the edit control when pressing the button get text.
HWND hLstBx = CreateWindowExW(WS_EX_CLIENTEDGE,
L"Listbox", NULL, WS_BORDER | WS_VISIBLE | WS_OVERLAPPEDWINDOW,
500, 100, 170, 400, 0, 0, GetModuleHandle(NULL), NULL);
// A button when pressed adds the content of the edit to the listbox.
HWND hGetText = CreateWindowExW(WS_EX_CLIENTEDGE,
L"Button", L"中文", WS_BORDER | WS_VISIBLE,
450, 300, 100, 70, 0, 0, GetModuleHandle(NULL), NULL);
// msg struct to pass to GetMessage to receive the messages from the queue.
MSG msg;
// blocking and getting messages from the queue.
while (GetMessageW(&msg, 0, 0, 0)) {
// some virtual keys translation.
TranslateMessage(&msg);
// sending the message to the specified window.
DispatchMessageW(&msg);
// Now after the messages sent to the target Window I check which control the message has been passed to, So if it is the button then:
if (msg.message == WM_LBUTTONDOWN &&
msg.hwnd == hGetText) {
std::wstring wstrBuff;
int txtLen = SendMessageW(hEdit, WM_GETTEXTLENGTH, 0, 0);
// SendMessageW(hEdit, WM_GETTEXT, txtLen + 1, (LPARAM)wstrBuff.c_str());
// MessageBoxW(0, wstrBuff.c_str(), 0, 0);
wchar_t lpTxt[MAX_PATH];
SendMessageW(hEdit, WM_GETTEXT, MAX_PATH, (LPARAM)lpTxt);
SendMessageW(hLstBx, LB_ADDSTRING, 0, (LPARAM)lpTxt);
MessageBoxW(0, lpTxt, L"你好! 你好吗?", 0);
//delete[]lpTxt;
}
}
std::wcout << std::endl << std::endl;
std::wcin.get();
return 0;
}
Every thing works fine except: If I un-comment the lines above I get run-time error facing the assertion message showing me the txtLen and the size of the text of the edit control. Is this because there's some string overlapping?
If I enter a small text it works fine but with a text about 14 characters I get the error.
Also is that the right way to pass std::wstring.c_str() to SendMessageW() to get the text?
One last question: How to correctly and effectively retrieve Unicode text from a control? How to use LPWSTR with dynamic memory: I don't want to exhaust stack.
NB: I saved the source file as utf8 /BOM otherwise I get unreadable characters. Thanks to the members who helped me about that.
Sending (LPARAM)wstrBuff.c_str() will return a pointer to read-only buffer with a single null symbol, not a buffer of txtLen + 1 symbols. If you are using latest VS (with C++17 standard support) you can modify your code to supply a proper pointer:
std::wstring wstrBuff;
wstrBuff.resize(txtLen + 1);
const LRESULT copied_symbols_count = SendMessageW
(
hEdit
, WM_GETTEXT
, static_cast<WPARAM>(wstrBuff.size())
, reinterpret_cast<LPARAM>(wstrBuff.data())
);
if(copied_symbols_count == txtLen)
{
assert(L'\0' == wstrBuff.back());
wstrBuff.pop_back(); // get rid of terminating null
}
else
{
wstrBuff.clear(); // something went wrong...
}
Note that C++17 standard adds non-const-qualified wstring::data() method that can be safely used to obtain a pointer to writable buffer.

C++ CreateWindow: button position gets offset at window maximize

I have a weird problem over here. I created a DLL proxy for Spotify so I can "overlay" a button onto it. Basicially, thats how it works:
DllMain
-> Creates CMain class
-> Creates CToggleButton class
-> Hooks the button onto the Spotify window
It has two methods, one static one which I use for the thread since threads can't call member functions, and one non-static function which gets called by the member function.
With this, I create the thread and pass an instance of the CToggleButton class via lpParam:
CreateThread(0, NULL, WindowThreadStatic, (void*)this, NULL, NULL);
Then, the WindowThreadStatic function:
DWORD WINAPI CToggleButton::WindowThreadStatic(void* lpParam)
{
return ((CToggleButton*)lpParam)->WindowThread();
}
And the main window thread function inside the class:
DWORD CToggleButton::WindowThread()
{
MSG msg;
hButton = CreateWindowA("BUTTON", "Test", (WS_VISIBLE | WS_CHILD), 0, 0, 100, 20, parenthWnd, NULL, hInst, NULL);
bool bQueueRunning = true;
while (bQueueRunning)
{
if (PeekMessage(&msg, parenthWnd, 0, 0, PM_REMOVE))
{
switch (msg.message)
{
case WM_QUIT:
bQueueRunning = false;
break;
case WM_LBUTTONDOWN:
if (msg.hwnd == hButton)
{
MessageBoxA(parenthWnd, "Button!", "Button", MB_OK);
continue;
}
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Sleep(10);
}
return 0;
}
As you can see, this also contains the message loop for the button (I didn't use GetMessage() here because it was very unresponsive so I decided to use PeekMessage() together with a 10ms delay, which works fine.)
Little picture to show how it looks like:
All great, but if I maximize the window, the button disappears. When I minimize and maximize the window a few times, the button can be seen again, but with very weird coordinates (not the original 0,0 I gave him).
So what is my problem here? Why do the coordinates get offset?
Thanks for reading my long post :)

CreateWindowEx posts WM_SIZE?

CreateWindowEx API really posts WM_SIZE message?
When I create a window via CreateWindowEx as full screen mode,
CreateWindowEx posts WM_SIZE but window mode doesn't.
My code sets the window style like this :
if(bFullscr)
{
//When the window is in full screen mode.
nStyle = WS_POPUP;
nExtraStyle = WS_EX_APPWINDOW;
}
else
{
//Otherwise.
nStyle = WS_OVERLAPPEDWINDOW;
nExtraStyle = (WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
}
And changes display settings like this (full screen mode only) :
if(bFullscr)
{
DEVMODE sScrSet;
memset(&sScrSet, 0, sizeof(DEVMODE));
sScrSet.dmSize = sizeof(DEVMODE);
sScrSet.dmPelsWidth = nWidth;
sScrSet.dmPelsHeight = nHeight;
sScrSet.dmBitsPerPel = nColorBit;
sScrSet.dmFields = (DM_BITSPERPEL | DM_PELSHEIGHT | DM_PELSWIDTH);
if(ChangeDisplaySettings(&sScrSet, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
{
//Error routine.
}
}
I'm really wonder why CreateWindowEx posts WM_SIZE message selectively.
If you simply want to resize the window, somewhere in your code you should have ShowWindow(hWnd, nCmdShow); change it as follows:
ShowWindow(hWnd, SW_SHOWDEFAULT);//show normal
ShowWindow(hWnd, SW_SHOWMAXIMIZED);//show maximized (full screen)
SetWindowPos(hWnd, NULL, 10, 10, 300, 300, SWP_SHOWWINDOW);//show at specific position
Also you could use WS_MAXIMIZE in CreateWindow, but that could complicate things. Window usually has WS_OVERLAPPEDWINDOW or WS_POPUP|WS_CAPTION|WS_SYSMENU. You should pick one and keep it simple.
When Window size changes, it receives WM_SIZE, you can catch that and examine it.