File: com.rb

package info (click to toggle)
puppet-agent 7.23.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 19,092 kB
  • sloc: ruby: 245,074; sh: 456; makefile: 38; xml: 33
file content (225 lines) | stat: -rw-r--r-- 6,042 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
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
175
176
177
178
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
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