Ruby操作Windows剪贴板


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

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/280213.html

(0)
上一篇 2022年8月13日
下一篇 2022年8月13日

相关推荐

发表回复

登录后才能评论