Module: Chef::ReservedNames::Win32::API::Installer

Extended by:
Chef::ReservedNames::Win32, Chef::ReservedNames::Win32::API
Included in:
Provider::Package::Windows::MSI
Defined in:
lib/chef/win32/api/installer.rb

Instance Method Summary collapse

Instance Method Details

#get_installed_version(product_code) ⇒ Object

All installed product_codes should have a VersionString Returns a version if installed, nil if not installed


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
# File 'lib/chef/win32/api/installer.rb', line 132

def get_installed_version(product_code)
  version = 0.chr
  version_length = FFI::Buffer.new(:long).write_long(0)

  status = msi_get_product_info(product_code, "VersionString", version, version_length)

  return nil if status == 1605 # ERROR_UNKNOWN_PRODUCT (0x645)

  # We expect error ERROR_MORE_DATA (234) here because we passed a buffer length of 0
  if status != 234
    msg = "msi_get_product_info: product code '#{product_code}' returned unknown error #{status} when retrieving VersionString: "
    msg << Chef::ReservedNames::Win32::Error.format_message(status)
    raise Chef::Exceptions::Package, msg
  end

  # We could fetch the product version now that we know the variable length, but we don't need it here.

  version_length = FFI::Buffer.new(:long).write_long(version_length.read_long + 1)
  version = 0.chr * version_length.read_long

  status = msi_get_product_info(product_code, "VersionString", version, version_length)

  if status != 0
    msg = "msi_get_product_info: product code '#{product_code}' returned unknown error #{status} when retrieving VersionString: "
    msg << Chef::ReservedNames::Win32::Error.format_message(status)
    raise Chef::Exceptions::Package, msg
  end

  version.chomp(0.chr)
end

#get_product_property(package_path, property_name) ⇒ Object

Opens a Microsoft Installer (MSI) file from an absolute path and returns the specified property


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
# File 'lib/chef/win32/api/installer.rb', line 81

def get_product_property(package_path, property_name)
  pkg_ptr = open_package(package_path)

  buffer = 0.chr
  buffer_length = FFI::Buffer.new(:long).write_long(0)

  # Fetch the length of the property
  status = msi_get_product_property(pkg_ptr.read_pointer, property_name, buffer, buffer_length)

  # We expect error ERROR_MORE_DATA (234) here because we passed a buffer length of 0
  if status != 234
    msg = "msi_get_product_property: returned unknown error #{status} when retrieving #{property_name}: "
    msg << Chef::ReservedNames::Win32::Error.format_message(status)
    raise Chef::Exceptions::Package, msg
  end

  buffer_length = FFI::Buffer.new(:long).write_long(buffer_length.read_long + 1)
  buffer = 0.chr * buffer_length.read_long

  # Fetch the property
  status = msi_get_product_property(pkg_ptr.read_pointer, property_name, buffer, buffer_length)

  if status != 0
    msg = "msi_get_product_property: returned unknown error #{status} when retrieving #{property_name}: "
    msg << Chef::ReservedNames::Win32::Error.format_message(status)
    raise Chef::Exceptions::Package, msg
  end

  msi_close_handle(pkg_ptr.read_pointer)
  buffer.chomp(0.chr)
end

#open_package(package_path) ⇒ Object

Opens a Microsoft Installer (MSI) file from an absolute path and returns a pointer to a handle Remember to close the handle with msi_close_handle()

Raises:

  • (ArgumentError)

115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/chef/win32/api/installer.rb', line 115

def open_package(package_path)
  # MsiOpenPackage expects a perfect absolute Windows path to the MSI
  raise ArgumentError, "Provided path '#{package_path}' must be an absolute path" unless Pathname.new(package_path).absolute?

  pkg_ptr = FFI::MemoryPointer.new(:pointer, 4)
  status = msi_open_package(package_path, 1, pkg_ptr)
  case status
  when 0
    # success
  else
    raise Chef::Exceptions::Package, "msi_open_package: unexpected status #{status}: #{Chef::ReservedNames::Win32::Error.format_message(status)}"
  end
  pkg_ptr
end