Get the last ShowWindow state for a window in winapi - windows

When you click a window in your taskbar (Windows users) it will retain it's last state - maximised or normal scalable window. I'm trying to do a similar thing, but programatically and without the window gaining focus (eg. becoming foreground and disturbing my current activity in another window).
Can I do that? Current window state can be obtained using this API call:
//Empty Window placement structure
WinDefExt.WINDOWPLACEMENT placement = new WinDefExt.WINDOWPLACEMENT();
//winapi call to external User32.dll file
UserExt.GetWindowPlacement(hwnd, placement);
//showCmd should be equal to one of the SW_ constants (here: https://msdn.microsoft.com/en-us/library/windows/desktop/ms633548%28v=vs.85%29.aspx)
placement.showCmd;

ShowWindow isn't a "state", it's an "action". There's no GetShowState command. You can infer a value from the current state of the window, but there's no way to find out the actual last value used with ShowWindow.
if (!IsWindowVisible(hWnd))
swState = SW_HIDE;
else
if (IsIconic(hWnd))
swState = SW_MINIMIZE;
else
if (IsZoomed(hWnd))
swState = SW_MAXIMIZE;
else
{
// not hidden, minimized or zoomed, so we are a normal visible window
// last ShowWindow flag could have been SW_RESTORE, SW_SHOW, SW_SHOWNA, etc
// no way to tell
swState = SW_SHOW;
}

Related

Window is invisible, but IsWindow + IsWindowVisible + IsWindowEnabled return true and GetWindowRect returns plausible values

I'm iterating through all windows on the Windows desktop in order to allow disable patients select them easily in order to type into them.
I get all windows using
hw = GetDesktopWindow()
' It's first child is the 1st top level window
hw = GetWindow(hw, GW_CHILD)
'Now loop through all windows
etc.
There are many windows which are invisible or disabled, and I simply skip them because the user should not be able to select them. He wouldn't be able to type into them anyway.
Now I am confronted with a strange window.
Its title is "Groove Music".
It's a window from the Window Groove music app, and I can see it in the taskmanager.
I use the following API calls to check if this is a valid window that the user should be able to select:
IsWindow
IsWindowEnabled
IsWindowVisible
The declarations of these API calls are 100% perfect, I use them since years already.
And last, I even check its GetWindowRect values to see if it's in the screen at all.
And then I even check if it's a layered window (like an overlay that some apps use for non-clickable messages):
Public Function IsWindowLayered(ByVal uHwnd As Long) As Boolean
Dim lret&
lret = GetWindowLong(uHwnd, GWL_EXSTYLE)
If (lret And WS_EX_LAYERED) = WS_EX_LAYERED Then
IsWindowLayered = True
End If
End Function
It returns false.
The funny thing how is that the window is actually invisible.
So the user should not be able to select it.
Which function may I have missed to check if the window is actually visible?
Thank you!
Edit: It's also the calculator window (calc.exe) after I had opened it and closed it again.
Edit 2:
I also check for WS_VISIBLE like that, but even that return true for that window:
Public Function IsWindowVisibleEx(ByVal uHwnd As Long) As Boolean
Dim lret&
lret = GetWindowLong(uHwnd, GWL_STYLE)
If (lret And WS_VISIBLE) = WS_VISIBLE Then
IsWindowVisibleEx = True
End If
End Function
Edit 3: The taskmanager says that Calculator.exe and Groove have the status "Suspended".

How can I get a borderless child window to re-scale to current screen in multi-monitor setup?

My app has a main window which creates and opens an instance of a subclass of a QML Window {} using createObject(). This window has its flags: set to be a borderless window (I've added code so that it can be grabbed and dragged around).
When I attach a monitor to my laptop and set its font scale factor to 125% (or 150%), when I drag my main window over to the second monitor, you can see it suddenly "snap" to the larger size when it reaches the halfway point. Likewise, when I drag it back to my laptop screen it again "snaps" to the smaller size when I get halfway over (this behavior is what I want, so no problems here).
My problem is that when I drag my created borderless window over into the monitor, it keeps the original 100% scale factor and does not "snap" to a larger size. If I drag my main window over to the monitor, it gets larger but the borderless window remains at the smaller scale; only when I grab the borderless window and move it slightly does it suddenly "snap" to the larger scale size. The same thing happens in reverse - if I then drag the borderless window back onto the laptop, it remains at the larger size until I drag the main window back over and then move the borderless window slightly (at which point it suddenly "snaps" to the smaller size).
So it appears that this created Window uses the scale factor of the screen that the parent window window that created it is currently in, even if it is in a different screen itself.
Is this happening because the Window is borderless? (I'm about to test this but my build process is incredibly slow) Or is there any way to set this borderless Window up so that it detects that it is crossing into a new screen and re-scales itself (in the same way that my main window does)?
Update: I just ran a test giving my Window a native titlebar, and with a titlebar the window instantly adopts ("snaps to") the scale factor of whichever screen it happens to be in, just like my main window (and independent of the main window's scale factor).
So is there any way to duplicate this auto-scaling window behavior with a borderless window? Some flag I need to call, or some method(s) I need to call to get the OS to rescale the window?
Update 2: I tried out Felix's SetWindowPos solution. It does move the window, but this does not fix the scaling problem - the behavior of the frameless window is the same and it still does not correctly pick up the scaling factor of the screen it is in.
I am running a test using MoveWindow instead of SetWindowPos to see if that affects anything [edit: MoveWindow does not work, either - same problem]. Then I'm going to try SendMessage or PostMessage along with NoBugz' suggestion of the WM_DPICHANGED message.
Any other suggestions are welcome.
Update 3: I just created a quick C# app (winforms) to see if the same problem occurs with that, and it doesn't - when a borderless form in the C# app is dragged over into the other monitor, it immediately picks up the scale factor change. So it appears this is a Qt problem.
Update 4: See my answer below for a working solution to this problem (if a bit of a hack).
So as far as I understand, your current goal is to move the window via the WIN-API.
You will have to do so via C++. The approach would be:
Pass the QML Window to a C++-Method exposed to QML as a QQuickWindow (The QML window instanciates that type, as seen in the documentation)
Use QWindow::winId to get the native HWND
Call the SetWindowPos WIN-API method to move it
Code sample (C++-part):
// the method
void moveWindow(QQuickWindow *window, int x, int y) {
HWND handle = (HWND)window->winId();
Q_ASSERT(handle);
::SetWindowPos(handle, HWND_TOP,
x, y, 0, 0,
SWP_NOSIZE | SWP_NOZORDER);
}
// assuming "moveWindow" is a member of "MyClass"
qmlEngine->rootContext()->setContextProperty("mover", new MyClass(qmlEngine));
Code sample (QML-part):
// call this method as soon as the drag has finished, with the new positions
mover.moveWindow(idOfWindow, xPos, yPos);
Note: I would recommend you try out calling this only after the drag was finished (and move the window as you do right now until then). If that works, you can try out what happens if you call this during the drag instead of changing the x/y of the window.
I figured out a relatively simple way to fix this problem. Since a frameless window in Qt gets its scaling factor from the window that created it, the trick is to create another window (that has a titlebar but is not visible to the user) and create the frameless window there, and then add code to the frameless window to keep the hidden window positioned underneath it as the user drags it. When the frameless window is dragged into another screen, the hidden window goes with it, picks up the new scale factor (since it has a titlebar) and then the frameless window immediately gets the new screen's scale factor as well.
Here is sample solution code:
// HiddenWindow.qml
Window {
id: hiddenWindow
// note: just making window visible: false does not work.
opacity: 0
visible: true
flags: Qt.Tool | Qt.WindowTitleHint | Qt.WindowTransparentForInput |
Qt.WindowStaysOnTopHint // Qt.Tool keeps this window out of the
// taskbar
function createVisibleWindow() {
var component = Qt.createComponent("VisibleWindow.qml")
if (component.status === Component.Ready) {
var win = component.createObject(hiddenWindow)
return win
}
}
}
// VisibleWindow.qml
Window {
id: visibleWindow
property var creatorWindow: undefined
flags: Qt.FramelessWindowHint
onXChanged: {
creatorWindow.x = x
}
onYChanged: {
creatorWindow.y = y
}
onWidthChanged: {
creatorWindow.width = width
}
onHeightChanged: {
creatorWindow.height = height
}
}
And then to use these classes from your main window QML:
property var hiddenWindow: undefined
property var visibleWindow: undefined
Component.onCompleted: {
var component = Qt.createComponent("HiddenWindow.qml")
if (component.status === Component.Ready) {
hiddenWindow = component.createObject(null)
}
visibleWindow = hiddenWindow.createVisibleWindow()
visibleWindow.creatorWindow = hiddenWindow
visibleWindow.show()
}
You need to resize window when window move to other screen
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
onPressed: {
movePos = Qt.point(mouse.x, mouse.y)
isDoubleClicked = false
lastWindowWidth = mainWindow.width
lastWindowHeight = mainWindow.height
}
onPositionChanged: {
if (!isDoubleClicked) {
const delta = Qt.point(mouse.x - movePos.x, mouse.y - movePos.y)
if (mainWindow.visibility !== Window.Maximized) {
mainWindow.x = mainWindow.x + delta.x
mainWindow.y = mainWindow.y + delta.y
mainWindow.width = lastWindowWidth
mainWindow.height = lastWindowHeight
}
}
}
}

Showing a windows with XCB / Strange Behaviour

I'm trying to show a window in xcb, inside the main window, but actually without luck.
The idea is that when the user press a button (in that case the X button) a small white window is shown (just for test).
But actually i'm stuck on that step. I watched the example code here:
http://en.wikibooks.org/wiki/X_Window_Programming/XCB
And tried to do the same in my application.
[EDIT 28/10/2013] Now with that code i can show a window, but if i try to add other variable like int i=0, or whatever, the window doesn't appear, and no expose events were raised (all events that were raised are or 0 or 2 (even if i add the variables inside other events). Any idea?
This is the XCB_KEY_PRESS event handler code:
Edit (with the new code)
case XCB_KEY_PRESS:{
xcb_key_press_event_t *kp = (xcb_key_press_event_t *)ev;
if(kp->detail==53){
printf("X pressed\n");
uint32_t vals[2];
mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK;
vals[0]=screen->white_pixel;
vals[1]=XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_KEY_PRESS;
win = xcb_generate_id(connection);
xcb_create_window(
connection,
XCB_COPY_FROM_PARENT,
win,
root,
80,80,
150,150,
10,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
screen->root_visual,
mask, values);
mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
vals[0]=screen->white_pixel;
vals[1]=0;
background=xcb_generate_id(connection);
xcb_create_gc(connection, background, win, mask, vals);
xcb_map_window(connection,win);
xcb_flush(connection);
printf("finished\n");
}
printf("KEY_PRESS - Pressed: %d\n", kp->detail);
}
root is the root window obtained from xcb_screen_t variable.
The definition of background and win are the following:
xcb_window_t win;
xcb_gcontext_t background;
And i added even a XCB_EXPOSE event handler:
case XCB_EXPOSE:{
printf("EXPOSE NEW WINDOW CREATED\n");
xcb_poly_fill_rectangle(connection, win, background,1,&rectangle);
xcb_flush(connection);
}
What is wrong with that code? What am i missing? (I'm trying to develop a very basic window manager, just for fun)
(My idea for that program is that when x is pressed an input box is shown, do you have any suggestion on how to do that?)

WinApi - How To Modify Console Window?

I want my console window to be modified. I got the handle. And this helps to change it. But how can I
remove close button
remove maximize button
remove icon
disable resizing
?
// C# syntax
StringBuilder buffer = new StringBuilder(260);
IntPtr window = FindWindow(null, buffer.ToString(0, GetConsoleTitle(buffer, buffer.Capacity)));
uint a = (uint)((WS_BORDER | WS_CAPTION) & (~WS_ICONIC));
SetWindowLongPtr(window, -16, new IntPtr(a)); // GWL_STYLE = -16
For some reason the window is broken after this call. I can't move it with the mouse anymore and all clicks go through it to other windows.
You removed all the window styles, and added back just WS_BORDER and WS_CAPTION. What you should do is:
Read the current window style with a call to GetWindowLongPtr.
Perform bitwise AND with the bitwise negation of the styles you want to remove.
Set the window style with a call to SetWindowLongPtr.

WinApi, hide cursor inside window client area

I want hide cursor inside window client area without borders and title bar (it is simple opengl application). So, function
ShowCursor(FALSE);
is not suitable. After some searching the winapi i find this solution:
//when create window class for application window
WNDCLASSEX WndClass;
//...
BYTE CursorMaskAND[] = { 0xFF };
BYTE CursorMaskXOR[] = { 0x00 };
WndClass.hCursor = CreateCursor(NULL, 0,0,1,1, CursorMaskAND, CursorMaskXOR);
Is this a good way to solve this typical task? What way is the best?
MSDN says that you can set the WNDCLASSEX hCursor field to NULL, in which case you must explicitly set the cursor in your window procedure (which means handling the WM_SETCURSOR message). For example:
if (Msg == WM_SETCURSOR && LOWORD(lParam) == HTCLIENT)
{
SetCursor(NULL);
return TRUE;
}
// Remainder of window procedure code
Checking for HTCLIENT ensures that the cursor is only hidden in the client area, and that the window frame and caption will use the correct cursors.
The SetCursor() call you're using doesn't take a BOOL - it takes an HCURSOR. So you're calling SetCursor( NULL ) which means "hide that cursor". What I found in the old days on Windows is that this is video driver dependent and many drivers don't respect it. The most consistent way to handle this is to make a transparent cursor resource in your app, and return a handle to that cursor in the WM_SETCURSOR message from your main window.
I found that first setting hCursor to NULL:
wc.hCursor = NULL;
and then setting the cursor to NULL:
SetCursor(NULL);
will make it disappear.
From MSDN, I read that the application will set its own cursor by default if one is not defined in hCursor. That's what the first line of code is doing.
Then, after the application sets its own cursor, I mess with it with the second line of code. Or at least, I think that's what happens.

Resources