require 'ffi'
module Puppet::Util::Windows::COM
extend FFI::Library
ffi_convention :stdcall
S_OK = 0
S_FALSE = 1
def SUCCEEDED(hr) hr >= 0 end
def FAILED(hr) hr < 0 end
module_function :SUCCEEDED, :FAILED
def raise_if_hresult_failed(name, *args)
failed = FAILED(result = send(name, *args)) and raise _("%{name} failed (hresult %{result}).") % { name: name, result: format('%#08x', result) }
result
ensure
yield failed if block_given?
end
module_function :raise_if_hresult_failed
CLSCTX_INPROC_SERVER = 0x1
CLSCTX_INPROC_HANDLER = 0x2
CLSCTX_LOCAL_SERVER = 0x4
CLSCTX_INPROC_SERVER16 = 0x8
CLSCTX_REMOTE_SERVER = 0x10
CLSCTX_INPROC_HANDLER16 = 0x20
CLSCTX_RESERVED1 = 0x40
CLSCTX_RESERVED2 = 0x80
CLSCTX_RESERVED3 = 0x100
CLSCTX_RESERVED4 = 0x200
CLSCTX_NO_CODE_DOWNLOAD = 0x400
CLSCTX_RESERVED5 = 0x800
CLSCTX_NO_CUSTOM_MARSHAL = 0x1000
CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000
CLSCTX_NO_FAILURE_LOG = 0x4000
CLSCTX_DISABLE_AAA = 0x8000
CLSCTX_ENABLE_AAA = 0x10000
CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000
CLSCTX_ACTIVATE_32_BIT_SERVER = 0x40000
CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000
CLSCTX_ENABLE_CLOAKING = 0x100000
CLSCTX_PS_DLL = -0x80000000
CLSCTX_INPROC = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER
CLSCTX_ALL = CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
CLSCTX_SERVER = CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms686615(v=vs.85).aspx
# HRESULT CoCreateInstance(
# _In_ REFCLSID rclsid,
# _In_ LPUNKNOWN pUnkOuter,
# _In_ DWORD dwClsContext,
# _In_ REFIID riid,
# _Out_ LPVOID *ppv
# );
ffi_lib :ole32
attach_function_private :CoCreateInstance,
[:pointer, :lpunknown, :dword, :pointer, :lpvoid], :hresult
# code modified from Unknownr project https://github.com/rpeev/Unknownr
# licensed under MIT
module Interface
def self.[](*args)
spec, iid, *ifaces = args.reverse
spec.each { |name, signature| signature[0].unshift(:pointer) }
Class.new(FFI::Struct) do
const_set(:IID, iid)
vtable = Class.new(FFI::Struct) do
vtable_hash = Hash[(ifaces.map { |iface| iface::VTBL::SPEC.to_a } << spec.to_a).flatten(1)]
const_set(:SPEC, vtable_hash)
layout(
*self::SPEC.map { |name, signature| [name, callback(*signature)] }.flatten
)
end
const_set(:VTBL, vtable)
layout \
:lpVtbl, :pointer
end
end
end
module Helpers
def QueryInstance(klass)
instance = nil
FFI::MemoryPointer.new(:pointer) do |ppv|
QueryInterface(klass::IID, ppv)
instance = klass.new(ppv.read_pointer)
end
begin
yield instance
return self
ensure
instance.Release
end if block_given?
instance
end
def UseInstance(klass, name, *args)
instance = nil
FFI::MemoryPointer.new(:pointer) do |ppv|
send(name, *args, ppv)
yield instance = klass.new(ppv.read_pointer)
end
self
ensure
instance.Release if instance && ! instance.null?
end
end
module Instance
def self.[](iface)
Class.new(iface) do
send(:include, Helpers)
def initialize(pointer)
self.pointer = pointer
@vtbl = self.class::VTBL.new(self[:lpVtbl])
end
attr_reader :vtbl
self::VTBL.members.each do |name|
define_method(name) do |*args|
if Puppet::Util::Windows::COM.FAILED(result = @vtbl[name].call(self, *args))
raise Puppet::Util::Windows::Error.new(_("Failed to call %{klass}::%{name} with HRESULT: %{result}.") % { klass: self, name: name, result: result }, result)
end
result
end
end
layout \
:lpVtbl, :pointer
end
end
end
module Factory
def self.[](iface, clsid)
Class.new(iface) do
send(:include, Helpers)
const_set(:CLSID, clsid)
def initialize(opts = {})
@opts = opts
@opts[:clsctx] ||= CLSCTX_INPROC_SERVER
FFI::MemoryPointer.new(:pointer) do |ppv|
hr = Puppet::Util::Windows::COM.CoCreateInstance(self.class::CLSID, FFI::Pointer::NULL, @opts[:clsctx], self.class::IID, ppv)
if Puppet::Util::Windows::COM.FAILED(hr)
raise _("CoCreateInstance failed (%{klass}).") % { klass: self.class }
end
self.pointer = ppv.read_pointer
end
@vtbl = self.class::VTBL.new(self[:lpVtbl])
end
attr_reader :vtbl
self::VTBL.members.each do |name|
define_method(name) do |*args|
if Puppet::Util::Windows::COM.FAILED(result = @vtbl[name].call(self, *args))
raise Puppet::Util::Windows::Error.new(_("Failed to call %{klass}::%{name} with HRESULT: %{result}.") % { klass: self, name: name, result: result }, result)
end
result
end
end
layout \
:lpVtbl, :pointer
end
end
end
IUnknown = Interface[
FFI::WIN32::GUID['00000000-0000-0000-C000-000000000046'],
QueryInterface: [[:pointer, :pointer], :hresult],
AddRef: [[], :win32_ulong],
Release: [[], :win32_ulong]
]
Unknown = Instance[IUnknown]
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms678543(v=vs.85).aspx
# HRESULT CoInitialize(
# _In_opt_ LPVOID pvReserved
# );
ffi_lib :ole32
attach_function_private :CoInitialize, [:lpvoid], :hresult
# https://msdn.microsoft.com/en-us/library/windows/desktop/ms688715(v=vs.85).aspx
# void CoUninitialize(void);
ffi_lib :ole32
attach_function_private :CoUninitialize, [], :void
def InitializeCom
raise_if_hresult_failed(:CoInitialize, FFI::Pointer::NULL)
at_exit { CoUninitialize() }
end
module_function :InitializeCom
end
Anons79 File Manager Version 1.0, Coded By Anons79
Email: [email protected]