Jump to content

SPI_SETWORKAREA Updates Windows If Reduced, But Not Increased

Go to solution Solved by Valiante,

Recommended Posts

Hello one and all.

I am creating a toolbar which reserves 40 pixels of the right-hand side of the screen by updating the "Desktop Work Area" via _WinAPI_SystemParametersInfo, so windows maximize up to it, much like the old Office Toolbar.  All worked great, new windows would maximize up to the new border, but existing maximized windows were a problem.

Initially I created a slightly noddy function utilising a combination of WinList and WinMove to find any visible maximized windows and adjust them accordingly.  While this worked ok, I didn't like it as it felt a bit, well, noddy.

I then discovered the 4th parameter of _WinAPI_SystemParametersInfo to send a WM_SETTINGCHANGE message after updating the work area which caused all maximized windows to instantly jump to their new position - great!  The Windows API was working for me - I could do away with my noddy function.

The trouble I face, however, is that on exit, while I set the work area back to the full width of the desktop, maximized windows aren't taking up the new larger area, unless manually restored down and maximized again.  With a bit of experimenting, I've discovered that using this method, maximized windows only respond to the message if the work area is reduced, not increased.

I created a little GUI which plays around with this very phenomenon;

#include <GUIConstantsEx.au3>
#include <GuiListView.au3>
#include <StaticConstants.au3>
#include <StructureConstants.au3>
#include <WinAPI.au3>

Global $iLeft, $iTop, $iRight, $iBottom

; Create the GUI
GUICreate("WorkArea", 210, 200)
GUICtrlCreateLabel("Pixel Amount:", 10, 10, 90, 20, $SS_RIGHT)
$Input = GUICtrlCreateInput("40", 110, 8, 90, 20)
$Reduce = GUICtrlCreateButton("Reduce", 10, 40, 90, 30)
$Increase = GUICtrlCreateButton("Increase", 110, 40, 90, 30)
$Reset = GUICtrlCreateButton("Reset", 10, 80, 190, 30)
$ListView = GUICtrlCreateListView("Left|Top|Right|Bottom", 10, 120, 190, 50)
$WorkArea = GUICtrlCreateListViewItem("", $ListView)
_GUICtrlListView_SetColumnWidth($ListView, 0, 33)
_GUICtrlListView_SetColumnWidth($ListView, 1, 33)
_GUICtrlListView_SetColumnWidth($ListView, 2, 60)
_GUICtrlListView_SetColumnWidth($ListView, 3, 60)
_GetDesktopWorkArea($iLeft, $iTop, $iRight, $iBottom)

; Loop until message received
While 1
    $msg = GUIGetMsg()
        Case $msg = $GUI_EVENT_CLOSE ; Reset the work area to Desktop Width then close
            _SetDesktopWorkArea($iLeft, $iTop, @DesktopWidth, $iBottom)
        Case $msg = $Reduce ; Reduce the work area width by the amount in input box
            $Pixels = GUICtrlRead($Input)
            _SetDesktopWorkArea($iLeft, $iTop, $iRight-$Pixels, $iBottom)
            _GetDesktopWorkArea($iLeft, $iTop, $iRight, $iBottom)
        Case $msg = $Increase ; Increase the work area width by the amount in input box
            $Pixels = GUICtrlRead($Input)
            _SetDesktopWorkArea($iLeft, $iTop, $iRight+$Pixels, $iBottom)
            _GetDesktopWorkArea($iLeft, $iTop, $iRight, $iBottom)
        Case $msg = $Reset ; Reset the work area to Desktop Width
            _SetDesktopWorkArea($iLeft, $iTop, @DesktopWidth, $iBottom)
            _GetDesktopWorkArea($iLeft, $iTop, $iRight, $iBottom)

Func _GetDesktopWorkArea(ByRef $iLeft, ByRef $iTop, ByRef $iRight, ByRef $iBottom)
    Local Const $SPI_GETWORKAREA = 48
    Local $tStruct = DllStructCreate($tagRECT)
    If _WinAPI_SystemParametersInfo($SPI_GETWORKAREA, 0, DllStructGetPtr($tStruct)) Then
        $iLeft = DllStructGetData($tStruct, "Left")
        $iRight = DllStructGetData($tStruct, "Right")
        $iTop = DllStructGetData($tStruct, "Top")
        $iBottom = DllStructGetData($tStruct, "Bottom")
        GUICtrlSetData($WorkArea, $iLeft&"|"&$iTop&"|"&$iRight&"|"&$iBottom)
        Return True
    Return False
EndFunc   ;==>_GetDesktopWorkArea

Func _SetDesktopWorkArea($iLeft, $iTop, $iRight, $iBottom)
    Local Const $SPI_SETWORKAREA = 47
    Local Const $SPIF_SENDWININICHANGE  = 0x2
    Local $tStruct = DllStructCreate($tagRECT)
    DllStructSetData($tStruct, "Left", $iLeft)
    DllStructSetData($tStruct, "Right", $iRight)
    DllStructSetData($tStruct, "Top", $iTop)
    DllStructSetData($tStruct, "Bottom", $iBottom)
    If _WinAPI_SystemParametersInfo($SPI_SETWORKAREA, 0, DllStructGetPtr($tStruct), $SPIF_SENDWININICHANGE) Then
        Return True
    Return False
EndFunc   ;==>_SetDesktopWorkArea

Would anyone know why this might be, and indeed if it's possible with another API call to have maximized windows respond in the same way regardless of work area reduction or enlargement?  I'm loathe to go back to the noddy function!

Many thanks in advance.



Link to comment
Share on other sites

OK, so I think I've been taking the wrong approach.  After finding >Franks Gadgets I can see he's not using SPI_SETWORKAREA at all, but rather sending an SHAppBarMessage to the system to both register and unregister his AppBar window.  Interestingly, this is something I considered using >back in 2006 but I was put off this idea by Excalibur who suggested it only reported the "start bar" (taskbar) position and size.  Wish I hadn't listened!  As you can see I've had this idea on the back burner for some time...!

So I'm going to rework my script using this same approach, as it absolutely works.  If I ever get around to finishing it, I'll post the solution here :)

Link to comment
Share on other sites

  • 2 weeks later...
  • Solution

Finally, using snippets of code borrowed from the aforementioned >Franks Gadgets, I have managed to come up with a basic GUI script which registers itself as a toolbar.

Disclaimer: This is a proof of concept only, kept as short as possible for this example, which makes lots of assumptions (such as taskbar position & size).

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Const $ABE_RIGHT = 2, $ABM_NEW = 0, $ABM_REMOVE = 1, $ABM_SETPOS = 3

; Create the GUI
$title = StringTrimRight(@ScriptName, 4)
$toolbarWidth = 40
$height = @DesktopHeight-40
$left = @DesktopWidth-$toolbarWidth
$top = 0
$right = @DesktopWidth
$bottom = $height
$hwnd = GUICreate($title, $toolbarWidth, $height, $left, $top, $WS_POPUPWINDOW, $WS_EX_TOOLWINDOW)

; Register the AppBar
$AppBarData = DllStructCreate("dword;int;uint;uint;int;int;int;int;int")
DllStructSetData($AppBarData, 1, DllStructGetSize($AppBarData))
DllStructSetData($AppBarData, 2, $hwnd)
DllStructSetData($AppBarData, 4, $ABE_RIGHT)
DllStructSetData($AppBarData, 5, $left)
DllStructSetData($AppBarData, 6, $top)
DllStructSetData($AppBarData, 7, $right)
DllStructSetData($AppBarData, 8, $bottom)
_SHAppBarMessage($ABM_NEW, $AppBarData)
_SHAppBarMessage($ABM_SETPOS, $AppbarData)

; Idle around
While 1
    Local $msg = GUIGetMsg()
        Case $msg = $GUI_EVENT_CLOSE

; Unregister the AppBar
_SHAppBarMessage($ABM_REMOVE, $AppBarData)

; Delete the GUI

; Exit

Func _ShAppBarMessage($dwMessage, ByRef $AppBarData)
    DllCall("shell32.dll", "int", "SHAppBarMessage", "int", $dwMessage, "ptr", DllStructGetPtr($AppBarData))

It only took me 8 years...


Link to comment
Share on other sites

  • 1 year later...

hi im trying to display the appbar on top of the screen this is what i used but it doesn't worked so far


$toolbarWidth =@DesktopWidth
$height = 40
$left =0
$top = 0
$right = @DesktopWidth
$bottom = 40

this is what i have for position, any ideas how to fix it, thanks


found the problem changed this and now is working     $ABE_TOP =  1

Edited by leogianguajor
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Create New...