win32-clipboard 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,29 +1,29 @@
1
- ##########################################################################
2
- # clipboard_test.rb (win32-clipboard)
3
- #
4
- # Generic test script for those without Test::Unit installed, or for
5
- # general futzing. You can run this example via the 'rake example' task.
6
- ##########################################################################
7
- require "win32/clipboard"
8
- require "pp"
9
- include Win32
10
-
11
- puts "VERSION: " + Clipboard::VERSION
12
-
13
- pp Clipboard.formats
14
- pp Clipboard.data(Clipboard::UNICODETEXT)
15
- pp Clipboard.format_available?(49161)
16
- pp Clipboard.format_name(999999999)
17
- pp Clipboard.format_available?(9999999)
18
-
19
- puts "Data was: [" + Clipboard.data + "]"
20
-
21
- Clipboard.set_data("foobar")
22
-
23
- puts "Data is now: [" + Clipboard.data + "]"
24
-
25
- puts "Number of available formats: " + Clipboard.num_formats.to_s
26
-
27
- Clipboard.empty
28
-
1
+ ##########################################################################
2
+ # clipboard_test.rb (win32-clipboard)
3
+ #
4
+ # Generic test script for those without Test::Unit installed, or for
5
+ # general futzing. You can run this example via the 'rake example' task.
6
+ ##########################################################################
7
+ require "win32/clipboard"
8
+ require "pp"
9
+ include Win32
10
+
11
+ puts "VERSION: " + Clipboard::VERSION
12
+
13
+ pp Clipboard.formats
14
+ pp Clipboard.data(Clipboard::UNICODETEXT)
15
+ pp Clipboard.format_available?(49161)
16
+ pp Clipboard.format_name(999999999)
17
+ pp Clipboard.format_available?(9999999)
18
+
19
+ puts "Data was: [" + Clipboard.data + "]"
20
+
21
+ Clipboard.set_data("foobar")
22
+
23
+ puts "Data is now: [" + Clipboard.data + "]"
24
+
25
+ puts "Number of available formats: " + Clipboard.num_formats.to_s
26
+
27
+ Clipboard.empty
28
+
29
29
  puts "Clipboard emptied"
@@ -1,412 +1,414 @@
1
- require 'windows/clipboard'
2
- require 'windows/memory'
3
- require 'windows/error'
4
- require 'windows/shell'
5
- require 'windows/library'
6
- require 'windows/window'
7
- require 'windows/msvcrt/buffer'
8
- require 'windows/gdi/metafile'
9
- require 'windows/window/message'
10
- require 'windows/window/classes'
11
-
12
- # The Win32 module serves as a namespace only.
13
- module Win32
14
- # The Clipboard class encapsulates functions that relate to the MS Windows
15
- # clipboard.
16
- class Clipboard
17
- # The Clipboard::Error class is raised if any of the Win32::Clipboard
18
- # methods should fail.
19
- class Error < StandardError; end
20
-
21
- include Windows::Clipboard
22
- include Windows::Memory
23
- include Windows::Error
24
- include Windows::Window::Classes
25
- include Windows::Window::Message
26
-
27
- extend Windows::Clipboard
28
- extend Windows::Memory
29
- extend Windows::Error
30
- extend Windows::Shell
31
- extend Windows::Library
32
- extend Windows::Window
33
- extend Windows::MSVCRT::Buffer
34
- extend Windows::GDI::MetaFile
35
- extend Windows::Window::Message
36
- extend Windows::Window::Classes
37
-
38
- # The version of this library
39
- VERSION = '0.5.2'
40
-
41
- # Clipboard formats
42
-
43
- # Text
44
- TEXT = CF_TEXT
45
- OEMTEXT = CF_OEMTEXT
46
- UNICODETEXT = CF_UNICODETEXT
47
-
48
- # Images
49
- DIB = CF_DIB
50
- BITMAP = CF_BITMAP
51
-
52
- # Metafiles
53
- ENHMETAFILE = CF_ENHMETAFILE
54
-
55
- # Files
56
- HDROP = CF_HDROP
57
-
58
- # Sets the clipboard contents to the data that you specify. You may
59
- # optionally specify a clipboard format. The default is Clipboard::TEXT.
60
- #
61
- # Example:
62
- #
63
- # # Put the string 'hello' on the clipboard
64
- # Win32::Clipboard.set_data('hello')
65
- #
66
- def self.set_data(clip_data, format = TEXT)
67
- self.open
68
- EmptyClipboard()
69
-
70
- # NULL terminate text
71
- case format
72
- when TEXT, OEMTEXT, UNICODETEXT
73
- clip_data << "\0"
74
- end
75
-
76
- # Global Allocate a movable piece of memory.
77
- hmem = GlobalAlloc(GHND, clip_data.length + 4)
78
- mem = GlobalLock(hmem)
79
- memcpy(mem, clip_data, clip_data.length)
80
-
81
- # Set the new data
82
- begin
83
- if SetClipboardData(format, hmem) == 0
84
- raise Error, "SetClipboardData() failed: " + get_last_error
85
- end
86
- ensure
87
- GlobalFree(hmem)
88
- self.close
89
- end
90
-
91
- self
92
- end
93
-
94
- # Returns the data currently in the clipboard. If +format+ is
95
- # specified, it will attempt to retrieve the data in that format. The
96
- # default is Clipboard::TEXT.
97
- #
98
- # If there is no data in the clipboard, or data is available but the
99
- # format doesn't match the data, then an empty string is returned.
100
- #
101
- # Examples:
102
- #
103
- # # Get some plain text
104
- # Win32::Clipboard.data # => e.g. 'hello'
105
- #
106
- # # Get a list of files copied from the Windows Explorer window
107
- # Win32::Clipboard.data(Clipboard::HDROP) # => ['foo.rb', 'bar.rb']
108
- #
109
- # # Get a bitmap and write it to another file
110
- # File.open('bitmap_copy', 'wb'){ |fh|
111
- # fh.write Win32::Clipboard.data(Clipboard::DIB)
112
- # }
113
- #
114
- def self.data(format = TEXT)
115
- begin
116
- self.open
117
- if IsClipboardFormatAvailable(format)
118
- handle = GetClipboardData(format)
119
- case format
120
- when TEXT, OEMTEXT, UNICODETEXT
121
- clip_data = 0.chr * GlobalSize(handle)
122
- memcpy(clip_data, handle, clip_data.size)
123
- clip_data = clip_data[ /^[^\0]*/ ]
124
- when HDROP
125
- clip_data = get_file_list(handle)
126
- when ENHMETAFILE
127
- clip_data = get_metafile_data(handle)
128
- when DIB, BITMAP
129
- clip_data = get_image_data(handle)
130
- else
131
- raise Error, 'format not supported'
132
- end
133
- else
134
- clip_data = ''
135
- end
136
- ensure
137
- self.close
138
- end
139
-
140
- clip_data
141
- end
142
-
143
- # Empties the contents of the clipboard.
144
- #
145
- def self.empty
146
- begin
147
- self.open
148
- EmptyClipboard()
149
- ensure
150
- self.close
151
- end
152
-
153
- self
154
- end
155
-
156
- # Singleton aliases
157
- #
158
- class << self
159
- alias :get_data :data
160
- alias :clear :empty
161
- end
162
-
163
- # Returns the number of different data formats currently on the
164
- # clipboard.
165
- #
166
- def self.num_formats
167
- count = 0
168
-
169
- begin
170
- self.open
171
- count = CountClipboardFormats()
172
- ensure
173
- self.close
174
- end
175
-
176
- count
177
- end
178
-
179
- # Returns whether or not +format+ (an int) is currently available.
180
- #
181
- def self.format_available?(format)
182
- IsClipboardFormatAvailable(format)
183
- end
184
-
185
- # Returns the corresponding name for the given +format_num+, or nil
186
- # if it does not exist. You cannot specify any of the predefined
187
- # clipboard formats (or nil is returned), only registered formats.
188
- #
189
- def self.format_name(format_num)
190
- val = nil
191
- buf = 0.chr * 80
192
-
193
- begin
194
- self.open
195
- if GetClipboardFormatName(format_num, buf, buf.length) != 0
196
- val = buf
197
- end
198
- ensure
199
- self.close
200
- end
201
-
202
- val.split(0.chr).first rescue nil
203
- end
204
-
205
- # Returns a hash of all the current formats, with the format number as
206
- # the key and the format name as the value for that key.
207
- #
208
- # Example:
209
- #
210
- # Win32::Clipboard.formats # => {1 => nil, 49335 => "HTML Format"}
211
- #
212
- def self.formats
213
- formats = {}
214
- format = 0
215
-
216
- begin
217
- self.open
218
- while 0 != (format = EnumClipboardFormats(format))
219
- buf = 0.chr * 80
220
- GetClipboardFormatName(format, buf, buf.length)
221
- formats[format] = buf.split(0.chr).first
222
- end
223
- ensure
224
- self.close
225
- end
226
-
227
- formats
228
- end
229
-
230
- # Registers the given +format+ (a String) as a clipboard format, which
231
- # can then be used as a valid clipboard format. Returns the integer
232
- # value of the registered format.
233
- #
234
- # If a registered format with the specified name already exists, a new
235
- # format is not registered and the return value identifies the existing
236
- # format. This enables more than one application to copy and paste data
237
- # using the same registered clipboard format. Note that the format name
238
- # comparison is case-insensitive.
239
- #
240
- # Registered clipboard formats are identified by values in the range 0xC000
241
- # through 0xFFFF.
242
- #
243
- def self.register_format(format)
244
- raise TypeError unless format.is_a?(String)
245
-
246
- format_value = RegisterClipboardFormat(format)
247
-
248
- if format_value == 0
249
- error = "RegisterClipboardFormat() call failed: " + get_last_error
250
- raise Error, error
251
- end
252
-
253
- format_value
254
- end
255
-
256
- # Sets up a notification loop that will call the provided block whenever
257
- # there's a change to the clipboard.
258
- #
259
- # Example:
260
- #
261
- # Win32::Clipboard.notify_change{
262
- # puts "There's been a change in the clipboard contents!"
263
- # }
264
- #--
265
- # We skip the first notification because the very act of attaching the
266
- # new window causes it to trigger once.
267
- #
268
- def self.notify_change(&block)
269
- name = 'ruby-clipboard-' + Time.now.to_s
270
- handle = CreateWindow('static', name, 0, 0, 0, 0, 0, 0, 0, 0, 0)
271
-
272
- @first_notify = true
273
-
274
- wnd_proc = Win32::API::Callback.new('LLLL', 'L') do |hwnd, umsg, wparam, lparam|
275
- case umsg
276
- when WM_DRAWCLIPBOARD
277
- yield unless @first_notify
278
- next_viewer = GetWindowLongPtr(hwnd, GWL_USERDATA)
279
- if next_viewer != 0
280
- PostMessage(next_viewer, umsg, wparam, lparam)
281
- end
282
- rv = 0
283
- when WM_CHANGECBCHAIN
284
- yield unless @first_notify
285
- next_viewer = lparam if next_viewer == wparam
286
- if next_viewer != 0
287
- PostMessage(next_viewer, umsg, wparam, lparam)
288
- end
289
- rv = 0
290
- else
291
- rv = DefWindowProc(hwnd, umsg, wparam, lparam)
292
- end
293
-
294
- @first_notify = false
295
-
296
- rv
297
- end
298
-
299
- old_wnd_proc = SetWindowLongPtr(handle, GWL_WNDPROC, wnd_proc.address)
300
- next_viewer = SetClipboardViewer(handle)
301
-
302
- SetWindowLongPtr(handle, GWL_USERDATA, next_viewer)
303
-
304
- msg = 0.chr * 100
305
-
306
- while true
307
- while PeekMessage(msg, handle, 0, 0, 1)
308
- TranslateMessage(msg)
309
- DispatchMessage(msg)
310
- end
311
- sleep 0.1
312
- end
313
- end
314
-
315
- private
316
-
317
- # Opens the clipboard and prevents other applications from modifying
318
- # the clipboard content until it is closed.
319
- #
320
- def self.open
321
- if 0 == OpenClipboard(nil)
322
- raise Error, "OpenClipboard() failed: " + get_last_error
323
- end
324
- end
325
-
326
- # Close the clipboard
327
- #
328
- def self.close
329
- CloseClipboard()
330
- end
331
-
332
- # Get data for enhanced metadata files
333
- #
334
- def self.get_metafile_data(handle)
335
- buf_size = GetEnhMetaFileBits(handle, 0, nil)
336
- buf = 0.chr * buf_size
337
- GetEnhMetaFileBits(handle, buf_size, buf)
338
- buf
339
- end
340
-
341
- # Get data for bitmap files
342
- #
343
- def self.get_image_data(handle)
344
- buf = nil
345
- bmi = 0.chr * 44 # BITMAPINFO
346
-
347
- begin
348
- address = GlobalLock(handle)
349
- buf_size = GlobalSize(handle)
350
-
351
- memcpy(bmi, address, bmi.length)
352
-
353
- size = bmi[0,4].unpack('L').first # biSize
354
- bit_count = bmi[14,2].unpack('S').first # biBitCount
355
- compression = bmi[16,4].unpack('L').first # biCompression
356
- size_image = bmi[20,4].unpack('L').first # biSizeImage
357
- clr_used = bmi[32,4].unpack('L').first # biClrUsed
358
-
359
- size_image = buf_size + 16 if size_image == 0
360
-
361
- # Calculate the header size
362
- case bit_count
363
- when 1
364
- table_size = 2
365
- when 4
366
- table_size = 16
367
- when 8
368
- table_size = 256
369
- when 16, 32
370
- if compression == 0
371
- table_size = clr_used
372
- elsif compression == 3
373
- table_size = 3
374
- else
375
- raise Error, "invalid bit/compression combination"
376
- end
377
- when 24
378
- table_size = clr_used
379
- else
380
- raise Error, "invalid bit count"
381
- end # case
382
-
383
- offset = 0x36 + (table_size * 4)
384
- buf = 0.chr * buf_size
385
-
386
- memcpy(buf, address, buf.size)
387
-
388
- buf = "\x42\x4D" + [size_image].pack('L') + 0.chr * 4 + [offset].pack('L') + buf
389
- ensure
390
- GlobalUnlock(handle)
391
- end
392
-
393
- buf
394
- end
395
-
396
- # Get and return an array of file names that have been copied.
397
- #
398
- def self.get_file_list(handle)
399
- array = []
400
- count = DragQueryFile(handle, 0xFFFFFFFF, nil, 0)
401
-
402
- 0.upto(count - 1){ |i|
403
- length = DragQueryFile(handle, i, nil, 0) + 1
404
- buf = 0.chr * length
405
- DragQueryFile(handle, i, buf, buf.length)
406
- array << buf.strip
407
- }
408
-
409
- array
410
- end
411
- end
412
- end
1
+ require File.join(File.dirname(__FILE__), 'windows', 'constants')
2
+ require File.join(File.dirname(__FILE__), 'windows', 'structs')
3
+ require File.join(File.dirname(__FILE__), 'windows', 'functions')
4
+
5
+ # The Win32 module serves as a namespace only.
6
+ module Win32
7
+
8
+ # The Clipboard class encapsulates functions that relate to the MS Windows clipboard.
9
+ class Clipboard
10
+
11
+ include Windows::Constants
12
+ include Windows::Structs
13
+ include Windows::Functions
14
+
15
+ extend Windows::Functions
16
+ extend Windows::Structs
17
+
18
+ # The version of this library
19
+ VERSION = '0.6.0'
20
+
21
+ # Clipboard formats
22
+
23
+ # Text
24
+
25
+ TEXT = 1
26
+ OEMTEXT = 7
27
+ UNICODETEXT = 13
28
+
29
+ # Images
30
+
31
+ DIB = 8
32
+ BITMAP = 2
33
+
34
+ # Metafiles
35
+
36
+ ENHMETAFILE = 14
37
+
38
+ # Files
39
+
40
+ HDROP = 15
41
+
42
+ # Empties the contents of the clipboard.
43
+ #
44
+ def self.empty
45
+ begin
46
+ open
47
+ unless EmptyClipboard()
48
+ raise SystemCallError.new('EmptyClipboard', FFI.errno)
49
+ end
50
+ ensure
51
+ close
52
+ end
53
+ end
54
+
55
+ class << self
56
+ alias clear empty
57
+ end
58
+
59
+ # Returns the number of different data formats currently on the clipboard.
60
+ #
61
+ def self.num_formats
62
+ CountClipboardFormats() || 0
63
+ end
64
+
65
+ # Returns whether or not +format+ (an integer) is currently available.
66
+ #
67
+ def self.format_available?(format)
68
+ IsClipboardFormatAvailable(format)
69
+ end
70
+
71
+ # Returns the data currently in the clipboard. If +format+ is
72
+ # specified, it will attempt to retrieve the data in that format. The
73
+ # default is Clipboard::TEXT.
74
+ #
75
+ # If there is no data in the clipboard, or data is available but the
76
+ # format doesn't match the data, then an empty string is returned.
77
+ #
78
+ # Examples:
79
+ #
80
+ # # Get some plain text
81
+ # Win32::Clipboard.data # => e.g. 'hello'
82
+ #
83
+ # # Get a list of files copied from the Windows Explorer window
84
+ # Win32::Clipboard.data(Clipboard::HDROP) # => ['foo.rb', 'bar.rb']
85
+ #
86
+ # # Get a bitmap and write it to another file
87
+ # File.open('bitmap_copy', 'wb'){ |fh|
88
+ # fh.write Win32::Clipboard.data(Clipboard::DIB)
89
+ # }
90
+ #
91
+ def self.data(format = TEXT)
92
+ begin
93
+ open
94
+
95
+ if IsClipboardFormatAvailable(format)
96
+ handle = GetClipboardData(format)
97
+
98
+ case format
99
+ when UNICODETEXT
100
+ size = GlobalSize(handle)
101
+ ptr = GlobalLock(handle)
102
+ clip_data = ptr.read_bytes(size).force_encoding('UTF-16LE').encode('UTF-8').strip
103
+ when TEXT, OEMTEXT
104
+ size = GlobalSize(handle)
105
+ ptr = GlobalLock(handle)
106
+
107
+ clip_data = ptr.read_bytes(size).strip
108
+
109
+ unless clip_data.ascii_only?
110
+ clip_data.force_encoding('BINARY')
111
+ end
112
+ when HDROP
113
+ clip_data = get_file_list(handle)
114
+ when ENHMETAFILE
115
+ clip_data = get_metafile_data(handle)
116
+ when DIB, BITMAP
117
+ clip_data = get_image_data(handle)
118
+ else
119
+ raise "format '#{format}' not supported"
120
+ end
121
+ else
122
+ clip_data = ''
123
+ end
124
+ ensure
125
+ close
126
+ end
127
+
128
+ clip_data
129
+ end
130
+
131
+ class << self
132
+ alias get_data data
133
+ end
134
+
135
+ # Sets the clipboard contents to the data that you specify. You may
136
+ # optionally specify a clipboard format. The default is Clipboard::TEXT.
137
+ #
138
+ # Example:
139
+ #
140
+ # # Put the string 'hello' on the clipboard
141
+ # Win32::Clipboard.set_data('hello')
142
+ #
143
+ # # Put the image test.bmp on the clipboard
144
+ # f = File.open("test.bmp", "rb")
145
+ # str = f.read
146
+ # Clipboard.set_data(str, Win32::Clipboard::DIB)
147
+ #
148
+ def self.set_data(clip_data, format = TEXT)
149
+ begin
150
+ clear
151
+ open
152
+
153
+ # NULL terminate text or strip header of bitmap file
154
+ case format
155
+ when UNICODETEXT
156
+ clip_data.encode!('UTF-16LE')
157
+ clip_data << "\0".encode('UTF-16LE')
158
+ buf = clip_data
159
+ extra = 4
160
+ when TEXT, OEMTEXT
161
+ clip_data << "\0"
162
+ buf = clip_data
163
+ extra = 4
164
+ when BITMAP, DIB
165
+ buf = clip_data[14..clip_data.length] # sizeof(BITMAPFILEHEADER) = 14
166
+ extra = 0
167
+ end
168
+
169
+ # Global Allocate a movable piece of memory.
170
+ hmem = GlobalAlloc(GHND, buf.bytesize + extra)
171
+ mptr = GlobalLock(hmem)
172
+
173
+ mptr.write_bytes(buf, 0, buf.bytesize)
174
+
175
+ # Set the new data
176
+ if SetClipboardData(format, hmem) == 0
177
+ raise SystemCallError.new('SetClipboardData', FFI.errno)
178
+ end
179
+ ensure
180
+ GlobalFree(hmem)
181
+ close
182
+ end
183
+ end
184
+
185
+ # Returns a hash of all the current formats, with the format number as
186
+ # the key and the format name as the value for that key.
187
+ #
188
+ # Example:
189
+ #
190
+ # Win32::Clipboard.formats # => {1 => nil, 49335 => "HTML Format"}
191
+ #
192
+ def self.formats
193
+ formats = {}
194
+ format = 0
195
+
196
+ begin
197
+ self.open
198
+ while 0 != (format = EnumClipboardFormats(format))
199
+ buf = FFI::MemoryPointer.new(:char, 80)
200
+ GetClipboardFormatName(format, buf, buf.size)
201
+ string = buf.read_string
202
+ formats[format] = string.empty? ? nil : string
203
+ end
204
+ ensure
205
+ self.close
206
+ end
207
+
208
+ formats
209
+ end
210
+
211
+ # Returns the corresponding name for the given +format_num+, or nil
212
+ # if it does not exist. You cannot specify any of the predefined
213
+ # clipboard formats (or nil is returned), only registered formats.
214
+ #
215
+ def self.format_name(format_num)
216
+ val = nil
217
+ buf = FFI::MemoryPointer.new(:char, 80)
218
+
219
+ begin
220
+ open
221
+
222
+ if GetClipboardFormatName(format_num, buf, buf.size) != 0
223
+ val = buf.read_string
224
+ end
225
+ ensure
226
+ close
227
+ end
228
+
229
+ val
230
+ end
231
+
232
+ # Registers the given +format+ (a String) as a clipboard format, which
233
+ # can then be used as a valid clipboard format. Returns the integer
234
+ # value of the registered format.
235
+ #
236
+ # If a registered format with the specified name already exists, a new
237
+ # format is not registered and the return value identifies the existing
238
+ # format. This enables more than one application to copy and paste data
239
+ # using the same registered clipboard format. Note that the format name
240
+ # comparison is case-insensitive.
241
+ #
242
+ # Registered clipboard formats are identified by values in the range 0xC000
243
+ # through 0xFFFF.
244
+ #
245
+ def self.register_format(format)
246
+ format_value = RegisterClipboardFormat(format)
247
+
248
+ if format_value == 0
249
+ raise SystemCallError.new('RegisterClipboardFormat', FFI.errno)
250
+ end
251
+
252
+ format_value
253
+ end
254
+
255
+ # Sets up a notification loop that will call the provided block whenever
256
+ # there's a change to the clipboard.
257
+ #
258
+ # Example:
259
+ #
260
+ # Win32::Clipboard.notify_change{
261
+ # puts "There's been a change in the clipboard contents!"
262
+ # }
263
+ #--
264
+ # We skip the first notification because the very act of attaching the
265
+ # new window causes it to trigger once.
266
+ #
267
+ def self.notify_change(&block)
268
+ name = 'ruby-clipboard-' + Time.now.to_s
269
+ handle = CreateWindowEx(0, 'static', name, 0, 0, 0, 0, 0, 0, 0, 0, nil)
270
+
271
+ @first_notify = true
272
+
273
+ wnd_proc = FFI::Function.new(:uintptr_t, [:uintptr_t, :uint, :uintptr_t, :uintptr_t]) do |hwnd, umsg, wparam, lparam|
274
+ case umsg
275
+ when WM_DRAWCLIPBOARD
276
+ yield unless @first_notify
277
+ next_viewer = GetWindowLongPtr(hwnd, GWL_USERDATA)
278
+ if next_viewer != 0
279
+ PostMessage(next_viewer, umsg, wparam, lparam)
280
+ end
281
+ rv = 0
282
+ when WM_CHANGECBCHAIN
283
+ yield unless @first_notify
284
+ next_viewer = lparam if next_viewer == wparam
285
+ if next_viewer != 0
286
+ PostMessage(next_viewer, umsg, wparam, lparam)
287
+ end
288
+ rv = 0
289
+ else
290
+ rv = DefWindowProc(hwnd, umsg, wparam, lparam)
291
+ end
292
+
293
+ @first_notify = false
294
+
295
+ rv
296
+ end
297
+
298
+ if SetWindowLongPtr(handle, GWL_WNDPROC, wnd_proc.address) == 0
299
+ raise SystemCallError.new('SetWindowLongPtr', FFI.errno)
300
+ end
301
+
302
+ next_viewer = SetClipboardViewer(handle)
303
+
304
+ if next_viewer.nil?
305
+ raise SystemCallError.new('SetClipboardViewer', FFI.errno)
306
+ end
307
+
308
+ SetWindowLongPtr(handle, GWL_USERDATA, next_viewer)
309
+
310
+ msg = FFI::MemoryPointer.new(:char, 100)
311
+
312
+ while true
313
+ while PeekMessage(msg, handle, 0, 0, 1)
314
+ TranslateMessage(msg)
315
+ DispatchMessage(msg)
316
+ end
317
+ sleep 0.1
318
+ end
319
+ end
320
+
321
+ private
322
+
323
+ # Opens the clipboard and prevents other applications from modifying
324
+ # the clipboard content until it is closed.
325
+ #
326
+ def self.open
327
+ unless OpenClipboard(0)
328
+ raise SystemCallError.new('OpenClipboard', FFI.errno)
329
+ end
330
+ end
331
+
332
+ # Close the clipboard
333
+ #
334
+ def self.close
335
+ unless CloseClipboard()
336
+ raise SystemCallError.new('CloseClipboard', FFI.errno)
337
+ end
338
+ end
339
+
340
+ # Get data for enhanced metadata files
341
+ #
342
+ def self.get_metafile_data(handle)
343
+ buf_size = GetEnhMetaFileBits(handle, 0, nil)
344
+ buf = FFI::MemoryPointer.new(:char, buf_size)
345
+ GetEnhMetaFileBits(handle, buf.size, buf)
346
+ buf.read_string
347
+ end
348
+
349
+ # Get data for bitmap files
350
+ #
351
+ def self.get_image_data(handle)
352
+ buf = nil
353
+
354
+ begin
355
+ ptr = GlobalLock(handle)
356
+ buf_size = GlobalSize(handle)
357
+
358
+ bmi = BITMAPINFO.new(ptr)
359
+
360
+ size_image = bmi[:bmiHeader][:biSizeImage]
361
+ size_image = buf_size + 16 if size_image == 0
362
+
363
+ # Calculate the header size
364
+ case bmi[:bmiHeader][:biBitCount]
365
+ when 1
366
+ table_size = 2
367
+ when 4
368
+ table_size = 16
369
+ when 8
370
+ table_size = 256
371
+ when 16, 32
372
+ if bmi[:bmiHeader][:biCompression] == 0
373
+ table_size = bmi[:bmiHeader][:biClrUsed]
374
+ elsif compression == 3
375
+ table_size = 3
376
+ else
377
+ raise "invalid bit/compression combination"
378
+ end
379
+ when 24
380
+ table_size = bmi[:bmiHeader][:biClrUsed]
381
+ else
382
+ raise "invalid bit count"
383
+ end # case
384
+
385
+ # TODO: Document what's happening here.
386
+ offset = 0x36 + (table_size * 4)
387
+
388
+ buf = "\x42\x4D" + [size_image].pack('L') + 0.chr * 4 + [offset].pack('L') + ptr.read_bytes(buf_size)
389
+ ensure
390
+ GlobalUnlock(handle) if handle
391
+ end
392
+
393
+ buf
394
+ end
395
+
396
+ # Get and return an array of file names that have been copied.
397
+ #
398
+ def self.get_file_list(handle)
399
+ array = []
400
+ count = DragQueryFileA(handle, 0xFFFFFFFF, nil, 0)
401
+
402
+ 0.upto(count - 1){ |i|
403
+ length = DragQueryFileA(handle, i, nil, 0) + 1
404
+ buf = FFI::MemoryPointer.new(:char, length)
405
+ DragQueryFileA(handle, i, buf, buf.size)
406
+ array << buf.read_string
407
+ }
408
+
409
+ array
410
+ end
411
+ end
412
+ end
413
+
414
+ require File.join(File.dirname(__FILE__), 'html_clipboard')