Jump to content

GUI randomly creates huge overlay, corrupting rest of Windows UI


Go to solution Solved by LarsJ,

Recommended Posts

Hi all,

I'm really new to AutoIt and encountered an issue with my script. I'd be happy if someone could point me in the right direction to solve the issue.

First a few words about what the script does:

Every given time interval, it will perform a bunch of actions. Between these actions, I am running a loop, which prints text on screen, updating every 100ms and showing the remaining time in seconds until the next action will be triggered. All of this runs in a permanent while loop until terminated.

The issue:

Randomly the program will generate a huge colored rectangle the size of my monitor, just offset to the bottom right by the same distance at which my info text is off-set. When this happens, interaction with my task bar is also corrupted, with the task bar display being randomly offset as I interact with elements. Using the task manager is not possible, as it is hidden by the colored rectangle. Interaction with the tray icons is not possible, as the arrow to expand tray icons is corrupted and won't properly open.

This always happens at some random point while the countdown is running. It never happens during the execution of the main actions. Always during the countdown.

The script is below. Given the corruption only happens during the countdown, I guess it must happen during the do-loop in lines 19-22. That's where all the GUI functions get called (which I mostly obtained from various snippets around the forums/internet). I have no clue why the script updates the counter perfectly fine for several hundred iterations and then just randomly it starts corrupting like explained above. Does anyone have an idea what is happening? 

Thanks in advance!

 

#include <GUIConstants.au3>

; ####### CONFIG #######

Global $text = "Next action in " ; Info text to show on screen
Global $txtcolor = 0xFF0000 ; Text color
Global $printInfo = 1 ; Show info text at all?

; ##### END CONFIG #####

Global $delay = 0.0
Global $hwnd = GUICreate("Text Region", @DesktopWidth, @DesktopHeight, 10, 10, $WS_POPUP, BitOR($WS_EX_TOPMOST,$WS_EX_TOOLWINDOW))
GUISetBkColor($txtcolor) ; text color

While 1

    $time = TimerInit()
    
    do
        If $printInfo Then DrawNotice($hwnd, $text, Round(($delay - TimerDiff($time))/1000))
        Sleep(100)
    until TimerDiff($time) >= $delay
    
    Action()
    
    $delay = Random(52,71,1)*1000
    
WEnd

Func DrawNotice($hwnd, $text, $delay)

    Local $message = $text & $delay & "s"
    Local $rgn = CreateTextRgn($hwnd, $message, 100, "Arial", 500)
    SetWindowRgn($hwnd,$rgn)
    GUISetState()
    
EndFunc

Func SetWindowRgn($h_win, $rgn)
    DllCall("user32.dll", "long", "SetWindowRgn", "hwnd", $h_win, "long", $rgn, "int", 1)
EndFunc

Func CreateTextRgn(ByRef $CTR_hwnd,$CTR_Text,$CTR_height,$CTR_font="Microsoft Sans Serif",$CTR_weight=1000)
    Local Const $ANSI_CHARSET = 0
    Local Const $OUT_CHARACTER_PRECIS = 2
    Local Const $CLIP_DEFAULT_PRECIS = 0
    Local Const $PROOF_QUALITY = 2
    Local Const $FIXED_PITCH = 1
    Local Const $RGN_XOR = 3
    
    If $CTR_font = "" Then $CTR_font = "Microsoft Sans Serif"
    If $CTR_weight = -1 Then $CTR_weight = 1000
    Local $gdi_dll = DLLOpen("gdi32.dll")
    Local $CTR_hDC= DLLCall("user32.dll","int","GetDC","hwnd",$CTR_hwnd)
    Local $CTR_hMyFont = DLLCall($gdi_dll,"hwnd","CreateFont","int",$CTR_height,"int",0,"int",0,"int",0, _
                "int",$CTR_weight,"int",0,"int",0,"int",0,"int",$ANSI_CHARSET,"int",$OUT_CHARACTER_PRECIS, _
                "int",$CLIP_DEFAULT_PRECIS,"int",$PROOF_QUALITY,"int",$FIXED_PITCH,"str",$CTR_font )
    Local $CTR_hOldFont = DLLCall($gdi_dll,"hwnd","SelectObject","int",$CTR_hDC[0],"hwnd",$CTR_hMyFont[0])
    DLLCall($gdi_dll,"int","BeginPath","int",$CTR_hDC[0])
    DLLCall($gdi_dll,"int","TextOut","int",$CTR_hDC[0],"int",0,"int",0,"str",$CTR_Text,"int",StringLen($CTR_Text))
    DLLCall($gdi_dll,"int","EndPath","int",$CTR_hDC[0])
    Local $CTR_hRgn1 = DLLCall($gdi_dll,"hwnd","PathToRegion","int",$CTR_hDC[0])
    Local $CTR_rc = DLLStructCreate("int;int;int;int")
    DLLCall($gdi_dll,"int","GetRgnBox","hwnd",$CTR_hRgn1[0],"ptr",DllStructGetPtr($CTR_rc))
    Local $CTR_hRgn2 = DLLCall($gdi_dll,"hwnd","CreateRectRgnIndirect","ptr",DllStructGetPtr($CTR_rc))
    DLLCall($gdi_dll,"int","CombineRgn","hwnd",$CTR_hRgn2[0],"hwnd",$CTR_hRgn2[0],"hwnd",$CTR_hRgn1[0],"int",$RGN_XOR)
    DLLCall($gdi_dll,"int","DeleteObject","hwnd",$CTR_hRgn1[0])
    DLLCall("user32.dll","int","ReleaseDC","hwnd",$CTR_hwnd,"int",$CTR_hDC[0])
    DLLCall($gdi_dll,"int","SelectObject","int",$CTR_hDC[0],"hwnd",$CTR_hOldFont[0])
    DLLClose($gdi_dll)
    Return $CTR_hRgn2[0]
EndFunc

Func Action()

        ; Main program activity goes here
EndFunc

 

Link to comment
Share on other sites

Small update:

The huge rectangle initially does not get drawn. Initially only Windows UI and other programs get visually corrupted. When I ctrl alt del and start the task manager, the rectangle turns visible as soon as the desktop is shown again. From this moment on, I can foreground the task bar again normally, by clicking on the small segment that's visible on the left, given the rectangle's offset. Interacting with the tray-icon arrow is then possible normally and I can terminate the Autoit script from there.

I still don't get why this happens in the first place, especially since it always does hundreds of iterations (even dozens of iterations of the outer while-loop) without any issues...

Link to comment
Share on other sites

  • Moderators

Swimlanes,

Welcome to the AutoIt forum.

I will leave the GDI gurus to look into your problem, but could I point you to my Notify or Toast UDFs (the links are in my sig below) either of which look to be something which would do exactly what you say you want:

Quote

 showing the remaining time in seconds until the next action will be triggered

it might be quicker then trying to debug what you already have. And I would be happy to help you integrate either into your script.

M23

Public_Domain.png.2d871819fcb9957cf44f4514551a2935.png Any of my own code posted anywhere on the forum is available for use by others without any restriction of any kind

Open spoiler to see my UDFs:

Spoiler

ArrayMultiColSort ---- Sort arrays on multiple columns
ChooseFileFolder ---- Single and multiple selections from specified path treeview listing
Date_Time_Convert -- Easily convert date/time formats, including the language used
ExtMsgBox --------- A highly customisable replacement for MsgBox
GUIExtender -------- Extend and retract multiple sections within a GUI
GUIFrame ---------- Subdivide GUIs into many adjustable frames
GUIListViewEx ------- Insert, delete, move, drag, sort, edit and colour ListView items
GUITreeViewEx ------ Check/clear parent and child checkboxes in a TreeView
Marquee ----------- Scrolling tickertape GUIs
NoFocusLines ------- Remove the dotted focus lines from buttons, sliders, radios and checkboxes
Notify ------------- Small notifications on the edge of the display
Scrollbars ----------Automatically sized scrollbars with a single command
StringSize ---------- Automatically size controls to fit text
Toast -------------- Small GUIs which pop out of the notification area

 

Link to comment
Share on other sites

  • Solution

Most of these issues related to GDI and GDI+ code is about memory leaks. That's also the case here. There are two memory leaks in the code: neither $CTR_hMyFont[0] nor $CTR_hRgn2[0] are deleted using the DeleteObject() function. Fix these errors and the code will probably run forever.

I would write the code this way: 

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#AutoIt3Wrapper_UseX64=Y

Opt( "MustDeclareVars", 1 )

#include <GUIConstants.au3>

Example()

Func Example()
  Local $text = "Next action in " ; Info text to show on screen
  Local $txtcolor = 0xFF0000 ; Text color
  Local $printInfo = 1 ; Show info text at all?

  Local $delay = 0.0
  Local $hwnd = GUICreate("Text Region", 800, 200, 10, 10, $WS_POPUP, BitOR($WS_EX_TOPMOST,$WS_EX_TOOLWINDOW))
  GUISetBkColor($txtcolor) ; text color
  GUISetState()

  While 1
    Local $time = TimerInit()
    do
      If $printInfo Then CreateTextRgn($hwnd, $text & Round(($delay - TimerDiff($time))/1000) & "s", 100, "Arial", 500)
      Sleep(100)
    until TimerDiff($time) >= $delay
    $delay = Random(52,71,1)*1000
    Action()
  WEnd
EndFunc

Func CreateTextRgn($CTR_hwnd,$CTR_Text,$CTR_height,$CTR_font="Microsoft Sans Serif",$CTR_weight=1000)
  Local Const $ANSI_CHARSET = 0, $OUT_CHARACTER_PRECIS = 2, $CLIP_DEFAULT_PRECIS = 0, $PROOF_QUALITY = 2, $FIXED_PITCH = 1, $RGN_XOR = 3
  
  If $CTR_font = "" Then $CTR_font = "Microsoft Sans Serif"
  If $CTR_weight = -1 Then $CTR_weight = 1000

  Local Static $gdi_dll = DLLOpen("gdi32.dll")
  Local Static $CTR_hDC= DLLCall("user32.dll","int","GetDC","hwnd",$CTR_hwnd)
  Local Static $CTR_hMyFont = DLLCall($gdi_dll,"hwnd","CreateFont","int",$CTR_height,"int",0,"int",0,"int",0, _
    "int",$CTR_weight,"int",0,"int",0,"int",0,"int",$ANSI_CHARSET,"int",$OUT_CHARACTER_PRECIS, _
    "int",$CLIP_DEFAULT_PRECIS,"int",$PROOF_QUALITY,"int",$FIXED_PITCH,"str",$CTR_font )
  Local Static $CTR_hOldFont = DLLCall($gdi_dll,"hwnd","SelectObject","int",$CTR_hDC[0],"hwnd",$CTR_hMyFont[0])

  DLLCall($gdi_dll,"int","BeginPath","int",$CTR_hDC[0])
  DLLCall($gdi_dll,"int","TextOut","int",$CTR_hDC[0],"int",0,"int",0,"str",$CTR_Text,"int",StringLen($CTR_Text))
  DLLCall($gdi_dll,"int","EndPath","int",$CTR_hDC[0])
  Local $CTR_hRgn1 = DLLCall($gdi_dll,"hwnd","PathToRegion","int",$CTR_hDC[0])
  Local $CTR_rc = DLLStructCreate("int;int;int;int")
  DLLCall($gdi_dll,"int","GetRgnBox","hwnd",$CTR_hRgn1[0],"ptr",DllStructGetPtr($CTR_rc))
  Local $CTR_hRgn2 = DLLCall($gdi_dll,"hwnd","CreateRectRgnIndirect","ptr",DllStructGetPtr($CTR_rc))
  DLLCall($gdi_dll,"int","CombineRgn","hwnd",$CTR_hRgn2[0],"hwnd",$CTR_hRgn2[0],"hwnd",$CTR_hRgn1[0],"int",$RGN_XOR)
  DllCall("user32.dll", "long", "SetWindowRgn", "hwnd", $CTR_hwnd, "long", $CTR_hRgn2[0], "int", 1)

  ;DLLCall($gdi_dll,"int","DeleteObject","hwnd",$CTR_hMyFont[0]) ; Memory leak <<<<<<<<<<<<<<<<<<<<
  DLLCall($gdi_dll,"int","DeleteObject","hwnd",$CTR_hRgn1[0])
  ;DLLCall("user32.dll","int","ReleaseDC","hwnd",$CTR_hwnd,"int",$CTR_hDC[0])
  ;DLLCall($gdi_dll,"int","SelectObject","int",$CTR_hDC[0],"hwnd",$CTR_hOldFont[0])
  DLLCall($gdi_dll,"int","DeleteObject","hwnd",$CTR_hRgn2[0]) ; Memory leak <<<<<<<<<<<<<<<<<<<<<<<
  ;DLLClose($gdi_dll)
  #forceref $CTR_hOldFont
EndFunc

Func Action()
  ; Main program activity goes here
EndFunc

 

Link to comment
Share on other sites

Sorry for the late reply, I was pretty busy the last days.

 

Lars, your adjustments did it! I used your code and had it run for a couple of thousand iterations without any of these weird corruptions. Seems to be working fine now. And the parts you mentioned as memleaks also make sense, now that you pointed it out. Thanks a lot!

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
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...