Created
March 23, 2011 05:17
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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