Monthly Archives: October 2011

Currently I am building the prototype of a PowerShell-based automation solution. From the high level perspective it will work like a Print Spooler meaning that some kind of watchdog listens in the background to a particular directory for incoming files in order to process them or rather pass them to a processing engine.

With PowerShell 2.0 it is an easy task to leverage the .NET Framework’s FileSystemWatcher Class to establish such a file system watchdog:

PowerShell

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

PSC:\Scripts>$fsw=New-ObjectSystem.IO.FileSystemWatcher

PSC:\Scripts>$fsw

NotifyFilter:FileName,DirectoryName,LastWrite

EnableRaisingEvents:False

Filter:*.*

IncludeSubdirectories:False

InternalBufferSize:8192

Path:

Site:

SynchronizingObject:

Container:

PSC:\Scripts>

Obviously, a freshly initialized instance of System.IO.FileSystemWatcher is very roughly prepared to watch for file system changes. Five properties are preset, namely NotifyFilter (specifies the changes to watch for in a file system object), EnableRaisingEvents (indicates whether events are raised or not – yes, we want!), Filter (specifies what files are monitored), IncludeSubdirectories (indicates whether subdirectories are monitored or not), and InternalBufferSize. (Please look in the MSDN for more info on the FileSystemWatcher class.)

So, how about a PowerShell function to initialize a .NET FileSystemWatcher object with proper values for Patch, Filter, NotifyFilter, IncludeSubdirectories, and EnableRaisingEvents? Here we go!

PowerShell

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

functionNew-FSWatcher

{

<#

.SYNOPSIS

Initializes a .NET file system watcher

.DESCRIPTION

Initializes a .NET file system watcher, given the specified directory and optionally the type of files to monitor.

That’s only half the battle. With New-FSWatcher you are able to initialize a FileSystemWatcher. But how to start monitoring?

If you look at the methods of System.IO.FileSystemWatcher you will sooner or later discover WaitForChanged(), “a synchronous method that returns a structure that contains specific information on the change that occurred, given the type of change you want to monitor and the time (in milliseconds) to wait before timing out”.

So, with WaitForChanged() it is perfectly possible to stop script processing for a given amount of maximum time in order to wait for a file. Apart from the fact that I have a watchdog in mind that should wait for file system events in the background, I will provide a function to leverage a synchronous (foreground) wait for files anyways:

PowerShell

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

functionStart-FSWatcher

{

<#

.SYNOPSIS

Listens the file system changes for a given amount of time.

.DESCRIPTION

Uses a previously configured FileSystemWatcher to listen for changes in a directory using a synchronous method that returns specific information on each change that occurred, given the type of change you want to monitor and the time to wait before timing out.

.PARAMETER FileSystemWatcher

Specifies a FileSystemWatcher object.

.PARAMETER Type

Specifies the type of change you want to monitor. Valid type are All (default), Changed, Created, Deleted, Disposed, Error, and Renamed.

Please note that Start-FSWatcher will stop monitoring the file system as soon as the first change occurs that matches the FileSystemWatcher object’s configuration (unless the Infinite switch has been specified which causes the function to invoke the WaitForChanges() method again and again till the end of time). If you want to wait for a particular file you can define a FileSystemWatcher object and setup its Filter property to match the exact file name. Therefore, concerning a specific file name to be monitored, there’s no need to change the function.

To return to square one finally: how to define the file system watcher event handlers that will be fired on change, creation, deletion, or renaming of a file or directory? This is really straightforward. Basically you need to use the Register-ObjectEvent Cmdlet with a previously initialized FileSystemWatcher object, the event to act on, and the action to be executed on that event. Although it seems to be overkill, I’ll provide a third function, Register-FSWatcherEventHandler, that helps to define a proper event action for a file system watcher:

PowerShell

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

functionRegister-FSWatcherEventHandler

{

<#

.SYNOPSIS

Registers a FileSystemWatcher event handler.

.DESCRIPTION

Specifies what is done when a file is changed, created, deleted, or renamed.

The PowerShell 2.0 function below, Set-PageFile, sets a page file to the given initial and maximum size.

At first sight this seems to be an easy task – just set the properties InitialSize and MaximumSize accordingly (Win32_PageFileSetting class). The tricky part is that the system won’t let you change any page file setting as long as the system automatically manages these settings. My function takes care of this fact and disables the Win32_ComputerSystem‘s AutomaticManagedPagefile property if necessary.

The function supports PowerShell’s -WhatIf switch. Therefore you can test it safely without any impact.

I wrote the PowerShell function below, Register-PSSnapin, to facilitate the usage of the InstallUtil.exe program in order to register a series of PowerShell snap-ins. Since this utility isn’t located in the “normal” command path, you have to find it in the .NET Framework’s directory. Or, with my function you just don’t care 😉

Side note: in PowerShell 2.0 the concept of snap-ins is substantially replaced by binary modules. Apart from the fact that the core of PowerShell 2.0 is delivered as snap-ins, many software vendors still provide a PowerShell snap-in to allow for command-line based administration and automation. (For example the latest release of Citrix Provisioning Services 6.0 ships a PowerShell Snap-in.)

PowerShell

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

<#

.SYNOPSIS

Registers PowerShell Snap-ins.

.DESCRIPTION

Uses the InstallUtil tool in the .NET Framework to register one ore more snap-ins

.PARAMETER Path

Specifies the path to the file name or &quot;module name&quot; of the snap-in