제작자: 예지력(ahk.org Soft, 블로그)

기능: SetWinEvents 후킹으로 윈도우를 도킹한다


[+] 테일스타도 iframe 태그가 안먹는구나! ㅜㅜ

이게 뭐하는 기능인지 보려면 예제 동영상을 보세요


원문 블로그 글은 여기를 클릭하세요


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
#NoEnv
#SingleInstance, force
 
Gui+hwndGuihwnd
Gui, Font, s15
Gui, Add, Text,, class Dock 예제                  
Gui, Add, Button, gBtn, Dock to Top
Gui, Add, Button, gBtn, Dock to Bottom
Gui, Add, Button, gBtn, Dock to Right
Gui, Add, Button, gBtn, Dock to Left
 
exDock := new Dock(Guihwnd, Dock.HelperFunc.Run("notepad.exe"1))
exDock.AttachTo("R")
exDock.CloseCallback := Func("CloseCallback")
 
Gui, Show, xCenter yCenter w500
Return
 
Btn:
exDock.AttachTo(A_GuiControl)
Return
 
CloseCallback(self)
{
    WinKill, % "ahk_id " self.hwnd.attach
    ExitApp
}
 
GuiClose:
Gui, Destroy
 
 
/*
    Class Dock
        윈도우를 도킹한다
    제작자
        예지력 (ahk org 포럼 Soft, GitHub Visionary1, 블로그 예지력)
    버전
        0.1 (2017.04.20)
        0.2 (2017.05.06)
    라이센스
        WTFPL (http://wtfpl.net/)
    제작환경
        Windows 10 pro x64
        AutoHotKey H v1.1.25.01 32bit
    To Do...
        Multiple Dock, group windows...
*/
class Dock
{
    static EVENT_OBJECT_LOCATIONCHANGE := 0x800B
    , EVENT_OBJECT_FOCUS := 0x8005, EVENT_OBJECT_DESTROY := 0x8001
 
    /*
        인스턴스 := new Dock(main hwnd, attach hwnd, [Callback], [CloseCallback])
            main hwnd
                호스트 윈도우의 핸들
            attach hwnd
                클라이언트 윈도우의 핸들
            [Callback]
                Shell 이벤트의 Callback
                함수 Object 또는 BoundFunc 객체를 패스하세요
                생략 시, 기본 EventHandler 가 사용되고 이 코드는 Dock.EventsHandler에 하드코딩되어있어요
            [CloseCallback]
                함수 Object 또는 BoundFunc 객체를 패스하세요
                호스트 윈도우가 Destroy 될 때, CloseCallback 이 포인팅하는 함수/메소드를 호출해요
                사용 예는 예제 확인하세요
    */
    __New(main, attach, Callback := "", CloseCallback := "")
    {
        this.hwnd := []
        this.hwnd.main := main
        this.hwnd.attach := attach
        WinSet, ExStyle, +0x80, % "ahk_id " this.hwnd.attach
        this.Callback := StrLen(Callback) ? Callback : ObjBindMethod(Dock.EventsHandler, "Calls")
        this.CloseCallback := CloseCallback
        
        /*
            lpfnWinEventProc
        */
        this.hookProcAdr := RegisterCallback("_DockHookProcAdr",,, &this)
 
        /*
            idProcess
        */
        WinGet, idProcess, PID, % "ahk_id " . this.hwnd.main
 
        /*
            idThread
        */
        idThread := DllCall("GetWindowThreadProcessId""Ptr"this.hwnd.main, "Int"0)
 
        DllCall("CoInitialize""Int"0)
        this.Hook := DllCall("SetWinEventHook"
                , "UInt", Dock.EVENT_OBJECT_DESTROY            ;eventMin
                , "UInt", Dock.EVENT_OBJECT_LOCATIONCHANGE     ;eventMax
                , "Ptr"0                                     ;hmodWinEventProc
                , "Ptr"this.hookProcAdr                      ;lpfnWinEventProc
                , "UInt", idProcess                            ;idProcess
                , "UInt", idThread                             ;idThread
                , "UInt"0)                                   ;dwFlags
    }
 
    /*
        인스턴스.Unhook()
            언후킹하고 Circular Reference 를 Break 합니다
            오토핫키는 Circular Ref 가 형성되어 있다면
            일반적인 초기화(인스턴스 := "") 방식으로는 클래스 Destructor 가 호출되지 않습니다
    */
    Unhook()
    {
        DllCall("UnhookWinEvent""Ptr"this.Hook)
        DllCall("CoUninitialize")
        DllCall("GlobalFree""Ptr"this.hookProcAdr)
        this.Hook := ""
        this.hookProcAdr := ""
        this.Callback := ""
        WinSet, ExStyle, -0x80, % "ahk_id " this.hwnd.attach
    }
 
    /*
        클래스 Destructor
    */
    __Delete()
    {
        this.CloseCallback := ""
    }
 
    /*
        인스턴스.AttachTo(Position)
            Position
                클라이언트 윈도우가 도킹되는 위치를 설정해요
                Top - 호스트 윈도우 위로
                Bottom - 호스트 윈도우 아래로
                R 또는 Right - 호스트 윈도우 오른쪽으로
                L 또는 Left - 호스트 윈도우 왼쪽으로
    */
    AttachTo(pos := "Right")
    {
        this.pos := pos
        Return this.EventsHandler.EVENT_OBJECT_LOCATIONCHANGE(this)
    }
 
    /*
        Dock 이벤트 핸들러
        개인 메소드를 Callback 로서 Passing 하지 않았다면, 아래를 기본적으로 사용해요
    */
    class EventsHandler extends Dock.HelperFunc
    {
        Calls(self, hWinEventHook, event, hwnd)
        {
            If !(hwnd = self.hwnd.main)
                Return
 
            If (event = Dock.EVENT_OBJECT_FOCUS)
            {
                Return this.EVENT_OBJECT_FOCUS(self.hwnd.attach)
            }
 
            If (event = Dock.EVENT_OBJECT_LOCATIONCHANGE)
            {
                Return this.EVENT_OBJECT_LOCATIONCHANGE(self)
            }
 
            If (event = Dock.EVENT_OBJECT_DESTROY)
            {
                self.Unhook()
                If (IsFunc(self.CloseCallback) || IsObject(self.CloseCallback))
                    Return self.CloseCallback()
            }
        }
 
        /*
            호스트 윈도우가 Focus 를 얻을 때, called
        */
        EVENT_OBJECT_FOCUS(hwnd)
        {
            Return this.WinSetTop(hwnd)
        }
 
        /*
            호스트 윈도우가 EVENT_OBJECT_LOCATIONCHANGE 이벤트를 받을 때 called
        */
        EVENT_OBJECT_LOCATIONCHANGE(self)
        {
            main := this.WinGetPos(self.hwnd.main)
            attach := this.WinGetPos(self.hwnd.attach)
 
            /*
                Top 포지션
            */
            If InStr(self.pos, "Top")
            {
                Return this.MoveWindow(self.hwnd.attach ;hwnd
                            , main.x                    ;x
                            , main.y - attach.h         ;y
                            , attach.w                  ;width
                            , attach.h)                 ;height
            }
 
            /*
                Bottom 포지션
            */
            If InStr(self.pos, "Bottom")
            {
                Return this.MoveWindow(self.hwnd.attach  ;hwnd
                            , main.x                     ;x
                            , main.y + main.h            ;y
                            , attach.w                   ;width
                            , attach.h)                  ;height
            }
 
            /*
                R, Right 포지션
            */
            If InStr(self.pos, "R")
            {
                Return this.MoveWindow(self.hwnd.attach  ;hwnd
                            , main.x + main.w            ;x
                            , main.y                     ;y
                            , attach.w                   ;width
                            , attach.h)                  ;height
            }
 
            /*
                L, Left 포지션
            */
            If InStr(self.pos, "L")
            {
                Return this.MoveWindow(self.hwnd.attach ;hwnd
                            , main.x - attach.w         ;x
                            , main.y                    ;y
                            , attach.w                  ;width
                            , attach.h)                 ;height
            }
        }
    }
 
    class HelperFunc
    {
        WinGetPos(hwnd)
        {
            WinGetPos, hX, hY, hW, hH, % "ahk_id " . hwnd
            Return {x: hX, y: hY, w: hW, h: hH}
        }
 
        WinSetTop(hwnd)
        {
            WinSet, AlwaysOnTop, On, % "ahk_id " . hwnd
            WinSet, AlwaysOnTop, Off, % "ahk_id " . hwnd
        }
 
        MoveWindow(hwnd, x, y, w, h)
        {
            Return DllCall("MoveWindow""Ptr", hwnd, "Int", x, "Int", y, "Int", w, "Int", h, "Int"1)
        }
 
        Run(Target, AdjustSize := False)
        {
            Try Run, % Target,,, OutputVarPID
            Catch
                Throw"Target 을 실행 할 수 없었어요"
            WinWait, % "ahk_pid " OutputVarPID
            hwnd := WinExist("ahk_pid " OutputVarPID)
            If (AdjustSize)
            {
                tar := this.WinGetPos(hwnd)
                this.MoveWindow(hwnd, tar.x, tar.y, A_ScreenWidth*0.3, A_ScreenWidth*0.3)
            }
            Return hwnd
        }
    }
}
 
_DockHookProcAdr(hWinEventHook, event, hwnd, idObject, idChild, dwEventThread, dwmsEventTime)
{
    this := Object(A_EventInfo)
    this.Callback.Call(this, hWinEventHook, event, hwnd)
    ObjRelease(this)
}
 
cs

답글