ffi-win32-extensions 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/ffi-win32-extensions.svg)](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
|