WildByDesign Posted October 27 Posted October 27 This is a continuation (in a slightly expanded way) from the original Move window behind desktop icons thread by @Parsix and should be an interesting challenge. This example uses some code put together from @UEZ, @Nine and @argumentum. For those not familiar with the previous thread, it is essentially moving the GUI in front of the wallpaper but behind the desktop icons in a WorkerW window within Progman (Program Manager). The goal of this thread is to see if there is any possible way to (at least momentarily) access controls in that GUI because you generally cannot click on them. Example: desktop widgets, live video wallpapers, etc. The example below can be exited by pressing Escape. It contains a simple Slider control with a label that changes based on the value of the Slider. There is a button, that when pressed, will initiate the drop into the WorkerW window. There is a Tooltip that will show the Parent window class and Child window class of whatever the cursor is over at the time. This may or may not be helpful for figuring out ideas. Anytime you click on the desktop (Progman), it will output a line into the console. It is that part of the code where I placed a comment "; do action here if needed" that would be a potential place to add code if there is something that we can do to access the control(s) based on clicking on the desktop. To be quite honest, I don't know if this is even possible. I don't know if we need to temporarily reverse the _WinAPI_SetParent function (initially GUI to WorkerW, maybe WorkerW back to GUI) although that would bring the entire GUI forward. It would be better if just the Slider control could somehow come forward in some way. Example: expandcollapse popup;Code by UEZ, Nine, argumentum #include <WinAPI.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <WindowsNotifsConstants.au3> #include <GuiSlider.au3> #include <SliderConstants.au3> #include <WinAPIvkeysConstants.au3> #include <Misc.au3> Opt("GUIOnEventMode", 1) HotKeySet("{Esc}", "_Exit") DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext" , "HWND", "DPI_AWARENESS_CONTEXT" -4) Global $idSlider1, $idLabel1 Global Const $TRBN_THUMBPOSCHANGING = -1502 Global Const $tagTRBTHUMBPOSCHANGING = $tagNMHDR & ";dword Pos;int Reason" Global Const $TBS_NOTIFYBEFOREMOVE = 2048 Global $hParentWnd, $hMainWnd, $hRoot, $hChildWnd, $hMainWnd_Old = -1, $tPoint Global Static $iTimeLast Global $aKeys[1] = [$VK_LBUTTON] Global $hUser32 = DllOpen('user32.dll') Global $aPrimary = GetPrimaryMonitorCoords() If @error Then Exit MsgBox(16, "Error", "Unable to get primary monitor") Global $hProgman = WinGetHandle("[CLASS:Progman]"), $hWorkerW, $i If Not $hProgman Then Exit MsgBox(16, "ERROR", "Couldn't find Progman", 30) _WinAPI_SendMessageTimeout($hProgman, 0x052C, 13, 1, 250, $SMTO_NORMAL) Global $hWorkerW = _WinAPI_FindWindowEx($hProgman, 0, "WorkerW", "") If $hWorkerW = 0 Then Exit MsgBox(16, "ERROR", "Couldn't find WorkerW under Progman", 30) Local $aOrigin = GetDesktopOrigin() Local $iX = $aPrimary[0] - $aOrigin[0] Local $iY = $aPrimary[1] - $aOrigin[1] Global $hGUI = GUICreate("GUI behind Desktop icons", $aPrimary[4], $aPrimary[5], $iX, $iY, $WS_POPUP, $WS_EX_TOOLWINDOW) GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit") GUISetBkColor(0x202020) GUISetFont(40) $idSlider1 = GUICtrlCreateSlider(@DesktopWidth / 4, @DesktopHeight / 4, @DesktopWidth / 2, @DesktopHeight / 6, BitOR($TBS_AUTOTICKS, $TBS_NOTIFYBEFOREMOVE)) GUICtrlSetBkColor(-1, 0x202020) GUICtrlSetColor(-1, 0xFFFFFF) GUICtrlSetLimit(-1, 100, 0) GUICtrlSetData($idSlider1, 100) $idLabel1 = GUICtrlCreateLabel("100", @DesktopWidth / 2.1, @DesktopHeight / 3, -1, -1) GUICtrlSetBkColor(-1, 0x202020) GUICtrlSetColor(-1, 0xFFFFFF) GUISetFont(12) GUICtrlCreateButton("Place GUI Behind WorkerW", @DesktopWidth / 2.2, @DesktopHeight / 2, -1, -1) GUICtrlSetOnEvent(-1, "_dropBehind") GUICtrlSendMsg($idSlider1, $WM_CHANGEUISTATE, 65537, 0) GUIRegisterMsg($WM_NOTIFY, "WM_NOTIFY") GUISetState(@SW_SHOWNOACTIVATE, $hGUI) While 1 $tPoint = _WinAPI_GetMousePos() $hMainWnd = _WinAPI_WindowFromPoint($tPoint) $hChildWnd = GetRealChild($hMainWnd) $hParentWnd = _WinAPI_GetAncestor(_WinAPI_WindowFromPoint($tPoint), $GA_ROOT) If $hChildWnd And $hMainWnd <> $hChildWnd Then $hMainWnd = $hChildWnd If $hMainWnd <> $hMainWnd_Old Then ToolTip("Parent: " & @TAB & _WinAPI_GetClassName($hParentWnd) & @CRLF & "Child: " & @TAB & _WinAPI_GetClassName($hMainWnd)) ;ConsoleWrite("Parent: " & _WinAPI_GetClassName($hParentWnd) & @CRLF) ;ConsoleWrite("Child: " & _WinAPI_GetClassName($hMainWnd) & @CRLF) ;$hMainWnd_Old = $hMainWnd EndIf Local $iRet = _IsPressed($aKeys, $hUser32, False) Switch $iRet Case 1 ; MouseClick Left $tPoint = _WinAPI_GetMousePos() $hWndCur = _WinAPI_GetAncestor(_WinAPI_WindowFromPoint($tPoint), $GA_ROOT) $sParent = _WinAPI_GetClassName($hWndCur) If $sParent = "Progman" Then $iTime = Round((_WinAPI_GetTickCount64() / 1000)) $iTimeDiff = $iTime - $iTimeLast ; ensure that it doesn't get hit more than once per second (approx.) If $iTime <> $iTimeLast Then ; do action here if needed ; ConsoleWrite("Desktop clicked at: " & HourAmPm(@HOUR & ":" & @MIN & ":" & @SEC) & @CRLF) $iTimeLast = $iTime EndIf EndIf EndSwitch Sleep(200) WEnd GUIDelete($hGUI) Func _dropBehind() _WinAPI_SetParent($hGUI, $hWorkerW) _WinAPI_SetWindowPos($hGUI, $HWND_BOTTOM, 0, 0, 0, 0, BitOR($SWP_NOMOVE, $SWP_NOSIZE, $SWP_NOACTIVATE)) _WinAPI_SetWindowLong($hGUI, $GWL_EXSTYLE, BitOR(_WinAPI_GetWindowLong($hGUI, $GWL_EXSTYLE), $WS_EX_LAYERED, $WS_EX_TRANSPARENT)) _WinAPI_SetLayeredWindowAttributes($hGUI, 0, 180, $LWA_ALPHA) EndFunc Func _WinAPI_FindWindowEx($hParent, $hAfter, $sClass, $sTitle = "") Local $ret = DllCall("user32.dll", "hwnd", "FindWindowExW", "hwnd", $hParent, "hwnd", $hAfter, "wstr", $sClass, "wstr", $sTitle) If @error Or Not IsArray($ret) Then Return 0 Return $ret[0] EndFunc ;==>_WinAPI_FindWindowEx Func GetPrimaryMonitorCoords() Local $tPoint = DllStructCreate("int x;int y") $tPoint.x = 0 $tPoint.y = 0 Local $hMonitor = _WinAPI_MonitorFromPoint($tPoint, $MONITOR_DEFAULTTOPRIMARY) If Not $hMonitor Then Return SetError(1, 0, 0) Local $tMI = DllStructCreate("dword cbSize;long rcMonitor[4];long rcWork[4];dword dwFlags") DllStructSetData($tMI, "cbSize", DllStructGetSize($tMI)) Local $aCall = DllCall("user32.dll", "bool", "GetMonitorInfoW", "handle", $hMonitor, "ptr", DllStructGetPtr($tMI)) If @error Or Not $aCall[0] Then Return SetError(2, 0, 0) Local $iLeft = $tMI.rcMonitor(1) Local $iTop = $tMI.rcMonitor(2) Local $iRight = $tMI.rcMonitor(3) Local $iBottom = $tMI.rcMonitor(4) Local $iWidth = $iRight - $iLeft Local $iHeight = $iBottom - $iTop Local $a[6] = [$iLeft, $iTop, $iRight, $iBottom, $iWidth, $iHeight] Return $a EndFunc ;==>GetPrimaryMonitorCoords Func GetDesktopOrigin() Local $minX = 0, $minY = 0, $x, $y Local $i = 0, $tDevice, $aRet, $tDevMode, $aED While True $tDevice = DllStructCreate("dword cb; char DeviceName[32]; char DeviceString[128]; dword StateFlags; char DeviceID[128]; char DeviceKey[128]") $tDevice.cb = DllStructGetSize($tDevice) $aRet = DllCall("user32.dll", "bool", "EnumDisplayDevicesA", "ptr", 0, "dword", $i, "ptr", DllStructGetPtr($tDevice), "dword", 0) If @error Or Not $aRet[0] Then ExitLoop If BitAND($tDevice.StateFlags, 1) Then $tDevMode = DllStructCreate( _ "byte dmDeviceName[32]; word dmSpecVersion; word dmDriverVersion; word dmSize; word dmDriverExtra; dword dmFields;" & _ "long dmPositionX; long dmPositionY; dword dmDisplayOrientation; dword dmDisplayFixedOutput;" & _ "short dmColor; short dmDuplex; short dmYResolution; short dmTTOption; short dmCollate; char dmFormName[32];" & _ "ushort dmLogPixels; dword dmBitsPerPel; dword dmPelsWidth; dword dmPelsHeight;" & _ "dword dmDisplayFlags; dword dmDisplayFrequency; dword dmICMMethod; dword dmICMIntent;" & _ "dword dmMediaType; dword dmDitherType; dword dmReserved1; dword dmReserved2; dword dmPanningWidth; dword dmPanningHeight") $tDevMode.dmSize = DllStructGetSize($tDevMode) $aED = DllCall("user32.dll", "bool", "EnumDisplaySettingsA", "str", $tDevice.DeviceName, "dword", -1, "ptr", DllStructGetPtr($tDevMode)) If Not @error And $aED[0] Then $x = $tDevMode.dmPositionX $y = $tDevMode.dmPositionY If $x < $minX Then $minX = $x If $y < $minY Then $minY = $y EndIf EndIf $i += 1 WEnd Local $a[2] = [$minX, $minY] Return $a EndFunc ;==>GetDesktopOrigin Func _Exit() GUIDelete($hGUI) Exit EndFunc Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam) Local $tChange = DllStructCreate($tagTRBTHUMBPOSCHANGING, $lParam) If $tChange.IDfrom = $idSlider1 And $tChange.Code = $TRBN_THUMBPOSCHANGING Then GUICtrlSetData($idLabel1, GUICtrlRead($idSlider1)) ;If $tChange2.Pos <> $sWatchBlurColorIntensitySlider Then ; _SavedChangesStatus() ;EndIf EndIf Return $GUI_RUNDEFMSG EndFunc ;==>WM_NOTIFY ; #FUNCTION# ==================================================================================================================== ; Name...........: HourAmPm ; Description....: Converts a time in 24-hour format to AM/PM format. ; Syntax.........: HourAmPm( $sDateTime [, $sAmPm = "AM|PM" [, $iTrimRight = 0]] ) ; Parameters.....: $sDateTime - The time (with date or not) string with "HH:" in it. ; $sAmPm - [optional] The AM/PM representation (default is "AM|PM"). ; $iTrimRight - [optional] The number of characters to trim from the right of the result (default is 0). ; $iNoDate - [optional] Whether to omit the date from the output. Defaults to False. ; Return values .: Success: Returns the formatted date and time in AM/PM format. ; Failure: None. ; Author ........: argumentum ; Modified ......: ; Remarks .......: This function takes a 24-hour time string, converts it to AM or PM format, and returns the result with optional trimming. ; Related .......: ; Link ..........: https://www.autoitscript.com/forum/index.php?showtopic=213061 ; Example .......: MsgBox(64, "Converted Time", HourAmPm("12/31/1999 18:59:59")) ; =============================================================================================================================== Func HourAmPm($sDateTime, $sAmPm = Default, $iTrimRight = Default, $iNoDate = Default) Local $aAmPm = StringSplit((StringInStr($sAmPm, "|") ? $sAmPm : "AM|PM"), "|"), $sFormat = $aAmPm[2] Local $iHourPos = StringInStr($sDateTime, ":"), $sHour = StringMid($sDateTime, $iHourPos - 2, 2) Local $sDate = StringLeft($sDateTime, $iHourPos - 3), $sTime = StringTrimLeft($sDateTime, $iHourPos - 1) If $sHour < 12 Then $sFormat = $aAmPm[1] ; https://www.autoitscript.com/forum/index.php?showtopic=213061 $sHour = Mod($sHour, 12) If Not $sHour Then $sHour = 12 Return StringTrimRight((Int($iNoDate) ? "" : $sDate) & StringRight('0' & $sHour, 2) & $sTime, Int($iTrimRight)) & " " & $sFormat EndFunc ;==>HourAmPm Func _WinAPI_RealChildWindowFromPoint($hWnd, $tPoint) Local $aRet = DllCall('user32.dll', 'hwnd', 'RealChildWindowFromPoint', 'hwnd', $hWnd, 'struct', $tPoint) If @error Then Return SetError(@error, @extended, 0) Return $aRet[0] EndFunc ;==>_WinAPI_RealChildWindowFromPoint Func GetRealChild($hWnd) Local $tPoint, $hRoot = _WinAPI_GetAncestor($hWnd, $GA_ROOT) If $hWnd = $hRoot Then $tPoint = _WinAPI_GetMousePos(True, $hWnd) Return _WinAPI_ChildWindowFromPointEx($hWnd, $tPoint) EndIf Local $hParent = _WinAPI_GetAncestor($hWnd, $GA_PARENT) Local $aChild = _WinAPI_EnumChildWindows($hParent) If @error Then Return 0 Local $hFound For $i = 1 To $aChild[0][0] $hParent = _WinAPI_GetParent($aChild[$i][0]) $tPoint = _WinAPI_GetMousePos(True, $hParent) $hFound = _WinAPI_RealChildWindowFromPoint($hParent, $tPoint) If $hFound = $aChild[$i][0] Then Return $hFound Next Return 0 EndFunc ;==>GetRealChil
argumentum Posted October 27 Posted October 27 44 minutes ago, WildByDesign said: and @argumentum. me ?. Well thank you, but Global $hWorkerW = _WinAPI_FindWindowEx($hProgman, 0, "WorkerW", "") If $hWorkerW = 0 Then Exit MsgBox(16, "ERROR", "Couldn't find WorkerW under Progman", 30) didn't work for me so I had to Global $hWorkerW = _WinAPI_FindWindowEx($hProgman, 0, "WorkerW", "") If Not $hWorkerW Then ; dah Local $aEnumWindows = _WinAPI_EnumWindows(False) For $n = 1 To UBound($aEnumWindows) - 1 If $aEnumWindows[$n][1] <> "WorkerW" Then ContinueLoop If _WinAPI_GetParent($aEnumWindows[$n][0]) = $hProgman Then $hWorkerW = $aEnumWindows[$n][0] ExitLoop ; but is likely one at the end of the Z-order EndIf Next EndIf If $hWorkerW = 0 Then Exit MsgBox(16, "ERROR", "Couldn't find WorkerW under Progman", 30) to find it. So my only collaboration wasn't included !? 47 minutes ago, WildByDesign said: The goal of this thread is to see if there is any possible way to (at least momentarily) access controls in that GUI because you generally cannot click on them. Not really but you know that. You could use something to be aware of the wheel changing and use that to move the control if, condition are met ( you can come up with something am sure ). Also, after I closed the script, in the monitor that displayed the GUI, the wallpaper had to be reset. It was gone Follow the link to my code contribution ( and other things too ). FAQ - Please Read Before Posting.
Solution ioa747 Posted October 27 Solution Posted October 27 (edited) The example below can be exited by pressing F1 and then Escape expandcollapse popup#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 #include <WinAPI.au3> #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> #include <StaticConstants.au3> HotKeySet('{F1}', F1) ; <-- Global $idSlider, $idValueLabel, $iSliderValue = 50, $hSliderGUI, $bFlag Global $hGUI, $BkColor = 0x000000 Example() Func Example() Local $hWorkerW = _GetDesktopWorkerW() If @error Then Exit MsgBox(16, @ScriptName, "! Error - Couldn't find WorkerW under Progman", 30) ConsoleWrite("WorkerW = " & $hWorkerW & @CRLF) ; Overlay GUICreate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $hGUI = GUICreate("Overlay", @DesktopWidth, @DesktopHeight, -1, -1, $WS_POPUP, $WS_EX_TOOLWINDOW) GUISetBkColor($BkColor, $hGUI) Local $idLabelTime = GUICtrlCreateLabel("123456790", (@DesktopWidth - 400) / 2, (@DesktopHeight - 100) / 2, 400, 100, BitOR($SS_CENTER, $SS_CENTERIMAGE)) GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT) GUICtrlSetColor(-1, 0xFFD800) ; text GUICtrlSetFont(-1, 60, 700, 0, "consolas") _WinAPI_SetParent($hGUI, $hWorkerW) _WinAPI_SetWindowLong($hGUI, $GWL_EXSTYLE, BitOR(_WinAPI_GetWindowLong($hGUI, $GWL_EXSTYLE), $WS_EX_LAYERED)) _WinAPI_SetLayeredWindowAttributes($hGUI, $BkColor, $iSliderValue, $LWA_ALPHA) GUISetState(@SW_SHOWNOACTIVATE) ; SliderGUI GUICreate ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ $hSliderGUI = GUICreate("Slider Example", 250, 70, -1, @DesktopHeight * 0.6, $WS_POPUP) $idSlider = GUICtrlCreateSlider(10, 10, 230, 25) GUICtrlSetLimit(-1, 255, 0) GUICtrlSetData(-1, $iSliderValue) $idValueLabel = GUICtrlCreateLabel($iSliderValue, 10, 50, 230, 20, $SS_CENTER) GUISetState(@SW_HIDE, $hSliderGUI) GUIRegisterMsg($WM_HSCROLL, "WM_HSCROLL") Local $Tick, $sTime ;********************************** While 1 Switch GUIGetMsg() Case -3 ;$GUI_EVENT_CLOSE ExitLoop EndSwitch If $bFlag Then If Not IsMouseOverWin($hSliderGUI, 10) Then GUISetState(@SW_HIDE, $hSliderGUI) $bFlag = False EndIf EndIf If $Tick <> @SEC Then $sTime = @HOUR & ":" & @MIN & ":" & @SEC GUICtrlSetData($idLabelTime, $sTime) $Tick = @SEC EndIf WEnd ;********************************** EndFunc ;==>Example ;--------------------------------------------------------------------------------------- Func F1() $bFlag = Not $bFlag If $bFlag Then GUISetState(@SW_SHOW, $hSliderGUI) MouseMove(@DesktopWidth / 2, @DesktopHeight * 0.62, 1) EndIf EndFunc ;==>F1 ;--------------------------------------------------------------------------------------- Func IsMouseOverWin($hWnd, $iOffSet = 0) Local $aMPos = MouseGetPos() Local $aWPos = WinGetPos($hWnd) If $aMPos[0] > $aWPos[0] - $iOffSet _ And $aMPos[1] > $aWPos[1] - $iOffSet _ And $aMPos[0] < $aWPos[0] + $aWPos[2] + $iOffSet _ And $aMPos[1] < $aWPos[1] + $aWPos[3] + $iOffSet Then Return True EndIf Return False EndFunc ;==>IsMouseOverWin ;--------------------------------------------------------------------------------------- Func _GetDesktopWorkerW() Local $hProgman = _WinAPI_GetShellWindow() _WinAPI_SendMessageTimeout($hProgman, 0x052C, 0, 0, 3000, $SMTO_NORMAL) ; same as _SendMessage() Local $aEnumWindows = WinList("[CLASS:WorkerW]") For $n = 1 To $aEnumWindows[0][0] Local $hWnd = $aEnumWindows[$n][1] If _WinAPI_GetParent($hWnd) = $hProgman Then Return SetError(0, $hProgman, $hWnd) Next Return SetError(1, $hProgman, 0) EndFunc ;==>_GetDesktopWorkerW ;--------------------------------------------------------------------------------------- Func WM_HSCROLL($hWnd, $iMsg, $wParam, $lParam) #forceref $hWnd, $iMsg, $wParam, $lParam Local $hControl = $lParam If $hControl = GUICtrlGetHandle($idSlider) Then $iSliderValue = GUICtrlRead($idSlider) _WinAPI_SetLayeredWindowAttributes($hGUI, $BkColor, $iSliderValue, $LWA_ALPHA) GUICtrlSetData($idValueLabel, $iSliderValue) EndIf Return $GUI_RUNDEFMSG EndFunc ;==>WM_HSCROLL ;--------------------------------------------------------------------------------------- Edited October 27 by ioa747 improvement argumentum 1 I know that I know nothing
WildByDesign Posted October 27 Author Posted October 27 14 hours ago, argumentum said: So my only collaboration wasn't included !? I was initially referring to your recent HourAmPm() function. I was actually losing my marbles over trying to convert the hour/minute/second macros into a proper time display. But then I realized that I was wasting too much time on something that was only to show on a ConsoleWrite. So I found your recent HourAmPm() function and it saved my morning and allowed time for an extra coffee. But thank you for pointing it out your fallback WorkerW code though because it's brilliant. And clearly it's crucial for when the initial attempt to get WorkerW handle fails. So I added that to my main super-duper top secret project. 😜 Sorry about your wallpaper disappearing. This whole Progman/WorkerW thing can be finicky from system to system. Similar to how some of the other scripts to get WorkerW handle fail on mine for some reason. argumentum 1
WildByDesign Posted October 27 Author Posted October 27 12 hours ago, ioa747 said: The example below can be exited by pressing F1 and then Escape You always have really creative ways of "thinking outside the box". I love what you came up with. I had never even thought about creating a second GUI to be able to show the slider control. Everytime you share something creative like this, I quite often learn even more from it than what I was initially intending to learn. So I just wanted to thank you for always being willing to share your time and your knowledge. ioa747 1
ioa747 Posted October 27 Posted October 27 Thank you for your kind words, regarding Date/Time Format, take a look at Free style DateTimeFormat WildByDesign and argumentum 2 I know that I know nothing
Parsix Posted October 28 Posted October 28 (edited) Hello It was a good job Just having WM_NOTIFY does not cause any problems in small and independent designs, but in projects that use modular connection of different codes, WM_NOTIFY management requires centralized management, which creates problems with accessing global variables In my opinion, we should move the designs towards modularity so that we do not encounter any problems when using them in large designs. In this case, I prefer not to use WM_NOTIFY, which is needed in different places. Although in this example WM_NOTIFY is not very important. @WildByDesign Edited October 28 by Parsix WildByDesign 1
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now