Simple Class_Gdip.ahk By v2-a102 with my own thinking

Post your working scripts, libraries and tools.
User avatar
hyaray
Posts: 85
Joined: 20 Jun 2015, 01:37
Contact:

Simple Class_Gdip.ahk By v2-a102 with my own thinking

Post by hyaray » 28 Dec 2020, 11:15

Hello everyone, I am a script enthusiast from China and I have been using AutoHotkey for 6 years.
Currently using v2 a102 version https://www.autohotkey.com/download/2.0/AutoHotkey_2.0-a102-5c9fb78f.zip
All my scripts are concentrated in one process, and the amount of code is quite large. I want to continue to upgrade to the new version, but I get stuck.
I don’t know how to adjust some grammatical changes of a104 :sick: . The documentation in this area does not seem to be comprehensive. I hope someone can clearly tell how to adjust the grammar.

This time I share the Class_Gdip.ahk and partially rewritten sample examples, as well as autohotkey_gdi.snippets for Vim ultisnips,
gdip is learned from the https://github.com/mmikeww/AHKv2-Gdip , thanks for the detailed examples inside.
I have been groping for many days, but I still feel that it will be tiring to write.
There are also some class_gdip.ahk I found at network, but the global functions are still used, and the design ideas do not suit my taste.
So I wrote Class_Gdip.ahk again as practice by the way, I rewrite some of the sample examples into my scripts for comparison.
I am not a professional programmer, the ideas inside may need to be adjusted again. I hope someone can help and improve it.
And I don't know what's the difference of hBitmap and pBitmap. :headwall:
The classes and methods inside are not complete yet, need to be improved over time.
Not sure if this script is suitable for people who do not understand gdip to take pictures of cats and tigers.

example1: Class_Gdip.01-simple draw.ahk

Code: Select all

#SingleInstance Force

#Include ../Class_Gdip.ahk

;rectangle to draw
w := 800
h := 600

oGdip := new _GDIP() ;pToken

oHBitmap := new GDIP_HBitmap(w, h)

;Encapsulate 'DC' into 'GDIP_Graphics'
;auto run SelectObject(oHBitmap) and save to oGraphics.pSelectSave 
oGraphics := new GDIP_Graphics(oHBitmap)
oGraphics.GdipSetSmoothingMode(4)

oBrush := new GDIP_Brush(0xFFFF0000)
oGraphics.GdipFillEllipse(oBrush.ptr, [100,300,200,300])

;modify oBrush.ptr, needn't new oBrush2
oBrush.GdipCreateSolidFill(0x660000FF)
oGraphics.GdipFillRectangle(oBrush.ptr, [300,300,300,200])

oPen := new GDIP_Pen(0xFFFF0000, 3)
oGraphics.GdipDrawEllipse(oPen.ptr, [100,50,200,300])

;modify oPen.ptr, needn't new oPen2
oPen.createByArgb(0x660000FF, 10)
oGraphics.GdipDrawEllipse(oPen.ptr, [250,80,300,200])
oPen := "" ;release here

oBrush.GdipCreateSolidFill(0x77000000)
oGraphics.fillRoundedRectangle(oBrush.ptr, [0,0,100,100], 20)
oBrush := "" ;release here

oGui := GuiCreate("-Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs")
oGui.Show("NA")
OnMessage(0x201, ()=>PostMessage(0xA1, 2))

oGraphics.UpdateLayeredWindow(oGui.hwnd, [100,0,w,h])
;restore oGraphics.pSelectSave
oGraphics.SelectObject()

;NOTE order is important
oHBitmap := ""
oGraphics := ""
oGdip := ""
return
example2: Class_Gdip.03-show half size of file.ahk

Code: Select all

#SingleInstance Force

#Include ../Class_Gdip.ahk

oGdip := new _GDIP()

oPBitmap := new GDIP_PBitmap("bs.jpg")
w := oPBitmap.getWidth()
h := oPBitmap.getHeight()

; 创建一个gdi位图,其宽度和高度与我们要绘制的位图相同。 这是所有内容的整个绘制区域
; 我们正在以实际图像大小的一半创建此“画布”
; 我们将其减半,因为我们希望图像以一半的尺寸显示在屏幕上的图形用户界面中
oHBitmap := new GDIP_HBitmap(w//2, h//2)

oGraphics := new GDIP_Graphics(oHBitmap)
; 我们不需要前面的示例中绘制图像时所需要的SmoothingMode
; 相反,我们必须设置InterpolationMode。 这指定了如何调整文件大小(调整大小的质量)
; 插值模式已设置为HighQualityBicubic = 7
oGraphics.GdipSetInterpolationMode(7)

oGraphics.drawImage(oPBitmap, [0,0,w//2,h//2])

oGui := GuiCreate("-Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs")
oGui.Show("NA")

oGraphics.UpdateLayeredWindow(oGui.hwnd, [200,0,w//2,h//2])

oGraphics.SelectObject()
oHBitmap := ""
oGraphics := ""
oPBitmap := ""
oGdip := ""
return

Code: Select all

;Gdip_All.ahk - GDI+ library compilation of user contributed GDI+ functions
;made by Marius Șucan: https://github.com/marius-sucan/AHK-GDIp-Library-Compilation
;a fork from: https://github.com/mmikeww/AHKv2-Gdip
;based on https://github.com/tariqporter/Gdip

;Features:
;   The parent class is '_GDIP', all other 'GDIP_*' inherit from it
;   Use the object method, the logic is clear
;   Simplified release, only need oInstance := "" (but pay attention to the order)
;   Encapsulate 'DC' into 'GDIP_Graphics', no need to consider the generation and release order
;       Two methods 'SelectObject' and 'UpdateLayeredWindow' are converted into 'GDIP_Graphics' methods
;       Simplified 'SelectObject': no need to record the old object, if not pass the parameter, will restore it.
;   Simplify the method call parameters, no need to pass in the properties of the object itself, for example, 'oPen' method needn't to pass in the 'pPen' parameter
;   The parameters of (x, y, w, h) are packaged and passed in in 'aRect' array instead of 4 parameters

; Important note:
;   Generally, 'oInstance := new class()' is required, for example, 'oPen' is an instance, and 'oPen.ptr' is the native 'pPen' of gdip. For the time being, oInstance only supports 1 'ptr' attribute.
;【OInstance】Used to call methods, or used as parameters by other class new (because 'oInstance.__class' can be used to determine), such as new GDIP_Graphics(oPBitmap/oHBitmap) instead of oPBitmap.ptr.
;【method】
;   The name is generally the same as the dllcall function. Those that do not involve dllcall do not start with 'Gdip'. 'DrawImage' is an exception (support oPBitmap)
;   Parameter, using pPen (compatible with native calls)
;   PS: If the parameter is filled with oPen, v2 a108+ will automatically read its ptr attribute, for example, the following two sentences are equivalent
;       oGraphics.GdipDrawEllipse(oPen,     [0,0,w,h])
;       oGraphics.GdipDrawEllipse(oPen.ptr, [0,0,w,h])

; Main class and its representation method: (used in ultisnips and positioning: for example, search pp to find the position of the GDIP_Pen class)
;主要 class 及其表示方法:(用在 ultisnips 和定位:比如搜索 pp 就能搜索到 GDIP_Pen 类的位置)
;   GDIP_PBitmap = m(NOTE)
;   GDIP_FontFamily = v(NOTE)
;   GDIP_Brush = b
;   GDIP_Font = f
;   GDIP_Graphics = g
;   GDIP_HBitmap = h
;   GDIP_Pen = p
;   GDIP_StringFormat = s

;所有 UPtr 都是由这句简化 A_PtrSize ? "UPtr" : "uint"
;特色:
;   父类是 _GDIP,其他 GDIP_* 全继承于它
;   用对象的方式,逻辑清晰
;   简化了释放,只需要 oInstance := "" 即可(但要注意顺序)
;   把 DC 封装到 GDIP_Graphics,不用考虑生成和释放顺序,使用更简单
;       两个方法 SelectObject 和 UpdateLayeredWindow 转成 GDIP_Graphics 的方法
;       简化了 SelectObject,不需要记录旧对象,不要传参即是还原
;   简化了方法调用的参数,无需传入对象本身的属性,比如 oPen的方法不需要传入 pPen 参数
;   x,y,w,h 的参数统一用 aRect 数组方式打包传入,而不是传入4个参数

; NOTE 重要说明:
;一般都需要 oInstance := new class(),比如 oPen 为实例,oPen.ptr 为 gdip 原生的 pPen。暂时 oInstance 只支持一个 ptr 属性。
;【oInstance】用来调用方法,或被其他 class new 当参数用(因为可用 oInstance.__class 判断),比如 new GDIP_Graphics(oPBitmap/oHBitmap),而不是用 oPBitmap.ptr。
;【方法】
;   名称,一般都是和 dllcall 函数同名,没涉及 dllcall 的则不是以 Gdip 开头,drawImage 是个例外(支持 oPBitmap)
;   参数,用的是 pPen(兼容原生调用)
;   PS:如果参数填oPen,v2 a108+会自动读取其 ptr 属性,比如以下两句等效
;   oGraphics.GdipDrawEllipse(oPen,     [0,0,w,h])
;   oGraphics.GdipDrawEllipse(oPen.ptr, [0,0,w,h])

class _GDIP {
    pToken := 0

    __new() {
        Ptr := A_PtrSize ? "UPtr" : "uint"
        pToken := 0
        if !dllcall("GetModuleHandle", "str", "gdiplus",Ptr)
            dllcall("LoadLibrary", "str", "gdiplus")
        VarSetCapacity(si, A_PtrSize == 8 ? 24 : 16, 0), si := chr(1)
        dllcall("gdiplus\GdiplusStartup",A_PtrSize ? "UPtr*" : "uint*",pToken, Ptr,&si, Ptr,0)
        this.pToken := pToken
    }

    __delete() {
        dllcall("gdiplus\GdiplusShutdown", "UPtr",this.pToken)
        if hModule := dllcall("GetModuleHandle", "str","gdiplus", "UPtr")
            dllcall("FreeLibrary", "UPtr",hModule)
        return 0
    }

    ; aRect := [100,200,(arrWH)=>0.3*arrWH[1]+20, (arrWH)=>0.4*arrWH[2]]
    ; arrWH := [300,400]
    ;aRect的每个项目都可以是函数,参数是图片的[宽,高] arrWH
    ;转成 [100, 200, 110, 160]
    rectDeal(aRect, arrWH) {
        for k, v in aRect {
            if isobject(v)
                aRect[k] := v.call(arrWH)
            else if (v < 0)
                aRect[k] := arrWH[mod(k-1,2)+1] + v ;1,3转成 w+v, 2,4转成 h+v
            else if (v < 1)
                aRect[k] := integer(arrWH[mod(k-1,2)+1] * v) ;1,3转成 w*v, 2,4转成 h*v
        }
        return aRect
    }

    ;确认鼠标选择区域
    ;aRect := _GDIP.getRect(()=>GetKeyState(RegExReplace(A_ThisLabel, ".*\s"), "P"))
    ;同 _Mouse.getRect
    getRect(funDo:="") {
        if !isobject(funDo)
            funDo := ()=>GetKeyState("LButton", "P")
        ;截图时显示的Gui
        oGui := GuiCreate("-caption +AlwaysOnTop +Border +E0x80000 +LastFound +OwnDialogs +ToolWindow")
        oGui.BackColor := "FFFFFF"
        WinSetTransparent(110)
        ;记录初始位置
        CoordMode("Mouse", "screen")
        MouseGetPos(x0, y0)
        while(funDo.call()) { ;左键按住不放
            sleep(10)
            MouseGetPos(x1, y1)
            x := min(x0, x1)
            y := min(y0, y1)
            w := abs(x0 - x1)
            h := abs(y0 - y1)
            oGui.show(format("x{1} y{2} w{3} h{4} NA", x,y,w,h))
            tooltip(format("{1}x{2}",w,h))
        }
        oGui.destroy()
        SetTimer("tooltip", -100)
        if (w<=3 || h<=3)
            exit
        return [x,y,w,h]
    }

    DeleteObject(hObject) {
        return dllcall("DeleteObject", "UPtr",hObject)
    }

    StretchBlt(ddc, aRectTo, sdc, aRectFrom, Raster:=0x00CC0020) {
        return dllcall("gdi32\StretchBlt"
            , "UPtr", ddc
            , "int", aRectTo[1],"int",aRectTo[2],"int",aRectTo[3],"int", aRectTo[4]
            , "UPtr", sdc
            , "int",aRectFrom[1],"int",aRectFrom[2],"int",aRectFrom[3],"int",aRectFrom[4]
            , "uint", Raster)
    }

    GetMonitorInfo(MonitorNum) {
        Monitors := this.MDMF_Enum()
        for k,v in Monitors
            if (v.Num = MonitorNum)
                return v
    }

    ;数组 aRect 转成 struct
    CreateRect(byref Rect, aRect) {
        VarSetCapacity(Rect, 16)
        numput(aRect[1], Rect, 0, "uint")
        numput(aRect[2], Rect, 4, "uint")
        numput(aRect[3], Rect, 8, "uint")
        numput(aRect[4], Rect, 12, "uint")
    }

    ; ======================================================================================================================
    ; Multiple Display Monitors Functions -> msdn.microsoft.com/en-us/library/dd145072(v=vs.85).aspx
    ; by 'just me'
    ; https://autohotkey.com/boards/viewtopic.php?f=6&t=4606
    ; ======================================================================================================================
    GetMonitorCount() {
        Monitors := this.MDMF_Enum()
        for k,v in Monitors
            count := A_Index
        return count
    }

    GetPrimaryMonitor() {
        Monitors := this.MDMF_Enum()
        for k,v in Monitors
            if (v.Primary)
                return v.Num
    }
    ; ======================================================================================================================
    ; Enumerates display monitors and returns an object containing the properties of all monitors or the specified monitor.
    ; ======================================================================================================================
    MDMF_Enum(HMON:="") {
        static CbFunc := (A_AhkVersion < "2") ? func("RegisterCallback") : func("CallbackCreate")
        static Monitors := {}
        this.EnumProc := CbFunc.call("MDMF_EnumProc")
        if (HMON == "") ; new enumeration
            Monitors := {}
        if (Monitors.MaxIndex() = "") ; enumerate
            if !dllcall("User32.dll\EnumDisplayMonitors", "Ptr",0, "Ptr",0, "Ptr",this.EnumProc, "Ptr",&Monitors, "uint")
                return false
        return (HMON == "") ? Monitors : Monitors.haskey(HMON) ? Monitors[HMON] : false
    }
    ; ======================================================================================================================
    ;  Callback function that is called by the MDMF_Enum function.
    ; ======================================================================================================================
    MDMF_EnumProc(HMON, hDC, PRECT, ObjectAddr) {
        Monitors := object(ObjectAddr)
        Monitors[HMON] := this.MDMF_GetInfo(HMON)
        return true
    }
    ; ======================================================================================================================
    ;  Retrieves the display monitor that has the largest area of intersection with a specified window.
    ; ======================================================================================================================
    MDMF_FromHWND(hwnd) {
        return dllcall("User32.dll\MonitorFromWindow", "Ptr",hwnd, "uint",0, "UPtr")
    }
    ; ======================================================================================================================
    ; Retrieves the display monitor that contains a specified point.
    ; if either X or Y is empty, the function will use the current cursor position for this value.
    ; ======================================================================================================================
    MDMF_FromPoint(X := "", Y := "") {
        VarSetCapacity(PT, 8, 0)
        if (X = "") || (Y = "") {
            dllcall("User32.dll\GetCursorPos", "Ptr", &PT)
            if (X = "")
                X := numget(PT, 0, "int")
            if (Y = "")
                Y := numget(PT, 4, "int")
        }
        return dllcall("User32.dll\MonitorFromPoint", "int64", (X & 0xFFFFFFFF) | (Y << 32), "uint",0, "UPtr")
    }
    ; ======================================================================================================================
    ; Retrieves the display monitor that has the largest area of intersection with a specified rectangle.
    ; Parameters are consistent with the common AHK definition of a rectangle, which is X, Y, W, H instead of
    ; left, top, right, bottom.
    ; ======================================================================================================================
    MDMF_FromRect(X, Y, W, H) {
        VarSetCapacity(RC, 16, 0)
        numput(X, RC, 0, "int"), numput(Y, RC, 4, int), numput(X + W, RC, 8, "int"), numput(Y + H, RC, 12, "int")
        return dllcall("User32.dll\MonitorFromRect", "Ptr", &RC, "uint",0, "UPtr")
    }
    ; ======================================================================================================================
    ; Retrieves information about a display monitor.
    ; ======================================================================================================================
    MDMF_GetInfo(HMON) {
        numput(VarSetCapacity(MIEX, 40 + (32 << !!A_IsUnicode)), MIEX, 0, "uint")
        if dllcall("User32.dll\GetMonitorInfo", "Ptr",HMON, "Ptr", &MIEX) {
            MonName := strget(&MIEX + 40, 32)	; CCHDEVICENAME = 32
            MonNum := RegExReplace(MonName, ".*(\d+)$", "$1")
            return {	Name:		(Name := strget(&MIEX + 40, 32))
                ,	Num:		RegExReplace(Name, ".*(\d+)$", "$1")
                ,	left:		numget(MIEX, 4, "int")		; display rectangle
                ,	top:		numget(MIEX, 8, "int")		; "
                ,	right:		numget(MIEX, 12, "int")		; "
                ,	bottom:		numget(MIEX, 16, "int")		; "
                ,	WALeft:		numget(MIEX, 20, "int")		; work area
                ,	WATop:		numget(MIEX, 24, "int")		; "
                ,	WARight:	numget(MIEX, 28, "int")		; "
                ,	WABottom:	numget(MIEX, 32, "int")		; "
                ,	Primary:	numget(MIEX, 36, "uint")}	; contains a non-zero value for the primary monitor.
        }
        return false
    }

}

;mm
class GDIP_PBitmap extends _GDIP {
    ptr := 0
    w := 0
    h := 0
    ;fp 文件存在
    ;w,h w是数字
    ;oHBitmap
    ;[x,y,w,h] aRect
    __new(w:="", h:=0) {
        if isobject(w) {
            if (w.__class == "GDIP_HBitmap")
                this.GdipCreateBitmapFromHBITMAP(hBitmap, Palette:=0)
            else if (w.length() == 4)
                this.getFromScreen(w)
        } else {
            if (w ~= "^\d+$")
                this.GdipCreateBitmapFromScan0(w, h)
            else if FileExist(w)
                this.GdipCreateBitmapFromFile(w)
            else
                msgbox(A_ThisFunc . "`n" . w . "`n" . h)
        }
    }

    ;出错,可排查释放顺序
    __delete() {
        ; msgbox(A_ThisFunc . '---')
        dllcall("gdiplus\GdipDisposeImage", "UPtr",this.ptr)
        return
    }

    GdipCreateBitmapFromScan0(w, h, PixelFormat:=0x26200A) {
        dllcall("gdiplus\GdipCreateBitmapFromScan0", "int",w, "int",h, "int",0, "int",PixelFormat, "UPtr",0, "UPtr*",pBitmap)
        if !pBitmap {
            msgbox(A_ThisFunc . "`n" . w . "`n" . h)
        }
        this.w := w
        this.h := h
        this.ptr := pBitmap
    }

    ; GdipCreateBitmapFromFile(fp, IconNumber:=1, IconSize:="")
    GdipCreateBitmapFromFile(fp) {
        dllcall("gdiplus\GdipCreateBitmapFromFile", "UPtr",&fp, "UPtr*",pBitmap)
        if (!pBitmap)
            msgbox(A_ThisFunc . "`n" . fp)
        this.w := 0
        this.h := 0
        this.ptr := pBitmap
        ; SplitPath(fp,,, ext)
        ; if RegExMatch(ext, "^(i:exe|dll)$") {
        ;     Sizes := IconSize ? IconSize : 256 "|" 128 "|" 64 "|" 48 "|" 32 "|" 16
        ;     BufSize := 16 + (2*A_PtrSize)
        ;     VarSetCapacity(buf, BufSize, 0)
        ;     for eachSize, Size in StrSplit(Sizes, "|") {
        ;         dllcall("PrivateExtractIcons", "str",fp, "int",IconNumber-1, "int",Size, "int",Size, "UPtr*",hIcon, "UPtr*",0, "uint",1, "uint",0)
        ;         if !hIcon
        ;             continue
        ;         if !dllcall("GetIconInfo", "UPtr",hIcon, "UPtr",&buf) {
        ;             DestroyIcon(hIcon)
        ;             continue
        ;         }
        ;         hbmMask  := numget(buf, 12 + (A_PtrSize - 4))
        ;         hbmColor := numget(buf, 12 + (A_PtrSize - 4) + A_PtrSize)
        ;         if !(hbmColor && dllcall("GetObject", "UPtr",hbmColor, "int",BufSize, "UPtr",&buf)) {
        ;             DestroyIcon(hIcon)
        ;             continue
        ;         }
        ;         break
        ;     }
        ;     if !hIcon
        ;         return -1
        ;     width := numget(buf, 4, "int"), height := numget(buf, 8, "int")
        ;     hbm := CreateDIBSection(width, -height)
        ;     hDC := GDIP_DC.create()
        ;     obm := SelectObject(hDC, hbm)
        ;     if !dllcall("DrawIconEx", "UPtr",hDC, "int",0, "int",0, "UPtr",hIcon, "uint",width, "uint",height, "uint",0, "UPtr",0, "uint",3) {
        ;         DestroyIcon(hIcon)
        ;         return -2
        ;     }
        ;     VarSetCapacity(dib, 104)
        ;     ; sizeof(DIBSECTION) = 76+2*(A_PtrSize=8?4:0)+2*A_PtrSize
        ;     dllcall("GetObject", "UPtr",hbm, "int",A_PtrSize == 8 ? 104 : 84, "UPtr",&dib)
        ;     Stride := numget(dib, 12, "int"), Bits := numget(dib, 20 + (A_PtrSize - 4)) ; padding
        ;     dllcall("gdiplus\GdipCreateBitmapFromScan0", "int",width, "int",height, "int",Stride, "int",0x26200A, "UPtr",Bits, "UPtr*",pBitmapOld)
        ;     pBitmap := Gdip_CreateBitmap(width, height)
        ;     _G := Gdip_GraphicsFromImage(pBitmap)
        ;         , Gdip_DrawImage(_G, pBitmapOld, 0, 0, width, height, 0, 0, width, height)
        ;     SelectObject(hDC, obm), dllcall("gdi32\DeleteObject", "UPtr",hbm), DeleteDC(hDC)
        ;     Gdip_DeleteGraphics(_G), Gdip_DisposeImage(pBitmapOld)
        ;     DestroyIcon(hIcon)
        ;     return this.ptr := pBitmap
        ; }
    }

    ; GdipCreateBitmapFromFileICM(bitmap) {
    ;     dllcall("gdiplus\GdipCreateBitmapFromFileICM", "wstr",bitmap, "ptr*",pBitmap)
    ;     if (!pBitmap)
    ;         msgbox(A_ThisFunc)
    ;     return this.ptr := pBitmap
    ; }

    getFromScreen(aRect, Raster:="") {
        this.GdipCreateBitmapFromHBITMAP(new GDIP_HBitmap(aRect))
    }

    GdipCreateBitmapFromHBITMAP(oHBitmap, Palette:=0) {
        dllcall("gdiplus\GdipCreateBitmapFromHBITMAP", "UPtr",oHBitmap.ptr, "UPtr",Palette, "UPtr*",pBitmap)
        return this.ptr := pBitmap
    }

    ; getFromClipboard() {
    ;     if !dllcall("IsClipboardFormatAvailable", "uint",8)
    ;         return -2
    ;     if !dllcall("OpenClipboard", "UPtr",0)
    ;         return -1
    ;     if !hBitmap := dllcall("GetClipboardData", "uint",2, "UPtr")
    ;         return -3
    ;     if !pBitmap := this.GdipCreateBitmapFromHBITMAP(oHBitmap)
    ;         return -4
    ;     if !dllcall("CloseClipboard")
    ;         return -5
    ;     dllcall("gdi32\DeleteObject", "UPtr",hBitmap)
    ;     return this.ptr := pBitmap
    ; }

    ; getFromHICON(hIcon) {
    ;     dllcall("gdiplus\GdipCreateBitmapFromHICON",A_PtrSize ? "UPtr" : "uint",hIcon, A_PtrSize ? "UPtr*" : "uint*",pBitmap)
    ;     return this.ptr := pBitmap
    ; }

    getSize(byref w, byref h) {
        if !this.w {
            dllcall("gdiplus\GdipGetImageWidth", "UPtr",this.ptr, "uint*",w)
            this.w := w
        }
        if !this.h {
            dllcall("gdiplus\GdipGetImageHeight", "UPtr",this.ptr, "uint*",h)
            this.h := h
        }
        return [this.w, this.h]
    }
    getWidth() {
        if !this.w {
            dllcall("gdiplus\GdipGetImageWidth", "UPtr",this.ptr, "uint*",w)
            this.w := w
        }
        return this.w
    }
    getHeight() {
        if !this.h {
            dllcall("gdiplus\GdipGetImageHeight", "UPtr",this.ptr, "uint*",h)
            this.h := h
        }
        return this.h
    }

    getPixel(x, y) {
        dllcall("gdiplus\GdipBitmapGetPixel", "Ptr",this.ptr, "int",x, "int",y, "Uint*",ARGB:=0)
        return ARGB
    }
    setPixel(x, y, ARGB) {
        return dllcall("gdiplus\GdipBitmapSetPixel", "Ptr",this.ptr, "int",x, "int",y, "int",ARGB)
    }

    ; TODO
    ; https://autohotkey.com/board/topic/29449-gdi-standard-library-145-by-tic/page-58#entry455137
    rotate(angle) {
        this.getSize(w, h)
        aRect := GDIP_Graphics.getRotatedRect(w, h, angle)
        xOffset := aRect[1]
        yOffset := aRect[2]
        wNew := aRect[3]
        hNew := aRect[4]
        oPBitmap := new GDIP_PBitmap(wNew, hNew)
        oGraphics := new GDIP_Graphics(oPBitmap)
        oGraphics.GdipSetInterpolationMode(7)
        oGraphics.GdipSetSmoothingMode(4)
        oGraphics.GdipDrawImageRectRect(this.ptr, [0,0,wNew,hNew], [0,0,wNew,hNew])
        oGraphics := ""
        this.ptr := oPBitmap.ptr
        oPBitmap := ""
        return this.ptr
    }

    ;剪切
    crop(left:=0, right:=0, up:=0, down:=0, Dispose:=1) {
        this.getSize(w, h)
        wNew := w-left-right
        hNew := h-up-down
        oPBitmap := new GDIP_PBitmap(wNew, hNew)
        oGraphics := new GDIP_Graphics(oPBitmap)
        oGraphics.GdipSetInterpolationMode(7)
        oGraphics.GdipSetSmoothingMode(4)
        oGraphics.GdipDrawImageRectRect(this.ptr, [0,0,wNew,hNew], [left,up,wNew,hNew])
        oGraphics := ""
        this.ptr := oPBitmap.ptr
        oPBitmap := ""
        return this.ptr
    }

    ; http://www.autohotkey.com/community/viewtopic.php?p=477333
    ; returns resized bitmap. By Learning one.
    resize(PercentOrWH, Dispose:=1) {
        this.getSize(w, h)
        if isobject(PercentOrWH) {
            wNew := PercentOrWH[1]
            hNew := PercentOrWH[2]
        } else {
            wNew := w*PercentOrWH/100
            hNew := h*PercentOrWH/100		
        }
        oPBitmap := new GDIP_PBitmap(wNew, hNew)
        oGraphics := new GDIP_Graphics(oPBitmap)
        oGraphics.GdipSetInterpolationMode(7)
        oGraphics.GdipSetSmoothingMode(4)
        oGraphics.GdipDrawImageRectRect(this.ptr, [0,0,wNew,hNew], [0,0,w,h])
        oGraphics := ""
        this.ptr := oPBitmap.ptr
        if Dispose
            oPBitmap := ""
        return this.ptr
    }

    ;FIXME 马赛克
    ; GdipPixelateBitmap(pBitmap, oBitmapOut, BlockSize) {
    ;     if (!PixelateBitmap) {
    ;         if A_PtrSize != 8 {
    ;             MCode_PixelateBitmap := "
    ;             (ltrim join
    ;             558BEC83EC3C8B4514538B5D1C99F7FB56578BC88955EC894DD885C90F8E830200008B451099F7FB8365DC008365E000894DC88955F08945E833FF897DD4
    ;             397DE80F8E160100008BCB0FAFCB894DCC33C08945F88945FC89451C8945143BD87E608B45088D50028BC82BCA8BF02BF2418945F48B45E02955F4894DC4
    ;             8D0CB80FAFCB03CA895DD08BD1895DE40FB64416030145140FB60201451C8B45C40FB604100145FC8B45F40FB604020145F883C204FF4DE475D6034D18FF
    ;             4DD075C98B4DCC8B451499F7F98945148B451C99F7F989451C8B45FC99F7F98945FC8B45F899F7F98945F885DB7E648B450C8D50028BC82BCA83C103894D
    ;             C48BC82BCA41894DF48B4DD48945E48B45E02955E48D0C880FAFCB03CA895DD08BD18BF38A45148B7DC48804178A451C8B7DF488028A45FC8804178A45F8
    ;             8B7DE488043A83C2044E75DA034D18FF4DD075CE8B4DCC8B7DD447897DD43B7DE80F8CF2FEFFFF837DF0000F842C01000033C08945F88945FC89451C8945
    ;             148945E43BD87E65837DF0007E578B4DDC034DE48B75E80FAF4D180FAFF38B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945CC0F
    ;             B6440E030145140FB60101451C0FB6440F010145FC8B45F40FB604010145F883C104FF4DCC75D8FF45E4395DE47C9B8B4DF00FAFCB85C9740B8B451499F7
    ;             F9894514EB048365140033F63BCE740B8B451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB
    ;             038975F88975E43BDE7E5A837DF0007E4C8B4DDC034DE48B75E80FAF4D180FAFF38B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955CC8A55
    ;             1488540E038A551C88118A55FC88540F018A55F888140183C104FF4DCC75DFFF45E4395DE47CA68B45180145E0015DDCFF4DC80F8594FDFFFF8B451099F7
    ;             FB8955F08945E885C00F8E450100008B45EC0FAFC38365DC008945D48B45E88945CC33C08945F88945FC89451C8945148945103945EC7E6085DB7E518B4D
    ;             D88B45080FAFCB034D108D50020FAF4D18034DDC8BF08BF88945F403CA2BF22BFA2955F4895DC80FB6440E030145140FB60101451C0FB6440F010145FC8B
    ;             45F40FB604080145F883C104FF4DC875D8FF45108B45103B45EC7CA08B4DD485C9740B8B451499F7F9894514EB048365140033F63BCE740B8B451C99F7F9
    ;             89451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975103975EC7E5585DB7E468B4DD88B450C
    ;             0FAFCB034D108D50020FAF4D18034DDC8BF08BF803CA2BF22BFA2BC2895DC88A551488540E038A551C88118A55FC88540F018A55F888140183C104FF4DC8
    ;             75DFFF45108B45103B45EC7CAB8BC3C1E0020145DCFF4DCC0F85CEFEFFFF8B4DEC33C08945F88945FC89451C8945148945103BC87E6C3945F07E5C8B4DD8
    ;             8B75E80FAFCB034D100FAFF30FAF4D188B45088D500203CA8D0CB18BF08BF88945F48B45F02BF22BFA2955F48945C80FB6440E030145140FB60101451C0F
    ;             B6440F010145FC8B45F40FB604010145F883C104FF4DC875D833C0FF45108B4DEC394D107C940FAF4DF03BC874068B451499F7F933F68945143BCE740B8B
    ;             451C99F7F989451CEB0389751C3BCE740B8B45FC99F7F98945FCEB038975FC3BCE740B8B45F899F7F98945F8EB038975F88975083975EC7E63EB0233F639
    ;             75F07E4F8B4DD88B75E80FAFCB034D080FAFF30FAF4D188B450C8D500203CA8D0CB18BF08BF82BF22BFA2BC28B55F08955108A551488540E038A551C8811
    ;             8A55FC88540F018A55F888140883C104FF4D1075DFFF45088B45083B45EC7C9F5F5E33C05BC9C21800
    ;             )"
    ;         } else {
    ;             MCode_PixelateBitmap := "
    ;             (ltrim join
    ;             4489442418488954241048894C24085355565741544155415641574883EC28418BC1448B8C24980000004C8BDA99488BD941F7F9448BD0448BFA8954240C
    ;             448994248800000085C00F8E9D020000418BC04533E4458BF299448924244C8954241041F7F933C9898C24980000008BEA89542404448BE889442408EB05
    ;             4C8B5C24784585ED0F8E1A010000458BF1418BFD48897C2418450FAFF14533D233F633ED4533E44533ED4585C97E5B4C63BC2490000000418D040A410FAF
    ;             C148984C8D441802498BD9498BD04D8BD90FB642010FB64AFF4403E80FB60203E90FB64AFE4883C2044403E003F149FFCB75DE4D03C748FFCB75D0488B7C
    ;             24188B8C24980000004C8B5C2478418BC59941F7FE448BE8418BC49941F7FE448BE08BC59941F7FE8BE88BC69941F7FE8BF04585C97E4048639C24900000
    ;             004103CA4D8BC1410FAFC94863C94A8D541902488BCA498BC144886901448821408869FF408871FE4883C10448FFC875E84803D349FFC875DA8B8C249800
    ;             0000488B5C24704C8B5C24784183C20448FFCF48897C24180F850AFFFFFF8B6C2404448B2424448B6C24084C8B74241085ED0F840A01000033FF33DB4533
    ;             DB4533D24533C04585C97E53488B74247085ED7E42438D0C04418BC50FAF8C2490000000410FAFC18D04814863C8488D5431028BCD0FB642014403D00FB6
    ;             024883C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC17CB28BCD410FAFC985C9740A418BC299F7F98BF0EB0233F685C9740B418BC3
    ;             99F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585C97E4D4C8B74247885ED7E3841
    ;             8D0C14418BC50FAF8C2490000000410FAFC18D04814863C84A8D4431028BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2413BD17CBD
    ;             4C8B7424108B8C2498000000038C2490000000488B5C24704503E149FFCE44892424898C24980000004C897424100F859EFDFFFF448B7C240C448B842480
    ;             000000418BC09941F7F98BE8448BEA89942498000000896C240C85C00F8E3B010000448BAC2488000000418BCF448BF5410FAFC9898C248000000033FF33
    ;             ED33F64533DB4533D24533C04585FF7E524585C97E40418BC5410FAFC14103C00FAF84249000000003C74898488D541802498BD90FB642014403D00FB602
    ;             4883C2044403D80FB642FB03F00FB642FA03E848FFCB75DE488B5C247041FFC0453BC77CAE85C9740B418BC299F7F9448BE0EB034533E485C9740A418BC3
    ;             99F7F98BD8EB0233DB85C9740A8BC699F7F9448BD8EB034533DB85C9740A8BC599F7F9448BD0EB034533D24533C04585FF7E4E488B4C24784585C97E3541
    ;             8BC5410FAFC14103C00FAF84249000000003C74898488D540802498BC144886201881A44885AFF448852FE4883C20448FFC875E941FFC0453BC77CBE8B8C
    ;             2480000000488B5C2470418BC1C1E00203F849FFCE0F85ECFEFFFF448BAC24980000008B6C240C448BA4248800000033FF33DB4533DB4533D24533C04585
    ;             FF7E5A488B7424704585ED7E48418BCC8BC5410FAFC94103C80FAF8C2490000000410FAFC18D04814863C8488D543102418BCD0FB642014403D00FB60248
    ;             83C2044403D80FB642FB03D80FB642FA03F848FFC975DE41FFC0453BC77CAB418BCF410FAFCD85C9740A418BC299F7F98BF0EB0233F685C9740B418BC399
    ;             F7F9448BD8EB034533DB85C9740A8BC399F7F9448BD0EB034533D285C9740A8BC799F7F9448BC0EB034533C033D24585FF7E4E4585ED7E42418BCC8BC541
    ;             0FAFC903CA0FAF8C2490000000410FAFC18D04814863C8488B442478488D440102418BCD40887001448818448850FF448840FE4883C00448FFC975E8FFC2
    ;             413BD77CB233C04883C428415F415E415D415C5F5E5D5BC3
    ;             )"
    ;         }
    ;         VarSetCapacity(PixelateBitmap, strlen(MCode_PixelateBitmap)//2)
    ;         loop(strlen(MCode_PixelateBitmap)//2)
    ;             NumPut("0x" substr(MCode_PixelateBitmap, (2*A_Index)-1, 2), PixelateBitmap, A_Index-1, "uchar")
    ;         dllcall("VirtualProtect", "UPtr",&PixelateBitmap, "UPtr",VarSetCapacity(PixelateBitmap), "uint",0x40, "UPtr*",0)
    ;     }
    ;     this.getSize(Width, Height)
    ;     E1 := this.GdipBitmapLockBits([0, 0, Width, Height], Stride1, Scan01, BitmapData1)
    ;     E2 := oBitmapOut.GdipBitmapLockBits([0, 0, Width, Height], Stride2, Scan02, BitmapData2)
    ;     if (E1 || E2)
    ;         return -3
    ;     ; E := - unused exit code
    ;     dllcall(&PixelateBitmap, "UPtr",Scan01, "UPtr",Scan02, "int",Width, "int",Height, "int",Stride1, "int",BlockSize)
    ;     this.GdipBitmapUnlockBits(BitmapData1)
    ;     oBitmapOut.GdipBitmapUnlockBits(BitmapData2)
    ;     return 0
    ; }

    ;Quality 0-100
    GdipSaveImageToFile(fp, Quality:=75) {
        SplitPath(fp,,, ext)
        if !(ext ~= "^(?i:bmp|dib|rle|jpg|jpeg|jpe|jfif|gif|tif|tiff|png)$")
            return -1
        ext := "." . ext
        dllcall("gdiplus\GdipGetImageEncodersSize", "uint*",nCount, "uint*",nSize)
        VarSetCapacity(ci, nSize)
        dllcall("gdiplus\GdipGetImageEncoders", "uint",nCount, "uint",nSize, "UPtr", &ci)
        if !(nCount && nSize)
            return -2
        loop(nCount) {
            sString := strget(numget(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
            if !instr(sString, "*" . ext)
                continue
            pCodec := &ci+idx
            break
        }
        if !pCodec
            return -3
        pQuality := 0
        if (Quality != 75) {
            Quality := (Quality < 0) ? 0 : (Quality > 100) ? 100 : Quality
            if (ext ~= "^\.(?i:JPG|JPEG|JPE|JFIF)$") {
                dllcall("gdiplus\GdipGetEncoderParameterListSize","UPtr",this.ptr, "UPtr",pCodec, "uint*",nSize)
                VarSetCapacity(EncoderParameters, nSize, 0)
                dllcall("gdiplus\GdipGetEncoderParameterList","UPtr",this.ptr, "UPtr",pCodec, "uint",nSize, "UPtr",&EncoderParameters)
                loop(numget(EncoderParameters, "uint")) {
                    elem := (24+(A_PtrSize ? A_PtrSize : 4)) * (A_Index-1) + 4 + (pad := A_PtrSize-4)
                    if (numget(EncoderParameters, elem+16, "uint") == 1) && (numget(EncoderParameters, elem+20, "uint") == 6) {
                        pQuality := elem + &EncoderParameters - pad - 4
                        numput(Quality, numget(numput(4, numput(1, pQuality+0)+20, "uint")), "uint")
                        break
                    }
                }
            }
        }
        _E := dllcall("gdiplus\GdipSaveImageToFile","UPtr",this.ptr, "UPtr",&fp, "UPtr",pCodec, "uint",pQuality)
        return _E ? -5 : 0
    }

    ; https://docs.microsoft.com/en-us/windows/win32/api/gdiplusheaders/nf-gdiplusheaders-bitmap-lockbits
    ; LockMode https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.imagelockmode
    ;       ReadOnly	1	
    ;       WriteOnly	2	
    ;       ReadWrite	3	
    ;       UserInputBuffer	4	
    ; PixelFormat https://docs.microsoft.com/en-us/dotnet/api/system.drawing.imaging.pixelformat
    ;       Argb	0x26200A
    ;       rgb		?不是0x21808
    GdipBitmapLockBits(aRect, ByRef Stride, ByRef Scan0, ByRef BitmapData, LockMode:=3, PixelFormat:=0x21808) {
        VarSetCapacity(_Rect, A_PtrSize*4)
        numput(aRect[1], _Rect, A_PtrSize*0, "uint")
        numput(aRect[2], _Rect, A_PtrSize*1, "uint")
        numput(aRect[3], _Rect, A_PtrSize*2, "uint")
        numput(aRect[4], _Rect, A_PtrSize*3, "uint")
        VarSetCapacity(BitmapData, A_PtrSize*6, 0)
        _E := dllcall("Gdiplus\GdipBitmapLockBits", "UPtr",this.ptr, "UPtr",&_Rect, "uint",LockMode, "int",PixelFormat, "UPtr",&BitmapData)
        Stride := NumGet(BitmapData, 8, "Int")
        Scan0 := NumGet(BitmapData, 16, "UPtr")
        return _E
    }

    ;获取的可能是 agrb
    getLockBitPixel(Scan0, x, y, Stride) {
        return NumGet(Scan0, (x*4)+(y*Stride), "UInt")
    }

    GdipBitmapUnlockBits(ByRef BitmapData) {
        return dllcall("Gdiplus\GdipBitmapUnlockBits", "UPtr",this.ptr, "UPtr",&BitmapData)
    }

}

;hh
;需要用 Gui 显示的,需要 oHBitmap 并生成 oGraphics
class GDIP_HBitmap extends _GDIP {
    ptr := 0

    __new(widthOrPBitmap, heightOrColor:=0xffFFFFFF, hDC:=0) {
        if isobject(widthOrPBitmap) {
            if (widthOrPBitmap.__class == "GDIP_PBitmap") {
                dllcall("gdiplus\GdipCreateHBITMAPFromBitmap", "UPtr",widthOrPBitmap.ptr, "UPtr*",hBitmap, "int",heightOrColor)
                this.ptr := hBitmap
            } else if (widthOrPBitmap.length() == 4)
                this.getFromScreen(widthOrPBitmap)
        } else
            this.CreateDIBSection(widthOrPBitmap, heightOrColor, hDC)
    }

    __delete() {
        ; msgbox(A_ThisFunc . '---')
        base.DeleteObject(this.ptr)
    }

    getFromScreen(aRect:=0) {
        x := aRect[1]
        y := aRect[2]
        w := aRect[3]
        h := aRect[4]
        ;方式1,原始
        tDC := dllcall("CreateCompatibleDC", "Uint", 0)
        ;创建 hBitmap
        this.CreateDIBSection(w, h, tDC)
        ;修改 hBitmap
        oBM := dllcall("SelectObject", "Uint", tDC, "Uint", this.ptr)
        hDC := dllcall("GetDC", "Uint", 0)
        dllcall("BitBlt", "Uint", tDC, "int",0, "int",0, "int",w, "int",h, "Uint",hDC, "int",x, "int",y, "Uint",0x40000000 | 0x00CC0020)
        dllcall("ReleaseDC", "Uint", 0, "Uint", hDC)
        dllcall("SelectObject", "Uint",tDC, "Uint",oBM)
        dllcall("DeleteDC", "Uint", tDC)
    }

    CreateDIBSection(w, h, hDC:=0, bpp:=32, ByRef ppvBits:=0) {
        hDC2 := hDC ? hDC : dllcall("GetDC", "UPtr",0)
        VarSetCapacity(bi, 40, 0)
        numput(w, bi, 4, "uint")
        numput(h, bi, 8, "uint")
        numput(40, bi, 0, "uint")
        numput(1, bi, 12, "ushort")
        numput(0, bi, 16, "uint")
        numput(bpp, bi, 14, "ushort")
        hBitmap := dllcall("CreateDIBSection", "ptr",hDC2, "ptr",&bi, "uint",0, "UPtr*",ppvBits, "ptr",0, "uint",0, "UPtr")
        if !hDC
            dllcall("ReleaseDC", "UPtr",0, "UPtr",hDC2)
        if (!hBitmap)
            msgbox(A_ThisFunc . "`n" . w . "`n" . h)
        return this.ptr := hBitmap
    }

    ; Gdip_CreateHICONFromBitmap(pBitmap) {
    ;     pBitmap := ""
    ;     hIcon := 0
    ;     dllcall("gdiplus\GdipCreateHICONFromBitmap", "UPtr",pBitmap, "UPtr*",hIcon)
    ;     return hIcon
    ; }

    ; SetImage(ctlID) {
    ;     res := SendMessage(0x172,, this.ptr,, "ahk_id " . ctlID)
    ;     dllcall("DeleteObject", "Uint",res)
    ;     return res
    ; }

    ;复制 hBitmap 图像到剪切板
    SetClipboardData(bDelete:=false) {
        dllcall("GetObject", "Uint",this.ptr, "int",VarSetCapacity(oi,84,0), "Uint",&oi)
        hDIB := dllcall("GlobalAlloc", "Uint",2, "Uint",40+NumGet(oi,44))
        pDIB := dllcall("GlobalLock", "Uint",hDIB)
        dllcall("RtlMoveMemory", "Uint",pDIB, "Uint",&oi+24, "Uint",40)
        dllcall("RtlMoveMemory", "Uint",pDIB+40, "Uint",NumGet(oi,20), "Uint",NumGet(oi,44))
        dllcall("GlobalUnlock", "Uint",hDIB)
        if bDelete
            dllcall("DeleteObject", "Uint",this.ptr)
        dllcall("OpenClipboard", "Uint",0)
        dllcall("EmptyClipboard")
        dllcall("SetClipboardData", "Uint",8, "Uint",hDIB)
        dllcall("CloseClipboard")
    }

    showByGui(aRect) {
        ;放入gui
        oGui := GuiCreate("-Caption +ToolWindow +AlwaysOnTop +LastFound +Border -DPIScale")
        ;oGui.title := "hyd-" . A_Now
        oGui.OnEvent("ContextMenu", ()=>oGui.destroy())
        oGui.MarginX := 0
        oGui.MarginY := 0
        oPic := oGui.Addpicture(format("w{1} h{2} +0xE", aRect[3],aRect[4]))
        oPic.OnEvent("click", ()=>PostMessage(0xA1, 2)) ;WM_NCLBUTTONDOWN 随着鼠标移动
        oPic.OnEvent("DoubleClick", ObjBindMethod(this,"zoom"))
        SendMessage(STM_SETIMAGE:=0x172,, this.ptr,, "ahk_id " . oPic.hwnd)
        oGui.show(format("x{1} y{2}", aRect[1]-1,aRect[2]-1))
    }
    zoom(oCtl) {
        n := 2
        objPos := oCtl.pos
        objGuiPos := oCtl.Gui.pos
        w := objPos.w
        h := objPos.h
        oGdip := new _GDIP()
        oPBitmap := new GDIP_PBitmap([objGuiPos.x+1,objGuiPos.y+1,w,h])
        this.resize(n*100)
        oHBitmap := new GDIP_HBitmap(oPBitmap)
        oPBitmap := ""
        SendMessage(STM_SETIMAGE:=0x172,, this.ptr,, "ahk_id " . oCtl.hwnd)
        ControlMove(,,w*n,h*n, oCtl)
        WinMove(,, w*n, h*n, "ahk_id " . oCtl.Gui.hwnd)
        ;if !isobject(oCtl)
        ;{
        ;MouseGetPos(,, idWin, oCtl)
        ;ControlGetPos(x,y,w,h, oCtl, "ahk_id " . idWin)
        ;}
        ;else
        ;{
        ;objPos := oCtl.pos
        ;w := objPos.w
        ;h := objPos.h
        ;objGuiPos := oCtl.Gui.pos
        ;x := objGuiPos.x+1
        ;y := objGuiPos.y+1
        ;}
        ;pToken  := Gdip_Startup()
        ;pBitmap := Gdip_BitmapFromScreen(format("{1}|{2}|{3}|{4}", x,y,w,h))
        ;pBitmap := Gdip_ResizeBitmap(pBitmap, 200)
        ;hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap)
        ;Gdip_DisposeImage(pBitmap)
        ;Gdip_Shutdown(pToken)
        ;E := SendMessage(0x172, 0, hBitmap,, "ahk_id " . oCtl)
        ;;E := SendMessage(0x172, 0, hBitmap,, "ahk_id " . oCtl.hwnd)
        ;DllCall("DeleteObject", "UPtr",E)
        ;ControlMove(,,w*2,h*2, oCtl)
        ;WinMove(,,w*2, h*2,"ahk_id " . oCtl.Gui.hwnd)
    }
}

;gg
;NOTE 写内容都通过 GDIP_Graphics,写好后,相应的 GDIP_PBitmap就已修改,可直接 GdipSaveImageToFile
class GDIP_Graphics extends _GDIP {
    ptr := 0
    hDC := 0

    __new(oInstance) {
        if isobject(oInstance) {
            if (oInstance.__class == "GDIP_PBitmap")
                this.GdipGetImageGraphicsContext(oInstance)
            else if (oInstance.__class == "GDIP_HBitmap") { ;NOTE 先生成 this.hDC
                this.hDC := dllcall("CreateCompatibleDC", "UPtr",0)
                this.SelectObject(oInstance.ptr) ;NOTE 必须先运行
                res := dllcall("gdiplus\GdipCreateFromHDC", "UPtr",this.hDC, "UPtr*",pGraphics)
                if (!pGraphics)
                    msgbox(A_ThisFunc . "`n" . res . "`n" . oInstance)
                this.ptr := pGraphics
            }
        }
    }

    __delete() {
        if this.hDC
            dllcall("DeleteDC", "UPtr",this.hDC)
        dllcall("gdiplus\GdipDeleteGraphics", "Ptr",this.ptr)
    }

    GdipGetImageGraphicsContext(oPBitmap) {
        dllcall("gdiplus\GdipGetImageGraphicsContext",A_PtrSize ? "UPtr" : "uint",oPBitmap.ptr, A_PtrSize ? "UPtr*" : "uint*",pGraphics)
        if (!pGraphics)
            msgbox(A_ThisFunc)
        this.ptr := pGraphics
    }

    GdipGraphicsClear(color := 0) {
        return dllcall("gdiplus\GdipGraphicsClear", "Ptr",this.ptr, "uint",color)
    }

    GdipResetClip() {
        dllcall("gdiplus\GdipResetClip", "UPtr",this.ptr)
    }

    ; default = 0
    ; HighSpeed = 1
    ; HighQuality = 2
    ; None = 3
    ; AntiAlias = 4 边缘平滑
    GdipSetSmoothingMode(smoothingMode:=4) {
        return dllcall("gdiplus\GdipSetSmoothingMode", "Ptr",this.ptr, "int",smoothingMode)
    }

    ; default = 0
    ; LowQuality = 1
    ; HighQuality = 2
    ; Bilinear = 3
    ; Bicubic = 4
    ; NearestNeighbor = 5
    ; HighQualityBilinear = 6
    ; HighQualityBicubic = 7
    GdipSetInterpolationMode(interpolationMode:=7) {
        return dllcall("gdiplus\GdipSetInterpolationMode", "Ptr",this.ptr, "int",interpolationMode)
    }

    ;TextRenderingHintSystemDefault              = 0,
    ;TextRenderingHintSingleBitPerPixelGridFit   = 1,
    ;TextRenderingHintSingleBitPerPixel          = 2,
    ;TextRenderingHintAntiAliasGridFit           = 3,
    ;TextRenderingHintAntiAlias                  = 4,
    ;TextRenderingHintClearTypeGridFit           = 5
    GdipSetTextRenderingHint(TextRenderingHint:=0) {
        return dllcall("gdiplus\GdipSetTextRenderingHint", "Ptr",this.ptr, "uint",textRenderingHint)
    }

    ;------------------------------------------------rotate------------------------------------------------
    ;旋转相关
    ;根据【左上角】旋转,新的画布宽高会调整为最小矩形
    ;是给 GdipDrawImageRectRect 等【绘制】工作指定参数
    ;   1.getRotatedRect 旋转后的左上角坐标-原坐标【差值】和【新的宽高】

    ;旋转后新的宽高
    ;原左上角坐标相对新左上角坐标的偏移:xOffset, yOffset
    ;后续
    ;   oGraphics.GdipTranslateWorldTransform(xOffset, yOffset)
    getRotatedRect(w, h, angle) {
        pi := 3.14159
        TAngle := angle*(pi/180)
        bound := (angle >= 0) ? mod(angle, 360) : 360-mod(-angle, -360)
        if (bound <= 90) {
            xOffset := h*sin(TAngle)
            yOffset := 0
        } else if (bound <= 180) {
            xOffset := (h*sin(TAngle))-(w*cos(TAngle))
            yOffset := -h*cos(TAngle)
        } else if (bound <= 270) {
            xOffset := -w*cos(TAngle)
            yOffset := -(h*cos(TAngle))-(w*sin(TAngle))
        } else {
            xOffset := 0
            yOffset := -w*sin(TAngle)
        }
        newW := ceil(abs(w*cos(TAngle))+abs(h*sin(TAngle)))
        newH := ceil(abs(w*sin(TAngle))+abs(h*cos(Tangle)))
        return [xOffset, yOffset, newW, newH]
    }

    ;翻转
    ;水平翻转(以右线翻转)
    ;   GdipScaleWorldTransform(-1, 1)
    ;   GdipTranslateWorldTransform(-w, 0)
    ;垂直翻转(以下线翻转)
    ;   GdipScaleWorldTransform(1, -1)
    ;   GdipTranslateWorldTransform(0, -h)
    GdipScaleWorldTransform(xScale, yScale, MatrixOrder:=0) {
        return dllcall("gdiplus\GdipScaleWorldTransform", "Ptr",this.ptr, "float",xScale, "float",yScale, "int",MatrixOrder)
    }

    ;偏移坐标(新-旧)
    ;NOTE 要在 GdipRotateWorldTransform 之后运行
    GdipTranslateWorldTransform(xOffset, yOffset, MatrixOrder:=0) {
        return dllcall("gdiplus\GdipTranslateWorldTransform", "Ptr",this.ptr, "float",xOffset, "float",yOffset, "int",MatrixOrder)
    }

    ;回收
    GdipResetWorldTransform() {
        return dllcall("gdiplus\GdipResetWorldTransform", "Ptr",this.ptr)
    }

    ;旋转 angle 度
    ;TODO MatrixOrder = 0; The new operation is applied before the old operation.
    ; MatrixOrder = 1; The new operation is applied after the old operation.
    ;NOTE 要在 GdipTranslateWorldTransform 等调整好之后再运行
    GdipRotateWorldTransform(angle:=90, MatrixOrder:=0) {
        return dllcall("gdiplus\GdipRotateWorldTransform", "Ptr",this.ptr, "float",angle, "int",MatrixOrder)
    }

    ;------------------------------------------------draw------------------------------------------------

    ;推荐 drawImage
    GdipDrawImageRectRect(pBitmap, aRectTo, aRectFrom:="", Matrix:=1) {
        if !(Matrix ~= "^\d+$")
            ImageAttr := this.GdipSetImageAttributesColorMatrix(Matrix)
        else if (Matrix != 1)
            ImageAttr := this.GdipSetImageAttributesColorMatrix(format("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|{1}|0|0|0|0|0|1", Matrix))
        return dllcall("gdiplus\GdipDrawImageRectRect", "Ptr",this.ptr, "Ptr",pBitmap
            , "float",aRectTo[1],"float",aRectTo[2],"float",aRectTo[3],"float",aRectTo[4]
            , "float",aRectFrom[1],"float",aRectFrom[2],"float",aRectFrom[3],"float",aRectFrom[4]
            , "uint",2 , "Ptr",ImageAttr, "Ptr",0, "Ptr",0)
    }

    ;GdipDrawImageRectRect 简化,用 oPBitmap 当参数,可省略 aRectFrom(默认是全图)
    drawImage(oPBitmap, aRectTo, aRectFrom:="", Matrix:=1) {
        if (!isobject(aRectFrom))
            aRectFrom := [0,0,oPBitmap.getWidth(),oPBitmap.getHeight()]
        if !(Matrix ~= "^\d+$")
            ImageAttr := this.GdipSetImageAttributesColorMatrix(Matrix)
        else if (Matrix != 1)
            ImageAttr := this.GdipSetImageAttributesColorMatrix(format("1|0|0|0|0|0|1|0|0|0|0|0|1|0|0|0|0|0|{1}|0|0|0|0|0|1", Matrix))
        return dllcall("gdiplus\GdipDrawImageRectRect", "Ptr",this.ptr, "Ptr",oPBitmap.ptr
            , "float",aRectTo[1],"float",aRectTo[2],"float",aRectTo[3],"float",aRectTo[4]
            , "float",aRectFrom[1],"float",aRectFrom[2],"float",aRectFrom[3],"float",aRectFrom[4]
            , "uint",2 , "Ptr",ImageAttr, "Ptr",0, "Ptr",0)
    }

    GdipSetImageAttributesColorMatrix(Matrix) {
        VarSetCapacity(ColourMatrix, 100, 0)
        Matrix := RegExReplace(RegExReplace(Matrix, "^[^\d-\.]+([\d\.])", "$1", , 1), "[^\d-\.]+", "|")
        Matrix := StrSplit(Matrix, "|")
        loop(25) {
            M := (Matrix[A_Index] != "") ? Matrix[A_Index] : Mod(A_Index-1, 6) ? 0 : 1
            NumPut(M, ColourMatrix, (A_Index-1)*4, "float")
        }
        DllCall("gdiplus\GdipCreateImageAttributes", "UPtr*",ImageAttr)
        DllCall("gdiplus\GdipSetImageAttributesColorMatrix", "UPtr",ImageAttr, "int",1, "int",1, "UPtr",&ColourMatrix, "UPtr",0, "int",0)
        return ImageAttr
    }

    GdipDrawRectangle(pPen, aRect) {
        return dllcall("gdiplus\GdipDrawRectangle", "UPtr",this.ptr, "UPtr",pPen, "float",aRect[1],"float",aRect[2],"float",aRect[3],"float",aRect[4])
    }

    ; Pen:		the pen used to draw the line
    ;points		[x1,y1,x2,y2]
    GdipDrawLine(pPen, points) {
        return dllcall("gdiplus\GdipDrawLine", "UPtr",this.ptr, "UPtr",pPen, "float",points.1, "float",points.2, "float",points.3, "float",points.4)
    }

    GdipDrawEllipse(pPen, aRect) {
        return dllcall("gdiplus\GdipDrawEllipse", "UPtr",this.ptr, "UPtr",pPen, "float",aRect[1],"float",aRect[2],"float",aRect[3],"float",aRect[4])
    }

    drawLines(pPen, points) {
        points := points.clone()
        loop(points.length() - 1)
            this.GdipDrawLine(pPen, points), points.RemoveAt(1)
    }

    ; GdipDrawString:	Writes some text with a specified font, rectangle, _stringFormat and Brush on the Graphics
    ; sText:		The text you want to write.
    ; font:		The font you want to use. Has to be a GDIp.font object
    ; rect:		A 4 value array defining [ x, y, w, h ] of the area you want to write to.
    ; stringFormat:	Some options of the text like the text direction. Has to be a GDIp.StringFormat object
    ; brush:		Defines the color of the text. Has to be a GDI+ Brush object. (currently GDIp.SolidBrush & GDIp.LinearGradientBrush)
    ; 参数太多,可能不好用
    GdipDrawString(sText, pFont, pStringFormat, pBrush, aRect) {
        ; hyf_objView(aRect, pFont . "`n" . pStringFormat . "`n" . pBrush)
        VarSetCapacity(rectF, 16, 0)
        for k, entry in aRect
            numput(entry, rectF, k * 4 - 4, "float")
        return dllcall("gdiplus\GdipDrawString", "UPtr",this.ptr, "WStr",sText, "int",-1, "UPtr",pFont, "UPtr",&rectF, "UPtr",pStringFormat, "UPtr",pBrush)
    }

    ; opts := "x10p y60p w80p Centre cff000000 r4 s18p Bold"
    DrawText(sText, opts, sFont:="Arial", width:="", height:="", Measure:=0) {
        arrOpt := StrSplit(opts, " ")
        objStyle := {
            "Regular":0,
            "Bold":1,
            "Italic":2,
            "BoldItalic":3,
            "Underline":4,
            "Strikeout":8,
        }
        objAlign := {
            "Near":0,
            "Left":0,
            "Centre":1,
            "Center":1,
            "Far":2,
            "Right":2,
        }
        objPos := {
            "Top":1,
            "Up":1,
            "Bottom":1,
            "Down":1,
            "vCentre":1,
            "vCenter":1,
        }
        objRes := {}
        style := align := 0
        for _, v in arrOpt {
            if objStyle.haskey(v)
                style |= objStyle[v]
            else if objAlign.haskey(v)
                align |= objAlign[v]
            else if objPos.haskey(v)
                arrPos := [1]
            else { ;处理其他选项
                objType := {
                    "x":0,
                    "y":0,
                    "w":width,
                    "h":height,
                    "c":0xff000000,
                    "r":4,
                    "s":12,
                    "NoWrap":0x4000,
                }
                ;完整匹配
                if (v == "NoWrap") {
                    objType["NoWrap"] := 0x4000 | 0x1000
                    continue
                } else if (v ~= "i)Bottom|Down|vCentre|vCenter") {
                    if (v = "vCentre") || (v = "vCenter")
                        objType["y"] += (height-objType["h"]) // 2
                    else if (v = "Bottom") || (v = "Down")
                        objType["y"] := height - objType["h"]
                }
                ;匹配首字母
                RegExMatch(v, "i)([a-z])([a-f0-9]+)([a-z])?", m)
                try
                    tp := StrLower(m[1])
                catch
                    msgbox(v)
                ; msgbox(m[1] . "`n" . m[2] . "`n" . m[3])
                if (tp == "x")
                    objType[tp] := m[3] ? width*(m[2]/100) : m[2]
                else if (tp == "y")
                    objType[tp] := m[3] ? height*(m[2]/100) : m[2]
                else if (tp == "w")
                    objType[tp] := m[3] ? width*(m[2]/100) : m[2]
                else if (tp == "h")
                    objType[tp] := m[3] ? height*(m[2]/100) : m[2]
                else if (tp == "c")
                    objType[tp] := "0x" . m[2]
                else if (tp == "r")
                    objType[tp] := m[2] ;要求0-5
                else if (tp == "s")
                    objType[tp] := m[2]
            }
        }
        ; pattern_opts := (A_AhkVersion < "2") ? "iO)" : "i)"
        ; RegExMatch(opts, pattern_opts . "X([\-\d\.]+)(p*)", xpos)
        ; RegExMatch(opts, pattern_opts . "Y([\-\d\.]+)(p*)", ypos)
        ; RegExMatch(opts, pattern_opts . "W([\-\d\.]+)(p*)", width)
        ; RegExMatch(opts, pattern_opts . "H([\-\d\.]+)(p*)", height)
        ; RegExMatch(opts, pattern_opts . "C(?!(entre|enter))([a-f\d]+)", Colour)
        ; RegExMatch(opts, pattern_opts . "Top|Up|Bottom|Down|vCentre|vCenter", vPos)
        ; RegExMatch(opts, pattern_opts . "R(\d)", Rendering)
        ; RegExMatch(opts, pattern_opts . "S(\d+)(p*)", Size)
        ; ; if Colour && !GdipDeleteBrush(this.GdipCloneBrush(Colour[2])) {
        ; ;     PassBrush := 1
        ; ;     pBrush := Colour[2]
        ; ; }
        ; if !(width && IHeight) && ((xpos && xpos[2]) || (ypos && ypos[2]) || (width && width[2]) || (height && height[2]) || (Size && Size[2]))
        ;     return -1
        ; style := 0
        ; Styles := "Regular|Bold|Italic|BoldItalic|Underline|Strikeout"
        ; for k, valStyle in StrSplit( Styles, "|" ) {
        ;     if RegExMatch(opts, "\b" . valStyle)
        ;         style |= (valStyle != "StrikeOut") ? (A_Index-1) : 8
        ; }
        ; Align := 0
        ; Alignments := [
        ;     "Near",
        ;     "Left",
        ;     "Centre",
        ;     "Center",
        ;     "Far",
        ;     "Right",
        ; ]
        ; For k, valAlignment in Alignments {
        ;     if (opts ~= "\b" . valAlignment)
        ;         Align |= A_Index//2.1	; 0|0|1|1|2|2
        ; }
        ; xpos := (xpos && (xpos[1] != "")) ? (xpos[2] ? width*(xpos[1]/100) : xpos[1]) : 0
        ; ypos := (ypos && (ypos[1] != "")) ? (ypos[2] ? height*(ypos[1]/100) : ypos[1]) : 0
        ; width := (width && width[1]) ? (width[2] ? width*(width[1]/100) : width[1]) : width
        ; height := (height && height[1]) ? (height[2] ? IHeight*(height[1]/100) : height[1]) : IHeight
        ; Colour := "0x" . (Colour && Colour[2] ? Colour[2] : "ff000000")
        ; ; if !PassBrush
        ; ;     Colour := format("0x{1}", Colour && Colour[2] ? Colour[2] : "ff000000")
        ; Rendering := (Rendering && (Rendering[1] >= 0) && (Rendering[1] <= 5)) ? Rendering[1] : 4
        ; Size := (Size && (Size[1] > 0)) ? (Size[2] ? IHeight*(Size[1]/100) : Size[1]) : 12
        oFont := new GDIP_Font(sFont, objType["s"])
        oStringFormat := new GDIP_StringFormat(objType["NoWrap"])
        oBrush := new GDIP_Brush(objType["c"])
        ; pBrush := PassBrush ? pBrush : Gdip_BrushCreateSolid(Colour)
        ; if !(hFamily && hFont && hFormat && pBrush && this.ptr)
        ;     return !this.ptr ? -2 : !hFamily ? -3 : !hFont ? -4 : !hFormat ? -5 : !pBrush ? -6 : 0
        aRect := [objType["x"], objType["y"], objType["w"], objType["h"]]
        this.GdipSetTextRenderingHint(objType["r"])
        oStringFormat.GdipSetStringFormatAlign(align)
        arrRes := this.GdipMeasureString(sText, oFont.ptr, oStringFormat.ptr, aRect)
        if !Measure
            _E := this.GdipDrawString(sText, oFont.ptr, oStringFormat.ptr, oBrush.ptr, aRect)
        ; if !PassBrush
        ;     Gdip_DeleteBrush(pBrush)
        oBrush := ""
        oStringFormat := ""
        oFont := ""
        return _E ? _E : arrRes
    }

    GdipMeasureString(sText, pFont, pStringFormat, aRect) {
        base.CreateRect(RectF, aRect)
        VarSetCapacity(outRect, 16, 0)
        res := dllcall("gdiplus\GdipMeasureString"
            , "UPtr",this.ptr
            , "UPtr", &sText
            , "int",-1
            , "UPtr",pFont
            , "UPtr",&RectF
            , "UPtr",pStringFormat
            , "UPtr",&outRect
            , "uint*",codePointsFitted
            , "uint*",linesFitted)
        return [
            numget(outRect, 0, "float"),
            numget(outRect, 4, "float"),
            numget(outRect, 8, "float"),
            numget(outRect, 12,"float"),
            codePointsFitted,
            linesFitted,
        ]
    }

    ; GdipDrawBeziers: draw a Bezier Curve onto the graphics with the specified pen and points
    ; pen: 	the pen you want to use to draw on the graphics
    ; points: 	An array of starting and control points of a Bezier line
    ; A single Bezier line consists of 4 points a starting point 2 control points and an end point
    ; The line never actually goes through the control points
    ; The control points control the tangent in the starting and end point and their distance controls how strongly the curve follows there
    GdipDrawBeziers(pPen, points) {
        pointsBuffer := ""
        VarSetCapacity(pointsBuffer,  8 * points.length(), 0)
        for each, point in points
            numput(point.1, pointsBuffer, each * 8 - 8, "float"), numput(point.2, pointsBuffer, each * 8 - 4, "float")
        return dllcall("gdiplus\GdipDrawBeziers", "UPtr",this.ptr, "UPtr",pPen, "UPtr", &pointsBuffer, "uint",points.length())
    }

    ;画箭头:妖提供
    ;=======================================================
    ;
    ;                                            M5
    ;                                            M4
    ; A------------------------------------------M1-d-B
    ;                                            M2
    ;                                            M3
    ;
    ;M1-M2,宽为w1
    ;M1-M3,宽为w2
    ;=======================================================
    drawArrow(pGraphics, pBrush, Ax, Ay, Bx, By) {
        if (Ax=Bx)  ;水平线、垂直线的斜率为0、无斜率,对于作图都有问题,所以人为补了1个像素
            Bx := Ax+1
        if (Ay=By)
            By := Ay+1
        l := sqrt((Ax- Bx)**2 +(Ay - By)**2) ; 起点终点之间的距离
        w1 := 3
        w2 := 5
        ; d:=l/10   ;箭头取1/10长度
        if (l < 50)
            d := 10
        else if (l<100)
            d := 20
        else
            d := 30
        My1 := By-(d*(By-Ay))/l
        Mx1 := Bx-(d*(Bx-Ax))/l
        k := (By-Ay)/(Bx-Ax)   ;斜率
        kk := -1/k             ;垂直线的斜率
        zz := sqrt(kk*kk+1)
        Mx2 := Mx1+d/(w1*zz)
        My2 := My1+kk*d/(w1*zz)
        Mx4 := Mx1-d/(w1*zz)
        My4 := My1-kk*d/(w1*zz)
        Mx3 := Mx1+d/(w2*zz)
        My3 := My1+kk*d/(w2*zz)
        Mx5 := Mx1-d/(w2*zz)
        My5 := My1-kk*d/(w2*zz)
        point :=  format("{1},{2}|{3},{4}|{5},{6}|{7},{8}|{9},{10}|{11},{12}", Ax,Ay,Mx3,My3,Mx2,My2,Bx,By,Mx4,My4,Mx5,My5)
        this.GdipFillPolygon(pBrush, point, FillMode:=1)
    }

    ;------------------------------------------------fill------------------------------------------------

    fillRoundedRectangle(pBrush, aRect, r) {
        x := aRect[1]
        y := aRect[2]
        w := aRect[3]
        h := aRect[4]
        this.GdipGetClip()
        this.GdipSetClipRect([x-r, y-r, 2*r, 2*r], 4)
        this.GdipSetClipRect([x+w-r, y-r, 2*r, 2*r], 4)
        this.GdipSetClipRect([x-r, y+h-r, 2*r, 2*r], 4)
        this.GdipSetClipRect([x+w-r, y+h-r, 2*r, 2*r], 4)
        _E := this.GdipFillRectangle(pBrush, aRect)
        this.GdipSetClipRegion(0)
        this.GdipSetClipRect([x-(2*r), y+r, w+(4*r), h-(2*r)], 4)
        this.GdipSetClipRect([x+r, y-(2*r), w-(2*r), h+(4*r)], 4)
        this.GdipFillEllipse(pBrush, [x, y, 2*r, 2*r])
        this.GdipFillEllipse(pBrush, [x+w-(2*r), y, 2*r, 2*r])
        this.GdipFillEllipse(pBrush, [x, y+h-(2*r), 2*r, 2*r])
        this.GdipFillEllipse(pBrush, [x+w-(2*r), y+h-(2*r), 2*r, 2*r])
        this.GdipSetClipRegion(0)
        this.GdipDeleteRegion()
        return _E
    }

    ; extracted from: https://github.com/tariqporter/Gdip2/blob/master/lib/Object.ahk
    ; and adapted by Marius Șucan
    ; fillRoundedRectangle2(pBrush, aRect, r) {
    ;     x := aRect[1]
    ;     y := aRect[2]
    ;     w := aRect[3]
    ;     h := aRect[4]
    ;     r := (w <= h) ? (r < w // 2) ? r : w // 2 : (r < h // 2) ? r : h // 2
    ;     path1 := this.GdipCreatePath(0)
    ;     this.GdipAddPathRectangle(path1, [x+r, y, w-(2*r), r])
    ;     this.GdipAddPathRectangle(path1, [x+r, y+h-r, w-(2*r), r])
    ;     this.GdipAddPathRectangle(path1, [x, y+r, r, h-(2*r)])
    ;     this.GdipAddPathRectangle(path1, [x+w-r, y+r, r, h-(2*r)])
    ;     this.GdipAddPathRectangle(path1, [x+r, y+r, w-(2*r), h-(2*r)])
    ;     this.GdipAddPathPie(path1, [x, y, 2*r, 2*r], 180, 90)
    ;     this.GdipAddPathPie(path1, [x+w-(2*r), y, 2*r, 2*r], 270, 90)
    ;     this.GdipAddPathPie(path1, [x, y+h-(2*r), 2*r, 2*r], 90, 90)
    ;     this.GdipAddPathPie(path1, [x+w-(2*r), y+h-(2*r), 2*r, 2*r], 0, 90)
    ;     E := this.GdipFillPath(this.ptr, pBrush, path1)
    ;     this.GdipDeletePath(path1)
    ;     return E
    ; }

    GdipFillRectangle(pBrush, aRect) {
        return dllcall("gdiplus\GdipFillRectangle", "Ptr",this.ptr, "Ptr",pBrush, "float",aRect.1, "float",aRect.2, "float",aRect.3, "float",aRect.4)
    }

    GdipFillEllipse(pBrush, aRect) {
        if (!pBrush)
            msgbox(A_ThisHotkey)
        return dllcall("gdiplus\GdipFillEllipse", "Ptr",this.ptr, "Ptr",pBrush, "float",aRect.1, "float",aRect.2, "float",aRect.3, "float",aRect.4)
    }

    GdipFillPolygon(pBrush, points, fillMode:=0) {
        VarSetCapacity(pointBuffer, 8 * points.length(), 0)
        for pointNr, point in points {
            numput(point.1, pointBuffer, pointNr * 8 - 8, "float")
            numput(point.2, pointBuffer, pointNr * 8 - 4, "float")
        }
        return dllcall("gdiplus\GdipFillPolygon", "Ptr",this.ptr, "Ptr",pBrush, "Ptr", &pointBuffer, "int",points.length(), "int",fillMode)
    }

    ; 起始角度(右边为0), 阴影角度
    GdipFillPie(pBrush, aRect, angles) {
        return dllcall("gdiplus\GdipFillPie", "Ptr",this.ptr, "Ptr",pBrush, "float",aRect[1],"float",aRect[2],"float",aRect[3],"float",aRect[4], "float",angles.1, "float",angles.2)
    }

    GdipFillPath(pGraphics, pBrush, pPath) {
        return dllcall("gdiplus\GdipFillPath", "UPtr",pGraphics, "UPtr",pBrush, "UPtr",pPath)
    }

    GdipCreateRegion() {
        dllcall("gdiplus\GdipCreateRegion", "UInt*",region)
        if (!region)
            msgbox(A_ThisFunc)
        return this.region := region
    }
    GdipGetClip() {
        this.GdipCreateRegion()
        dllcall("gdiplus\GdipGetClip", "UPtr",this.ptr, "UInt",this.region)
        return this.region
    }

    GdipSetClipRect(aRect, CombineMode:=0) {
        return dllcall("gdiplus\GdipSetClipRect",  "UPtr",this.ptr, "float",aRect[1],"float",aRect[2],"float",aRect[3],"float",aRect[4], "int",CombineMode)
    }

    GdipSetClipRegion(CombineMode:=0) {
        return dllcall("gdiplus\GdipSetClipRegion", "UPtr",this.ptr, "UPtr",this.region, "int",CombineMode)
    }

    GdipDeleteRegion() {
        return dllcall("gdiplus\GdipDeleteRegion", "UPtr",this.region)
    }

    GdipCreatePath(BrushMode:=0) {
        dllcall("gdiplus\GdipCreatePath", "int",BrushMode, "UPtr*",pPath)
        return pPath
    }

    GdipAddPathRectangle(pPath, aRect) {
        return dllcall("gdiplus\GdipAddPathRectangle",A_PtrSize ? "UPtr" : "UInt",pPath, "float",aRect[1],"float",aRect[2],"float",aRect[3],"float",aRect[4])
    }

    GdipAddPathPie(pPath, aRect, StartAngle, SweepAngle) {
        return dllcall("gdiplus\GdipAddPathPie", "UPtr",pPath, "float",aRect[1],"float",aRect[2],"float",aRect[3],"float",aRect[4], "float",StartAngle, "float",SweepAngle)
    }

    Gdip_AddPathBeziers(pPath, Points) {
        Points := StrSplit(Points, "|")
        VarSetCapacity(PointF, 8*Points.Length())
        for _, Point in Points {
            Coord := StrSplit(Point, ",")
            NumPut(Coord[1], PointF, 8*(A_Index-1), "float")
            NumPut(Coord[2], PointF, (8*(A_Index-1))+4, "float")
        }
        return DllCall("gdiplus\GdipAddPathBeziers", "UPtr", pPath, "UPtr", &PointF, "int", Points.Length())
    }

    ; Adds a Bézier spline to the current figure of this path
    GdipAddPathBezier(pPath, x1, y1, x2, y2, x3, y3, x4, y4) {
        return DllCall("gdiplus\GdipAddPathBezier", "UPtr", pPath
            , "float", x1, "float", y1, "float", x2, "float", y2
            , "float", x3, "float", y3, "float", x4, "float", y4)
    }

    ;#####################################################################################
    ; Function Gdip_AddPathLines
    ; Description Adds a sequence of connected lines to the current figure of this path.
    ;
    ; pPath Pointer to the GraphicsPath
    ; Points the coordinates of all the points passed as x1,y1|x2,y2|x3,y3.....
    ;
    ; return status enumeration. 0 = success
    GdipAddPathLine2(pPath, Points) {
        Points := StrSplit(Points, "|")
        VarSetCapacity(PointF, 8*Points.Length())
        for _, Point in Points {
            Coord := StrSplit(Point, ",")
            NumPut(Coord[1], PointF, 8*(A_Index-1), "float")
            NumPut(Coord[2], PointF, (8*(A_Index-1))+4, "float")
        }
        return DllCall("gdiplus\GdipAddPathLine2", "UPtr", pPath, "UPtr", &PointF, "int", Points0)
    }

    Gdip_AddPathLine(pPath, aRect) {
        return DllCall("gdiplus\GdipAddPathLine", "UPtr", pPath, "float",aRect[1],"float",aRect[2],"float",aRect[3],"float",aRect[4])
    }

    GdipAddPathArc(pPath, aRect, StartAngle, SweepAngle) {
        return DllCall("gdiplus\GdipAddPathArc", "UPtr", pPath, "float",aRect[1],"float",aRect[2],"float",aRect[3],"float",aRect[4], "float", StartAngle, "float", SweepAngle)
    }

    ; Starts a new figure without closing the current figure. Subsequent points added to this path are added to the new figure.
    GdipStartPathFigure(pPath) {
        return DllCall("gdiplus\GdipStartPathFigure", "UPtr", pPath)
    }

    ; Closes the current figure of this path.
    GdipClosePathFigure(pPath) {
        return DllCall("gdiplus\GdipClosePathFigure", "UPtr", pPath)
    }


    ; Replaces this path with curves that enclose the area that is filled when this path is drawn by a specified pen. This method also flattens the path.
    GdipWidenPath(pPath, pPen, Matrix:=0, Flatness:=1) {
        return DllCall("gdiplus\GdipWidenPath", "UPtr", pPath, "uint", pPen, "UPtr", Matrix, "float", Flatness)
    }

    GdipClonePath(pPath) {
        DllCall("gdiplus\GdipClonePath", "UPtr",pPath, "UPtr*",pPathClone)
        return pPathClone
    }

    ;#####################################################################################
    ; Function Gdip_DrawPath
    ; Description draws a sequence of lines and curves defined by a GraphicsPath object
    ; pGraphics Pointer to the Graphics of a bitmap
    ; pPen Pointer to a pen
    ; pPath Pointer to a Path
    ; return status enumeration. 0 = success
    GdipDrawPath(pPen, pPath) {
        return DllCall("gdiplus\GdipDrawPath", "UPtr",this.ptr, "UPtr",pPen, "UPtr",pPath)
    }

    GdipDeletePath(pPath) {
        return dllcall("gdiplus\GdipDeletePath", "UPtr",pPath)
    }

    ;oDC 相关方法

    ; DCX_CACHE = 0x2
    ; DCX_CLIPCHILDREN = 0x8
    ; DCX_CLIPSIBLINGS = 0x10
    ; DCX_EXCLUDERGN = 0x40
    ; DCX_EXCLUDEUPDATE = 0x100
    ; DCX_INTERSECTRGN = 0x80
    ; DCX_INTERSECTUPDATE = 0x200
    ; DCX_LOCKWINDOWUPDATE = 0x400
    ; DCX_NORECOMPUTE = 0x100000
    ; DCX_NORESETATTRS = 0x4
    ; DCX_PARENTCLIP = 0x20
    ; DCX_VALIDATE = 0x200000
    ; DCX_WINDOW = 0x1
    ; getEx(hwnd, flags:=0, hrgnClip:=0) {
    ;     Ptr := A_PtrSize ? "UPtr" : "uint"
    ;     return this.hDC := dllcall("GetDCEx",ptr,hwnd, ptr,hrgnClip, "int",flags)
    ; }

    release(hwnd:=0) {
        return dllcall("ReleaseDC", "uptr",hwnd, "ptr",this.hDC)
    }

    ;TODO
    ; Raster
    ;   SRCCOPY			= 0x00CC0020
    ;   BLACKNESS		= 0x00000042
    ;   NOTSRCERASE		= 0x001100A6
    ;   NOTSRCCOPY		= 0x00330008
    ;   SRCERASE		= 0x00440328
    ;   DSTINVERT		= 0x00550009
    ;   PATINVERT		= 0x005A0049
    ;   SRCINVERT		= 0x00660046
    ;   SRCAND			= 0x008800C6
    ;   MERGEPAINT		= 0x00BB0226
    ;   MERGECOPY		= 0x00C000CA
    ;   SRCPAINT		= 0x00EE0086
    ;   PATCOPY			= 0x00F00021
    ;   PATPAINT		= 0x00FB0A09
    ;   WHITENESS		= 0x00FF0062
    ;   CAPTUREBLT		= 0x40000000
    ;   NOMIRRORBITMAP		= 0x80000000
    BitBlt(aRectTo, sDC, sx, sy, Raster:=0x00CC0020) {
        if (!this.hDC || !sDC)
            msgbox(A_ThisFunc)
        return dllcall("gdi32\BitBlt", "UPtr",this.hDC
            , "int",aRectTo[1],"int",aRectTo[2],"int",aRectTo[3],"int",aRectTo[4]
            , "UPtr",sDC, "int",sx, "int",sy, "uint",Raster)
    }

    SelectObject(hGdiObj:=0) {
        if (hGdiObj) {
            if !this.pSelectSave ;只记录一次
                return this.pSelectSave := dllcall("SelectObject", "UPtr",this.hDC, "UPtr",hGdiObj)
            else
                return dllcall("SelectObject", "UPtr",this.hDC, "UPtr",hGdiObj)
        } else ;还原
            return dllcall("SelectObject", "UPtr",this.hDC, "UPtr",this.pSelectSave)
    }

    ;aRect =屏幕上显示的位置大小
    UpdateLayeredWindow(hwnd, aRect, alpha:=255) {
        if (!hwnd || !this.hDC)
            msgbox(A_ThisFunc)
        x := aRect[1]
        y := aRect[2]
        w := aRect[3]
        h := aRect[4]
        VarSetCapacity(pt, 8, 0)
        numput(x, pt, 0, "uint")
        numput(y, pt, 4, "uint")
        return dllcall("UpdateLayeredWindow"
            , "uptr", hwnd
            , "uptr", 0
            , "uptr", &pt
            , "int64*", w|h<<32
            , "uptr", this.hDC
            , "int64*", 0
            , "uint", 0
            , "uint*", alpha<<16|1<<24
            , "uint", 2)
    }

    showByGui() {
        oGui := GuiCreate("-Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs")
        oGui.Show("NA")
        this.UpdateLayeredWindow(oGui.hwnd, [0, 0, A_ScreenWidth, A_ScreenHeight])
    }

}

;pp
class GDIP_Pen extends _GDIP {
    ptr := 0

    __new(argbOrBrush, width) {
        if isobject(argbOrBrush)
            this.createByBrush(argbOrBrush)
        else
            this.createByArgb(argbOrBrush, width)
        ; base.registerObject(this)
    }

    __delete() {
        ; msgbox(A_ThisFunc . '---')
        dllcall("gdiplus\GdipDeletePen", "UPtr",this.ptr)
    }

    createByArgb(argb, width) {
        ;TODO 是否需要先删除原画笔?
        if this.ptr
            this.__delete()
        res := dllcall("gdiplus\GdipCreatePen1", "uint",argb, "float",width, "int",2, "UPtr*",pPen)
        if (!pPen)
            msgbox(A_ThisFunc . "`n" . argb . "`n" . width)
        this.ptr := pPen
        return res
    }

    createByBrush(oBrush){
        if this.ptr
            this.__delete()
        this.pBrush := oBrush ;TODO 是否需要保存 <2020-12-11 14:36:59> hyaray
        res := dllcall("gdiplus\GdipCreatePen2", "UPtr",argbOrBrush, "float",width, "int",2, "UPtr*",pPen)
        this.ptr := pPen
        return res
    }

    GdipGetPenWidth() {
        dllcall("gdiplus\GdipGetPenWidth", "UPtr",this.ptr, "float*",width)
        return width
    }
    GdipSetPenWidth(width) {
        return dllcall("gdiplus\GdipSetPenWidth", "UPtr",this.ptr, "float",width)
    }

    GdipGetPenColor() {
        dllcall("gdiplus\GdipGetPenColor", "UPtr",this.ptr, "uint*",color)
        return color
    }
    GdipSetPenColor(color) {
        return dllcall("gdiplus\GdipSetPenColor", "UPtr",this.ptr, "uint",color)
    }

    ; getBrush() {
    ;     return this.ptr
    ; }
    ; setBrush(pBrush := "") {
    ;     if (this.haskey("pBrush") && pBrush)
    ;         this.ptr := pBrush
    ;     return dllcall("gdiplus\GdipSetPenBrushFill", "UPtr",this.ptr, "UPtr",this.ptr.getpBrush())
    ; }

}

;bb
class GDIP_Brush extends _GDIP {
    ptr := 0
    __new(argb, argbBack:=0, HatchStyle:=0) {
        if argbBack
            this.GdipCreateHatchBrush(argb, argbBack, HatchStyle)
        else
            this.GdipCreateSolidFill(argb)
    }

    __delete() {
        ; msgbox(A_ThisFunc . '---')
        dllcall("gdiplus\GdipDeleteBrush", "UPtr",this.ptr)
    }

    GdipCreateSolidFill(argb) {
        if !argb
            msgbox(A_ThisFunc . "`nargb = 0")
        ;TODO 是否需要先删除原画刷?
        if this.ptr
            this.__delete()
        dllcall("gdiplus\GdipCreateSolidFill", "uint",argb, "UPtr*",pBrush)
        if (!pBrush)
            msgbox(A_ThisFunc)
        this.ptr := pBrush
    }

    ; LinearGradientModeHorizontal = 0
    ; LinearGradientModeVertical = 1
    ; LinearGradientModeForwardDiagonal = 2
    ; LinearGradientModeBackwardDiagonal = 3
    GdipCreateLineBrushFromRect(aRect, ARGB1, ARGB2, LinearGradientMode:=1, WrapMode:=1) {
        this.CreateRect(Rect, aRect)
        dllcall("gdiplus\GdipCreateLineBrushFromRect", "UPtr",&Rect, "int",ARGB1, "int",ARGB2, "int",LinearGradientMode, "int",WrapMode, "UPtr*",LGpBrush)
        return this.ptr := LGpBrush
    }

    ; HatchStyleHorizontal = 0
    ; HatchStyleVertical = 1
    ; HatchStyleForwardDiagonal = 2
    ; HatchStyleBackwardDiagonal = 3
    ; HatchStyleCross = 4
    ; HatchStyleDiagonalCross = 5
    ; HatchStyle05Percent = 6
    ; HatchStyle10Percent = 7
    ; HatchStyle20Percent = 8
    ; HatchStyle25Percent = 9
    ; HatchStyle30Percent = 10
    ; HatchStyle40Percent = 11
    ; HatchStyle50Percent = 12
    ; HatchStyle60Percent = 13
    ; HatchStyle70Percent = 14
    ; HatchStyle75Percent = 15
    ; HatchStyle80Percent = 16
    ; HatchStyle90Percent = 17
    ; HatchStyleLightDownwardDiagonal = 18
    ; HatchStyleLightUpwardDiagonal = 19
    ; HatchStyleDarkDownwardDiagonal = 20
    ; HatchStyleDarkUpwardDiagonal = 21
    ; HatchStyleWideDownwardDiagonal = 22
    ; HatchStyleWideUpwardDiagonal = 23
    ; HatchStyleLightVertical = 24
    ; HatchStyleLightHorizontal = 25
    ; HatchStyleNarrowVertical = 26
    ; HatchStyleNarrowHorizontal = 27
    ; HatchStyleDarkVertical = 28
    ; HatchStyleDarkHorizontal = 29
    ; HatchStyleDashedDownwardDiagonal = 30
    ; HatchStyleDashedUpwardDiagonal = 31
    ; HatchStyleDashedHorizontal = 32
    ; HatchStyleDashedVertical = 33
    ; HatchStyleSmallConfetti = 34
    ; HatchStyleLargeConfetti = 35
    ; HatchStyleZigZag = 36
    ; HatchStyleWave = 37
    ; HatchStyleDiagonalBrick = 38
    ; HatchStyleHorizontalBrick = 39
    ; HatchStyleWeave = 40
    ; HatchStylePlaid = 41
    ; HatchStyleDivot = 42
    ; HatchStyleDottedGrid = 43
    ; HatchStyleDottedDiamond = 44
    ; HatchStyleShingle = 45
    ; HatchStyleTrellis = 46
    ; HatchStyleSphere = 47
    ; HatchStyleSmallGrid = 48
    ; HatchStyleSmallCheckerBoard = 49
    ; HatchStyleLargeCheckerBoard = 50
    ; HatchStyleOutlinedDiamond = 51
    ; HatchStyleSolidDiamond = 52
    ; HatchStyleTotal = 53
    ; https://docs.microsoft.com/en-us/windows/win32/api/gdiplusenums/ne-gdiplusenums-hatchstyle
    GdipCreateHatchBrush(argbFront, argbBack, HatchStyle:=0) {
        dllcall("gdiplus\GdipCreateHatchBrush", "int",HatchStyle, "UInt",argbFront, "UInt",argbBack, "UPtr*",pBrush)
        if (!pBrush)
            msgbox(A_ThisFunc)
        return this.ptr := pBrush
    }

    GdipCloneBrush() {
        dllcall("gdiplus\GdipCloneBrush", "UPtr",this.ptr, "UPtr*",pBrushClone)
        return pBrushClone
    }

    SetColor(argb) {
        dllcall("gdiplus\GdipSetSolidFillColor", "UPtr",this.ptr, "uint",argb)
    }


    getColor() {
        dllcall("gdiplus\GdipSetSolidFillColor", "UPtr",this.ptr, "uint*",argb)
        return argb
    }

}

;ff
class GDIP_Font extends _GDIP {
    ptr := 0

    ;oInstance
    ;   oDC
    ;   oFontFamily
    ;   sFont
    __new(oInstance:="Arial", size:=12) {
        if isobject(oInstance) {
            if (oInstance.__class == "GDIP_Graphics")
                res := dllcall("gdiplus\GdipCreateFontFromDC", "UPtr",oInstance.hDC, "UPtr*",pFont)
            else if (oInstance.__class == "GDIP_FontFamily")
                this.GdipCreateFont(oInstance.ptr, size)
        } else { ;字体名称
            oFontFamily := new GDIP_FontFamily(oInstance)
            this.GdipCreateFont(oFontFamily.ptr, size)
            oFontFamily := ""
        }
        if res
            msgbox(A_ThisFunc)
    }

    __delete() {
        dllcall("gdiplus\GdipDeleteFont", "UPtr",this.ptr)
    }

    ; Regular = 0
    ; Bold = 1
    ; Italic = 2
    ; BoldItalic = 3
    ; Underline = 4
    ; Strikeout = 8
    GdipCreateFont(pFontFamily, size, style:=0) {
        res := dllcall("gdiplus\GdipCreateFont", "UPtr",pFontFamily, "float",size, "uint",style, "uint",0, "UPtr*",pFont)
        if res
            msgbox(A_ThisFunc)
        return this.ptr := pFont
    }
}

class GDIP_FontFamily {
    __new(sFont) {
        ; res := dllcall("gdiplus\GdipCreateFontFamilyFromName" , "UPtr",&sFont , "uint",0 , "UPtr*",pFontFamily)
        res := dllcall("gdiplus\GdipCreateFontFamilyFromName", "WStr",sFont, "UPtr",0, "UPtr*",pFontFamily )
        if !pFontFamily {
            msgbox(format("字体{1}不存在", sFont),,0x40000)
            exit
        }
        this.ptr := pFontFamily
    }

    __delete() {
        dllcall( "gdiplus\GdipDeleteFontFamily", "UPtr",this.ptr )
    }

}

;ss
;文字在框中的对齐方式
class GDIP_StringFormat extends _GDIP {

    /*
    formatFlags: Defines some settings of the _StringFormat object
    typedef enum  {
        StringFormatFlagsDirectionRightToLeft    = 0x0001,
        StringFormatFlagsDirectionVertical       = 0x0002,
        StringFormatFlagsNoFitBlackBox           = 0x0004,
        StringFormatFlagsDisplayFormatControl    = 0x0020,
        StringFormatFlagsNoFontFallback          = 0x0400,
        StringFormatFlagsMeasureTrailingSpaces   = 0x0800,
        StringFormatFlagsNoWrap                  = 0x1000,
        StringFormatFlagsLineLimit               = 0x2000,
        StringFormatFlagsNoClip                  = 0x4000
    } StringFormatFlags;
    langId: Defines the language this _StringFormat object should use.
    I don't actually know any besides 0 which is LANG_NEUTRAL and represents the users language - further research is necessary.
    */

    __new(formatFlags:=0, langId:=0) {
        this.GdipCreateStringFormat(formatFlags, langId)
    }

    __delete() {
        dllcall("gdiplus\GdipDeleteStringFormat", "UPtr",this.ptr)
    }

    GdipCreateStringFormat(formatFlags, langId) {
        res := dllcall("gdiplus\GdipCreateStringFormat", "uint",formatFlags, "UShort",langId, "UPtr*",pStringFormat)
        if res
            msgbox(A_ThisFunc)
        this.ptr := pStringFormat
    }

    ; Near = 0
    ; Center = 1
    ; Far = 2
    GdipSetStringFormatAlign(align:=1) {
        return dllcall("gdiplus\GdipSetStringFormatAlign", "UPtr",this.ptr, "int",align)
    }

}
Attachments
gdip.zip
(360.43 KiB) Downloaded 101 times

burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: Simple Class_Gdip.ahk By v2-a102 with my own thinking

Post by burque505 » 20 Aug 2021, 12:31

@hyaray, thanks for all your contributions. I recently downloaded your rewrite of UIA.ahk from here and would like to start experimenting with it. It appears I need a file called 'hyaray.ahk' for your example, but I didn't find it in the download. Could I trouble you for a copy?
Regards,
burque505

User avatar
hyaray
Posts: 85
Joined: 20 Jun 2015, 01:37
Contact:

Re: Simple Class_Gdip.ahk By v2-a102 with my own thinking

Post by hyaray » 24 Aug 2021, 20:15

hi @burque505, I have updated my blog yet, "hyaray.ahk" is not necessary, just delete it and try again, enjoy it

burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: Simple Class_Gdip.ahk By v2-a102 with my own thinking

Post by burque505 » 25 Aug 2021, 08:06

@hyaray, thank you very much. @tuzi told me you said that, and I had deleted it already. Do you have any examples of using your class with Office (Word, Excel, PowerPoint, Outlook)? It doesn't matter to me if the strings and comments are in Mandarin. I would really appreciate that! I see from your blog you have done lots of work with Excel and AHK.
In fact, any examples at all you care to share will be very, very welcome.
Best regards,
burque505

User avatar
hyaray
Posts: 85
Joined: 20 Jun 2015, 01:37
Contact:

Re: Simple Class_Gdip.ahk By v2-a102 with my own thinking

Post by hyaray » 25 Aug 2021, 11:47

@burque505
I'm glad that it helps you. and it's kind of @tuzi
You mean class of UIA working in office?
I think ComObjActive can do much already. UIA maybe is useless??
I only found a function used UIA in Excel: for ^f and ^h in Excel.
params can set find in worksheet or workbook(judge by UIA), and show option controls or not.

Code: Select all

;查找和替换
;tp=1查找/0替换
;bFull=1完整模式(建议)
_openFind(tp:=0, sFind:="", bFull:=1, bInBook:=false) {
    ctl := "EDTBX1"
    PostMessage(0x111, 742+tp,,, "ahk_class XLMAIN")
    hwnd := WinWaitActive("ahk_class bosa_sdm_XL9")
    ;完整模式/精简模式
    sFull := isFull()
    if (bFull != sFull) ;r表示要求,s表示当前状态
        send("{alt down}t{alt up}")
    ;查找范围(工作表/工作簿)
    if (bInBook != isInBook()) {
        send("{alt down}h{alt up}")
        bInBook ? send("{down}") : send("{up}")
        send("{enter}")
        sleep(100)
    }
    ;设置查找内容
    if strlen(sFind)
        ControlSetText(sFind, ctl)
    ;设置替换内容
    lClip := strlen(clipboard)
    if (lClip && lClip < 20)
        ControlSetText(clipboard, "EDTBX2")
    ;激活【查找内容】控件
    if (tp && ControlGetClassNN(ControlGetFocus()) != ctl) {
        send("{shift down}{tab 2}{shift up}") ;按下tab不会乱跳
        ;ControlFocus(ctl) ;TODO 按下tab会乱跳
    }
    SendMessage(0xB1, 0, -1, ctl) ;选择文本
    isInBook() {
        oUIA := new IUIAutomation()
        oAE := oUIA.ElementFromHandle(WinGetID("查找和替换")) ;ahk_class bosa_sdm_XL9
        condition := oUIA.CreateAndCondition(oUIA.CreatePropertyCondition("AccessKey", "Alt+H", 8), oUIA.CreatePropertyCondition("ControlType", oUIA._ct("ComboBox"), VT_I4:=3))
        oAE_ComboBox := oAE.FindFirst(condition, TreeScope_Descendants:=0x4)
        return (oAE_ComboBox.GetCurrentPropertyValue("ValueValue") == "工作簿") ; worksheet
    }
    isFull() {
        ControlGetPos(x,,w,, ctl)
        WinGetPos(, , wWin)
        return (wWin-x-w) > 50
    }
}

burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: Simple Class_Gdip.ahk By v2-a102 with my own thinking

Post by burque505 » 25 Aug 2021, 12:15

@hyaray, I do use COM for Office now. Thank you for your example, I'll experiment with it. I had not considered using the "AccessKey" property ("UIA_AccessKeyPropertyId" is 30007, I think), and that will come in useful other places, I'm sure. I look forward to seeing more examples if you can share them, for anything, not just Office.
Best regards,
burque505

User avatar
hyaray
Posts: 85
Joined: 20 Jun 2015, 01:37
Contact:

Re: Simple Class_Gdip.ahk By v2-a102 with my own thinking

Post by hyaray » 25 Aug 2021, 22:49

@burque505
"AccessKey", I really don't know what is this mean,
it's value in inspect.exe is mostly "", will you show me a demo?

burque505
Posts: 1731
Joined: 22 Jan 2017, 19:37

Re: Simple Class_Gdip.ahk By v2-a102 with my own thinking

Post by burque505 » 26 Aug 2021, 08:02

@hyaray, so far I can only give a simple example using UIA_Interface.ahk and AHK v.1. I will try later to use v2 and your class. The "AccessKey" is frequently exactly the same as the "Legacy Shortcut".

Code: Select all

SetTitleMatchMode, 2

#Include UIA_Interface.ahk
uia := UIA_Interface()
Run, Notepad.exe
WinWaitActive, ahk_exe Notepad.exe
WinGet, notepadHwnd, ID, ahk_exe Notepad.exe
;msgbox %notepadHwnd%
elementMain := uia.ElementFromHandle(notepadHwnd)

cond1 := uia.CreatePropertyCondition(30007, "Alt+f", 8) ; 30005 is "Name", 

buttonElement := elementMain.FindFirst(cond1, 0x4)

clickFile := buttonElement.GetCurrentPatternAs("Invoke")

clickFile.Invoke()





elementMain := ""
This is a screenshot of my little tool hovering over the "File" menu in Notepad:
uia_tool_6.PNG
uia_tool_6.PNG (26.62 KiB) Viewed 1670 times
Thanks very much for your interest and your help!
Regards,
burque505

P.S. Extending code above, this clicks "File", then clicks "Open"
Spoiler

Post Reply

Return to “Scripts and Functions (v2)”