关于修改GUI控件的问题

关于Autohotkey 修改GUI控件Background 颜色窗口崩溃
win10 x64、Autohotkey1.1.36
使用gdi32.dll\SetBkColor修改控件Background 颜色,在修改同一个控件9910次后窗口崩溃,或都修改多个控件,累计达到9910次后窗口崩溃,只是窗口崩溃了,脚本仍然正常运行(如fileappend仍然正常往文件写内容),增加延时无变化,dllcall返回没有错误。
希望可以无限次修改控件Background 颜色,感谢!
#Persistent
#Singleinstance force
#Include ControlCol.ahk

Gui Destroy
Gui, Add, Text,         hwndh_1, Text1
Gui, Add, Edit,         hwndh_2, Edit
Gui, Add, Edit,         hwndh_3, Edit
Gui, Add, UpDown,       hwndh_4, UpDown
Gui, Add, Picture,      hwndh_5, Picture
Gui, Add, Button,       hwndh_6, Button
Gui, Add, CheckBox,     hwndh_7, CheckBox
Gui, Add, Radio,        hwndh_8, Radio
Gui, Add, ListBox,      hwndh_9, ListBox
Gui, Add, Link,         hwndh_10, Link


Gui, Add, Edit, w320 h50 HwndMyTextHwnd,  Here is some text that is given`na custom background color.
Gui, Add, Edit, w320 h50 HwndMyTextHwnd2, Here is some text that is given`na custom background color.
Gui, Add, Edit, w320 h50 HwndMyTextHwnd3, Here is some text that is given`na custom background color.
Gui +LastFound
GuiHwnd := WinExist()
;MsgBox, % GuiHwnd
Gui Show
loop
{
    Sleep, 50
    Random, t, 0, 0xffffff
    Random, b, 0, 0xffffff
    ;MsgBox, %t%, %b%
    t := "0x" SubStr(Format("{:p}", t), 3)
    b := "0x" SubStr(Format("{:p}", b), 3)
    
    loop, 10 {    
        ControlCol(H_%A_Index%, GuiHwnd, b, t)    
    }    
    
    GuiControl, text, Edit5, %A_Index%
}

ControlCol(h_1, hMainWnd, 0x9FCFFF, 0xffffff)
ControlCol(MyTextHwnd2, hMainWnd, 0x00ff00, 0x0000ff)
ControlCol(MyTextHwnd3, hMainWnd, 0x0000ff, 0xff0000)
count := 0
;SetTimer, c, 500
return

c:
Random, b, 0, 0xffffff
Random, t, 0, 0xffffff
ControlCol(MyTextHwnd,GuiHwnd, b, t)
ControlCol(MyTextHwnd2,GuiHwnd, b, t)
abc := ControlCol(MyTextHwnd3,GuiHwnd, b, t)
count ++
GuiControl, text, Edit5, %Count% %abc%
return

f4::
ExitApp
return


GuiClose:
ExitApp

ControlCol.ahk

; ------------------------
; http://www.autohotkey.com/board/topic/104539-controlcol-set-background-and-text-color-gui-controls/
; v1.0.2
; ------------------------
ControlCol(Control, Window, bc="", tc="", redraw=1) {
        ; msgbox
    a := []
    a["c"]  := Control
    a["g"]  := Window
    a["bc"] := (bc="")?"":(((bc&255)<<16)+(((bc>>8)&255)<<8)+(bc>>16))
    a["tc"] := (tc="")?"":((tc&255)<<16)+(((tc>>8)&255)<<8)+(tc>>16)
    
    WindowProc("Set", a, "", "")
    If redraw
    {
        SizeOfWINDOWINFO := 10
        VarSetCapacity(WINDOWINFO, SizeOfWINDOWINFO, 0)       
        NumPut(SizeOfWINDOWINFO, WINDOWINFO, "UInt")
        DllCall("GetWindowInfo",  "Ptr", Control, "Ptr", &WINDOWINFO)
        ;FileAppend, GetWindowInfo %ErrorLevel%`n, %A_ScriptDir%\ControlCol.log
        DllCall("ScreenToClient", "Ptr", Window,  "Ptr", &WINDOWINFO+20) ; x1,y1 of Client area
        ;FileAppend, ScreenToClient %ErrorLevel%`n, %A_ScriptDir%\ControlCol.log
        DllCall("ScreenToClient", "Ptr", Window,  "Ptr", &WINDOWINFO+28) ; x2,y2 of Client area
        ;FileAppend, ScreenToClient %ErrorLevel%`n, %A_ScriptDir%\ControlCol.log
        DllCall("RedrawWindow"
        , "Ptr",  Window             ; A handle to the window to be redrawn. If this parameter is NULL, the desktop window is updated.
        , "UInt", &WINDOWINFO+20    ; A pointer to a RECT structure containing the coordinates, in device units, of the update rectangle. This parameter is ignored if the hrgnUpdate parameter identifies a region.
        , "UInt", 0                    ; A handle to the update region. If both the hrgnUpdate and lprcUpdate parameters are NULL, the entire client area is added to the update region.
        , "UInt", 0x101)            ; One or more redraw flags. This parameter can be used to invalidate or validate a window, control repainting, and control which windows are affected by RedrawWindow.
        ;FileAppend, RedrawWindow %ErrorLevel%`n, %A_ScriptDir%\ControlCol.log
        VarSetCapacity(WINDOWINFO, 0)
    }
    
    return    
}


WindowProc(hwnd, uMsg, wParam, lParam)
{
    Static Win := {}    
    ;Critical
    If uMsg between 0x132 and 0x138    
    If Win[hwnd].HasKey(lparam)
    {
      FileAppend, %A_Now% %A_MSec% %ErrorLevel%`n, %A_ScriptDir%\ControlCol.log
        If tc := Win[hwnd, lparam, "tc"]
        DllCall("SetTextColor", "UInt", wParam, "UInt", tc)
        If bc := Win[hwnd, lparam, "bc"]
        DllCall("SetBkColor", "UInt", wParam, "UInt", bc)
        
        return Win[hwnd, lparam, "Brush"]  ; Return the HBRUSH to notify the OS that we altered the HDC.
    }
    
    ;}
    If (hwnd = "Set")
    {
      ;MsgBox, fsefs
        a := uMsg
        Win[a.g, a.c] := a
        If (Win[a.g, a.c, "tc"] = "") and (Win[a.g, a.c, "bc"] = "")
            Win[a.g].Remove(a.c, "")
        If not Win[a.g, "WindowProcOld"]
            Win[a.g,"WindowProcOld"] := DllCall("SetWindowLong", "Ptr", a.g, "Int", -4, "Int", RegisterCallback("WindowProc", "", 4), "UInt")
        If Win[a.g, a.c, "Brush"]
            DllCall("DeleteObject", "Ptr", Brush)
        If (Win[a.g, a.c, "bc"] != "")
            Win[a.g, a.c, "Brush"] := DllCall("CreateSolidBrush", "UInt", a.bc)
        ; array_list(a)
        return
    } 
    
    q1234 := DllCall("CallWindowProcA", "UInt", Win[hwnd, "WindowProcOld"], "UInt", hwnd, "UInt", uMsg, "UInt", wParam, "UInt", lParam)
    
    return q1234
}

http://www.autohotkey.com/board/topic/104539-controlcol-set-background-and-text-color-gui-controls/

这种情况是由于你的脚本运行次数太多导致的。在修改同一个控件或多个控件多次后窗口崩溃是因为 GDI 对象泄露。GDI 对象是 Windows 使用的图形对象,当你创建太多的 GDI 对象而不释放它们,就会导致 GDI 对象泄露。

解决方法是使用 gdi32.dll 的 DeleteObject 函数来释放创建的 GDI 对象。可以在 ControlCol.ahk 中添加一个函数来删除之前创建的 GDI 对象:

ControlCol.ahk:

; ------------------------
; http://www.autohotkey.com/board/topic/104539-controlcol-set-background-and-text-color-gui-controls/
; v1.0.2
; ------------------------
ControlCol(Control, Window, bc="", tc="", redraw=1) {
static hBrush
; ...
if (hBrush)
DllCall("gdi32.dll\DeleteObject", "ptr", hBrush)
; ...
}

这样,在每次调用 ControlCol 函数时都会释放上一次创建的 GDI 对象,避免 GDI 对象泄露。

  • 这篇博客也许可以解决你的问题👉 :GUI编程

您好,根据您描述的问题,在修改GUI控件Background 颜色窗口时,发生多次修改后窗口奔溃的现象。从您提供的信息来分析,不排除是楼上所说的GDI资源泄露问题,这个问题一般都是发生在对主程序刷新界面,导致主程序重绘,消耗GDI资源。
解决方法是:
1、在每次修改GUI控件Background 颜色后观察GDI资源使用量,操作方法以win10系统的电脑为例:
1)打开任务管理器,选择详细信息
2)在名称的那行标题栏上右键
3)在弹出的窗口中,选择GDI对象那一列
4)然后你回到任务管理器中,就可以观察到GDI资源使用量
2、当你确定多次修改GUI控件Background 颜色后,GDI资源使用量太大后,比如达到几千,那么基本就确定是这个问题了
3、那您就需要检查或调试您的代码,看下是否调用相关对象后没有及时释放掉
4、如果您不知道怎么定位哪里的代码导致的泄露问题,可以下载一些第三方软件来帮助您,比如GDI泄露的工具GDILeaks