WindowsInstaller 0.1.21 → 0.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/WindowsInstaller.rb +111 -108
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad91de66d76881675a1be9601c6d237bfafe58a0
|
4
|
+
data.tar.gz: dea6ee339c0deaf36d9c51a5e348630c49f4e04d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6416653a2488548c9933db8098f0fd917f8cdeb178ed66929dcd39450b13b3115d4998ba959581d5b817c5dc3f55d7386975bac2e97f572c6d41ef2fec39ce86
|
7
|
+
data.tar.gz: 29c07858abdbcd44a8e062f0d2a81b62597858dacb07fcec25535f5dac26cb15e5e9ffc81a7696571d325dae4b317b74bfab0cee45ca00efa07081b16b39f818
|
data/lib/WindowsInstaller.rb
CHANGED
@@ -9,11 +9,11 @@ class WindowsInstaller < Hash
|
|
9
9
|
@@default_options.each { |key, value| self[key] = value }
|
10
10
|
options.each { |key, value| self[key] = value}
|
11
11
|
|
12
|
-
|
12
|
+
@installer = WIN32OLE.new('WindowsInstaller.Installer')
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.default_options(hash)
|
16
|
-
|
16
|
+
hash.each { |key,value| @@default_options[key] = value }
|
17
17
|
end
|
18
18
|
|
19
19
|
def msi_installed?(msi_file)
|
@@ -22,15 +22,15 @@ class WindowsInstaller < Hash
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def product_installed?(product_name)
|
25
|
-
|
25
|
+
product_code = installed_get_product_code(product_name)
|
26
26
|
return false if(product_code.empty?)
|
27
27
|
|
28
|
-
|
28
|
+
return product_code_installed?(product_code)
|
29
29
|
end
|
30
30
|
|
31
31
|
def product_code_installed?(product_code)
|
32
|
-
|
33
|
-
|
32
|
+
@installer.Products.each { |installed_product_code| return true if (product_code == installed_product_code) }
|
33
|
+
return false
|
34
34
|
end
|
35
35
|
|
36
36
|
def install_msi(msi_file)
|
@@ -38,12 +38,12 @@ class WindowsInstaller < Hash
|
|
38
38
|
|
39
39
|
msi_file = File.absolute_path(msi_file).gsub(/\//, '\\')
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
cmd = "msiexec.exe"
|
42
|
+
cmd = "#{cmd} #{self[:mode]}" if(has_key?(:mode))
|
43
|
+
cmd = "#{cmd} /i #{msi_file}"
|
44
44
|
|
45
|
-
|
46
|
-
|
45
|
+
msiexec(cmd)
|
46
|
+
raise "Failed to install msi_file: #{msi_file}" unless(msi_installed?(msi_file))
|
47
47
|
end
|
48
48
|
|
49
49
|
def uninstall_msi(msi_file)
|
@@ -55,20 +55,20 @@ class WindowsInstaller < Hash
|
|
55
55
|
def uninstall_product(product_name)
|
56
56
|
raise StandardError.new("Product '#{product_name}' is not installed") unless(product_installed?(product_name))
|
57
57
|
|
58
|
-
|
59
|
-
|
58
|
+
product_code = installed_get_product_code(product_name)
|
59
|
+
uninstall_product_code(product_code)
|
60
60
|
end
|
61
61
|
def uninstall_product_code(product_code)
|
62
62
|
raise "#{product_code} is not installed" unless(product_code_installed?(product_code))
|
63
63
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
64
|
+
cmd = "msiexec.exe"
|
65
|
+
cmd = "#{cmd} #{self[:mode]}" if(has_key?(:mode))
|
66
|
+
cmd = "#{cmd} /x #{product_code}"
|
67
|
+
msiexec(cmd)
|
68
|
+
if(product_code_installed?(product_code))
|
69
|
+
properties = installed_properties(product_code)
|
70
70
|
raise "Failed to uninstall #{properties['InstalledProductName']} #{properties['VersionString']}"
|
71
|
-
|
71
|
+
end
|
72
72
|
end
|
73
73
|
|
74
74
|
def msi_properties(msi_file)
|
@@ -78,51 +78,51 @@ class WindowsInstaller < Hash
|
|
78
78
|
|
79
79
|
sql_query = "SELECT * FROM `Property`"
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
81
|
+
db = @installer.OpenDatabase(msi_file, 0)
|
82
|
+
view = db.OpenView(sql_query)
|
83
|
+
view.Execute(nil)
|
84
84
|
|
85
|
-
record = view.Fetch()
|
86
|
-
return nil if(record == nil)
|
87
|
-
|
88
|
-
while(!record.nil?)
|
89
|
-
properties[record.StringData(1)] = record.StringData(2)
|
90
85
|
record = view.Fetch()
|
91
|
-
|
92
|
-
db.ole_free
|
93
|
-
db = nil
|
86
|
+
return nil if(record == nil)
|
94
87
|
|
95
|
-
|
88
|
+
while(!record.nil?)
|
89
|
+
properties[record.StringData(1)] = record.StringData(2)
|
90
|
+
record = view.Fetch()
|
91
|
+
end
|
92
|
+
db.ole_free
|
93
|
+
db = nil
|
94
|
+
|
95
|
+
return properties
|
96
96
|
end
|
97
97
|
|
98
98
|
def installation_properties(product_name_or_product_code)
|
99
99
|
product_code = product_name_or_product_code
|
100
100
|
properties = installed_properties(product_code)
|
101
101
|
if(properties.nil?)
|
102
|
-
|
103
|
-
|
104
|
-
|
102
|
+
product_name = product_name_or_product_code
|
103
|
+
product_code = installed_get_product_code(product_name)
|
104
|
+
return nil if(product_code == '')
|
105
105
|
|
106
|
-
|
107
|
-
|
106
|
+
properties = installed_properties(product_code)
|
107
|
+
end
|
108
108
|
|
109
|
-
|
109
|
+
return nil if(properties.nil?)
|
110
110
|
|
111
|
-
|
112
|
-
|
111
|
+
upgrade_code = get_upgrade_code(product_code)
|
112
|
+
properties['UpgradeCode'] = upgrade_code unless(upgrade_code.nil?)
|
113
113
|
|
114
114
|
return properties
|
115
115
|
end
|
116
116
|
|
117
117
|
def installed_products
|
118
118
|
products = []
|
119
|
-
|
120
|
-
|
119
|
+
@installer.Products.each { |installed_product_code| products << installed_product_code }
|
120
|
+
return products
|
121
121
|
end
|
122
122
|
|
123
123
|
def product_codes(upgrade_code)
|
124
|
-
|
125
|
-
|
124
|
+
upgrade_codes = get_upgrade_codes
|
125
|
+
return (upgrade_codes.key?(upgrade_code)) ? upgrade_codes[upgrade_code] : []
|
126
126
|
end
|
127
127
|
|
128
128
|
private
|
@@ -132,98 +132,101 @@ class WindowsInstaller < Hash
|
|
132
132
|
@installer.Products.each do |product_code|
|
133
133
|
name = @installer.ProductInfo(product_code, "ProductName")
|
134
134
|
return product_code if (product_name == name)
|
135
|
-
|
135
|
+
end
|
136
136
|
|
137
137
|
return ''
|
138
138
|
end
|
139
139
|
|
140
140
|
def installed_properties(product_code)
|
141
|
-
|
141
|
+
return nil if(!product_code_installed?(product_code))
|
142
142
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
143
|
+
hash = Hash.new
|
144
|
+
# known product keywords found on internet. Would be nice to generate.
|
145
|
+
%w[Language PackageCode Transforms AssignmentType PackageName InstalledProductName VersionString RegCompany
|
146
|
+
RegOwner ProductID ProductIcon InstallLocation InstallSource InstallDate Publisher LocalPackage HelpLink
|
147
|
+
HelpTelephone URLInfoAbout URLUpdateInfo InstanceType].sort.each do |prop|
|
148
|
+
value = @installer.ProductInfo(product_code, prop)
|
149
|
+
hash[prop] = value unless(value.nil? || value == '')
|
150
|
+
end
|
151
|
+
hash['ProductCode'] = product_code
|
152
|
+
return hash
|
153
153
|
end
|
154
154
|
|
155
155
|
def get_upgrade_codes
|
156
156
|
upgrade_codes = {}
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
157
|
+
property_value = @installer.ProductsEx('','',7).each do |prod|
|
158
|
+
begin
|
159
|
+
local_pkg = prod.InstallProperty('LocalPackage')
|
160
|
+
rescue
|
161
|
+
next
|
162
|
+
end
|
163
|
+
|
164
|
+
db = @installer.OpenDataBase(local_pkg, 0)
|
165
|
+
query = 'SELECT `Value` FROM `Property` WHERE `Property` = \'UpgradeCode\''
|
166
|
+
view = db.OpenView(query)
|
167
|
+
view.Execute
|
168
|
+
|
169
|
+
record = view.Fetch
|
170
|
+
unless(record.nil?)
|
171
|
+
upgrade_code = record.StringData(1)
|
172
|
+
|
173
|
+
upgrade_codes[upgrade_code] ||= []
|
174
|
+
upgrade_codes[upgrade_code] << prod.ProductCode
|
175
|
+
end
|
173
176
|
end
|
174
|
-
|
175
|
-
|
177
|
+
|
178
|
+
return upgrade_codes
|
176
179
|
end
|
177
180
|
|
178
181
|
def get_upgrade_code(product_code)
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
182
|
+
return nil if(!product_code_installed?(product_code))
|
183
|
+
upgrade_codes = get_upgrade_codes
|
184
|
+
upgrade_codes.each do |upgrade_code, product_codes|
|
185
|
+
product_codes.each { |pc| return upgrade_code if(pc == product_code) }
|
186
|
+
end
|
187
|
+
return nil
|
185
188
|
end
|
186
189
|
|
187
190
|
def msiexec(cmd)
|
188
191
|
cmd_options = { echo_command: false, echo_output: false} unless(self[:debug])
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
192
|
+
if(self.has_key?(:administrative_user))
|
193
|
+
msiexec_admin(cmd, cmd_options)
|
194
|
+
else
|
195
|
+
command = Execute.new(cmd, cmd_options)
|
196
|
+
command.execute
|
197
|
+
end
|
195
198
|
end
|
196
199
|
|
197
200
|
def msiexec_admin(cmd, options)
|
198
201
|
cmd = "runas /noprofile /savecred /user:#{self[:administrative_user]} \"#{cmd}\""
|
199
|
-
|
200
|
-
|
202
|
+
command = Execute.new(cmd, options)
|
203
|
+
wait_on_spawned(cmd) { cmd.execute }
|
201
204
|
end
|
202
205
|
|
203
206
|
def wait_on_spawned_process(cmd)
|
204
|
-
|
207
|
+
pre_execute = Sys::ProcTable.ps
|
205
208
|
|
206
|
-
|
207
|
-
|
209
|
+
pre_pids = []
|
210
|
+
pre_execute.each { |ps| pre_pids << ps.pid }
|
208
211
|
|
209
|
-
|
212
|
+
yield
|
210
213
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
+
exe = cmd[:command].match(/\\(?<path>.+\.exe)/i).named_captures['path']
|
215
|
+
exe = File.basename(exe)
|
216
|
+
#puts "Exe: #{exe}"
|
214
217
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
218
|
+
msiexe_pid = 0
|
219
|
+
post_execute = Sys::ProcTable.ps
|
220
|
+
post_execute.each do |ps|
|
221
|
+
msiexe_pid = ps.pid if((ps.name.downcase == exe.downcase) && pre_pids.index(ps.pid).nil?)
|
222
|
+
end
|
223
|
+
|
224
|
+
if(msiexe_pid != 0)
|
225
|
+
loop do
|
226
|
+
s = Sys::ProcTable.ps(msiexe_pid)
|
227
|
+
break if(s.nil?)
|
228
|
+
sleep(1)
|
229
|
+
end
|
230
|
+
end
|
228
231
|
end
|
229
232
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: WindowsInstaller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.22
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Marshall
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: execute
|
@@ -108,7 +108,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
108
|
version: '0'
|
109
109
|
requirements: []
|
110
110
|
rubyforge_project:
|
111
|
-
rubygems_version: 2.
|
111
|
+
rubygems_version: 2.5.2
|
112
112
|
signing_key:
|
113
113
|
specification_version: 4
|
114
114
|
summary: Class wrapper for Microsoft Windows WindowsInstaller object
|