win32-pipe 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES ADDED
@@ -0,0 +1,34 @@
1
+ == 0.2.0 - 28-May-2008
2
+ * Now pure Ruby.
3
+ * Major interface change. Pipe::Server.new and Pipe::Client.new replace the
4
+ Pipe.new_server and Pipe.new_client methods, respectively.
5
+ * An optional 3rd argument, the open mode, is now accepted which allows finer
6
+ control over how pipes are created.
7
+ * Several pipe mode and open mode constants were added.
8
+ * The asynchronous pipe server actually works now.
9
+ * Added the Pipe#name method.
10
+ * Added the Pipe#asynchronous? method.
11
+ * Added the Pipe#size method as an alias for Pipe#length.
12
+ * Added a Rakefile with tasks for testing and installation.
13
+ * Added a gemspec and uploaded a gem file to RubyForge.
14
+ * Merged the doc files into the README and/or replaced them with inlined
15
+ comments that are RDoc friendly.
16
+
17
+ == 0.1.2 - 1-Mar-2005
18
+ * Moved the 'examples' directory to the toplevel directory.
19
+ * Made the CHANGES and README files rdoc friendly.
20
+
21
+ == 0.1.1 - 25-Aug-2004
22
+ * Added many more tests to the test suite.
23
+ * Moved the example programs to doc/examples.
24
+ * Fixed minor bugs in the asynchronous client and server test programs.
25
+ * Removed the pipe.html file. You can generate your own html documentation
26
+ using rd2 on the pipe.rd file.
27
+
28
+ == 0.1.0 - 13-Feb-2004
29
+ * Asynchronous support added (thanks Park Heesob)
30
+ * Sample test programs added. See files under 'test'.
31
+ * Documentation updates.
32
+
33
+ == 0.0.1 - 20-Nov-2003
34
+ * Initial release
data/MANIFEST ADDED
@@ -0,0 +1,15 @@
1
+ * MANIFEST
2
+ * README
3
+ * CHANGES
4
+ * Rakefile
5
+ * win32-pipe.gemspec
6
+ * examples/test_server.rb
7
+ * examples/test_server_async.rb
8
+ * examples/test_client.rb
9
+ * examples/test_client_async.rb
10
+ * lib/win32/pipe.rb
11
+ * lib/win32/pipe/client.rb
12
+ * lib/win32/pipe/server.rb
13
+ * test/tc_pipe.rb
14
+ * test/tc_pipe_client.rb
15
+ * test/tc_pipe_server.rb
data/README ADDED
@@ -0,0 +1,56 @@
1
+ = Description
2
+ A Ruby interface for named pipes on Windows.
3
+
4
+ = Prerequisites
5
+ * windows-pr 0.8.5 or later
6
+
7
+ = Installation
8
+ == Local
9
+ rake install (non-gem) or rake install_gem (gem)
10
+
11
+ == Remote
12
+ gem install win32-pipe
13
+
14
+ = Synopsis
15
+ require 'win32/pipe'
16
+ include Win32
17
+
18
+ # In server.rb
19
+ pipe_server = Pipe::Server.new("foo_pipe")
20
+ pipe_server.connect
21
+ data = pipe_server.read
22
+ puts "Got #{data} from client"
23
+ pipe_server.close
24
+
25
+ # In client.rb (run from a different shell)
26
+ pipe_client = Pipe::Client.new("foo_pipe")
27
+ pipe_client.write("Hello World")
28
+ pipe_client.close
29
+
30
+ = What's a named pipe?
31
+ A pipe with a name - literally. In practice, it will feel more like a cross
32
+ between a socket and a pipe. At least, it does to me.
33
+
34
+ = What good is it?
35
+ My hope is that it can be used in certain circumstances where a fork might
36
+ be desirable, but which is not possible on Windows. It could also be handy
37
+ for the traditional "piping data to a server" usage. And if you come up
38
+ with anything cool, please let us all know!
39
+
40
+ = Future Plans
41
+ * Add transactions
42
+
43
+ = License
44
+ Ruby's
45
+
46
+ = Warranty
47
+ This package is provided "as is" and without any express or
48
+ implied warranties, including, without limitation, the implied
49
+ warranties of merchantability and fitness for a particular purpose.
50
+
51
+ = Copyright
52
+ (C) 2003-2008, Daniel J. Berger, All Rights Reserved.
53
+
54
+ = Authors
55
+ Daniel Berger
56
+ Park Heesob
data/Rakefile ADDED
@@ -0,0 +1,65 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'rbconfig'
5
+ include Config
6
+
7
+ desc 'Install the win32-pipe library (non-gem)'
8
+ task :install do
9
+ sitelibdir = CONFIG['sitelibdir']
10
+
11
+ pipe_installdir = File.join(sitelibdir, 'win32')
12
+ sub_installdir = File.join(sitelibdir, 'win32', 'pipe')
13
+
14
+ pipe_file = File.join('lib', 'win32', 'pipe.rb')
15
+ client_file = File.join('lib', 'win32', 'pipe', 'client.rb')
16
+ server_file = File.join('lib', 'win32', 'pipe', 'server.rb')
17
+
18
+ FileUtils.mkdir_p(sub_installdir)
19
+
20
+ FileUtils.cp(pipe_file, pipe_installdir, :verbose => true)
21
+ FileUtils.cp(client_file, sub_installdir, :verbose => true)
22
+ FileUtils.cp(server_file, sub_installdir, :verbose => true)
23
+ end
24
+
25
+ desc 'Install the win32-pipe library'
26
+ task :install_c => [:build] do
27
+ Dir.chdir('ext'){
28
+ sh 'nmake install'
29
+ }
30
+ end
31
+
32
+ desc "Clean any build files for win32-pipe"
33
+ task :clean do
34
+ Dir.chdir('ext') do
35
+ if File.exists?('pipe.so') ||
36
+ File.exists?('win32/pipe.so')
37
+ then
38
+ sh 'nmake distclean'
39
+ rm 'win32/pipe.so' if File.exists?('win32/pipe.so')
40
+ end
41
+ end
42
+ end
43
+
44
+ desc "Build win32-pipe (but don't install it)"
45
+ task :build => [:clean] do
46
+ Dir.chdir('ext') do
47
+ ruby 'extconf.rb'
48
+ sh 'nmake'
49
+ mv 'pipe.so', 'win32' # For the test suite
50
+ end
51
+ end
52
+
53
+ desc "Run the sample program"
54
+ task :example do |t|
55
+ Dir.chdir('examples'){
56
+ sh 'ruby test.rb'
57
+ }
58
+ end
59
+
60
+ Rake::TestTask.new('test') do |test|
61
+ test.libs << 'lib/win32'
62
+ test.libs << 'lib/win32/pipe'
63
+ test.test_files = FileList['test/tc*']
64
+ test.warning = true
65
+ end
@@ -0,0 +1,30 @@
1
+ #########################################################################
2
+ # test_client.rb
3
+ #
4
+ # Simple client test. Be sure to start the server first in a separate
5
+ # terminal. You can run this example via the 'rake example_client' task.
6
+ #
7
+ # Modify this code as you see fit.
8
+ #########################################################################
9
+ require 'win32/pipe'
10
+ include Win32
11
+
12
+ Thread.new { loop { sleep 0.01 } } # Allow Ctrl-C
13
+
14
+ puts "VERSION: " + Pipe::VERSION
15
+
16
+ # Block form
17
+ Pipe::Client.new('foo') do |pipe|
18
+ puts "Connected..."
19
+ pipe.write("Ruby rocks!")
20
+ data = pipe.read
21
+ puts "Got [#{data}] back from server"
22
+ end
23
+
24
+ # Non-block form
25
+ #pclient = Pipe::Client.new('foo')
26
+ #puts "Connected..."
27
+ #pclient.write("Ruby rocks!")
28
+ #data = pclient.read
29
+ #puts "Got [#{data}] back from server"
30
+ #pclient.close
@@ -0,0 +1,82 @@
1
+ #########################################################################
2
+ # test_client_async.rb
3
+ #
4
+ # Simple client test. Be sure to start the server first in a separate
5
+ # terminal. You can run this example via the 'rake example_async_client'
6
+ # task.
7
+ #########################################################################
8
+ require 'win32/pipe'
9
+ include Win32
10
+
11
+ puts "VERSION: " + Pipe::VERSION
12
+
13
+ Thread.new { loop { sleep 0.01 } } # Allow Ctrl-C
14
+
15
+ CONNECTING_STATE = 0
16
+ READING_STATE = 1
17
+ WRITING_STATE = 2
18
+
19
+ class MyPipe < Pipe::Client
20
+ def read_complete
21
+ puts "read_complete"
22
+ puts "Got [#{buffer}] back from server"
23
+ @state = WRITING_STATE
24
+ end
25
+
26
+ def write_complete
27
+ puts "write_complete"
28
+ @state = READING_STATE
29
+ end
30
+
31
+ def mainloop
32
+ @state = WRITING_STATE
33
+ while true
34
+ if wait(1) # wait for 1 second
35
+ if pending? # IO is pending
36
+ case @state
37
+ when READING_STATE
38
+ if transferred == 0
39
+ reconnect
40
+ break
41
+ end
42
+ read_complete
43
+ break
44
+ when WRITING_STATE
45
+ if transferred != length
46
+ reconnect
47
+ break
48
+ end
49
+ write_complete
50
+ end
51
+ end
52
+
53
+ case @state
54
+ when READING_STATE
55
+ if read
56
+ if not pending?
57
+ read_complete
58
+ break
59
+ end
60
+ end
61
+ when WRITING_STATE
62
+ if write("Ruby rocks!")
63
+ if not pending?
64
+ write_complete
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ sleep(1)
71
+ puts "pipe client is running"
72
+ end
73
+ end
74
+ end
75
+
76
+ flags = Pipe::DEFAULT_OPEN_MODE | Pipe::OVERLAPPED
77
+
78
+ MyPipe.new('foo', nil, flags) do |client|
79
+ puts "Connected..."
80
+ client.mainloop
81
+ end
82
+
@@ -0,0 +1,33 @@
1
+ #########################################################################
2
+ # test_server.rb
3
+ #
4
+ # A simple named pipe server. Start this up in its own terminal window.
5
+ # You may have to use the task manager to kill it if you don't connect
6
+ # with the test client program.
7
+ #
8
+ # You can start this server with the 'rake example_server' task. Modify
9
+ # this code as you see fit.
10
+ #########################################################################
11
+ require 'win32/pipe'
12
+ include Win32
13
+
14
+ Thread.new { loop { sleep 0.01 } } # Allow Ctrl-C
15
+
16
+ puts "VERSION: " + Pipe::VERSION
17
+
18
+ # Block form
19
+ Pipe::Server.new('foo') do |pipe|
20
+ pipe.connect
21
+ data = pipe.read
22
+ puts "Got [#{data}]"
23
+ pipe.write "Thanks for the data!"
24
+ end
25
+
26
+ # Non-block form
27
+ #pserver = Pipe::Server.new('foo')
28
+ #pserver.connect # put server in wait connect
29
+ #data = pserver.read
30
+ #puts "Got [#{data}]"
31
+ #pserver.write("Thanks for the data!")
32
+ #pserver.disconnect
33
+ #pserver.close
@@ -0,0 +1,100 @@
1
+ #######################################################################
2
+ # test_server_async.rb
3
+ #
4
+ # A simple, asynchronous named pipe server. Start this up in its own
5
+ # terminal window. You can run this program via the
6
+ # 'rake example_async_server' task.
7
+ #######################################################################
8
+ require 'win32/pipe'
9
+ include Win32
10
+
11
+ puts "VERSION: " + Pipe::VERSION
12
+
13
+ Thread.new { loop { sleep 0.01 } } # Allow Ctrl-C
14
+
15
+ CONNECTING_STATE = 0
16
+ READING_STATE = 1
17
+ WRITING_STATE = 2
18
+
19
+ class MyPipe < Pipe::Server
20
+ def connected
21
+ puts "connected"
22
+ @state = READING_STATE
23
+ end
24
+
25
+ def read_complete
26
+ puts "read_complete"
27
+ puts "Got [#{buffer}]"
28
+ @state = WRITING_STATE
29
+ end
30
+
31
+ def write_complete
32
+ puts "write_complete"
33
+ disconnect
34
+ @state = CONNECTING_STATE
35
+ end
36
+
37
+ def reconnect
38
+ disconnect
39
+ mainloop
40
+ end
41
+
42
+ def mainloop
43
+ @state = CONNECTING_STATE
44
+ while true
45
+ if wait(1) # wait for 1 second
46
+ if pending? # IO is pending
47
+ case @state
48
+ when CONNECTING_STATE
49
+ connected
50
+ when READING_STATE
51
+ if transferred == 0
52
+ reconnect
53
+ break
54
+ end
55
+ read_complete
56
+ when WRITING_STATE
57
+ if transferred != length
58
+ reconnect
59
+ break
60
+ end
61
+ write_complete
62
+ end
63
+ end
64
+
65
+ case @state
66
+ when CONNECTING_STATE
67
+ if connect
68
+ connected
69
+ end
70
+ when READING_STATE
71
+ if read
72
+ if !pending?
73
+ read_complete
74
+ end
75
+ else
76
+ reconnect
77
+ end
78
+ when WRITING_STATE
79
+ if write("Thanks for the data!")
80
+ if not pending?
81
+ write_complete
82
+ end
83
+ else
84
+ reconnect
85
+ break
86
+ end
87
+ end
88
+ end
89
+
90
+ sleep(1)
91
+ puts "pipe server is running"
92
+ end
93
+ end
94
+ end
95
+
96
+ flags = Pipe::ACCESS_DUPLEX | Pipe::OVERLAPPED
97
+
98
+ MyPipe.new('foo', 0, flags) do |pipe|
99
+ pipe.mainloop
100
+ end
data/lib/win32/pipe.rb ADDED
@@ -0,0 +1,253 @@
1
+ require 'windows/pipe'
2
+ require 'windows/synchronize'
3
+ require 'windows/handle'
4
+ require 'windows/file'
5
+ require 'windows/error'
6
+
7
+ # The Win32 module serves as a namespace only.
8
+ module Win32
9
+ # The Pipe class is an abstract base class for the Pipe::Server and
10
+ # Pipe::Client classes. Do not use this directly.
11
+ #
12
+ class Pipe
13
+ include Windows::Pipe
14
+ include Windows::Synchronize
15
+ include Windows::Handle
16
+ include Windows::File
17
+ include Windows::Error
18
+
19
+ class Error < StandardError; end
20
+
21
+ # The version of this library
22
+ VERSION = '0.2.0'
23
+
24
+ PIPE_BUFFER_SIZE = 512 #:nodoc:
25
+ PIPE_TIMEOUT = 5000 #:nodoc:
26
+
27
+ # Blocking mode is enabled
28
+ WAIT = PIPE_WAIT
29
+
30
+ # Nonblocking mode is enabled
31
+ NOWAIT = PIPE_NOWAIT
32
+
33
+ # The pipe is bi-directional. Both server and client processes can read
34
+ # from and write to the pipe.
35
+ ACCESS_DUPLEX = PIPE_ACCESS_DUPLEX
36
+
37
+ # The flow of data in the pipe goes from client to server only.
38
+ ACCESS_INBOUND = PIPE_ACCESS_INBOUND
39
+
40
+ # The flow of data in the pipe goes from server to client only.
41
+ ACCESS_OUTBOUND = PIPE_ACCESS_OUTBOUND
42
+
43
+ # Data is written to the pipe as a stream of bytes.
44
+ TYPE_BYTE = PIPE_TYPE_BYTE
45
+
46
+ # Data is written to the pipe as a stream of messages.
47
+ TYPE_MESSAGE = PIPE_TYPE_MESSAGE
48
+
49
+ # Data is read from the pipe as a stream of bytes.
50
+ READMODE_BYTE = PIPE_READMODE_BYTE
51
+
52
+ # Data is read from the pipe as a stream of messages.
53
+ READMODE_MESSAGE = PIPE_READMODE_MESSAGE
54
+
55
+ # All instances beyond the first will fail with access denied errors.
56
+ FIRST_PIPE_INSTANCE = FILE_FLAG_FIRST_PIPE_INSTANCE
57
+
58
+ # Functions do not return until the data is written across the network.
59
+ WRITE_THROUGH = FILE_FLAG_WRITE_THROUGH
60
+
61
+ # Overlapped mode enables asynchronous communication.
62
+ OVERLAPPED = FILE_FLAG_OVERLAPPED
63
+
64
+ # The default pipe mode
65
+ DEFAULT_PIPE_MODE = NOWAIT
66
+
67
+ # The default open mode
68
+ DEFAULT_OPEN_MODE = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH
69
+
70
+ # The data still in the pipe's buffer
71
+ attr_reader :buffer
72
+
73
+ # The number of bytes to be written to the pipe.
74
+ attr_reader :size
75
+
76
+ # The number of characters that are actually transferred over the pipe.
77
+ attr_reader :transferred
78
+
79
+ # The full name of the pipe, e.g. "\\\\.\\pipe\\my_pipe"
80
+ attr_reader :name
81
+
82
+ # The pipe mode of the pipe.
83
+ attr_reader :open_mode
84
+
85
+ # The open mode of the pipe.
86
+ attr_reader :pipe_mode
87
+
88
+ # Abstract initializer for base class. This handles automatic prepending
89
+ # of '\\.\pipe\' to each named pipe so that you don't have to. Don't
90
+ # use this directly. Add the full implementation in subclasses.
91
+ #
92
+ # The default pipe mode is PIPE_WAIT.
93
+ #
94
+ # The default open mode is FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH.
95
+ #
96
+ def initialize(name, pipe_mode = DEFAULT_PIPE_MODE, open_mode = DEFAULT_OPEN_MODE)
97
+ @name = "\\\\.\\pipe\\" + name
98
+
99
+ @pipe_mode = pipe_mode.nil? ? DEFAULT_PIPE_MODE : pipe_mode
100
+ @open_mode = open_mode.nil? ? DEFAULT_OPEN_MODE : open_mode
101
+
102
+ @pipe = nil
103
+ @pending_io = false
104
+ @buffer = 0.chr * PIPE_BUFFER_SIZE
105
+ @size = 0
106
+ @overlapped = 0.chr * 20 # sizeof(OVERLAPPED)
107
+ @transferred = 0
108
+ @asynchronous = false
109
+
110
+ if open_mode & FILE_FLAG_OVERLAPPED > 0
111
+ @asynchronous = true
112
+ end
113
+
114
+ if @asynchronous
115
+ @event = CreateEvent(nil, true, true, nil)
116
+ @overlapped[16, 4] = [@event].pack('L')
117
+ end
118
+ end
119
+
120
+ # Disconnects the pipe.
121
+ def disconnect
122
+ DisconnectNamedPipe(@pipe)
123
+ end
124
+
125
+ # Closes the pipe.
126
+ #
127
+ def close
128
+ CloseHandle(@pipe)
129
+ end
130
+
131
+ # Returns whether or not there is a pending IO operation on the pipe.
132
+ #
133
+ def pending?
134
+ @pending_io
135
+ end
136
+
137
+ # Returns whether or not the pipe is asynchronous.
138
+ #
139
+ def asynchronous?
140
+ @asynchronous
141
+ end
142
+
143
+ # Reads data from the pipe. You can read data from either end of a named
144
+ # pipe.
145
+ #
146
+ def read
147
+ bytes = [0].pack('L')
148
+ @buffer = 0.chr * PIPE_BUFFER_SIZE
149
+
150
+ if @asynchronous
151
+ bool = ReadFile(@pipe, @buffer, @buffer.size, bytes, @overlapped)
152
+
153
+ bytes_read = bytes.unpack('L').first
154
+
155
+ if bool && bytes_read > 0
156
+ @pending_io = false
157
+ @buffer = @buffer[0, bytes_read]
158
+ return true
159
+ end
160
+
161
+ error = GetLastError()
162
+ if !bool && error == ERROR_IO_PENDING
163
+ @pending_io = true
164
+ return true
165
+ end
166
+
167
+ return false
168
+ else
169
+ unless ReadFile(@pipe, @buffer, @buffer.size, bytes, nil)
170
+ raise Error, get_last_error
171
+ end
172
+ end
173
+
174
+ @buffer.unpack("A*")
175
+ end
176
+
177
+ # Writes 'data' to the pipe. You can write data to either end of a
178
+ # named pipe.
179
+ #
180
+ def write(data)
181
+ @buffer = data
182
+ @size = data.size
183
+ bytes = [0].pack('L')
184
+
185
+ if @asynchronous
186
+ bool = WriteFile(@pipe, @buffer, @buffer.size, bytes, @overlapped)
187
+
188
+ bytes_written = bytes.unpack('L').first
189
+
190
+ if bool && bytes_written > 0
191
+ @pending_io = false
192
+ return true
193
+ end
194
+
195
+ error = GetLastError()
196
+
197
+ if !bool && error == ERROR_IO_PENDING
198
+ @pending_io = true
199
+ return true
200
+ end
201
+
202
+ return false
203
+ else
204
+ unless WriteFile(@pipe, @buffer, @buffer.size, bytes, 0)
205
+ raise Error, get_last_error
206
+ end
207
+
208
+ return true
209
+ end
210
+ end
211
+
212
+ # Returns the pipe object if an event (such as a client connection)
213
+ # occurs within the +max_time+ specified (in seconds). Otherwise, it
214
+ # returns false.
215
+ #
216
+ def wait(max_time = nil)
217
+ unless @asynchronous
218
+ raise Error, 'cannot wait in synchronous (blocking) mode'
219
+ end
220
+
221
+ max_time = max_time ? max_time * 1000 : INFINITE
222
+
223
+ wait = WaitForSingleObject(@event, max_time)
224
+
225
+ if wait == WAIT_TIMEOUT
226
+ return false
227
+ else
228
+ if wait != WAIT_OBJECT_0
229
+ raise Error, get_last_error
230
+ end
231
+ end
232
+
233
+ if @pending_io
234
+ transferred = [0].pack('L')
235
+ bool = GetOverlappedResult(@pipe, @overlapped, transferred, false)
236
+
237
+ unless bool
238
+ raise Error, get_last_error
239
+ end
240
+
241
+ @transferred = transferred.unpack('L')[0]
242
+ @buffer = @buffer[0, @transferred]
243
+ end
244
+
245
+ self
246
+ end
247
+
248
+ alias length size
249
+ end
250
+ end
251
+
252
+ require 'win32/pipe/server'
253
+ require 'win32/pipe/client'
@@ -0,0 +1,49 @@
1
+ # The Win32 module serves as a namespace only
2
+ module Win32
3
+ # The Pipe::Client class encapsulates the client side of a named pipe
4
+ # connection.
5
+ #
6
+ class Pipe::Client < Pipe
7
+ # Create and return a new Pipe::Client instance.
8
+ #
9
+ # The default pipe mode is PIPE_WAIT.
10
+ #
11
+ # The default open mode is FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH.
12
+ #--
13
+ # 2147483776 is FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH
14
+ def initialize(name, pipe_mode = DEFAULT_PIPE_MODE, open_mode = DEFAULT_OPEN_MODE)
15
+ super(name, pipe_mode, open_mode)
16
+
17
+ @pipe = CreateFile(
18
+ @name,
19
+ GENERIC_READ | GENERIC_WRITE,
20
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
21
+ nil,
22
+ OPEN_EXISTING,
23
+ @open_mode,
24
+ nil
25
+ )
26
+
27
+ error = GetLastError()
28
+
29
+ if error == ERROR_PIPE_BUSY
30
+ unless WaitNamedPipe(@name, NMPWAIT_WAIT_FOREVER)
31
+ raise Error, get_last_error
32
+ end
33
+ end
34
+
35
+ if @pipe == INVALID_HANDLE_VALUE
36
+ raise Error, get_last_error
37
+ end
38
+
39
+ if block_given?
40
+ begin
41
+ yield self
42
+ ensure
43
+ disconnect
44
+ close
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,96 @@
1
+ # The Win32 module serves as a namespace only.
2
+ module Win32
3
+ # The Pipe::Server class encapsulates the server side of a named pipe
4
+ # connection.
5
+ class Pipe::Server < Pipe
6
+
7
+ # Creates and returns a new Pipe::Server instance, using +name+ as the
8
+ # name for the pipe. Note that this does not actually connect the pipe.
9
+ # Use Pipe::Server#connect for that.
10
+ #
11
+ # The default pipe_mode is PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT.
12
+ #
13
+ # The default open_mode is Pipe::ACCESS_DUPLEX.
14
+ #--
15
+ # The default pipe_mode also happens to be 0.
16
+ #
17
+ def initialize(name, pipe_mode = 0, open_mode = Pipe::ACCESS_DUPLEX)
18
+ super(name, pipe_mode, open_mode)
19
+
20
+ @pipe = CreateNamedPipe(
21
+ @name,
22
+ @open_mode,
23
+ @pipe_mode,
24
+ PIPE_UNLIMITED_INSTANCES,
25
+ PIPE_BUFFER_SIZE,
26
+ PIPE_BUFFER_SIZE,
27
+ PIPE_TIMEOUT,
28
+ 0
29
+ )
30
+
31
+ if @pipe == INVALID_HANDLE_VALUE
32
+ raise Error, get_last_error
33
+ end
34
+
35
+ if block_given?
36
+ begin
37
+ yield self
38
+ ensure
39
+ close
40
+ end
41
+ end
42
+ end
43
+
44
+ # Enables the named pipe server process to wait for a client process
45
+ # to connect to an instance of a named pipe. In other words, it puts
46
+ # the server in 'connection wait' status.
47
+ #
48
+ # In synchronous mode always returns true on success. In asynchronous
49
+ # mode returns true if there is pending IO, or false otherwise.
50
+ #
51
+ def connect
52
+ if @asynchronous
53
+ # An overlapped ConnectNamedPipe should return 0
54
+ if ConnectNamedPipe(@pipe, @overlapped)
55
+ raise Error, get_last_error
56
+ end
57
+
58
+ error = GetLastError()
59
+
60
+ case error
61
+ when ERROR_IO_PENDING
62
+ @pending_io = true
63
+ when ERROR_PIPE_CONNECTED
64
+ unless SetEvent(@event)
65
+ raise Error, get_last_error(error)
66
+ end
67
+ when ERROR_PIPE_LISTENING
68
+ # Do nothing
69
+ else
70
+ raise Error, get_last_error(error)
71
+ end
72
+
73
+ if @pending_io
74
+ return false
75
+ else
76
+ return true
77
+ end
78
+ else
79
+ unless ConnectNamedPipe(@pipe, nil)
80
+ raise Error, get_last_error
81
+ end
82
+ end
83
+
84
+ return true
85
+ end
86
+
87
+ # Close the server. This will flush file buffers, disconnect the
88
+ # pipe, and close the pipe handle.
89
+ #
90
+ def close
91
+ FlushFileBuffers(@pipe)
92
+ DisconnectNamedPipe(@pipe)
93
+ super
94
+ end
95
+ end
96
+ end
data/test/tc_pipe.rb ADDED
@@ -0,0 +1,122 @@
1
+ ##########################################################################
2
+ # tc_pipe.rb
3
+ #
4
+ # Test suite for the win32-pipe library. This test suite should be run
5
+ # via the 'rake test' task.
6
+ ##########################################################################
7
+ require 'test/unit'
8
+ require 'win32/pipe'
9
+ include Win32
10
+
11
+ class TC_Win32_Pipe < Test::Unit::TestCase
12
+ def setup
13
+ @pipe = Pipe.new('foo')
14
+ end
15
+
16
+ def test_version
17
+ assert_equal('0.2.0', Pipe::VERSION)
18
+ end
19
+
20
+ def test_name
21
+ assert_respond_to(@pipe, :name)
22
+ assert_nothing_raised{ @pipe.name }
23
+ assert_equal("\\\\.\\pipe\\foo", @pipe.name)
24
+ end
25
+
26
+ def test_pipe_mode
27
+ assert_respond_to(@pipe, :pipe_mode)
28
+ assert_nothing_raised{ @pipe.pipe_mode }
29
+ assert_equal(Pipe::DEFAULT_PIPE_MODE, @pipe.pipe_mode)
30
+ end
31
+
32
+ def test_open_mode
33
+ assert_respond_to(@pipe, :open_mode)
34
+ assert_nothing_raised{ @pipe.open_mode }
35
+ assert_equal(Pipe::DEFAULT_OPEN_MODE, @pipe.open_mode)
36
+ end
37
+
38
+ def test_buffer
39
+ assert_respond_to(@pipe, :buffer)
40
+ assert_nothing_raised{ @pipe.buffer }
41
+ end
42
+
43
+ def test_size
44
+ assert_respond_to(@pipe, :size)
45
+ assert_nothing_raised{ @pipe.size }
46
+ end
47
+
48
+ def test_length_alias
49
+ assert_respond_to(@pipe, :length)
50
+ assert_equal(true, @pipe.method(:length) == @pipe.method(:size))
51
+ end
52
+
53
+ def test_pending
54
+ assert_respond_to(@pipe, :pending?)
55
+ assert_nothing_raised{ @pipe.pending? }
56
+ assert_equal(false, @pipe.pending?)
57
+ end
58
+
59
+ def test_asynchronous
60
+ assert_respond_to(@pipe, :asynchronous?)
61
+ assert_nothing_raised{ @pipe.asynchronous? }
62
+ assert_equal(false, @pipe.asynchronous?)
63
+ end
64
+
65
+ def test_read
66
+ assert_respond_to(@pipe, :read)
67
+ assert_raises(Pipe::Error){ @pipe.read } # Nothing to read
68
+ end
69
+
70
+ def test_transferred
71
+ assert_respond_to(@pipe, :transferred)
72
+ assert_nothing_raised{ @pipe.transferred }
73
+ end
74
+
75
+ def test_wait
76
+ assert_respond_to(@pipe, :wait)
77
+ assert_raises(Pipe::Error){ @pipe.wait } # Can't wait in blocking mode
78
+ end
79
+
80
+ def test_write
81
+ assert_respond_to(@pipe, :write)
82
+ assert_raises(ArgumentError){ @pipe.write } # Must have 1 argument
83
+ assert_raises(Pipe::Error){ @pipe.write("foo") } # Nothing to write to
84
+ end
85
+
86
+ def test_disconnect
87
+ assert_respond_to(@pipe, :disconnect)
88
+ assert_nothing_raised{ @pipe.disconnect }
89
+ end
90
+
91
+ def test_close
92
+ assert_respond_to(@pipe, :close)
93
+ assert_nothing_raised{ @pipe.close }
94
+ end
95
+
96
+ def test_pipe_mode_constants
97
+ assert_not_nil(Pipe::WAIT)
98
+ assert_not_nil(Pipe::NOWAIT)
99
+ assert_not_nil(Pipe::TYPE_BYTE)
100
+ assert_not_nil(Pipe::TYPE_MESSAGE)
101
+ assert_not_nil(Pipe::READMODE_BYTE)
102
+ assert_not_nil(Pipe::READMODE_MESSAGE)
103
+ end
104
+
105
+ def test_open_mode_constants
106
+ assert_not_nil(Pipe::ACCESS_DUPLEX)
107
+ assert_not_nil(Pipe::ACCESS_INBOUND)
108
+ assert_not_nil(Pipe::ACCESS_OUTBOUND)
109
+ assert_not_nil(Pipe::FIRST_PIPE_INSTANCE)
110
+ assert_not_nil(Pipe::WRITE_THROUGH)
111
+ assert_not_nil(Pipe::OVERLAPPED)
112
+ end
113
+
114
+ def test_other_constants
115
+ assert_not_nil(Pipe::INFINITE)
116
+ end
117
+
118
+ def teardown
119
+ @pipe.close
120
+ @pipe = nil
121
+ end
122
+ end
@@ -0,0 +1,28 @@
1
+ ##########################################################################
2
+ # tc_pipe_client.rb
3
+ #
4
+ # Test suite for the Pipe::Client class. This test suite should be run
5
+ # as part of the 'rake test' task.
6
+ ##########################################################################
7
+ require 'test/unit'
8
+ require 'win32/pipe'
9
+ include Win32
10
+
11
+ class TC_Win32_Pipe_Client < Test::Unit::TestCase
12
+ def setup
13
+ @pipe = nil
14
+ end
15
+
16
+ def test_constructor_basic
17
+ assert_respond_to(Pipe::Client, :new)
18
+ end
19
+
20
+ def test_constructor_expected_errors
21
+ assert_raise(ArgumentError){ Pipe::Client.new }
22
+ assert_raise(TypeError){ Pipe::Client.new(1) }
23
+ end
24
+
25
+ def teardown
26
+ @pipe = nil
27
+ end
28
+ end
@@ -0,0 +1,34 @@
1
+ ##########################################################################
2
+ # tc_pipe_server.rb
3
+ #
4
+ # Test suite for the Pipe::Server class. This test suite should be run
5
+ # as part of the 'rake test' task.
6
+ ##########################################################################
7
+ require 'test/unit'
8
+ require 'win32/pipe'
9
+ include Win32
10
+
11
+ class TC_Win32_Pipe_Server < Test::Unit::TestCase
12
+ def setup
13
+ @pipe = nil
14
+ end
15
+
16
+ def test_constructor_basic
17
+ assert_respond_to(Pipe::Server, :new)
18
+ assert_nothing_raised{ Pipe::Server.new('foo') }
19
+ end
20
+
21
+ def test_connect
22
+ assert_nothing_raised{ @pipe = Pipe::Server.new('foo') }
23
+ assert_respond_to(@pipe, :connect)
24
+ end
25
+
26
+ def test_constructor_expected_errors
27
+ assert_raise(ArgumentError){ Pipe::Server.new }
28
+ assert_raise(TypeError){ Pipe::Server.new(1) }
29
+ end
30
+
31
+ def teardown
32
+ @pipe = nil
33
+ end
34
+ end
@@ -0,0 +1,26 @@
1
+ require "rubygems"
2
+
3
+ spec = Gem::Specification.new do |gem|
4
+ gem.name = "win32-pipe"
5
+ gem.version = "0.2.0"
6
+ gem.author = "Daniel J. Berger"
7
+ gem.email = "djberg96@gmail.com"
8
+ gem.homepage = "http://www.rubyforge.org/projects/win32utils"
9
+ gem.platform = Gem::Platform::RUBY
10
+ gem.summary = "An interface for named pipes on MS Windows"
11
+ gem.description = "An interface for named pipes on MS Windows"
12
+ gem.test_files = Dir["test/tc_*.rb"]
13
+ gem.has_rdoc = true
14
+ gem.extra_rdoc_files = ['CHANGES', 'README', 'MANIFEST']
15
+ gem.rubyforge_project = "win32utils"
16
+
17
+ files = Dir["doc/*"] + Dir["examples/*"] + Dir["lib/win32/**/*.rb"]
18
+ files += Dir["test/*"] + Dir["[A-Z]*"]
19
+ files.delete_if{ |item| item.include?("CVS") }
20
+ gem.files = files
21
+ end
22
+
23
+ if $0 == __FILE__
24
+ Gem.manage_gems
25
+ Gem::Builder.new(spec).build
26
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: win32-pipe
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.2.0
7
+ date: 2008-05-28 00:00:00 -06:00
8
+ summary: An interface for named pipes on MS Windows
9
+ require_paths:
10
+ - lib
11
+ email: djberg96@gmail.com
12
+ homepage: http://www.rubyforge.org/projects/win32utils
13
+ rubyforge_project: win32utils
14
+ description: An interface for named pipes on MS Windows
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: true
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Daniel J. Berger
31
+ files:
32
+ - examples/test_client.rb
33
+ - examples/test_client_async.rb
34
+ - examples/test_server.rb
35
+ - examples/test_server_async.rb
36
+ - lib/win32/pipe/client.rb
37
+ - lib/win32/pipe/server.rb
38
+ - lib/win32/pipe.rb
39
+ - test/tc_pipe.rb
40
+ - test/tc_pipe_client.rb
41
+ - test/tc_pipe_server.rb
42
+ - CHANGES
43
+ - examples
44
+ - ext
45
+ - lib
46
+ - MANIFEST
47
+ - Rakefile
48
+ - README
49
+ - test
50
+ - win32-pipe.gemspec
51
+ test_files:
52
+ - test/tc_pipe.rb
53
+ - test/tc_pipe_client.rb
54
+ - test/tc_pipe_server.rb
55
+ rdoc_options: []
56
+
57
+ extra_rdoc_files:
58
+ - CHANGES
59
+ - README
60
+ - MANIFEST
61
+ executables: []
62
+
63
+ extensions: []
64
+
65
+ requirements: []
66
+
67
+ dependencies: []
68
+