module Win32::Pdh

Constants

ItemEnum

Structure of instances and counters, for ::enum_object_items

Public Class Methods

check_status(status) click to toggle source

Simple convenience method that checks the status and raises an exception unless it's a successful status.

# File lib/win32/pdh.rb, line 19
def self.check_status(status)
  raise PdhError, status unless status == Constants::ERROR_SUCCESS
end
enum_object_items(object:, source: nil, machine: nil, detail: :novice) click to toggle source

Enumerates an object's counter and instance names. Returns an ItemEnum with the results.

Uses PdhEnumObjectItems: msdn.microsoft.com/en-us/library/windows/desktop/aa372595(v=vs.85).aspx

# File lib/win32/pdh.rb, line 159
def self.enum_object_items(object:, source: nil, machine: nil, detail: :novice)
  object = (object + "\0").encode('UTF-16LE')
  source =
    if source.nil?
      FFI::Pointer::NULL
    else
      (source + "\0").encode('UTF-16LE')
    end
  machine =
    if machine.nil?
      FFI::Pointer::NULL
    else
      (machine + "\0").encode('UTF-16LE')
    end
  detail =
    case detail
    when :wizard
      Constants::PERF_DETAIL_WIZARD
    when :expert
      Constants::PERF_DETAIL_EXPERT
    when :advanced
      Constants::PERF_DETAIL_ADVANCED
    else
      Constants::PERF_DETAIL_NOVICE
    end

  countersize = FFI::MemoryPointer.new(:uint)
  instancesize = FFI::MemoryPointer.new(:uint)
  countersize.write_uint(0)
  instancesize.write_uint(0)
  counterbuffer = FFI::Pointer::NULL
  instancebuffer = FFI::Pointer::NULL
  status = nil
  while status.nil? || status == Constants::PDH_MORE_DATA
    unless status.nil?
      counterbuffer = FFI::Buffer.new(:uint16, countersize.read_uint)
      instancebuffer = FFI::Buffer.new(:uint16, instancesize.read_uint)
    end
    status = PdhFFI.PdhEnumObjectItemsW(
      source,
      machine,
      object,
      counterbuffer,
      countersize,
      instancebuffer,
      instancesize,
      detail,
      0,
    )
  end

  Pdh.check_status status

  counterstring = counterbuffer.read_bytes(countersize.read_uint * 2).force_encoding('UTF-16LE').encode('UTF-8')
  instancestring = instancebuffer.read_bytes(instancesize.read_uint * 2).force_encoding('UTF-16LE').encode('UTF-8')

  enum = ItemEnum.new
  enum.counters = counterstring.split("\0").map(&:freeze).freeze
  enum.instances = instancestring.split("\0").map(&:freeze).freeze
  enum.freeze
end
enum_objects(source: nil, machine: nil, detail: :novice) click to toggle source

Uses PdhEnumObjects to enumerate objects at the given target. Returns the objects as an array of strings.

PdhEnumObjects: msdn.microsoft.com/en-us/library/windows/desktop/aa372600(v=vs.85).aspx

Params:

source

The same as szDataSource

machine

The same as szMachineName

detail

Alias for dwDetailLevel, as a symbol. May be :novice, :advanced, :expert, or :wizard. Defaults to :novice.

# File lib/win32/pdh.rb, line 100
def self.enum_objects(source: nil, machine: nil, detail: :novice)
  source =
    if source.nil?
      FFI::Pointer::NULL
    else
      (source + "\0").encode('UTF-16LE')
    end
  machine =
    if machine.nil?
      FFI::Pointer::NULL
    else
      (machine + "\0").encode('UTF-16LE')
    end
  detail =
    case detail
    when :wizard
      Constants::PERF_DETAIL_WIZARD
    when :expert
      Constants::PERF_DETAIL_EXPERT
    when :advanced
      Constants::PERF_DETAIL_ADVANCED
    else
      Constants::PERF_DETAIL_NOVICE
    end

  listsize = FFI::MemoryPointer.new(:uint)
  listsize.write_uint(0)
  listbuffer = FFI::Pointer::NULL
  status = nil
  while status.nil? || status == Constants::PDH_MORE_DATA
    listbuffer = FFI::Buffer.new(:uint16, listsize.read_uint) unless status.nil?

    status = PdhFFI.PdhEnumObjectsW(
      source,
      machine,
      listbuffer,
      listsize,
      detail,
      status.nil? ? :true : :false,
    )
  end

  Pdh.check_status status

  string = listbuffer.read_bytes(listsize.read_uint * 2).force_encoding('UTF-16LE').encode('UTF-8')

  # Split and return objects
  string.split("\0")
end
expand_wildcards(path:, source: nil, expand_counters: true, expand_instances: true) click to toggle source

Expands a wildcard path into all matching counter paths.

Returns a frozen array of frozen strings.

Uses PdhExpandWildCardPath: msdn.microsoft.com/en-us/library/windows/desktop/aa372606(v=vs.85).aspx

# File lib/win32/pdh.rb, line 227
def self.expand_wildcards(path:, source: nil, expand_counters: true, expand_instances: true)
  path = (path + "\0").encode('UTF-16LE')
  source =
    if source.nil?
      FFI::Pointer::NULL
    else
      (source + "\0").encode('UTF-16LE')
    end

  flags = 0
  flags |= PDH_NOEXPANDCOUNTERS unless expand_counters
  flags |= PDH_NOEXPANDINSTANCES unless expand_instances

  listsize = FFI::MemoryPointer.new(:uint)
  listsize.write_uint(0)
  listbuffer = FFI::Pointer::NULL
  status = nil
  while status.nil? || status == Constants::PDH_MORE_DATA
    listbuffer = FFI::Buffer.new(:uint16, listsize.read_uint) unless status.nil?
    status = PdhFFI.PdhExpandWildCardPathW(
      source,
      path,
      listbuffer,
      listsize,
      flags,
    )
  end

  Pdh.check_status status

  liststring = listbuffer.read_bytes(listsize.read_uint * 2).force_encoding('UTF-16LE').encode('UTF-8')

  liststring.split("\0").map(&:freeze).freeze
end
read_cwstr(pointer) click to toggle source

Takes a pointer to null-terminated utf-16 data and reads it into a utf-8 encoded string.

If pointer is null, return nil instead of a string.

# File lib/win32/pdh.rb, line 48
def self.read_cwstr(pointer)
  return nil if pointer.null?

  # length in wchars
  length = strlen_cwstr(pointer)

  pointer.read_bytes(length * 2).force_encoding('UTF-16LE').encode('UTF-8')
end
strlen_cwstr(pointer) click to toggle source

Gets the length of a cwstr (null-terminated UTF-16 string) in characters (16-bit units).

Returns nil if the pointer is null

# File lib/win32/pdh.rb, line 28
def self.strlen_cwstr(pointer)
  return nil if pointer.null?

  # Clone pointer, so we don't modify the original.
  pointer = FFI::Pointer.new(pointer)
  length = 0
  until pointer.get_uint16(0) == 0
    length += 1
    # Need to proceed 2 bytes at a time; Ruby ffi gives no special pointer
    # arithmetic by type.
    pointer += 2
  end

  length
end