ShowWindow() showing blank app until is interacted with via mouse/keyboard after restoring from systray in C++ - Flutter App - c++

I've been wanting to incorporate systray functionality into my Flutter app so I went to modify the native C++ code that initiates the window etc to see if I could hook into it.
Despite not having much prior experience in C++ I have been able to create an icon for my app in the systray with a menu that allows the window to be shown again when hidden (using ShowWindow(hwnd, SW_HIDE);) and to quit entirely.
However when an option in my systray menu is selected to show the window again using ShowWindow(hwnd, SW_NORMAL); after being hidden, the app stays blank like this:
Then, when the window is finally interacted with, the contents of the window show again:
Here is the code that I have added so far to my win32_window.cpp (from a default Flutter application). I haven't included the entire functions because I thought it would make things less clear, but I will also attach the full win32_window.cpp at the end of this post.
Win32Window::CreateAndShow():
//Systray:
HICON hMainIcon;
hMainIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_APP_ICON));
nidApp.cbSize = sizeof(NOTIFYICONDATA); // sizeof the struct in bytes
nidApp.hWnd = (HWND) window; //handle of the window which will process this app. messages
nidApp.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; //ORing of all the flags
nidApp.hIcon = hMainIcon; // handle of the Icon to be displayed, obtained from LoadIcon
nidApp.uCallbackMessage = WM_USER_SHELLICON;
StringCchCopy(nidApp.szTip, ARRAYSIZE(nidApp.szTip), L"All Platforms Test");
Shell_NotifyIcon(NIM_ADD, &nidApp);
return OnCreate();
Win32Window::WndProc():
if (message == WM_NCCREATE) { ... }
else if (message == WM_USER_SHELLICON) { //interacting with systray icon
if (LOWORD(lparam) == WM_RBUTTONDOWN) { //right clicked
POINT lpClickPoint;
GetCursorPos(&lpClickPoint);
hPopMenu = CreatePopupMenu();
InsertMenu(hPopMenu,0xFFFFFFFF,MF_BYPOSITION|MF_STRING,IDM_SHOW,_T("Show"));
InsertMenu(hPopMenu,0xFFFFFFFF,MF_BYPOSITION|MF_STRING,IDM_EXIT,_T("Quit"));
SetForegroundWindow(window);
TrackPopupMenu(hPopMenu,TPM_LEFTALIGN|TPM_LEFTBUTTON|TPM_BOTTOMALIGN,lpClickPoint.x, lpClickPoint.y,0,window,NULL);
}
else if (LOWORD(lparam) == WM_LBUTTONDOWN) { //left clicked
ShowWindow(window, SW_NORMAL);
//LOOK: works but shows blank screen until is interacted with (mouse enters or key is pressed etc)
}
}
else if (message == WM_COMMAND) { //if message is a command event such as a click on the exit menu option
int wmId;
wmId = LOWORD(wparam);
if (wmId == IDM_EXIT) { //if quit has been pressed
Shell_NotifyIcon(NIM_DELETE,&nidApp);
DestroyWindow(window);
}
else if (wmId == IDM_SHOW) {
ShowWindow(window, SW_NORMAL);
//LOOK: works but shows blank screen until is interacted with (mouse enters or key is pressed etc)
}
Win32Window::MessageHandler():
switch (message) {
...
case WM_CLOSE: //stop window from closing normally, can only be closed when DestroyWindow() is run from systray
//Hide window and continue running in background.
ShowWindow(hwnd, SW_HIDE);
return 0;
}
Link to full win32_window.cpp here.
What's going on here? I thought using UpdateWindow() would help but then I realise the app is painted upon ShowWindow() anyway. My guess is that this has something to do with Flutter's run loop being blocked but I can't figure out where to go next, especially considering I usually don't dabble in C++ but just wanted to add an extra feature to my app when running on Windows.
Any help would be greatly appreciated, thanks.

Ok so I've worked out why it wasn't working. When closing the window, I couldn't just use SW_HIDE, but SW_MINIMIZE too. Otherwise attempting to redraw the window wouldn't work correctly:
ShowWindow(hwnd, SW_MINIMIZE);
ShowWindow(hwnd, SW_HIDE);
After that, when showing the window it got drawn but wasn't the active window, but adding SetForegroundWindow() fixed that:
ShowWindow(window, SW_NORMAL);
SetForegroundWindow(window);
Thanks for everyone's help :)

Related

Shell_NotifyIcon: Tray icon show both - popup menu and taskbar menu [duplicate]

I create a notification icon with:
notifyIcon.cbSize = sizeof(NOTIFYICONDATA);
notifyIcon.hWnd = mainWnd;
notifyIcon.uID = 100;
notifyIcon.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
notifyIcon.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LOGO));
notifyIcon.dwState = NIS_SHAREDICON;
notifyIcon.uVersion = NOTIFYICON_VERSION;
notifyIcon.uTimeout = 15000;
notifyIcon.uCallbackMessage = APP_MSG_TRAY;
wcscpy_s(notifyIcon.szTip, 127, WTXT_APP_TRAY_TOOLTIP);
Shell_NotifyIcon(NIM_ADD, &notifyIcon);
Shell_NotifyIcon(NIM_SETVERSION, &notifyIcon);
And have a context menu popup on WM_RBUTTONDOWN and WM_CONTEXTMENU like this:
MENUITEMINFO separatorBtn = {0};
separatorBtn.cbSize = sizeof(MENUITEMINFO);
separatorBtn.fMask = MIIM_FTYPE;
separatorBtn.fType = MFT_SEPARATOR;
HMENU hMenu = CreatePopupMenu();
if(hMenu) {
InsertMenu(hMenu, -1, MF_BYPOSITION, APP_OPEN_OPTIONS, WTXT_OPTIONS);
InsertMenuItem(hMenu, -1, FALSE, &separatorBtn);
InsertMenu(hMenu, -1, MF_BYPOSITION, APP_MSG_EXIT, WTXT_EXIT);
POINT pt;
GetCursorPos(&pt);
SetForegroundWindow(mainWnd);
TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, mainWnd, NULL);
PostMessage(mainWnd, WM_NULL, 0, 0);
DestroyMenu(hMenu);
}
It works fine, but the context menu doesn't disappear always. Sometimes (often) if you have ie. winamp and my app icons in system tray, if you right click my app and winamp afterwards, bot menus will appear, and my menu won't disappear automatically until you click an item.
Any ideas?
thanks...
To display a context menu for a notification icon, the current window must be the foreground window before the application calls TrackPopupMenu or TrackPopupMenuEx. Otherwise, the menu will not disappear when the user clicks outside of the menu or the window that created the menu (if it is visible).
SetForegroundWindow(hDlg);
TrackPopupMenu( hSubMenu,
TPM_RIGHTBUTTON,
pt.x,
pt.y,
0,
hDlg,
NULL);
Do not catch WM_RBUTTONDOWN but WM_RBUTTONUP. And of course do not handle both WM_RBUTTONUP and WM_CONTEXTMENU, since they will both get handled and you'd show the context menu twice for every right-click.
Showing the menu twice would have the effect you describe: the menu shows up, but doesn't seem to disappear (because it shows up again right away a second time).
There are apps to try to hack around the restrictions of the notification area (tray) API. They'll hook the Explorer window and listen for Windows messages. That lets them do stuff that isn't otherwise possible but it inevitably destabilizes other apps. Getting two context menus is a sure sign of this kind of trouble.
You've got a good lead on what kind of program may do this, it's got an icon. Kill them one by one until you find the evil-doer. Not much you can do about it probably, other than not running it or complaining to the vendor.
You seem to already have both of the documented bugfixes (SetForegroundWindow & WM_NULL) I'd say anything beyond this is a bug in windows.
If you really want to do hacky things, you could probably get the menu window handle in WM_INITMENU* (And I don't mean the HMENU, but the HWND for the menu) and hide that window.

Sending BM_CLICK message to Windows 10 application not working

I have an installation dialog (made with nsis) that has two buttons (install and cancel). I'm trying to write automated tests for the install process using low level win32 api. To click on the button(s) I use the following code:
char windowName[] = "Desktop Application Setup";
char cancelButtonText[] = "Cancel";
HWND hWndMainWindow = NULL;
HWND hButton = NULL;
hWndMainWindow = FindWindow(NULL, windowName);
if (hWndMainWindow)
{
hButton = FindWindowEx(hWndMainWindow, NULL, NULL, cancelButtonText);
if (hButton)
{
SendMessage(hButton, BM_CLICK, 0, 0);
}
}
On Windows 7, this works perfectly. On Windows 10, it simply does nothing. It finds the button, it sends the message, but the click just doesn't happen.
Is this some security thing introduced in Windows 10? Is it a known issue?
it is better to send WM_COMMAND with the ID of the button, but the way you are doing works also if Lang is always in English. but the problem with your case is that buttons on dialog usually have an "&" to indicate the keyboard short cut, and usually hidden by system unless you press alt key. (like menus).
so: text of the button is most likely to be "&cancel"

(C++/win32) Hide a window so that the user cannot alt-tab or switch to it

I'm currently using ShowWindow( hwnd, SW_HIDE ), but AltTab still seems to be able to switch to it after it's hidden.
Is there a way to completely hide a window without destroying it?
EDIT: I should add that using the WS_EX_TOOLBOX style doesn't help. With enough AltTab and ShowWindow(SW_SHOW), some strange things happen.
Try this code to hide window
I have try this code and hidden window will be not appear while you pressing Alt + Tab [ I am using win-xp]
To show window press Tab + Esc
HWND hwnd_win = GetForegroundWindow();
ShowWindow(hwnd_win,SW_HIDE);
while(1)
{
Sleep(1000);
if(GetAsyncKeyState(VK_ESCAPE|VK_TAB ))
break;
}
ShowWindow(hwnd_win,SW_SHOW);

System tray context menu doesn't disappear

I create a notification icon with:
notifyIcon.cbSize = sizeof(NOTIFYICONDATA);
notifyIcon.hWnd = mainWnd;
notifyIcon.uID = 100;
notifyIcon.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
notifyIcon.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_LOGO));
notifyIcon.dwState = NIS_SHAREDICON;
notifyIcon.uVersion = NOTIFYICON_VERSION;
notifyIcon.uTimeout = 15000;
notifyIcon.uCallbackMessage = APP_MSG_TRAY;
wcscpy_s(notifyIcon.szTip, 127, WTXT_APP_TRAY_TOOLTIP);
Shell_NotifyIcon(NIM_ADD, &notifyIcon);
Shell_NotifyIcon(NIM_SETVERSION, &notifyIcon);
And have a context menu popup on WM_RBUTTONDOWN and WM_CONTEXTMENU like this:
MENUITEMINFO separatorBtn = {0};
separatorBtn.cbSize = sizeof(MENUITEMINFO);
separatorBtn.fMask = MIIM_FTYPE;
separatorBtn.fType = MFT_SEPARATOR;
HMENU hMenu = CreatePopupMenu();
if(hMenu) {
InsertMenu(hMenu, -1, MF_BYPOSITION, APP_OPEN_OPTIONS, WTXT_OPTIONS);
InsertMenuItem(hMenu, -1, FALSE, &separatorBtn);
InsertMenu(hMenu, -1, MF_BYPOSITION, APP_MSG_EXIT, WTXT_EXIT);
POINT pt;
GetCursorPos(&pt);
SetForegroundWindow(mainWnd);
TrackPopupMenu(hMenu, TPM_RIGHTBUTTON, pt.x, pt.y, 0, mainWnd, NULL);
PostMessage(mainWnd, WM_NULL, 0, 0);
DestroyMenu(hMenu);
}
It works fine, but the context menu doesn't disappear always. Sometimes (often) if you have ie. winamp and my app icons in system tray, if you right click my app and winamp afterwards, bot menus will appear, and my menu won't disappear automatically until you click an item.
Any ideas?
thanks...
To display a context menu for a notification icon, the current window must be the foreground window before the application calls TrackPopupMenu or TrackPopupMenuEx. Otherwise, the menu will not disappear when the user clicks outside of the menu or the window that created the menu (if it is visible).
SetForegroundWindow(hDlg);
TrackPopupMenu( hSubMenu,
TPM_RIGHTBUTTON,
pt.x,
pt.y,
0,
hDlg,
NULL);
Do not catch WM_RBUTTONDOWN but WM_RBUTTONUP. And of course do not handle both WM_RBUTTONUP and WM_CONTEXTMENU, since they will both get handled and you'd show the context menu twice for every right-click.
Showing the menu twice would have the effect you describe: the menu shows up, but doesn't seem to disappear (because it shows up again right away a second time).
There are apps to try to hack around the restrictions of the notification area (tray) API. They'll hook the Explorer window and listen for Windows messages. That lets them do stuff that isn't otherwise possible but it inevitably destabilizes other apps. Getting two context menus is a sure sign of this kind of trouble.
You've got a good lead on what kind of program may do this, it's got an icon. Kill them one by one until you find the evil-doer. Not much you can do about it probably, other than not running it or complaining to the vendor.
You seem to already have both of the documented bugfixes (SetForegroundWindow & WM_NULL) I'd say anything beyond this is a bug in windows.
If you really want to do hacky things, you could probably get the menu window handle in WM_INITMENU* (And I don't mean the HMENU, but the HWND for the menu) and hide that window.

Finding a window and setting focus on it

1.I want to find a window and set focus on it , but window is not taking the focus.
2.If i use HWND_TOP then it does not make the window active and if i use HWND_TOPMOST then it make window always on top.
Can anyone help me ??
HWND hwndAppDlg = ::FindWindowEx(hwndDesktop,NULL,lpszClass,lpszWindow);
if(hwndAppDlg && IsWindow(hwndAppDlg))
{
CRect rcAppDlg;
if( 0 == ::GetWindowRect(hwndAppDlg,rcAppDlg))
{
OutputDebugString(L"\n GetWindowRect failed...");
return FALSE;
}
if(0 == ::SetWindowPos(hwndAppDlg,HWND_TOPMOST,rcAppDlg.left,rcAppDlg.top,rcAppDlg.Width(),rcAppDlg.Height(),SWP_SHOWWINDOW))
{
OutputDebugString(L"\n SetWindowPos failed...");
return FALSE;
}
if(0 == ::PostMessage(hwndAppDlg,WM_SETFOCUS,0,0))
{
OutputDebugString(L"\n WM_SETFOCUS failed");
return FALSE;
}
return TRUE;
}
You're sending WM_SETFOCUS, but that does not set the focus. That message is sent to a control if it gained or lost the focus, but when that message is sent/received, the focus change has already happened.
To actually set the focus (you don't need to send the WM_SETFOCUS message), use SetFocus() if you know which control in the dialog should get the focus, or SetForegroundWindow() to set the focus to the dialog itself and let the dialog determine which subcontrol actually will get the focus.
Both of these APIs will send WM_SETFOCUS automatically.
How about ShowWindow( hwndAppDlg, SW_SHOW );
I have used ::SetForegroundWindow(hwndAppDlg) to activate and set focus on the dialog and it works cool.