fzeet 0.4.2 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/examples/Dialog/FileDialog.rbw +34 -0
- data/examples/Raw/UIRibbon/Command.dll +0 -0
- data/examples/Raw/UIRibbon/Command.rb +22 -0
- data/examples/Raw/UIRibbon/Command.rbw +51 -0
- data/examples/Raw/UIRibbon/Command.xml +65 -0
- data/examples/Raw/UIRibbon/Minimal.dll +0 -0
- data/examples/Raw/UIRibbon/Minimal.rb +16 -0
- data/examples/Raw/UIRibbon/Minimal.rbw +39 -0
- data/examples/Raw/UIRibbon/Minimal.xml +55 -0
- data/examples/UIRibbon/Command.dll +0 -0
- data/examples/UIRibbon/Command.rb +22 -0
- data/examples/UIRibbon/Command.rbw +20 -0
- data/examples/UIRibbon/Command.xml +65 -0
- data/examples/UIRibbon/Minimal.dll +0 -0
- data/examples/UIRibbon/Minimal.rb +16 -0
- data/examples/UIRibbon/Minimal.rbw +8 -0
- data/examples/UIRibbon/Minimal.xml +55 -0
- data/examples/UIRibbon/Viewer.rbw +9 -0
- data/examples/res/go-next-small.bmp +0 -0
- data/examples/res/go-next.bmp +0 -0
- data/examples/res/go-previous-small.bmp +0 -0
- data/examples/res/go-previous.bmp +0 -0
- data/lib/fzeet/Control.rb +1 -89
- data/lib/fzeet/ControlButton.rb +28 -0
- data/lib/fzeet/ControlCommon.rb +64 -0
- data/lib/fzeet/Dialog.rb +1 -0
- data/lib/fzeet/DialogCommon.rb +5 -0
- data/lib/fzeet/DialogFileDialog.rb +116 -0
- data/lib/fzeet/UIRibbon.rb +99 -0
- data/lib/fzeet/Window.rb +2 -336
- data/lib/fzeet/WindowCommon.rb +256 -0
- data/lib/fzeet/WindowDialog.rb +73 -0
- data/lib/fzeet/WindowWindow.rb +13 -0
- data/lib/fzeet/common.rb +2 -0
- data/lib/fzeet/windows/com.rb +186 -0
- data/lib/fzeet/windows/comctl.rb +1 -59
- data/lib/fzeet/windows/comctlbutton.rb +30 -0
- data/lib/fzeet/windows/comctlcommon.rb +34 -0
- data/lib/fzeet/windows/comdlg.rb +1 -0
- data/lib/fzeet/windows/comdlgcommon.rb +9 -0
- data/lib/fzeet/windows/comdlgofn.rb +70 -0
- data/lib/fzeet/windows/common.rb +18 -0
- data/lib/fzeet/windows/kernel.rb +3 -0
- data/lib/fzeet/windows/libc.rb +1 -0
- data/lib/fzeet/windows/ole.rb +213 -0
- data/lib/fzeet/windows/shell.rb +43 -0
- data/lib/fzeet/windows/uiribbon.rb +111 -0
- data/lib/fzeet/windows/user.rb +7 -438
- data/lib/fzeet/windows/usercommon.rb +8 -0
- data/lib/fzeet/windows/userctl.rb +1 -68
- data/lib/fzeet/windows/userctlbutton.rb +56 -0
- data/lib/fzeet/windows/userctlcommon.rb +12 -0
- data/lib/fzeet/windows/userkbd.rb +20 -0
- data/lib/fzeet/windows/usermbox.rb +62 -0
- data/lib/fzeet/windows/usermenu.rb +50 -0
- data/lib/fzeet/windows/usermsg.rb +1 -6
- data/lib/fzeet/windows/userspi.rb +29 -0
- data/lib/fzeet/windows/userwnd.rb +276 -0
- data/lib/fzeet/windows.rb +24 -4
- data/lib/fzeet.rb +2 -0
- metadata +54 -6
- /data/examples/{Dialog.rbw → Dialog/Dialog.rbw} +0 -0
- /data/examples/{DialogApplication.rbw → Dialog/DialogApplication.rbw} +0 -0
@@ -0,0 +1,256 @@
|
|
1
|
+
require_relative 'WindowMethods'
|
2
|
+
|
3
|
+
module Fzeet
|
4
|
+
class BasicWindow < Handle
|
5
|
+
include WindowMethods
|
6
|
+
|
7
|
+
Prefix = {
|
8
|
+
xstyle: [:ws_ex_],
|
9
|
+
style: [:ws_, :ds_],
|
10
|
+
message: [:wm_],
|
11
|
+
notification: []
|
12
|
+
}
|
13
|
+
|
14
|
+
WindowProc = FFI::Function.new(:long, [:pointer, :uint, :uint, :long], convention: :stdcall) { |hwnd, uMsg, wParam, lParam|
|
15
|
+
begin
|
16
|
+
if uMsg == Windows::WM_NCCREATE
|
17
|
+
@@instances[hwnd.to_i] = ObjectSpace._id2ref(
|
18
|
+
Windows::CREATESTRUCT.new(FFI::Pointer.new(lParam))[:lpCreateParams].to_i
|
19
|
+
)
|
20
|
+
|
21
|
+
@@instances[hwnd.to_i].instance_variable_set(:@handle, hwnd)
|
22
|
+
end
|
23
|
+
|
24
|
+
result = @@instances[hwnd.to_i].windowProc(hwnd, uMsg, wParam, lParam) if @@instances[hwnd.to_i]
|
25
|
+
rescue
|
26
|
+
answer = Fzeet.message %Q{#{$!}\n\n#{$!.backtrace.join("\n")}}, buttons: [:abort, :retry, :ignore], icon: :error
|
27
|
+
|
28
|
+
Application.quit if answer.abort?
|
29
|
+
ensure
|
30
|
+
if uMsg == Windows::WM_NCDESTROY
|
31
|
+
@@instances[hwnd.to_i].dialog = false
|
32
|
+
|
33
|
+
@@instances.delete(hwnd.to_i)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
result || Windows.DefWindowProc(hwnd, uMsg, wParam, lParam)
|
38
|
+
}
|
39
|
+
|
40
|
+
DialogProc = FFI::Function.new(:int, [:pointer, :uint, :uint, :long], convention: :stdcall) { |hwnd, uMsg, wParam, lParam|
|
41
|
+
begin
|
42
|
+
if uMsg == Windows::WM_INITDIALOG
|
43
|
+
@@instances[hwnd.to_i] = ObjectSpace._id2ref(lParam)
|
44
|
+
|
45
|
+
@@instances[hwnd.to_i].instance_variable_set(:@handle, hwnd)
|
46
|
+
end
|
47
|
+
|
48
|
+
result = @@instances[hwnd.to_i].windowProc(hwnd, uMsg, wParam, lParam) if @@instances[hwnd.to_i]
|
49
|
+
rescue
|
50
|
+
answer = Fzeet.message %Q{#{$!}\n\n#{$!.backtrace.join("\n")}}, buttons: [:abort, :retry, :ignore], icon: :error
|
51
|
+
|
52
|
+
Application.quit if answer.abort?
|
53
|
+
ensure
|
54
|
+
if uMsg == Windows::WM_NCDESTROY
|
55
|
+
@@instances[hwnd.to_i].dialog = false
|
56
|
+
|
57
|
+
@@instances.delete(hwnd.to_i)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
(result.nil?) ? 0 : 1
|
62
|
+
}
|
63
|
+
|
64
|
+
WindowClass = Fzeet::WindowClass.new(nil, 'Fzeet.BasicWindow')
|
65
|
+
|
66
|
+
def self.[](name, opts = {})
|
67
|
+
Class.new(self) { |klass|
|
68
|
+
klass.const_set(:WindowClass, Fzeet::WindowClass.new(self::WindowClass, name, opts))
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.crackMessage(hwnd, uMsg, wParam, lParam)
|
73
|
+
window = @@instances[hwnd.to_i]
|
74
|
+
|
75
|
+
args = {
|
76
|
+
hwnd: hwnd,
|
77
|
+
uMsg: uMsg,
|
78
|
+
wParam: wParam,
|
79
|
+
lParam: lParam,
|
80
|
+
result: (window.kind_of?(Dialog)) ? 1 : 0,
|
81
|
+
window: window,
|
82
|
+
sender: window
|
83
|
+
}
|
84
|
+
|
85
|
+
case uMsg
|
86
|
+
when Windows::WM_CREATE
|
87
|
+
args[:cs] = Windows::CREATESTRUCT.new(FFI::Pointer.new(lParam))
|
88
|
+
when Windows::WM_NCCREATE
|
89
|
+
args[:result] = 1
|
90
|
+
args[:cs] = Windows::CREATESTRUCT.new(FFI::Pointer.new(lParam))
|
91
|
+
when Windows::WM_COMMAND
|
92
|
+
args[:command], args[:notification], args[:hctl] = Windows.LOWORD(wParam), Windows.HIWORD(wParam), FFI::Pointer.new(lParam)
|
93
|
+
args[:sender] = @@instances[args[:hctl].to_i] unless args[:hctl].null?
|
94
|
+
when Windows::WM_NOTIFY
|
95
|
+
args[:nmh] = Windows::NMHDR.new(FFI::Pointer.new(lParam))
|
96
|
+
args[:command], args[:notification], args[:hctl] = args[:nmh][:idFrom], args[:nmh][:code], args[:nmh][:hwndFrom]
|
97
|
+
(args[:sender] = @@instances[args[:hctl].to_i]).class.crackNotification(args)
|
98
|
+
when \
|
99
|
+
Windows::WM_MOUSEMOVE,
|
100
|
+
Windows::WM_LBUTTONDOWN, Windows::WM_LBUTTONUP, Windows::WM_LBUTTONDBLCLK,
|
101
|
+
Windows::WM_RBUTTONDOWN, Windows::WM_RBUTTONUP, Windows::WM_RBUTTONDBLCLK,
|
102
|
+
Windows::WM_MBUTTONDOWN, Windows::WM_MBUTTONUP, Windows::WM_MBUTTONDBLCLK,
|
103
|
+
Windows::WM_CONTEXTMENU
|
104
|
+
|
105
|
+
args[:x], args[:y] = Windows.GET_X_LPARAM(lParam), Windows.GET_Y_LPARAM(lParam)
|
106
|
+
|
107
|
+
if uMsg == Windows::WM_CONTEXTMENU && args[:x] == -1
|
108
|
+
Windows.DetonateLastError(0, :GetCursorPos, pt = Point.new)
|
109
|
+
|
110
|
+
args[:keyboard], args[:x], args[:y] = true, pt[:x], pt[:y]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
args
|
115
|
+
end
|
116
|
+
|
117
|
+
def initialize(opts = {})
|
118
|
+
_opts = {
|
119
|
+
xstyle: [],
|
120
|
+
caption: Application.name,
|
121
|
+
style: [],
|
122
|
+
x: Windows::CW_USEDEFAULT,
|
123
|
+
y: Windows::CW_USEDEFAULT,
|
124
|
+
width: Windows::CW_USEDEFAULT,
|
125
|
+
height: Windows::CW_USEDEFAULT,
|
126
|
+
parent: nil,
|
127
|
+
menu: nil
|
128
|
+
}
|
129
|
+
badopts = opts.keys - _opts.keys; raise "Bad option(s): #{badopts.join(', ')}." unless badopts.empty?
|
130
|
+
_opts.merge!(opts)
|
131
|
+
|
132
|
+
yield self, _opts if block_given?
|
133
|
+
|
134
|
+
_opts[:xstyle] = Fzeet.flags(_opts[:xstyle], *self.class::Prefix[:xstyle])
|
135
|
+
_opts[:caption] = _opts[:caption].to_s
|
136
|
+
_opts[:style] = Fzeet.flags(_opts[:style], *self.class::Prefix[:style])
|
137
|
+
|
138
|
+
@messages ||= {}
|
139
|
+
@commands ||= {}
|
140
|
+
@notifies ||= {}
|
141
|
+
|
142
|
+
@processed = [0, 0]
|
143
|
+
|
144
|
+
@handle = Windows.CreateWindowEx(
|
145
|
+
_opts[:xstyle], self.class::WindowClass.name, _opts[:caption], _opts[:style],
|
146
|
+
_opts[:x], _opts[:y], _opts[:width], _opts[:height],
|
147
|
+
_opts[:parent] && _opts[:parent].handle, nil, Windows.GetModuleHandle(nil), FFI::Pointer.new(object_id)
|
148
|
+
)
|
149
|
+
|
150
|
+
if @handle.null?
|
151
|
+
raise "CreateWindowEx failed (last error #{Windows.GetLastError()})." unless [
|
152
|
+
[Windows::WM_NCCREATE, 0], [Windows::WM_CREATE, -1],
|
153
|
+
[Windows::WM_DESTROY, 0], [Windows::WM_NCDESTROY, 0]
|
154
|
+
].include?(@processed)
|
155
|
+
else
|
156
|
+
@parent = _opts[:parent]
|
157
|
+
self.menu = _opts[:menu] if _opts[:menu]
|
158
|
+
|
159
|
+
on(:destroy) {
|
160
|
+
menu.rdetach if self.menu
|
161
|
+
|
162
|
+
eachChild(&:dispose)
|
163
|
+
}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
attr_reader :parent
|
168
|
+
|
169
|
+
def dispose; Windows.DestroyWindow(@handle) end
|
170
|
+
|
171
|
+
def windowProc(hwnd, uMsg, wParam, lParam)
|
172
|
+
args, result = nil, nil
|
173
|
+
|
174
|
+
if (handlers = @messages[uMsg])
|
175
|
+
args ||= self.class.crackMessage(hwnd, uMsg, wParam, lParam)
|
176
|
+
|
177
|
+
handlers.each { |handler|
|
178
|
+
(handler.arity == 0) ? handler.call : handler.call(args)
|
179
|
+
|
180
|
+
result = args[:result]; @processed[0], @processed[1] = uMsg, result
|
181
|
+
}
|
182
|
+
end
|
183
|
+
|
184
|
+
if uMsg == Windows::WM_COMMAND && (handlers = @commands[Windows.LOWORD(wParam)])
|
185
|
+
args ||= self.class.crackMessage(hwnd, uMsg, wParam, lParam)
|
186
|
+
|
187
|
+
handlers[:all].each { |handler|
|
188
|
+
(handler.arity == 0) ? handler.call : handler.call(args)
|
189
|
+
|
190
|
+
result = args[:result]; @processed[0], @processed[1] = uMsg, result
|
191
|
+
} if handlers[:all]
|
192
|
+
|
193
|
+
handlers[Windows.HIWORD(wParam)].each { |handler|
|
194
|
+
(handler.arity == 0) ? handler.call : handler.call(args)
|
195
|
+
|
196
|
+
result = args[:result]; @processed[0], @processed[1] = uMsg, result
|
197
|
+
} if handlers[Windows.HIWORD(wParam)]
|
198
|
+
end
|
199
|
+
|
200
|
+
if uMsg == Windows::WM_NOTIFY && (handlers = @notifies[(nmh = Windows::NMHDR.new(FFI::Pointer.new(lParam)))[:idFrom]])
|
201
|
+
args ||= self.class.crackMessage(hwnd, uMsg, wParam, lParam)
|
202
|
+
|
203
|
+
handlers[:all].each { |handler|
|
204
|
+
(handler.arity == 0) ? handler.call : handler.call(args)
|
205
|
+
|
206
|
+
result = args[:result]; @processed[0], @processed[1] = uMsg, result
|
207
|
+
} if handlers[:all]
|
208
|
+
|
209
|
+
handlers[nmh[:code]].each { |handler|
|
210
|
+
(handler.arity == 0) ? handler.call : handler.call(args)
|
211
|
+
|
212
|
+
result = args[:result]; @processed[0], @processed[1] = uMsg, result
|
213
|
+
} if handlers[nmh[:code]]
|
214
|
+
end
|
215
|
+
|
216
|
+
result
|
217
|
+
end
|
218
|
+
|
219
|
+
def onMessage(msg, &block)
|
220
|
+
((@messages ||= {})[Fzeet.constant(msg, :wm_)] ||= []) << block
|
221
|
+
|
222
|
+
self
|
223
|
+
end
|
224
|
+
|
225
|
+
def onCommand(cmd, notification = :all, &block)
|
226
|
+
notification = Fzeet.constant(notification, *self[cmd].class::Prefix[:notification]) unless
|
227
|
+
notification.kind_of?(Integer) || notification == :all
|
228
|
+
|
229
|
+
(((@commands ||= {})[Command[cmd]] ||= {})[notification] ||= []) << block
|
230
|
+
|
231
|
+
self
|
232
|
+
end
|
233
|
+
|
234
|
+
def onNotify(cmd, notification = :all, &block)
|
235
|
+
notification = Fzeet.constant(notification, *self[cmd].class::Prefix[:notification]) unless
|
236
|
+
notification.kind_of?(Integer) || notification == :all
|
237
|
+
|
238
|
+
(((@notifies ||= {})[Command[cmd]] ||= {})[notification] ||= []) << block
|
239
|
+
|
240
|
+
self
|
241
|
+
end
|
242
|
+
|
243
|
+
def on(*args, &block)
|
244
|
+
case args.length
|
245
|
+
when 1; onMessage(*args, &block)
|
246
|
+
when 2, 3
|
247
|
+
case Fzeet.constant(args.shift, :wm_)
|
248
|
+
when Windows::WM_COMMAND; onCommand(*args, &block)
|
249
|
+
when Windows::WM_NOTIFY; onNotify(*args, &block)
|
250
|
+
else raise ArgumentError
|
251
|
+
end
|
252
|
+
else raise ArgumentError
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require_relative 'WindowCommon'
|
2
|
+
|
3
|
+
module Fzeet
|
4
|
+
Dialog = BasicWindow['Fzeet.Dialog',
|
5
|
+
wndProc: BasicWindow::DialogProc
|
6
|
+
]
|
7
|
+
|
8
|
+
class Dialog
|
9
|
+
def initialize(parent, opts = {}, &block)
|
10
|
+
@parent = parent
|
11
|
+
|
12
|
+
_opts = {
|
13
|
+
xstyle: [:noparentnotify],
|
14
|
+
caption: Application.name,
|
15
|
+
style: [:sysmenu, :visible],
|
16
|
+
x: 0,
|
17
|
+
y: 0,
|
18
|
+
width: 200,
|
19
|
+
height: 150,
|
20
|
+
modal: false
|
21
|
+
}
|
22
|
+
badopts = opts.keys - _opts.keys; raise "Bad option(s): #{badopts.join(', ')}." unless badopts.empty?
|
23
|
+
_opts.merge!(opts)
|
24
|
+
|
25
|
+
_opts[:xstyle] = Fzeet.flags(_opts[:xstyle], *self.class::Prefix[:xstyle])
|
26
|
+
_opts[:caption] = _opts[:caption].to_s
|
27
|
+
_opts[:style] = Fzeet.flags(_opts[:style], *self.class::Prefix[:style])
|
28
|
+
|
29
|
+
@messages ||= {}
|
30
|
+
@commands ||= {}
|
31
|
+
@notifies ||= {}
|
32
|
+
|
33
|
+
@processed = [0, 0]
|
34
|
+
|
35
|
+
dt = Windows::DLGTEMPLATE.new
|
36
|
+
|
37
|
+
dt[:style] = _opts[:style]
|
38
|
+
dt[:dwExtendedStyle] = _opts[:xstyle]
|
39
|
+
dt[:x] = _opts[:x]
|
40
|
+
dt[:y] = _opts[:y]
|
41
|
+
dt[:cx] = _opts[:width]
|
42
|
+
dt[:cy] = _opts[:height]
|
43
|
+
|
44
|
+
on(:initdialog) { self.text = _opts[:caption] }
|
45
|
+
|
46
|
+
on(:initdialog, &block) if block
|
47
|
+
|
48
|
+
if _opts[:modal]
|
49
|
+
@result = Windows.DetonateLastError([-1, 0], :DialogBoxIndirectParam,
|
50
|
+
Windows.GetModuleHandle(nil),
|
51
|
+
dt,
|
52
|
+
@parent && @parent.handle,
|
53
|
+
BasicWindow::DialogProc,
|
54
|
+
object_id
|
55
|
+
)
|
56
|
+
else
|
57
|
+
@handle = Windows.DetonateLastError(FFI::Pointer::NULL, :CreateDialogIndirectParam,
|
58
|
+
Windows.GetModuleHandle(nil),
|
59
|
+
dt,
|
60
|
+
@parent && @parent.handle,
|
61
|
+
BasicWindow::DialogProc,
|
62
|
+
object_id
|
63
|
+
)
|
64
|
+
|
65
|
+
self.dialog = true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def end(result) Windows.DetonateLastError(0, :EndDialog, @handle, Command[result]); self end
|
70
|
+
|
71
|
+
def result; DialogResult.new(@result) end
|
72
|
+
end
|
73
|
+
end
|
data/lib/fzeet/common.rb
CHANGED
@@ -0,0 +1,186 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Fzeet
|
4
|
+
module Windows
|
5
|
+
ffi_lib 'ole32'
|
6
|
+
ffi_convention :stdcall
|
7
|
+
|
8
|
+
S_OK = 0
|
9
|
+
|
10
|
+
E_NOTIMPL = 0x80004001 - 0x1_0000_0000
|
11
|
+
E_NOINTERFACE = 0x80004002 - 0x1_0000_0000
|
12
|
+
|
13
|
+
def SUCCEEDED(hr) hr >= 0 end
|
14
|
+
def FAILED(hr) hr < 0 end
|
15
|
+
|
16
|
+
module_function \
|
17
|
+
:SUCCEEDED,
|
18
|
+
:FAILED
|
19
|
+
|
20
|
+
class GUID < FFI::Struct
|
21
|
+
layout \
|
22
|
+
:Data1, :ulong,
|
23
|
+
:Data2, :ushort,
|
24
|
+
:Data3, :ushort,
|
25
|
+
:Data4, [:uchar, 8]
|
26
|
+
|
27
|
+
def self.[](s)
|
28
|
+
raise 'Bad GUID format.' unless s =~ /^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$/i
|
29
|
+
|
30
|
+
new.tap { |guid|
|
31
|
+
guid[:Data1] = s[0, 8].to_i(16)
|
32
|
+
guid[:Data2] = s[9, 4].to_i(16)
|
33
|
+
guid[:Data3] = s[14, 4].to_i(16)
|
34
|
+
guid[:Data4][0] = s[19, 2].to_i(16)
|
35
|
+
guid[:Data4][1] = s[21, 2].to_i(16)
|
36
|
+
s[24, 12].split('').each_slice(2).with_index { |a, i|
|
37
|
+
guid[:Data4][i + 2] = a.join('').to_i(16)
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
CLSCTX_INPROC_SERVER = 0x1
|
44
|
+
|
45
|
+
attach_function :CoCreateInstance, [:pointer, :pointer, :ulong, :pointer, :pointer], :long
|
46
|
+
|
47
|
+
module COM
|
48
|
+
module Interface
|
49
|
+
def self.[](*args)
|
50
|
+
spec, iid, *ifaces = args.reverse
|
51
|
+
|
52
|
+
spec.each { |name, signature| signature[0].unshift(:pointer) }
|
53
|
+
|
54
|
+
Class.new(FFI::Struct) { |iface|
|
55
|
+
iface.const_set(:IID, iid)
|
56
|
+
|
57
|
+
iface.const_set(:VTBL, Class.new(FFI::Struct) { |vtbl|
|
58
|
+
vtbl.const_set(:SPEC, Hash[(ifaces.map { |iface| iface::VTBL::SPEC.to_a } << spec.to_a).flatten(1)])
|
59
|
+
|
60
|
+
vtbl.layout \
|
61
|
+
*vtbl::SPEC.map { |name, signature| [name, callback(*signature)] }.flatten
|
62
|
+
})
|
63
|
+
|
64
|
+
iface.layout \
|
65
|
+
:lpVtbl, :pointer
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module Instance
|
71
|
+
def self.[](iface)
|
72
|
+
Class.new(iface) { |klass|
|
73
|
+
def initialize(pointer)
|
74
|
+
self.pointer = pointer
|
75
|
+
|
76
|
+
@vtbl = self.class::VTBL.new(self[:lpVtbl])
|
77
|
+
end
|
78
|
+
|
79
|
+
attr_reader :vtbl
|
80
|
+
|
81
|
+
VTBL.members.each { |name|
|
82
|
+
define_method(name) { |*args|
|
83
|
+
raise "#{self}.#{name} failed." if Windows.FAILED(result = @vtbl[name].call(self, *args)); result
|
84
|
+
}
|
85
|
+
}
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module Factory
|
91
|
+
def self.[](iface, clsid)
|
92
|
+
Class.new(iface) { |klass|
|
93
|
+
klass.const_set(:CLSID, clsid)
|
94
|
+
|
95
|
+
def initialize(opts = {})
|
96
|
+
@opts = opts
|
97
|
+
|
98
|
+
@opts[:clsctx] ||= CLSCTX_INPROC_SERVER
|
99
|
+
|
100
|
+
FFI::MemoryPointer.new(:pointer) { |ppv|
|
101
|
+
raise "CoCreateInstance failed (#{self.class})." if
|
102
|
+
Windows.FAILED(Windows.CoCreateInstance(self.class::CLSID, nil, @opts[:clsctx], self.class::IID, ppv))
|
103
|
+
|
104
|
+
self.pointer = ppv.read_pointer
|
105
|
+
}
|
106
|
+
|
107
|
+
@vtbl = self.class::VTBL.new(self[:lpVtbl])
|
108
|
+
end
|
109
|
+
|
110
|
+
attr_reader :vtbl
|
111
|
+
|
112
|
+
VTBL.members.each { |name|
|
113
|
+
define_method(name) { |*args|
|
114
|
+
raise "#{self}.#{name} failed." if Windows.FAILED(result = @vtbl[name].call(self, *args)); result
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
module Callback
|
122
|
+
def self.[](iface)
|
123
|
+
Class.new(iface) { |klass|
|
124
|
+
def initialize(opts = {})
|
125
|
+
@vtbl, @refc = self.class::VTBL.new, 1
|
126
|
+
|
127
|
+
@vtbl.members.each { |name|
|
128
|
+
@vtbl[name] = instance_variable_set("@fn#{name}",
|
129
|
+
FFI::Function.new(*@vtbl.class::SPEC[name].reverse, convention: :stdcall) { |*args|
|
130
|
+
send(name, *args[1..-1])
|
131
|
+
}
|
132
|
+
)
|
133
|
+
}
|
134
|
+
|
135
|
+
self[:lpVtbl] = @vtbl
|
136
|
+
|
137
|
+
begin
|
138
|
+
yield self
|
139
|
+
ensure
|
140
|
+
Release()
|
141
|
+
end if block_given?
|
142
|
+
end
|
143
|
+
|
144
|
+
attr_reader :vtbl, :refc
|
145
|
+
|
146
|
+
def QueryInterface(riid, ppv)
|
147
|
+
if [IID_IUnknown, self.class::IID].any? { |iid| Windows.memcmp(riid, iid, iid.size) == 0 }
|
148
|
+
ppv.write_pointer(self)
|
149
|
+
else
|
150
|
+
ppv.write_pointer(0); return E_NOINTERFACE
|
151
|
+
end
|
152
|
+
|
153
|
+
AddRef(); S_OK
|
154
|
+
end
|
155
|
+
|
156
|
+
def AddRef
|
157
|
+
@refc += 1
|
158
|
+
end
|
159
|
+
|
160
|
+
def Release
|
161
|
+
@refc -= 1
|
162
|
+
end
|
163
|
+
|
164
|
+
(VTBL.members - IUnknown::VTBL.members).each { |name|
|
165
|
+
define_method(name) { |*args|
|
166
|
+
E_NOTIMPL
|
167
|
+
}
|
168
|
+
}
|
169
|
+
}
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
IID_IUnknown = GUID['00000000-0000-0000-C000-000000000046']
|
175
|
+
|
176
|
+
IUnknown = COM::Interface[
|
177
|
+
IID_IUnknown,
|
178
|
+
|
179
|
+
QueryInterface: [[:pointer, :pointer], :long],
|
180
|
+
AddRef: [[], :ulong],
|
181
|
+
Release: [[], :ulong]
|
182
|
+
]
|
183
|
+
|
184
|
+
Unknown = COM::Instance[IUnknown]
|
185
|
+
end
|
186
|
+
end
|
data/lib/fzeet/windows/comctl.rb
CHANGED
@@ -1,59 +1 @@
|
|
1
|
-
require_relative '
|
2
|
-
|
3
|
-
module Fzeet
|
4
|
-
module Windows
|
5
|
-
ffi_lib 'comctl32'
|
6
|
-
ffi_convention :stdcall
|
7
|
-
|
8
|
-
ICC_LISTVIEW_CLASSES = 0x00000001
|
9
|
-
ICC_TREEVIEW_CLASSES = 0x00000002
|
10
|
-
ICC_BAR_CLASSES = 0x00000004
|
11
|
-
ICC_TAB_CLASSES = 0x00000008
|
12
|
-
ICC_UPDOWN_CLASS = 0x00000010
|
13
|
-
ICC_PROGRESS_CLASS = 0x00000020
|
14
|
-
ICC_HOTKEY_CLASS = 0x00000040
|
15
|
-
ICC_ANIMATE_CLASS = 0x00000080
|
16
|
-
ICC_WIN95_CLASSES = 0x000000FF
|
17
|
-
ICC_DATE_CLASSES = 0x00000100
|
18
|
-
ICC_USEREX_CLASSES = 0x00000200
|
19
|
-
ICC_COOL_CLASSES = 0x00000400
|
20
|
-
ICC_INTERNET_CLASSES = 0x00000800
|
21
|
-
ICC_PAGESCROLLER_CLASS = 0x00001000
|
22
|
-
ICC_NATIVEFNTCTL_CLASS = 0x00002000
|
23
|
-
ICC_STANDARD_CLASSES = 0x00004000
|
24
|
-
ICC_LINK_CLASS = 0x00008000
|
25
|
-
|
26
|
-
class INITCOMMONCONTROLSEX < FFI::Struct
|
27
|
-
layout \
|
28
|
-
:dwSize, :ulong,
|
29
|
-
:dwICC, :ulong
|
30
|
-
end
|
31
|
-
|
32
|
-
attach_function :InitCommonControlsEx, [:pointer], :int
|
33
|
-
|
34
|
-
BS_SPLITBUTTON = 0x0000000C
|
35
|
-
BS_DEFSPLITBUTTON = 0x0000000D
|
36
|
-
BS_COMMANDLINK = 0x0000000E
|
37
|
-
BS_DEFCOMMANDLINK = 0x0000000F
|
38
|
-
|
39
|
-
BCM_FIRST = 0x1600
|
40
|
-
BCM_GETIDEALSIZE = BCM_FIRST + 0x0001
|
41
|
-
BCM_SETIMAGELIST = BCM_FIRST + 0x0002
|
42
|
-
BCM_GETIMAGELIST = BCM_FIRST + 0x0003
|
43
|
-
BCM_SETTEXTMARGIN = BCM_FIRST + 0x0004
|
44
|
-
BCM_GETTEXTMARGIN = BCM_FIRST + 0x0005
|
45
|
-
BCM_SETDROPDOWNSTATE = BCM_FIRST + 0x0006
|
46
|
-
BCM_SETSPLITINFO = BCM_FIRST + 0x0007
|
47
|
-
BCM_GETSPLITINFO = BCM_FIRST + 0x0008
|
48
|
-
BCM_SETNOTE = BCM_FIRST + 0x0009
|
49
|
-
BCM_GETNOTE = BCM_FIRST + 0x000A
|
50
|
-
BCM_GETNOTELENGTH = BCM_FIRST + 0x000B
|
51
|
-
BCM_SETSHIELD = BCM_FIRST + 0x000C
|
52
|
-
|
53
|
-
BCN_FIRST = 0x1_0000_0000 - 1250
|
54
|
-
BCN_LAST = 0x1_0000_0000 - 1350
|
55
|
-
BCN_HOTITEMCHANGE = BCN_FIRST + 0x0001
|
56
|
-
BCN_DROPDOWN = BCN_FIRST + 0x0002
|
57
|
-
NM_GETCUSTOMSPLITRECT = BCN_FIRST + 0x0003
|
58
|
-
end
|
59
|
-
end
|
1
|
+
require_relative 'comctlbutton'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'comctlcommon'
|
2
|
+
|
3
|
+
module Fzeet
|
4
|
+
module Windows
|
5
|
+
BS_SPLITBUTTON = 0x0000000C
|
6
|
+
BS_DEFSPLITBUTTON = 0x0000000D
|
7
|
+
BS_COMMANDLINK = 0x0000000E
|
8
|
+
BS_DEFCOMMANDLINK = 0x0000000F
|
9
|
+
|
10
|
+
BCM_FIRST = 0x1600
|
11
|
+
BCM_GETIDEALSIZE = BCM_FIRST + 0x0001
|
12
|
+
BCM_SETIMAGELIST = BCM_FIRST + 0x0002
|
13
|
+
BCM_GETIMAGELIST = BCM_FIRST + 0x0003
|
14
|
+
BCM_SETTEXTMARGIN = BCM_FIRST + 0x0004
|
15
|
+
BCM_GETTEXTMARGIN = BCM_FIRST + 0x0005
|
16
|
+
BCM_SETDROPDOWNSTATE = BCM_FIRST + 0x0006
|
17
|
+
BCM_SETSPLITINFO = BCM_FIRST + 0x0007
|
18
|
+
BCM_GETSPLITINFO = BCM_FIRST + 0x0008
|
19
|
+
BCM_SETNOTE = BCM_FIRST + 0x0009
|
20
|
+
BCM_GETNOTE = BCM_FIRST + 0x000A
|
21
|
+
BCM_GETNOTELENGTH = BCM_FIRST + 0x000B
|
22
|
+
BCM_SETSHIELD = BCM_FIRST + 0x000C
|
23
|
+
|
24
|
+
BCN_FIRST = 0x1_0000_0000 - 1250
|
25
|
+
BCN_LAST = 0x1_0000_0000 - 1350
|
26
|
+
BCN_HOTITEMCHANGE = BCN_FIRST + 0x0001
|
27
|
+
BCN_DROPDOWN = BCN_FIRST + 0x0002
|
28
|
+
NM_GETCUSTOMSPLITRECT = BCN_FIRST + 0x0003
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'common'
|
2
|
+
|
3
|
+
module Fzeet
|
4
|
+
module Windows
|
5
|
+
ffi_lib 'comctl32'
|
6
|
+
ffi_convention :stdcall
|
7
|
+
|
8
|
+
ICC_LISTVIEW_CLASSES = 0x00000001
|
9
|
+
ICC_TREEVIEW_CLASSES = 0x00000002
|
10
|
+
ICC_BAR_CLASSES = 0x00000004
|
11
|
+
ICC_TAB_CLASSES = 0x00000008
|
12
|
+
ICC_UPDOWN_CLASS = 0x00000010
|
13
|
+
ICC_PROGRESS_CLASS = 0x00000020
|
14
|
+
ICC_HOTKEY_CLASS = 0x00000040
|
15
|
+
ICC_ANIMATE_CLASS = 0x00000080
|
16
|
+
ICC_WIN95_CLASSES = 0x000000FF
|
17
|
+
ICC_DATE_CLASSES = 0x00000100
|
18
|
+
ICC_USEREX_CLASSES = 0x00000200
|
19
|
+
ICC_COOL_CLASSES = 0x00000400
|
20
|
+
ICC_INTERNET_CLASSES = 0x00000800
|
21
|
+
ICC_PAGESCROLLER_CLASS = 0x00001000
|
22
|
+
ICC_NATIVEFNTCTL_CLASS = 0x00002000
|
23
|
+
ICC_STANDARD_CLASSES = 0x00004000
|
24
|
+
ICC_LINK_CLASS = 0x00008000
|
25
|
+
|
26
|
+
class INITCOMMONCONTROLSEX < FFI::Struct
|
27
|
+
layout \
|
28
|
+
:dwSize, :ulong,
|
29
|
+
:dwICC, :ulong
|
30
|
+
end
|
31
|
+
|
32
|
+
attach_function :InitCommonControlsEx, [:pointer], :int
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require_relative 'comdlgofn'
|