ffi-win32-extensions 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +31 -0
- data/CODE_OF_CONDUCT.md +1 -0
- data/Gemfile +22 -0
- data/LICENSE +201 -201
- data/MANIFEST +9 -9
- data/README +48 -48
- data/README.md +57 -0
- data/Rakefile +53 -42
- data/VERSION +1 -0
- data/ffi-win32-extensions.gemspec +19 -25
- data/lib/ffi-win32-extensions.rb +1 -1
- data/lib/ffi/win32/extensions.rb +133 -96
- data/test/test_ffi_extensions.rb +41 -41
- data/test/test_string_extensions.rb +20 -20
- metadata +15 -57
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/CHANGES +0 -13
- data/certs/djberg96_pub.pem +0 -21
- metadata.gz.sig +0 -3
data/MANIFEST
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
* ffi-win32-extensions.gemspec
|
2
|
-
* LICENSE
|
3
|
-
* MANIFEST
|
4
|
-
* Rakefile
|
5
|
-
* README
|
6
|
-
* lib/ffi-win32-extensions.rb
|
7
|
-
* lib/ffi/win32/extensions.rb
|
8
|
-
* test/test_ffi_extensions.rb
|
9
|
-
* test/test_string_extensions.rb
|
1
|
+
* ffi-win32-extensions.gemspec
|
2
|
+
* LICENSE
|
3
|
+
* MANIFEST
|
4
|
+
* Rakefile
|
5
|
+
* README
|
6
|
+
* lib/ffi-win32-extensions.rb
|
7
|
+
* lib/ffi/win32/extensions.rb
|
8
|
+
* test/test_ffi_extensions.rb
|
9
|
+
* test/test_string_extensions.rb
|
data/README
CHANGED
@@ -1,48 +1,48 @@
|
|
1
|
-
= Description
|
2
|
-
A Ruby library that adds some core FFI and String methods to supplement
|
3
|
-
development of Ruby libraries on Windows.
|
4
|
-
|
5
|
-
= Installation
|
6
|
-
gem install ffi-win32-extensions
|
7
|
-
|
8
|
-
= Details
|
9
|
-
The following FFI::MemoryPointer methods have been added:
|
10
|
-
|
11
|
-
* read_array_of_string - For reading char** types.
|
12
|
-
* read_wide_string - Similar to read_string but for wide char* types.
|
13
|
-
|
14
|
-
The following FFI module functions have been added:
|
15
|
-
|
16
|
-
* windows_error_message - A Windows specific error string using FormatMessage.
|
17
|
-
* raise_windows_error - Raises a windows specific error using windows_error_message.
|
18
|
-
|
19
|
-
The following String instance methods have been added:
|
20
|
-
|
21
|
-
* wincode - Converts a string to UTF-16LE for use in wide char functions.
|
22
|
-
* wstrip - Like String#strip, but for wide strings.
|
23
|
-
* read_wide_string - Reads a Ruby string up to the first double null.
|
24
|
-
|
25
|
-
Example:
|
26
|
-
|
27
|
-
require 'ffi/win32/extensions'
|
28
|
-
|
29
|
-
str = old_string.wincode
|
30
|
-
rv = SomeWideFunctionW(str)
|
31
|
-
|
32
|
-
unless rv == 0
|
33
|
-
FFI.raise_windows_error('SomeWindowsFunction', rv)
|
34
|
-
end
|
35
|
-
|
36
|
-
= Copyright
|
37
|
-
(C) 2016 Daniel J. Berger, All Rights Reserved
|
38
|
-
|
39
|
-
= Warranty
|
40
|
-
This package is provided "as is" and without any express or
|
41
|
-
implied warranties, including, without limitation, the implied
|
42
|
-
warranties of merchantability and fitness for a particular purpose.
|
43
|
-
|
44
|
-
= License
|
45
|
-
Apache 2.0
|
46
|
-
|
47
|
-
= Author
|
48
|
-
Daniel Berger
|
1
|
+
= Description
|
2
|
+
A Ruby library that adds some core FFI and String methods to supplement
|
3
|
+
development of Ruby libraries on Windows.
|
4
|
+
|
5
|
+
= Installation
|
6
|
+
gem install ffi-win32-extensions
|
7
|
+
|
8
|
+
= Details
|
9
|
+
The following FFI::MemoryPointer methods have been added:
|
10
|
+
|
11
|
+
* read_array_of_string - For reading char** types.
|
12
|
+
* read_wide_string - Similar to read_string but for wide char* types.
|
13
|
+
|
14
|
+
The following FFI module functions have been added:
|
15
|
+
|
16
|
+
* windows_error_message - A Windows specific error string using FormatMessage.
|
17
|
+
* raise_windows_error - Raises a windows specific error using windows_error_message.
|
18
|
+
|
19
|
+
The following String instance methods have been added:
|
20
|
+
|
21
|
+
* wincode - Converts a string to UTF-16LE for use in wide char functions.
|
22
|
+
* wstrip - Like String#strip, but for wide strings.
|
23
|
+
* read_wide_string - Reads a Ruby string up to the first double null.
|
24
|
+
|
25
|
+
Example:
|
26
|
+
|
27
|
+
require 'ffi/win32/extensions'
|
28
|
+
|
29
|
+
str = old_string.wincode
|
30
|
+
rv = SomeWideFunctionW(str)
|
31
|
+
|
32
|
+
unless rv == 0
|
33
|
+
FFI.raise_windows_error('SomeWindowsFunction', rv)
|
34
|
+
end
|
35
|
+
|
36
|
+
= Copyright
|
37
|
+
(C) 2016 Daniel J. Berger, All Rights Reserved
|
38
|
+
|
39
|
+
= Warranty
|
40
|
+
This package is provided "as is" and without any express or
|
41
|
+
implied warranties, including, without limitation, the implied
|
42
|
+
warranties of merchantability and fitness for a particular purpose.
|
43
|
+
|
44
|
+
= License
|
45
|
+
Apache 2.0
|
46
|
+
|
47
|
+
= Author
|
48
|
+
Daniel Berger
|
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# ffi-win32-extensions
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/ffi-win32-extensions)
|
4
|
+
|
5
|
+
A Ruby library that adds some core FFI and String methods to supplement development of Ruby libraries on Windows.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
```
|
10
|
+
gem install ffi-win32-extensions
|
11
|
+
```
|
12
|
+
|
13
|
+
## Details
|
14
|
+
|
15
|
+
The following FFI::MemoryPointer methods have been added:
|
16
|
+
|
17
|
+
- read_array_of_string - For reading char** types.
|
18
|
+
- read_wide_string - Similar to read_string but for wide char* types.
|
19
|
+
|
20
|
+
The following FFI module functions have been added:
|
21
|
+
|
22
|
+
- windows_error_message - A Windows specific error string using FormatMessage.
|
23
|
+
- raise_windows_error - Raises a windows specific error using windows_error_message.
|
24
|
+
|
25
|
+
The following String instance methods have been added:
|
26
|
+
|
27
|
+
- wincode - Converts a string to UTF-16LE for use in wide char functions.
|
28
|
+
- wstrip - Like String#strip, but for wide strings.
|
29
|
+
- read_wide_string - Reads a Ruby string up to the first double null.
|
30
|
+
|
31
|
+
### Example
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
require 'ffi/win32/extensions'
|
35
|
+
|
36
|
+
str = old_string.wincode rv = SomeWideFunctionW(str)
|
37
|
+
|
38
|
+
unless rv == 0
|
39
|
+
FFI.raise_windows_error('SomeWindowsFunction', rv)
|
40
|
+
end
|
41
|
+
```
|
42
|
+
|
43
|
+
## Copyright
|
44
|
+
|
45
|
+
(C) 2016 Daniel J. Berger, All Rights Reserved
|
46
|
+
|
47
|
+
## Warranty
|
48
|
+
|
49
|
+
This package is provided "as is" and without any express or implied warranties, including, without limitation, the implied warranties of merchantability and fitness for a particular purpose.
|
50
|
+
|
51
|
+
## License
|
52
|
+
|
53
|
+
Apache 2.0
|
54
|
+
|
55
|
+
## Author
|
56
|
+
|
57
|
+
Daniel Berger
|
data/Rakefile
CHANGED
@@ -1,42 +1,53 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
|
5
|
-
CLEAN.include(
|
6
|
-
|
7
|
-
namespace :gem do
|
8
|
-
desc "Create the ffi-win32-extensions gem"
|
9
|
-
task :
|
10
|
-
require
|
11
|
-
spec = eval(IO.read(
|
12
|
-
spec.signing_key = File.join(Dir.home,
|
13
|
-
Gem::Package.build(spec, true)
|
14
|
-
end
|
15
|
-
|
16
|
-
desc "Install the win32-clipboard library"
|
17
|
-
task :
|
18
|
-
file = Dir["*.gem"].first
|
19
|
-
sh "gem install -l #{file}"
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
namespace :test do
|
24
|
-
Rake::TestTask.new(:all) do |t|
|
25
|
-
t.warning = true
|
26
|
-
t.verbose = true
|
27
|
-
end
|
28
|
-
|
29
|
-
Rake::TestTask.new(:ffi) do |t|
|
30
|
-
t.warning = true
|
31
|
-
t.verbose = true
|
32
|
-
t.test_files = FileList[
|
33
|
-
end
|
34
|
-
|
35
|
-
Rake::TestTask.new(:string) do |t|
|
36
|
-
t.warning = true
|
37
|
-
t.verbose = true
|
38
|
-
t.test_files = FileList[
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
|
1
|
+
require "rake"
|
2
|
+
require "rake/clean"
|
3
|
+
require "rake/testtask"
|
4
|
+
|
5
|
+
CLEAN.include("**/*.gem", "**/*.rbc")
|
6
|
+
|
7
|
+
namespace :gem do
|
8
|
+
desc "Create the ffi-win32-extensions gem"
|
9
|
+
task create: [:clean] do
|
10
|
+
require "rubygems/package" unless defined?(Gem::Package)
|
11
|
+
spec = eval(IO.read("ffi-win32-extensions.gemspec")) # rubocop: disable Security/Eval
|
12
|
+
spec.signing_key = File.join(Dir.home, ".ssh", "gem-private_key.pem")
|
13
|
+
Gem::Package.build(spec, true)
|
14
|
+
end
|
15
|
+
|
16
|
+
desc "Install the win32-clipboard library"
|
17
|
+
task install: [:create] do
|
18
|
+
file = Dir["*.gem"].first
|
19
|
+
sh "gem install -l #{file}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
namespace :test do
|
24
|
+
Rake::TestTask.new(:all) do |t|
|
25
|
+
t.warning = true
|
26
|
+
t.verbose = true
|
27
|
+
end
|
28
|
+
|
29
|
+
Rake::TestTask.new(:ffi) do |t|
|
30
|
+
t.warning = true
|
31
|
+
t.verbose = true
|
32
|
+
t.test_files = FileList["test/test_ffi_extensions.rb"]
|
33
|
+
end
|
34
|
+
|
35
|
+
Rake::TestTask.new(:string) do |t|
|
36
|
+
t.warning = true
|
37
|
+
t.verbose = true
|
38
|
+
t.test_files = FileList["test/test_string_extensions.rb"]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
begin
|
43
|
+
require "chefstyle"
|
44
|
+
require "rubocop/rake_task"
|
45
|
+
desc "Run Chefstyle tests"
|
46
|
+
RuboCop::RakeTask.new(:style) do |task|
|
47
|
+
task.options += ["--display-cop-names", "--no-color"]
|
48
|
+
end
|
49
|
+
rescue LoadError
|
50
|
+
puts "chefstyle gem is not installed. bundle install first to make sure all dependencies are installed."
|
51
|
+
end
|
52
|
+
|
53
|
+
task default: "test:all"
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.4
|
@@ -1,25 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
spec.
|
5
|
-
spec.
|
6
|
-
spec.
|
7
|
-
spec.
|
8
|
-
spec.
|
9
|
-
spec.
|
10
|
-
spec.
|
11
|
-
|
12
|
-
spec.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
spec.description = <<-EOF
|
21
|
-
The ffi-win32-extensions library adds additional methods to the FFI
|
22
|
-
and String classes to aid in the development of FFI based libraries
|
23
|
-
on MS Windows.
|
24
|
-
EOF
|
25
|
-
end
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = "ffi-win32-extensions"
|
3
|
+
spec.version = "1.0.4"
|
4
|
+
spec.authors = "Daniel J. Berger"
|
5
|
+
spec.license = "Apache 2.0"
|
6
|
+
spec.email = "djberg96@gmail.com"
|
7
|
+
spec.homepage = "http://github.com/chef/ffi-win32-extensions"
|
8
|
+
spec.summary = "Extends the FFI and String classes on MS Windows"
|
9
|
+
spec.test_files = Dir["test/test*"]
|
10
|
+
spec.files = Dir["**/*"].reject { |f| f.include?("git") }
|
11
|
+
|
12
|
+
spec.add_dependency("ffi")
|
13
|
+
|
14
|
+
spec.description = <<-EOF
|
15
|
+
The ffi-win32-extensions library adds additional methods to the FFI
|
16
|
+
and String classes to aid in the development of FFI based libraries
|
17
|
+
on MS Windows.
|
18
|
+
EOF
|
19
|
+
end
|
data/lib/ffi-win32-extensions.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require_relative
|
1
|
+
require_relative "ffi/win32/extensions"
|
data/lib/ffi/win32/extensions.rb
CHANGED
@@ -1,96 +1,133 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
class FFI::Pointer
|
4
|
-
# Returns an array of strings for char** types.
|
5
|
-
#
|
6
|
-
def read_array_of_string
|
7
|
-
elements = []
|
8
|
-
|
9
|
-
loc = self
|
10
|
-
|
11
|
-
until (
|
12
|
-
elements << element.read_string
|
13
|
-
loc += FFI::Type::POINTER.size
|
14
|
-
end
|
15
|
-
|
16
|
-
elements
|
17
|
-
end
|
18
|
-
|
19
|
-
# Read +num_bytes+ from a wide character string pointer.
|
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
|
-
end
|
96
|
-
|
1
|
+
require "ffi" unless defined?(FFI)
|
2
|
+
|
3
|
+
class FFI::Pointer
|
4
|
+
# Returns an array of strings for char** types.
|
5
|
+
#
|
6
|
+
def read_array_of_string
|
7
|
+
elements = []
|
8
|
+
|
9
|
+
loc = self
|
10
|
+
|
11
|
+
until (element = loc.read_pointer).null?
|
12
|
+
elements << element.read_string
|
13
|
+
loc += FFI::Type::POINTER.size
|
14
|
+
end
|
15
|
+
|
16
|
+
elements
|
17
|
+
end
|
18
|
+
|
19
|
+
# Read +num_bytes+ from a wide character string pointer.
|
20
|
+
# If this fails (typically because there are only null characters)
|
21
|
+
# then an empty string is returned instead.
|
22
|
+
#
|
23
|
+
def read_wide_string(num_bytes = size)
|
24
|
+
read_bytes(num_bytes).force_encoding("UTF-16LE")
|
25
|
+
.encode("UTF-8", invalid: :replace, undef: :replace)
|
26
|
+
.split(0.chr).first.force_encoding(Encoding.default_external)
|
27
|
+
rescue
|
28
|
+
""
|
29
|
+
end
|
30
|
+
|
31
|
+
# Read null-terminated Unicode strings.
|
32
|
+
#
|
33
|
+
def read_wstring(num_wchars = nil)
|
34
|
+
if num_wchars.nil?
|
35
|
+
# Find the length of the string
|
36
|
+
length = 0
|
37
|
+
last_char = nil
|
38
|
+
while last_char != "\000\000"
|
39
|
+
length += 1
|
40
|
+
last_char = get_bytes(0, length * 2)[-2..-1]
|
41
|
+
end
|
42
|
+
|
43
|
+
num_wchars = length
|
44
|
+
end
|
45
|
+
|
46
|
+
wide_to_utf8(get_bytes(0, num_wchars * 2))
|
47
|
+
end
|
48
|
+
|
49
|
+
def wide_to_utf8(wstring)
|
50
|
+
# ensure it is actually UTF-16LE
|
51
|
+
# Ruby likes to mark binary data as ASCII-8BIT
|
52
|
+
wstring = wstring.force_encoding("UTF-16LE")
|
53
|
+
|
54
|
+
# encode it all as UTF-8 and remove trailing CRLF and NULL characters
|
55
|
+
wstring.encode("UTF-8").strip
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_utf16string
|
59
|
+
offset = 0
|
60
|
+
offset += 2 while get_bytes(offset, 2) != "\x00\x00"
|
61
|
+
get_bytes(0, offset).force_encoding("utf-16le").encode("utf-8")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
module FFI
|
66
|
+
extend FFI::Library
|
67
|
+
|
68
|
+
ffi_lib :kernel32
|
69
|
+
|
70
|
+
# We deliberately use the ANSI version because all Ruby error messages are English.
|
71
|
+
attach_function :FormatMessage, :FormatMessageA,
|
72
|
+
%i{ulong pointer ulong ulong pointer ulong pointer}, :ulong
|
73
|
+
|
74
|
+
# Returns a Windows specific error message based on +err+ prepended
|
75
|
+
# with the +function+ name. Note that this does not actually raise
|
76
|
+
# an error, it only returns the message.
|
77
|
+
#
|
78
|
+
# The message will always be English regardless of your locale.
|
79
|
+
#
|
80
|
+
def windows_error_message(function, err = FFI.errno)
|
81
|
+
error_message = ""
|
82
|
+
|
83
|
+
# ARGUMENT_ARRAY + SYSTEM + MAX_WIDTH_MASK
|
84
|
+
flags = 0x00001000 | 0x00000200 | 0x000000FF
|
85
|
+
|
86
|
+
# 0x0409 = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
|
87
|
+
# We use English for errors because Ruby uses English for errors.
|
88
|
+
|
89
|
+
FFI::MemoryPointer.new(:char, 1024) do |buf|
|
90
|
+
length = FormatMessage(flags, nil, err , 0x0409, buf, buf.size, nil)
|
91
|
+
error_message = function + ": " + buf.read_string(length).strip
|
92
|
+
end
|
93
|
+
|
94
|
+
error_message
|
95
|
+
end
|
96
|
+
|
97
|
+
# Raises a Windows specific error using SystemCallError that is based on
|
98
|
+
# the +err+ provided, with the message prepended with the +function+ name.
|
99
|
+
#
|
100
|
+
def raise_windows_error(function, err = FFI.errno)
|
101
|
+
raise SystemCallError.new(windows_error_message(function, err), err)
|
102
|
+
end
|
103
|
+
|
104
|
+
module_function :windows_error_message
|
105
|
+
module_function :raise_windows_error
|
106
|
+
end
|
107
|
+
|
108
|
+
class String
|
109
|
+
# Convenience method for converting strings to UTF-16LE for wide character
|
110
|
+
# functions that require it.
|
111
|
+
#
|
112
|
+
def wincode
|
113
|
+
(tr(File::SEPARATOR, File::ALT_SEPARATOR) + 0.chr).encode("UTF-16LE")
|
114
|
+
end
|
115
|
+
|
116
|
+
# Read a wide character string up until the first double null, and delete
|
117
|
+
# any remaining null characters. If this fails (typically because there
|
118
|
+
# are only null characters) then nil is returned instead.
|
119
|
+
#
|
120
|
+
def wstrip
|
121
|
+
force_encoding("UTF-16LE").encode("UTF-8", invalid: :replace, undef: :replace)
|
122
|
+
.split("\x00")[0].encode(Encoding.default_external)
|
123
|
+
rescue
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
# Read a wide character string up until the first double null, and delete
|
128
|
+
# any remaining null characters.
|
129
|
+
#
|
130
|
+
def read_wide_string
|
131
|
+
self[/^.*?(?=\x00{2})/].delete(0.chr)
|
132
|
+
end
|
133
|
+
end
|