WindowsInstaller 0.1.21 → 0.1.22
Sign up to get free protection for your applications and to get access to all the features.
- 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
|