File Coverage

blib/lib/Win32/WindowsUpdate.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Win32::WindowsUpdate;
2              
3 1     1   39767 use strict;
  1         2  
  1         630  
4 1     1   8 use warnings;
  1         2  
  1         45  
5 1     1   957 use Win32::OLE qw(in);
  0            
  0            
6             use Win32::TieRegistry (Delimiter => '/');
7              
8             our $VERSION = '0.04';
9              
10             =head1 NAME
11              
12             Win32::WindowsUpdate - Access to Windows Update functions
13              
14             =head1 DESCRIPTION
15              
16             Provides the ability to see installed and available (not installed) Windows Updates as well as install them.
17             It provides other features, such as the ability to disable/enable automatic reboot of the computer when users are logged on.
18             Read the list of methods below to see the available features.
19              
20             If a feature is missing, request it!
21             File a "wishlist" bug on the CPAN bug tracker or shoot me an email (C).
22              
23             If you test this, please let me know your results.
24             It's not ready for production use, but any testing is greatly appreciated.
25             It should work, but I haven't tested on enough systems to promise this.
26              
27             =head1 EXAMPLE
28              
29             use Win32::WindowsUpdate;
30             my $wu = Win32::WindowsUpdate->new;
31              
32             die "Reboot first...\n" if $wu->rebootRequired;
33              
34             $wu->setAutoRebootWhileLoggedOn(0); # don't auto reboot
35              
36             my @updates;
37             foreach my $update ($wu->updates)
38             {
39             next unless ($update->{Title} =~ m/Security Update.*Windows XP/i); # filter for something
40             push(@updates, $update); # push it into the updates list
41             }
42              
43             sleep 1 while ($wu->installerBusy); # if Windows Installer is busy
44             die "Reboot first...\n" if $wu->rebootRequired; # if whatever WI finished doing requires reboot
45             $wu->install(@updates); # install your selected updates
46             print "Installed updates want you to reboot...\n" if $wu->rebootRequired;
47              
48             # .. or to blindly install all updates:
49              
50             use Win32::WindowsUpdate;
51             my $wu = Win32::WindowsUpdate->new;
52             $wu->install($wu->updates);
53              
54             =head1 METHODS
55              
56             =head2 new
57              
58             my $wu = Win32::WindowsUpdate->new;
59             my $wu = Win32::WindowsUpdate->new({online => 0});
60              
61             Creates the WindowsUpdate object.
62              
63             =over
64              
65             =item online
66              
67             Defaults to C<1>.
68             Check online for updates.
69              
70             =back
71              
72             =cut
73              
74             sub new
75             {
76             my $class = shift;
77             my $args = shift;
78             my $self = {};
79              
80             $self->{online} = (defined($args->{online}) ? ($args->{online} ? 1 : 0) : 1); # online by default
81             $self->{updateSession} = Win32::OLE->new('Microsoft.Update.Session') or die "ERROR creating Microsoft.Update.Session\n";
82             $self->{updateSearcher} = $self->{updateSession}->CreateUpdateSearcher or die "ERROR creating CreateUpdateSearcher\n";
83             $self->{updateSearcher}->{Online} = $self->{online};
84             $self->{systemInfo} = Win32::OLE->new('Microsoft.Update.SystemInfo') or die "ERROR creating Microsoft.Update.SystemInfo\n";
85              
86             return bless($self, $class);
87             }
88              
89             =head2 updates
90              
91             my $updates = $wu->updates;
92             # .. or ..
93             my @updates = $wu->updates;
94              
95             my $updatesHidden = $wu->updates({IsHidden => 1});
96              
97             Returns a list of updates available for download and install.
98              
99             =over
100              
101             =item IsInstalled
102              
103             Returns installed (if true) or not installed (if false) updates.
104             Defaults to C<0>.
105              
106             =item IsHidden
107              
108             Returns hidden (if true) or not hidden (if false) updates.
109             Defaults to C<0>.
110              
111             =back
112              
113             =cut
114              
115             sub updates
116             {
117             my $self = shift;
118             my $args = shift;
119              
120             my $isInstalled = (defined($args->{IsInstalled}) ? ($args->{IsInstalled} ? 1 : 0) : 0);
121             my $isHidden = (defined($args->{IsHidden}) ? ($args->{IsHidden} ? 1 : 0) : 0);
122             my $query = $args->{query} || "IsInstalled = $isInstalled AND IsHidden = $isHidden";
123              
124             my $queryResult = $self->{updateSearcher}->Search($query);
125             my $updates = $queryResult->Updates;
126              
127             my @updates;
128             foreach my $update (in $updates)
129             {
130             my $info = {};
131              
132             $info->{UpdateId} = $update->Identity->UpdateID;
133             $info->{Title} = $update->Title;
134             $info->{Description} = $update->Description;
135             $info->{RebootRequired} = $update->RebootRequired;
136             $info->{ReleaseNotes} = $update->ReleaseNotes;
137             $info->{EulaText} = $update->EulaText;
138             $info->{EulaAccepted} = $update->EulaAccepted;
139              
140             push(@{$info->{Category}}, {Name => $_->Name, CategoryID=> $_->CategoryID}) foreach (in $update->Categories);
141             push(@{$info->{MoreInfoUrl}}, $_) foreach (in $update->MoreInfoUrls);
142             push(@{$info->{KBArticleID}}, $_) foreach (in $update->KBArticleIDs);
143             push(@{$info->{SecurityBulletinID}}, $_) foreach (in $update->SecurityBulletinIDs);
144             push(@{$info->{SupersededUpdateID}}, $_) foreach (in $update->SupersededUpdateIDs);
145              
146             push(@updates, $info);
147             }
148              
149             return (wantarray ? @updates : \@updates);
150             }
151              
152             =head2 installed
153              
154             my $installed = $wu->installed;
155             # .. or ..
156             my @installed = $wu->installed;
157              
158             Returns a list of installed updates.
159              
160             =cut
161              
162             sub installed
163             {
164             my $self = shift;
165             my $args = shift;
166              
167             return $self->updates({
168             IsInstalled => 1,
169             IsHidden => $args->{IsHidden},
170             });
171             }
172              
173             =head2 install
174              
175             my @updates;
176             foreach my $update ($wu->updates)
177             {
178             printf("Available Update: %s %s\n", $update->{UpdateId}, $update->{Title});
179             next unless ($update->{Title} =~ m/Security Update.*Windows XP/i);
180             printf("Want to install: %s\n", $update->{Title});
181              
182             push(@updates, $update->{UpdateId});
183             # .. or .. (both ways will work)
184             push(@updates, $update);
185             }
186             $wu->install(@updates);
187              
188             # .. or ..
189              
190             $wu->install($wu->updates); # if you want to install all updates
191              
192             Install specified updates.
193             Provide an array of either C (directly from C<< $wu->updates >>) or C.
194             See example above for usage.
195              
196             =cut
197              
198             sub install
199             {
200             my $self = shift;
201             my @updates = @_;
202              
203             return undef unless scalar(@updates); # no updates specified? don't run.
204              
205             my %updates;
206             # I'm sure there's a better way... gimme patch!
207             foreach my $update (@updates)
208             {
209             my $uid = (ref($update) eq 'HASH' ? $update->{UpdateId} : $update);
210             $updates{$uid}++;
211             }
212              
213             my $updatecoll = Win32::OLE->new('Microsoft.Update.UpdateColl') or die "ERROR creating Microsoft.Update.UpdateColl\n";
214             $updatecoll->Clear;
215              
216             my $queryResult = $self->{updateSearcher}->Search("IsInstalled = 0 AND IsHidden = 0") or die "ERROR in query\n";
217             my $updates = $queryResult->Updates;
218              
219             foreach my $update (in $updates)
220             {
221             my $updateID = $update->Identity->UpdateID;
222             next unless $updates{$updateID};
223             $update->AcceptEula;
224             $updatecoll->Add($update);
225             }
226              
227             my $downloader = $self->{updateSession}->CreateUpdateDownloader;
228             $downloader->LetProperty('Updates', $updatecoll);
229             my $downloadResult = $downloader->Download;
230              
231             my $installer = $self->{updateSession}->CreateUpdateInstaller;
232             $installer->LetProperty('Updates', $updatecoll);
233             $installer->{AllowSourcePrompts} = 0;
234             $installer->{ForceQuiet} = 1;
235             my $installResult = $installer->Install;
236              
237             return $installResult->ResultCode;
238             }
239              
240             =head2 rebootRequired
241              
242             my $needToReboot = $wu->rebootRequired();
243              
244             Returns bool. True if reboot is required, false otherwise.
245              
246             This doesn't necessarily mean any installs will fail, but there is something pending a reboot.
247             Your install might fail, though. Keep this in mind.
248              
249             You should check this before and after you run C.
250             If it's true before you install, you may want to reboot before you install.
251             If it's true after you install, you'll want to reboot sometime.
252              
253             =cut
254              
255             sub rebootRequired
256             {
257             my $self = shift;
258             return ($self->{systemInfo}->RebootRequired ? 1 : 0);
259             }
260              
261             =head2 installerBusy
262              
263             my $installerIsBusy = $wu->installerBusy();
264              
265             Returns bool. True if Windows Installer is busy, false otherwise.
266              
267             If the installer is busy, you probably won't have any success at running C.
268             You could just sit in a loop waiting for it to no longer be busy, but you'd want to check
269             C immediately before you run C to be sure you won't want to reboot
270             first.
271              
272             =cut
273              
274             sub installerBusy
275             {
276             my $self = shift;
277             my $installer = $self->{updateSession}->CreateUpdateInstaller;
278             return ($installer->IsBusy ? 1 : 0);
279             }
280              
281             =head2 setAutomaticUpdates
282              
283             Set the Automatic Updates setting.
284              
285             =over
286              
287             =item disable (or C<1>)
288              
289             Disable Automatic Updates altogether.
290             Any updates will need to performed manually (using this module, via Windows Updates, or your other methods)
291              
292             =item notifyonly (or C<2>)
293              
294             Notify of new updates, but don't download or install them.
295              
296             =item notifydownload (or C<3>)
297              
298             Download new updates automatically, but don't install them.
299             The user will be notified that updates are pending and will have the ability to choose which updates to install.
300              
301             =item automatic (or C<4>)
302              
303             Download and install updates automatically.
304             The user will only be notified that they need to reboot after updates are completed (if reboot is needed).
305             Windows might automatically reboot itself after some period of time to "be helpful".
306              
307             =back
308              
309             =cut
310              
311             sub setAutomaticUpdates
312             {
313             my $self = shift;
314             my $setting = lc(shift);
315             my $value = 0;
316              
317             $value = 1 if ($setting eq 'disable' or $setting eq '1');
318             $value = 2 if ($setting eq 'notifyonly' or $setting eq '2');
319             $value = 3 if ($setting eq 'notifydownload' or $setting eq '3');
320             $value = 4 if ($setting eq 'automatic' or $setting eq '4');
321              
322             return undef unless ($value >= 1 && $value <= 4);
323              
324             my $state = ($value == 1 ? 7 : 2); # 7=audisabled, 2=detectionpending
325              
326             my $key = $Registry->{'LMachine/Software/Microsoft/Windows/CurrentVersion/WindowsUpdate/Auto Update/'};
327             $key->{'/AUState'} = ['0x000'.$state, 'REG_DWORD'];
328             $key->{'/AUOptions'} = ['0x000'.$value, 'REG_DWORD'];
329              
330             return $value;
331             }
332              
333             =head2 setAutoRebootWhileLoggedOn
334              
335             $wu->setAutoRebootWhileLoggedOn(1); # enable (auto reboot, Windows default behavior)
336             $wu->setAutoRebootWhileLoggedOn(0); # disable (no auto reboot)
337              
338             Enable or disable the automatic reboot "feature".
339             If there is a pending reboot, it won't take effect until after the reboot.
340             Will still nag for a reboot, but the reboot won't be automatic after a period of time.
341             If the logged on user doesn't have reboot privilege, the nag will still appear, but it won't do anything except nag.
342             Without this setting, Windows will reboot automatically after the nag screen has been ignored for five minutes...
343             and for non-admins, they can't prevent the reboot.
344             This option (if set to C<0>) will prevent this behavior.
345             Defaults to C<0> if you don't specify a value.
346              
347             See also http://blogs.msdn.com/tim_rains/archive/2004/11/15/257877.aspx
348              
349             =cut
350              
351             sub setAutoRebootWhileLoggedOn
352             {
353             my $self = shift;
354             my $setting = shift;
355             $setting = !!$setting; # yummy double-negative makes a tasty bool
356              
357             my $key = $Registry->{'LMachine/Software/Policies/Microsoft/Windows/'};
358             $key = $key->CreateKey('Windows Update/');
359             $key = $key->CreateKey('AU/');
360             $key->{'/NoAutoRebootWithLoggedOnUsers'} = ['0x000'.(!$setting), 'REG_DWORD'];
361              
362             return $setting;
363             }
364              
365             =head2 setRebootNag (noop)
366              
367             NOOP.
368             Incomplete as I find a reasonable way to do this, preferably without requiring a reboot.
369             Needs to reliably support Win2k and newer, including Pro, Home, and Server versions.
370             Ideas or patches? Let me know.
371              
372             See also http://www.codinghorror.com/blog/archives/000294.html
373              
374             =cut
375              
376             sub setRebootNag
377             {
378             my $self = shift;
379             }
380              
381             =head1 TODO
382              
383             =over
384              
385             =item Provide ability to install specific updates.
386              
387             =item Provide ability to hide and unhide specific updates.
388              
389             =item Provide ability to uninstall updates (maybe?).
390              
391             =item Provide ability to do background install with ability to check on status.
392              
393             =item Provide ability to only download updates, not install them.
394              
395             =item Provide ability to disable/enable "Please reboot..." nag messages after update install. (see C above)
396              
397             =item Determine other necessary features. (email me with your requests: C or use the CPAN bug tracker)
398              
399             =back
400              
401             =head1 BUGS/WISHLIST
402              
403             B
404             Report any bugs to the CPAN bug tracker or email me C.
405              
406             To wishlist something, use the CPAN bug tracker (set as wishlist) or email me.
407             I'd be happy to implement useful functionality in this module on request.
408             I'd also be happy to accept patches.
409              
410             =head1 COPYRIGHT/LICENSE
411              
412             Copyright 2009 Megagram. You can use any one of these licenses: Perl Artistic, GPL (version >= 2), BSD.
413              
414             =head2 Perl Artistic License
415              
416             Read it at L.
417             This is the license we prefer.
418              
419             =head2 GNU General Public License (GPL) Version 2
420              
421             This program is free software; you can redistribute it and/or
422             modify it under the terms of the GNU General Public License
423             as published by the Free Software Foundation; either version 2
424             of the License, or (at your option) any later version.
425              
426             This program is distributed in the hope that it will be useful,
427             but WITHOUT ANY WARRANTY; without even the implied warranty of
428             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
429             GNU General Public License for more details.
430              
431             You should have received a copy of the GNU General Public License
432             along with this program. If not, see http://www.gnu.org/licenses/
433              
434             See the full license at L.
435              
436             =head2 GNU General Public License (GPL) Version 3
437              
438             This program is free software: you can redistribute it and/or modify
439             it under the terms of the GNU General Public License as published by
440             the Free Software Foundation, either version 3 of the License, or
441             (at your option) any later version.
442              
443             This program is distributed in the hope that it will be useful,
444             but WITHOUT ANY WARRANTY; without even the implied warranty of
445             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
446             GNU General Public License for more details.
447              
448             You should have received a copy of the GNU General Public License
449             along with this program. If not, see http://www.gnu.org/licenses/
450              
451             See the full license at L.
452              
453             =head2 BSD License
454              
455             Copyright (c) 2009 Megagram.
456             All rights reserved.
457              
458             Redistribution and use in source and binary forms, with or without modification, are permitted
459             provided that the following conditions are met:
460              
461             * Redistributions of source code must retain the above copyright notice, this list of conditions
462             and the following disclaimer.
463             * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
464             and the following disclaimer in the documentation and/or other materials provided with the
465             distribution.
466             * Neither the name of Megagram nor the names of its contributors may be used to endorse
467             or promote products derived from this software without specific prior written permission.
468              
469             THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
470             WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
471             PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
472             ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
473             LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
474             INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
475             OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
476             IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
477              
478             =cut
479              
480             1;