Class: Win32::Pipe

Inherits:
Object
  • Object
show all
Includes:
Windows::Constants, Windows::Functions, Windows::Structs
Defined in:
lib/win32/pipe.rb

Overview

The Pipe class is an abstract base class for the Pipe::Server and Pipe::Client classes. Do not use this directly.

Direct Known Subclasses

Client, Server

Defined Under Namespace

Classes: Client, Error, Server

Constant Summary collapse

VERSION =

The version of this library

'0.4.0'
DEFAULT_PIPE_BUFFER_SIZE =

:nodoc:

4096
PIPE_TIMEOUT =

:nodoc:

5000
WAIT =

Blocking mode is enabled

PIPE_WAIT
NOWAIT =

Nonblocking mode is enabled

PIPE_NOWAIT
ACCESS_DUPLEX =

The pipe is bi-directional. Both server and client processes can read from and write to the pipe.

PIPE_ACCESS_DUPLEX
ACCESS_INBOUND =

The flow of data in the pipe goes from client to server only.

PIPE_ACCESS_INBOUND
ACCESS_OUTBOUND =

The flow of data in the pipe goes from server to client only.

PIPE_ACCESS_OUTBOUND
TYPE_BYTE =

Data is written to the pipe as a stream of bytes.

PIPE_TYPE_BYTE
TYPE_MESSAGE =

Data is written to the pipe as a stream of messages.

PIPE_TYPE_MESSAGE
READMODE_BYTE =

Data is read from the pipe as a stream of bytes.

PIPE_READMODE_BYTE
READMODE_MESSAGE =

Data is read from the pipe as a stream of messages.

PIPE_READMODE_MESSAGE
FIRST_PIPE_INSTANCE =

All instances beyond the first will fail with access denied errors.

FILE_FLAG_FIRST_PIPE_INSTANCE
WRITE_THROUGH =

Functions do not return until the data is written across the network.

FILE_FLAG_WRITE_THROUGH
OVERLAPPED =

Overlapped mode enables asynchronous communication.

FILE_FLAG_OVERLAPPED
DEFAULT_PIPE_MODE =

The default pipe mode

PIPE_WAIT
DEFAULT_OPEN_MODE =

The default open mode

FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH

Constants included from Windows::Constants

Windows::Constants::ERROR_IO_PENDING, Windows::Constants::ERROR_PIPE_BUSY, Windows::Constants::ERROR_PIPE_CONNECTED, Windows::Constants::ERROR_PIPE_LISTENING, Windows::Constants::ERROR_SUCCESS, Windows::Constants::FILE_ATTRIBUTE_NORMAL, Windows::Constants::FILE_FLAG_FIRST_PIPE_INSTANCE, Windows::Constants::FILE_FLAG_OVERLAPPED, Windows::Constants::FILE_FLAG_WRITE_THROUGH, Windows::Constants::FILE_SHARE_READ, Windows::Constants::FILE_SHARE_WRITE, Windows::Constants::GENERIC_READ, Windows::Constants::GENERIC_WRITE, Windows::Constants::INFINITE, Windows::Constants::INVALID_HANDLE_VALUE, Windows::Constants::NMPWAIT_WAIT_FOREVER, Windows::Constants::OPEN_EXISTING, Windows::Constants::PIPE_ACCESS_DUPLEX, Windows::Constants::PIPE_ACCESS_INBOUND, Windows::Constants::PIPE_ACCESS_OUTBOUND, Windows::Constants::PIPE_CLIENT_END, Windows::Constants::PIPE_NOWAIT, Windows::Constants::PIPE_READMODE_BYTE, Windows::Constants::PIPE_READMODE_MESSAGE, Windows::Constants::PIPE_SERVER_END, Windows::Constants::PIPE_TYPE_BYTE, Windows::Constants::PIPE_TYPE_MESSAGE, Windows::Constants::PIPE_UNLIMITED_INSTANCES, Windows::Constants::PIPE_WAIT, Windows::Constants::WAIT_OBJECT_0, Windows::Constants::WAIT_TIMEOUT

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, pipe_mode = DEFAULT_PIPE_MODE, open_mode = DEFAULT_OPEN_MODE, pipe_buffer_size = DEFAULT_PIPE_BUFFER_SIZE) ⇒ Pipe

Abstract initializer for base class. This handles automatic prepending of ‘\.pipe' to each named pipe so that you don’t have to. Don’t use this directly. Add the full implementation in subclasses.

The default pipe mode is PIPE_WAIT.

The default open mode is FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/win32/pipe.rb', line 93

def initialize(name, pipe_mode = DEFAULT_PIPE_MODE, open_mode = DEFAULT_OPEN_MODE, pipe_buffer_size = DEFAULT_PIPE_BUFFER_SIZE)
  @name = "\\\\.\\pipe\\" + name

  @pipe_mode = pipe_mode.nil? ? DEFAULT_PIPE_MODE : pipe_mode
  @open_mode = open_mode.nil? ? DEFAULT_OPEN_MODE : open_mode

  @pipe         = nil
  @pending_io   = false
  @buffer       = ''
  @ffi_buffer   = FFI::Buffer.new( pipe_buffer_size )
  @size         = 0
  @overlapped   = nil
  @transferred  = 0
  @asynchronous = false
  @pipe_buffer_size = pipe_buffer_size

  if open_mode & FILE_FLAG_OVERLAPPED > 0
    @asynchronous = true
  end

  if @asynchronous
    @event = CreateEvent(nil, 1, 1, nil)
    @overlapped = Overlapped.new
    @overlapped[:hEvent] = @event
  end
end

Instance Attribute Details

#bufferObject (readonly)

The data still in the pipe’s buffer



68
69
70
# File 'lib/win32/pipe.rb', line 68

def buffer
  @buffer
end

#nameObject (readonly)

The full name of the pipe, e.g. “\\.\pipe\my_pipe”



77
78
79
# File 'lib/win32/pipe.rb', line 77

def name
  @name
end

#open_modeObject (readonly)

The pipe mode of the pipe.



80
81
82
# File 'lib/win32/pipe.rb', line 80

def open_mode
  @open_mode
end

#pipe_modeObject (readonly)

The open mode of the pipe.



83
84
85
# File 'lib/win32/pipe.rb', line 83

def pipe_mode
  @pipe_mode
end

#sizeObject (readonly) Also known as: length

The number of bytes to be written to the pipe.



71
72
73
# File 'lib/win32/pipe.rb', line 71

def size
  @size
end

#transferredObject (readonly)

The number of characters that are actually transferred over the pipe.



74
75
76
# File 'lib/win32/pipe.rb', line 74

def transferred
  @transferred
end

Instance Method Details

#asynchronous?Boolean

Returns whether or not the pipe is asynchronous.

Returns:

  • (Boolean)


139
140
141
# File 'lib/win32/pipe.rb', line 139

def asynchronous?
   @asynchronous
end

#closeObject

Closes the pipe.



127
128
129
# File 'lib/win32/pipe.rb', line 127

def close
  CloseHandle(@pipe) if @pipe
end

#disconnectObject

Disconnects the pipe.



121
122
123
# File 'lib/win32/pipe.rb', line 121

def disconnect
  DisconnectNamedPipe(@pipe) if @pipe
end

#pending?Boolean

Returns whether or not there is a pending IO operation on the pipe.

Returns:

  • (Boolean)


133
134
135
# File 'lib/win32/pipe.rb', line 133

def pending?
  @pending_io
end

#read(read_size = @ffi_buffer.size) ⇒ Object

Reads data from the pipe. You can read data from either end of a named pipe.

Raises:



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/win32/pipe.rb', line 146

def read(read_size = @ffi_buffer.size)
  bytes = FFI::MemoryPointer.new(:ulong)

  raise Error, "no pipe created" unless @pipe

  if @asynchronous
    bool = ReadFile(@pipe, @ffi_buffer, read_size, bytes, @overlapped)
    bytes_read = bytes.read_ulong

    if bool && bytes_read > 0
      @pending_io = false
      @buffer = @ffi_buffer.get_string(0, bytes_read)
      return true
    end

    error = GetLastError()
    if !bool && error == ERROR_IO_PENDING
      @pending_io = true
      return true
    end

    return false
  else
    unless ReadFile(@pipe, @ffi_buffer, read_size, bytes, nil)
      raise SystemCallError.new("ReadFile", FFI.errno)
    end
    @buffer = @ffi_buffer.get_string(0, bytes.read_ulong)
  end
end

#wait(max_time = nil) ⇒ Object

Returns the pipe object if an event (such as a client connection) occurs within the max_time specified (in seconds). Otherwise, it returns false.



213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/win32/pipe.rb', line 213

def wait(max_time = nil)
  unless @asynchronous
    raise Error, 'cannot wait in synchronous (blocking) mode'
  end

  max_time = max_time ? max_time * 1000 : INFINITE

  wait = WaitForSingleObject(@event, max_time)

  if wait == WAIT_TIMEOUT
    return false
  else
    if wait != WAIT_OBJECT_0
      raise SystemCallError.new("WaitForSingleObject", FFI.errno)
    end
  end

  if @pending_io
    transferred = FFI::MemoryPointer.new(:ulong)
    bool = GetOverlappedResult(@pipe, @overlapped, transferred, 0)

    unless bool
      raise SystemCallError.new("GetOverlappedResult", FFI.errno)
    end

    @transferred = transferred.read_ulong
    @buffer = @ffi_buffer.get_string(0, @transferred)
  end

  self
end

#write(data) ⇒ Object

Writes ‘data’ to the pipe. You can write data to either end of a named pipe.

Raises:



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/win32/pipe.rb', line 179

def write(data)
  bytes = FFI::MemoryPointer.new(:ulong)

  raise Error, "no pipe created" unless @pipe

  if @asynchronous
    bool = WriteFile(@pipe, data, data.size, bytes, @overlapped)
    bytes_written = bytes.read_ulong

    if bool && bytes_written > 0
      @pending_io = false
      return true
    end

    error = GetLastError()
    if !bool && error == ERROR_IO_PENDING
      @pending_io = true
      return true
    end

    return false
  else
    unless WriteFile(@pipe, data, data.size, bytes, nil)
      raise SystemCallError.new("WriteFile", FFI.errno)
    end

    return true
  end
end