How to reorder windows in an application in MFC - c++

Let's say that there are several different Windows (CWnd) open in a software, and the pointers to all active windows are saved in std::vector<Node> tabsInfo. Here is a portion of code for Node:
struct typedef Node {
...
CWnd *pWnd;
...
}
I have a handler to update the Z-order of all active windows using tabsInfo, which looks like this:
for (size_t i = tabsInfo.size(); i > 1; i--) {
Node *pN_cur = &tabsInfo.at(i - 1);
Node *pN_next = &tabsInfo.at(i - 2);
ret = pN_cur->pWnd->SetWindowPos(pN_next->pWnd, NULL, NULL, NULL, NULL, SWP_NOMOVE | SWP_NOSIZE);
ASSERT(ret != 0); // Sanity check
}
However, when I debug them, even though they run without error, it makes no changes to the ordering of all windows.
Am I misunderstanding on how to use SetWindowPos?
When I looked other questions answered (which deals with buttons inside dialogs instead of windows), they gave the similar solution, but it doesn't seem to work here.

For anyone who have the same problem, I added a few more lines, and it worked:
for (int i = tabsInfo.size() - 1; i >= 0; i--) {
Window_Node *pWN = &tabsInfo.at(i);
if (tabsInfo.at(i).checked) { // Ignore this line. This is program-specific
WINDOWPLACEMENT wp = {};
wp.length = sizeof(WINDOWPLACEMENT);
pWN->pWnd->GetParentFrame()->GetWindowPlacement(&wp);
wp.showCmd=SW_RESTORE;
pWN->pWnd->GetParentFrame()->SetWindowPlacement(&wp);
ret = pWN->pWnd->GetParentFrame()->SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
ASSERT (ret != 0);
} else {
pWN->pWnd->GetParentFrame()->ShowWindow(SW_MINIMIZE);
}
}
}
Hope it helps someone :)

Related

How change resolution and update window without flickering

I need to change the resolution different times and keep update the windows.
Actually I use this code:
bool SetDiplayResolution(int Width, int Height, int Refresh = 0)
{
LONG Outout;
DEVMODE desiredMode;
memset(&desiredMode, 0, sizeof(desiredMode));
desiredMode.dmSize = sizeof(desiredMode);
if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &desiredMode) != 0)
{
desiredMode.dmSize = sizeof(DEVMODE);
desiredMode.dmPelsWidth = Width;
desiredMode.dmPelsHeight = Height;
if (Refresh > 0)
{
desiredMode.dmDisplayFrequency = Refresh;
desiredMode.dmFields = DM_PELSHEIGHT | DM_PELSWIDTH | DM_DISPLAYFREQUENCY;
}
else
{
desiredMode.dmFields = DM_PELSHEIGHT | DM_PELSWIDTH;
}
Outout = ChangeDisplaySettings(&desiredMode, 0);
}
return Outout;
}
void myfunction
{
HWND hWnd = GetActiveWindow();
SetDiplayResolution(1920, 1080);
SetWindowPos(hWnd, 0, 0, 0, 1920, 1080, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
UpdateWindow(hWnd);
}
This code work fine but unfortunatenly the transaction beetween the old and new resolution produce some very short, but visble annoyng effects:
Temporary presence of the windows bar.
The screen flash for one instant.
I don't known if exist a way to solve. I have seen in some forum that other create a secondary top most window in order to avoid these side effects and then destroy this secondary window.
I don't sure if this is a good way.
Unfortunatenly I don't have access to window events becouse my is a dll (I have not create the window, but I have the handle).
Can you please help me ?
Thanks !

Windows setFocus, target window does not capture keyboard input

i'm coding a wrapper for a CMD window (and potentially any shell), which purpose is to keep a shell instance always opened out of a screen's border. Moving the mouse to the border of the screen would cause the window moving down. (the window is a topmost window).
The point is to have a terminal always accessible as process (not taking space in the taskbar) and which hides when not being used.
One useful feature would be to force the focus on that window, so that, once it starts moving in your screen you can directly start typing without clicking on it to give it focus.
I'm coding all of that in c++ within visual studio with sfml support (that program itself has many sfml graphic windows, and that prompt is the only non-graphic one). Sfml in the code relative to that wrapper is only used to get mouse coordinates, in the form of sf::Mouse::getPosition().x/y.
When i run the program from within visual studio, whether in debug or in release mode, it all works fine. I can focus some other window, do stuff there, and as soon as i move the mouse in the position which makes the prompt window move in the screen, if i start typing without clicking on the actual page, the prompt will actually start capturing keyboard input as intended.
However, if i run the program as a stand-alone executable, this behaviour is no longer achieved. It appears the prompt does get focus indeed, since the "typing cursor" is there, but the window does not capture the actual keyboard input, which is weird.
Relevant code is as follows:
//create terminal window beg
STARTUPINFO prompt_startupinfo;
PROCESS_INFORMATION prompt_processinfo;
HWND prompt_window;
Win::spawn_prompt(&prompt_startupinfo, &prompt_processinfo, &prompt_window);
//always on bottom
SetWindowPos(prompt_window, HWND_TOP, 0, -PROMPT_HEIGHT + 2, Screen::get_width(), PROMPT_HEIGHT, SWP_SHOWWINDOW);
SetWindowLong(prompt_window, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
SetWindowLong(prompt_window, GWL_STYLE, WS_POPUP | WS_VISIBLE);
//create terminal window end
bool outside = false;
bool exiting = false;
while (IsWindow(prompt_window))
{
//Console move beg
int my = sf::Mouse::getPosition().y;
int mx = sf::Mouse::getPosition().x;
RECT rect;
GetWindowRect(prompt_window, &rect);
int wy = rect.bottom;
if ((my <= wy + 1) and not exiting)
{
if ((not outside) or (mx < 32))
{
if (wy < PROMPT_HEIGHT)
{
wy += WINDOW_SPEED * 4;
outside = false;
if (wy > PROMPT_HEIGHT)
{
wy = PROMPT_HEIGHT;
}
}
SetForegroundWindow(prompt_window);
}
}
else if (wy > 0)
{
wy -= WINDOW_SPEED * 4;
exiting = true;
if (wy <= 0)
{
wy = 0;
outside = true;
exiting = false;
}
}
SetWindowPos(prompt_window, 0, 0, wy - PROMPT_HEIGHT, 0, 0, SWP_NOSIZE);
//Console move end
Sleep(1000 / 60);
}
As a quick note, when running from within visual studio, the desired behaviour is achieved by just having the SetForegroundWindow(prompt_window); call, no even need for SetFocus(prompt_window);
Just for the sake of completion here is the Win::spawn_prompt function:
HWND Win::find_main_window(unsigned long process_id)
{
handle_data data;
data.process_id = process_id;
data.best_handle = 0;
EnumWindows(enum_windows_callback, (LPARAM)&data);
return data.best_handle;
}
BOOL CALLBACK Win::enum_windows_callback(HWND handle, LPARAM lParam)
{
handle_data& data = *(handle_data*)lParam;
unsigned long process_id = 0;
GetWindowThreadProcessId(handle, &process_id);
if (data.process_id != process_id || !is_main_window(handle))
{
return TRUE;
}
data.best_handle = handle;
return FALSE;
}
BOOL Win::is_main_window(HWND handle)
{
return GetWindow(handle, GW_OWNER) == (HWND)0 && IsWindowVisible(handle);
}
bool Win::spawn_prompt(STARTUPINFO* prompt_startupinfo, PROCESS_INFORMATION* prompt_processinfo, HWND* prompt_window)
{
// additional information
STARTUPINFO si;
PROCESS_INFORMATION pi;
pi.hProcess;
// set the size of the structures
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
// start the program up
CreateProcess(L"C:\\Windows\\System32\\cmd.exe", // the path
L"", // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
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 (removed extra parentheses)
);
Sleep(1000);
HWND pw = Win::find_main_window(pi.dwProcessId);
*prompt_startupinfo = si;
*prompt_processinfo = pi;
*prompt_window = pw;
return false;
}
Ok i solved the problem.
Since that "set focus" event should have been triggered only when user's mouse was in a certain position, the most immediate workaround i could think of was simulating a click in that position.
Thanks for the hints #Daniel Sęk,
I'd still appreciate a more clean and less workaround-ish solution, so any further hint would be appreciated.
EDIT:
despite the window being topmost, sometimes the currently focused window still sits over the cmd window, and if that focused window is at the coordinates where i simulate the click, it captures that click making mentioned solution not always reliable.

Code works only in Visual Studio Debug but not in release mode nor .exe

I've wrote an application to change the display resolution. (I'm used to play CS at 4:3 resolution and working at 16:9 and I'm too lazy to go to Systemcontrol and change it there.)
The program works fine as long as i run it with Visual Studio (Pro 2013) in Debug Mode, but if I change to Release Mode or try to run the .exe files it doesnt work.
Edit: I've got no errors and it looks like it does change the resolution (black monitor) but it does'nt change the resolution. Also tried to run wih admin rights.
Does anyone has an idea to solve my problem?
Thanks in advance!
Code:
#include <Windows.h>
struct SResolution
{
int x;
int y;
};
static SResolution R_1440x1080 = { 1440, 1080 };
static SResolution R_1920x1080 = { 1920, 1080 };
bool GetPrimaryDisplayDevice(DISPLAY_DEVICE& _rDisplayDevice);
int main()
{
SResolution Resolution = R_1920x1080; // R_1440x1080 R_1920x1080
DISPLAY_DEVICE DDevice;
DEVMODE lpDevMode;
DEVMODE lpDevMode2;
ZeroMemory(&DDevice , sizeof(DDevice));
ZeroMemory(&lpDevMode , sizeof(lpDevMode));
ZeroMemory(&lpDevMode2, sizeof(lpDevMode2));
DDevice.cb = sizeof(DDevice);
lpDevMode .dmSize = sizeof(lpDevMode);
lpDevMode2.dmSize = sizeof(lpDevMode2);
if (!GetPrimaryDisplayDevice(DDevice)) return 0;
if (!EnumDisplaySettings(DDevice.DeviceName, ENUM_CURRENT_SETTINGS, &lpDevMode)) return 0;
// if (Resolution.x == lpDevMode.dmPelsWidth && Resolution.y == lpDevMode.dmPelsHeight) return 0;
int i = 0;
while (EnumDisplaySettings(DDevice.DeviceName, i, &lpDevMode))
{
if (Resolution.x == lpDevMode.dmPelsWidth && Resolution.y == lpDevMode.dmPelsHeight)
{
lpDevMode2 = lpDevMode;
}
i++;
}
lpDevMode2.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | DM_POSITION | DM_DISPLAYFREQUENCY | DM_DISPLAYFLAGS;
DWORD Flags = CDS_UPDATEREGISTRY | CDS_FULLSCREEN;
if (ChangeDisplaySettings(&lpDevMode2, Flags) != DISP_CHANGE_SUCCESSFUL)
{
MessageBox(NULL, (LPCWSTR)L"Fail", (LPCWSTR)L"Error", MB_ICONHAND);
}
return 0;
}
bool GetPrimaryDisplayDevice(DISPLAY_DEVICE& _rDisplayDevice)
{
DWORD NumberOfDevice = 0;
DWORD dwFlags = 0;
while (EnumDisplayDevices(NULL, NumberOfDevice, &_rDisplayDevice, dwFlags))
{
DISPLAY_DEVICE InnerDDevice = { 0 };
DWORD InnerNumberOfDevice = 0;
ZeroMemory(&InnerDDevice, sizeof(InnerDDevice));
InnerDDevice.cb = sizeof(DISPLAY_DEVICE);
if (_rDisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
{
return true;
}
NumberOfDevice++;
}
return false;
}
Edit2: Solved. - the problem was the CDS_FULLSCREEN flag at:
DWORD Flags = CDS_FULLSCREEN | CDS_UPDATEREGISTRY;
ChangeDisplaySettings(&DMode, CDS_UPDATEREGISTRY);
I've deleted this flag and only use CDS_UPDATEREGISTRY. Now it works.
Thanks to everyone who helped me or gave me tips :)
It actually does change the resolution. Add an else statement after this
if (ChangeDisplaySettings(&lpDevMode2, Flags) != DISP_CHANGE_SUCCESSFUL)
{
MessageBox(NULL, (LPCWSTR)L"Fail", (LPCWSTR)L"Error", MB_ICONHAND);
}
like this
else
{
MessageBox(NULL, (LPCWSTR)L"Success", (LPCWSTR)L"Congrats", MB_ICONHAND);
}
And you will see that the resolution has changed but switched back when the program finished.
I wanted to post it as a comment since I don't know how to fix it, but I don't have enough reputation for commenting yet.

createthread MFC dialog based app (Syntax)

I am trying to create a Worker thread but I have not done Visual C++ since 2004 and threading syntax has changed (Please don't worry about timing issues, I will have that covered). the issue is that in an MFC dialog app, every way I know of trying to create a thread won't get past the compiler. I also have tried the "&" trick. It can't seem to find this function at all. Can anyone help please? NOTE: I tried three methods and I left the first uncommented.
if (i_found >= 0) { //this is just a combobox snippet to show the calls
MessageBox(wch.GetBuffer(0), L"Port Select", MB_OK);
_serialPort->Close();
ConversationRight = gcnew Conversation(systrName);
_beginthread(&CDLP_Printer_ControlDlg::Mine_QL, 0, 0); //NONE WORKING!!
//unsigned long lpRecvThread = _beginthreadex(NULL, 0, Mine_QL, (void*)this, NULL, NULL);
//unsigned long lpThreadIdRequest;
//::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Mine_QL, (void*)NULL, 0, &lpThreadIdRequest);
}
else
MessageBox(L"Not the left board", L"Port Select", MB_OK);
_serialPort->Close();
}
void CDLP_Printer_ControlDlg::OnBnClickedBtnStop()
{
ConversationRight->WriteLn("5");
}
bool CDLP_Printer_ControlDlg::UpdateCommsWindow_left(String^ strCommsLine)
{
return false;
}
void CDLP_Printer_ControlDlg::Mine_QL()
{
//_endthread();
}

How can i attach userdata to each item in a listview? C++ Win32

I was thinking i could use the LVITEM structures LPARAM to attach a pointer to my class, but i can't seem to get it to work!
Heres the main parts of my code:
Creating the listview:
hlvQuiz = CreateChild(WC_LISTVIEW, "",
WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | LVS_ICON | LVS_AUTOARRANGE,
0, 0, 320, 240, m_hwnd, FontNormal);
Adding items:
if (vQuizes.size() > 0)
{
LVITEM lvi;
lvi.mask = LVIF_TEXT | LVIF_PARAM;
lvi.iItem = 0;
lvi.iSubItem = 0;
lvi.cchTextMax = QUIZSTRLEN;
for (unsigned int i = 0; i < vQuizes.size(); i++)
{
lvi.lParam = (LPARAM)&vQuizes[i]; // adding pointer to lparam
lvi.pszText = vQuizes[i].szName;
ListView_InsertItem(hlvQuiz, &lvi);
}
}
Then later when i go to get my class back from the LPARAM:
LVITEM lvi;
lvi.iItem = ListView_GetNextItem(hwnd, -1, LVNI_SELECTED);
lvi.iSubItem = 0;
if (ListView_GetItem(fm->hlvQuiz, &lvi) == TRUE)
{
Quiz* q = (Quiz*)lvi.lParam;
if (q != NULL) // i get stopped here because my pointer is NULL
if (Exists(q->szPath) == IS_FILE)
ShellExecute(NULL, "edit", q->szPath, NULL, NULL, SW_SHOWNORMAL);
}
Is there anything that i'm doing wrong? the listview creates fine, and the items add, but the pointer to my class which i put in the LPARAM value seems to be ignored, or changed by the time i come to dereference it
I haven't worked at this low level before, but I suspect you need to set the mask member of the LVITEM structure to LVIF_PARAM (as well as the appropriate values for anything else you need) for the call to ListView_GetItem.
Your code works fine in Debug mode but not in Release mode because
you missed to specify the name of LPARAM in lvi.mask (and the name of any other field you want back).
Try this:
lvi.iItem = ListView_GetNextItem(hlvQuiz, -1, LVNI_SELECTED);
lvi.mask = LVIF_PARAM;
if (ListView_GetItem(fm->hlvQuiz, &lvi) == TRUE) ...
You'll receive a copy of the LPARAM's value you've setted. I think that this little strange behaviour is due to the Debug mode's help that initalize everything for you. The Release mode instead do not.