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
|
# Copyright © 2022, 飞麦 <[email protected]>, All rights reserved.
# frozen_string_literal: true
require 'fiddle/import'
# Clipboard only for Windows, can use both in x86 and x64
# size_t and handles must use void* (NOT uint) in Fiddle to fit the pointer size of x86 or x64
# if use size_t as return value, you need add to_i to it.
module Clipboard
module_function
extend Fiddle::Importer
include Fiddle::CParser
dlload 'kernel32', 'user32'
# HWND GetClipboardOwner();
extern 'void* GetClipboardOwner()'
# HWND GetClipboardViewer();
extern 'void* GetClipboardViewer()'
# HWND GetDesktopWindow();
extern 'void* GetDesktopWindow()'
# BOOL OpenClipboard(
# _In_opt_ HWND hWndNewOwner
# );
extern 'int OpenClipboard(void*)'
# BOOL CloseClipboard();
extern 'int CloseClipboard()'
# BOOL EmptyClipboard();
extern 'int EmptyClipboard()'
# Predefined Clipboard Formats
CF_UNICODETEXT = 13
# HANDLE GetClipboardData(
# _In_ UINT uFormat
# );
extern 'void* GetClipboardData(uint)'
# HANDLE SetClipboardData(
# _In_ UINT uFormat,
# _In_opt_ HANDLE hMem
# );
extern 'void* SetClipboardData(uint, void*)'
# Global Memory Flags
GMEM_FIXED = 0x0000 # Don't use GMEM_MOVEABLE = 0x0002(need GlobalLock and GlobalUnlock)
# HGLOBAL GlobalAlloc(
# _In_ UINT uFlags,
# _In_ SIZE_T dwBytes
# );
extern 'void* GlobalAlloc(uint, void*)'
# SIZE_T GlobalSize(
# _In_ HGLOBAL hMem
# );
extern 'void* GlobalSize(void*)'
# void RtlCopyMemory(
# _In_ PVOID Destination,
# _In_ const VOID *Source,
# _In_ SIZE_T Length
# );
extern 'void RtlCopyMemory(void*, const void*, void*)'
# DWORD GetLastError();
extern 'uint GetLastError()'
def show(_str)
# puts _str # uncomment only when debug
nil
end
def clip_hwnd
hwnd = GetClipboardOwner()
show("GetClipboardOwner fail! Error=#{GetLastError()}") if hwnd.null?
hwnd.null? ? nil : hwnd
end
def view_hwnd
hwnd = GetClipboardViewer()
show("GetClipboardViewer fail! Error=#{GetLastError()}") if hwnd.null?
hwnd.null? ? nil : hwnd
end
def desk_hwnd
GetDesktopWindow()
end
def find_hwnd
clip_hwnd || view_hwnd || desk_hwnd
end
# Sometimes OpenClipboard will fail without any error, so we need retry
def open_cb
999.times do
hwnd = find_hwnd
next unless hwnd
break unless OpenClipboard(hwnd).zero?
show("OpenClipboard fail! Error=#{GetLastError()}")
end
end
def empty_cb
999.times do
break unless EmptyClipboard().zero?
show("EmptyClipboard fail! Error=#{GetLastError()}")
end
end
def close_cb
999.times do
break unless CloseClipboard().zero?
show("CloseClipboard fail! Error=#{GetLastError()}")
end
end
def move_it(text)
text_16le = "#{text}\u0000".encode(Encoding::UTF_16LE) # must append \u0000 for text
len = text_16le.bytesize
hmem = GlobalAlloc(GMEM_FIXED, len)
show("#GlobalAlloc fail! Error=#{GetLastError()}") if hmem.null?
RtlCopyMemory(hmem, text_16le, len)
hmem
end
def save_it(hmem)
hndl = SetClipboardData(CF_UNICODETEXT, hmem)
show("#SetClipboardData Error=#{GetLastError()} hmem=#{hmem.to_i} != hndl=#{hndl.to_i}") if hndl != hmem
end
def copy(text)
open_cb
empty_cb
hmem = move_it(text)
save_it(hmem)
close_cb
end
def clear
open_cb
empty_cb
close_cb
end
def load_it(hmem)
len = GlobalSize(hmem).to_i
show("GlobalSize Error=#{GetLastError()} len=#{len}") if len.zero?
len -= 2 # needn't copy tail \u0000
text = ''.encode(Encoding::UTF_16LE) * (len / 2)
RtlCopyMemory(text, hmem, len)
text.encode(Encoding::UTF_8)
end
def paste
open_cb
hmem = GetClipboardData(CF_UNICODETEXT)
text = hmem.null? ? '' : load_it(hmem)
close_cb
text
end
end
|