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.
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
@@ -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 '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'
11
- spec = eval(IO.read('ffi-win32-extensions.gemspec'))
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
- task :default => 'test:all'
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
- require 'rubygems'
2
-
3
- Gem::Specification.new do |spec|
4
- spec.name = 'ffi-win32-extensions'
5
- spec.version = '1.0.3'
6
- spec.authors = 'Daniel J. Berger'
7
- spec.license = 'Apache 2.0'
8
- spec.email = 'djberg96@gmail.com'
9
- spec.homepage = 'http://github.com/djberg96/ffi-win32-extensions'
10
- spec.summary = 'Extends the FFI and String classes on MS Windows'
11
- spec.test_files = Dir['test/test*']
12
- spec.files = Dir['**/*'].reject{ |f| f.include?('git') }
13
- spec.cert_chain = ['certs/djberg96_pub.pem']
14
-
15
- spec.extra_rdoc_files = ['README', 'CHANGES', 'MANIFEST']
16
-
17
- spec.add_dependency('ffi')
18
- spec.add_development_dependency('test-unit')
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
@@ -1 +1 @@
1
- require_relative 'ffi/win32/extensions'
1
+ require_relative "ffi/win32/extensions"
@@ -1,96 +1,133 @@
1
- require '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
- #
21
- def read_wide_string(num_bytes = self.size)
22
- read_bytes(num_bytes).force_encoding('UTF-16LE')
23
- .encode('UTF-8', :invalid => :replace, :undef => :replace)
24
- .split(0.chr).first.force_encoding(Encoding.default_external)
25
- end
26
- end
27
-
28
- module FFI
29
- extend FFI::Library
30
-
31
- ffi_lib :kernel32
32
-
33
- # We deliberately use the ANSI version because all Ruby error messages are English.
34
- attach_function :FormatMessage, :FormatMessageA,
35
- [:ulong, :pointer, :ulong, :ulong, :pointer, :ulong, :pointer], :ulong
36
-
37
- # Returns a Windows specific error message based on +err+ prepended
38
- # with the +function+ name. Note that this does not actually raise
39
- # an error, it only returns the message.
40
- #
41
- # The message will always be English regardless of your locale.
42
- #
43
- def windows_error_message(function, err=FFI.errno)
44
- error_message = ''
45
-
46
- # ARGUMENT_ARRAY + SYSTEM + MAX_WIDTH_MASK
47
- flags = 0x00001000 | 0x00000200 | 0x000000FF
48
-
49
- # 0x0409 = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
50
- # We use English for errors because Ruby uses English for errors.
51
-
52
- FFI::MemoryPointer.new(:char, 1024) do |buf|
53
- length = FormatMessage(flags, nil, err , 0x0409, buf, buf.size, nil)
54
- error_message = function + ': ' + buf.read_string(length).strip
55
- end
56
-
57
- error_message
58
- end
59
-
60
- # Raises a Windows specific error using SystemCallError that is based on
61
- # the +err+ provided, with the message prepended with the +function+ name.
62
- #
63
- def raise_windows_error(function, err=FFI.errno)
64
- raise SystemCallError.new(windows_error_message(function, err), err)
65
- end
66
-
67
- module_function :windows_error_message
68
- module_function :raise_windows_error
69
- end
70
-
71
- class String
72
- # Convenience method for converting strings to UTF-16LE for wide character
73
- # functions that require it.
74
- #
75
- def wincode
76
- (self.tr(File::SEPARATOR, File::ALT_SEPARATOR) + 0.chr).encode('UTF-16LE')
77
- end
78
-
79
- # Read a wide character string up until the first double null, and delete
80
- # any remaining null characters. If this fails (typically because there
81
- # are only null characters) then nil is returned instead.
82
- #
83
- def wstrip
84
- self.force_encoding('UTF-16LE').encode('UTF-8', :invalid=>:replace, :undef=>:replace).
85
- split("\x00")[0].encode(Encoding.default_external)
86
- rescue
87
- nil
88
- end
89
-
90
- # Read a wide character string up until the first double null, and delete
91
- # any remaining null characters.
92
- #
93
- def read_wide_string
94
- self[/^.*?(?=\x00{2})/].delete(0.chr)
95
- end
96
- end
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