Skip to content

Instantly share code, notes, and snippets.

@mrkn
Created March 23, 2011 05:17
Show Gist options
  • Save mrkn/882661 to your computer and use it in GitHub Desktop.
Save mrkn/882661 to your computer and use it in GitHub Desktop.
A sample script for calling EnumPortsW exported by winspool.drv from Ruby using dl library.
require 'dl/import'
require 'dl/types'
module Win32API
module Kernel32
extend DL::Importer
dlload "kernel32.dll"
include DL::Win32Types
# DWORD GetLastError(void)
extern "DWORD GetLastError()"
end
module Winspool
extend DL::Importer
dlload "winspool.drv"
include DL::Win32Types
PORT_INFO_1 = struct(["char* pName"])
PORT_INFO_2 = struct(["char* pPortName",
"char* pMonitorName",
"char* pDescription",
"DWORD fPortType",
"DWORD Reserved"])
# BOOL EnumPortsW(
# __in LPTSTR pName,
# __in DWORD Level,
# __out LPBYTE pPorts,
# __in DWORD cbBuf,
# LPDWORD pcbNeeded,
# LPDWORD pcReturned
# )
extern "BOOL EnumPortsW(char*, DWORD, PBYTE, DWORD, PDWORD, PDWORD)"
PORT_TYPES = {
:write => 0x1,
:read => 0x2,
:redirected => 0x4,
:net_attached => 0x8,
}.freeze
def unpack_port_types(n)
PORT_TYPES.select {|type, bit| (n & bit) != 0 }.map{|type, bit| type}
end
module_function :unpack_port_types
end
def wcslen(ptr)
i = 0
until ptr[i] == 0 && ptr[i+1]
i += 2
end
i.div 2
end
module_function :wcslen
def get_last_error
Kernel32.GetLastError()
end
module_function :get_last_error
ERROR_INSUFFICIENT_BUFFER = 122
def enum_ports(level, name=nil)
return nil unless [1, 2].include? level
needed_bytes = "\0" * DL::SIZEOF_LONG
returned = "\0" * DL::SIZEOF_LONG
rv = Winspool.EnumPortsW(name, level, nil, 0, needed_bytes, returned)
# the first call must fail
return nil if rv != 0
# GetLastError must return ERROR_INSUFFICIENT_BUFFER
return nil if get_last_error != ERROR_INSUFFICIENT_BUFFER
bufsiz = needed_bytes.unpack("l").first
buf = DL::CPtr.malloc(bufsiz)
rv = Winspool.EnumPortsW(name, level, buf, bufsiz, needed_bytes, returned)
returned = returned.unpack("l").first
ports = returned.times.map {|i|
hash = {}
case level
when 1
info = Winspool::PORT_INFO_1.new(buf)
len = wcslen(info.pName)
hash[:name] = info.pName.to_s(wcslen(info.pName)*2).encode(Encoding.default_external, 'UTF-16LE')
buf = buf + Winspool::PORT_INFO_1.size
when 2
info = Winspool::PORT_INFO_2.new(buf)
hash[:port_name] = info.pPortName.to_s(wcslen(info.pPortName)*2).encode(Encoding.default_external, 'UTF-16LE')
hash[:monitor_name] = info.pMonitorName.to_s(wcslen(info.pMonitorName)*2).encode(Encoding.default_external, 'UTF-16LE')
hash[:description] = info.pDescription.to_s(wcslen(info.pDescription)*2).encode(Encoding.default_external, 'UTF-16LE')
hash[:port_type] = Winspool.unpack_port_types(info.fPortType)
hash[:reserved] = info.Reserved
buf = buf + Winspool::PORT_INFO_2.size
end
hash
}
end
module_function :enum_ports
end
require 'pp'
pp ports = Win32API.enum_ports(2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment