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.
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