/
xagent.go
118 lines (103 loc) · 2.2 KB
/
xagent.go
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
package utils
import (
"golang.org/x/sys/windows"
"net"
"syscall"
"unsafe"
)
const (
xAgentClassName = "NSSSH:AGENTWND"
xAgentSingleClassName = "STATIC"
xAgentSingleWindowName = "_SINGLE_INSTANCE::XAGENT"
)
var (
pSetWindowLong = u32.NewProc("SetWindowLongW")
)
type XAgent struct {
socket net.Listener
cookieWin *window
singleInstanceWin *window
}
type window struct {
class *wndClassEx
window windows.Handle
}
func createDefaultWindow(class, name string) (*window, error) {
classNamePtr, err := syscall.UTF16PtrFromString(class)
if err != nil {
return nil, err
}
windowNamePtr, err := syscall.UTF16PtrFromString(name)
if err != nil {
return nil, err
}
win := new(window)
wcex := &wndClassEx{
WndProc: pDefWindowProc.Addr(),
ClassName: classNamePtr,
}
err = wcex.register()
if err != nil {
return nil, err
}
win.class = wcex
windowHandle, _, err := pCreateWindowEx.Call(
uintptr(0),
uintptr(unsafe.Pointer(classNamePtr)),
uintptr(unsafe.Pointer(windowNamePtr)),
uintptr(0),
uintptr(0),
uintptr(0),
uintptr(0),
uintptr(0),
uintptr(0),
uintptr(0),
uintptr(0),
uintptr(0),
)
if windowHandle == 0 {
wcex.unregister()
return nil, err
}
win.window = windows.Handle(windowHandle)
return win, nil
}
func (s *window) Close() {
pDestroyWindow.Call(uintptr(s.window))
s.class.unregister()
}
func NewXAgent(cookie string) (*XAgent, error) {
l, err := net.Listen("tcp", "localhost:0")
if err != nil {
return nil, err
}
win := new(XAgent)
win.socket = l
cookieWin, err := createDefaultWindow(xAgentClassName, cookie)
if err != nil {
return nil, err
}
siWin, err := createDefaultWindow(xAgentSingleClassName, xAgentSingleWindowName)
if err != nil {
return nil, err
}
SetWindowLong(cookieWin.window, 0xFFFFFFEB, uintptr(l.Addr().(*net.TCPAddr).Port))
win.cookieWin = cookieWin
win.singleInstanceWin = siWin
return win, nil
}
func (s *XAgent) Listener() net.Listener {
return s.socket
}
func (s *XAgent) Close() {
s.cookieWin.Close()
s.singleInstanceWin.Close()
}
func SetWindowLong(hWnd windows.Handle, index, value uintptr) int32 {
ret, _, _ := pSetWindowLong.Call(
uintptr(hWnd),
index,
value,
)
return int32(ret)
}