1

I have my PowerShell project broken into modules. But because they are modules I have to reload them every time I change them. So I wrote a loop that has a FileSystemWatcher and if one of the .psm1 file changes it will either reload or import that module.

The problem is that the above loop isn't going to let me run other scripts in its working environment, so a new environment will not have the same modules loaded/reloaded for it. I need to keep these modules out of the primary default PowerShell modules folder(s). Is there a way to run the script that reloads the modules when they change in the same environment or affect a certain environment?


UPDATE

So I run the following Module-Loader.ps1 script. The code block associated with the 'FileChanged' event does fire when I 'save' a *.psm1 file after having been modified. However two issues occure: 1) it fires twice when I save

2a) If the module is not loaded, it will run Import-Module $PWD\ModuleName, but it won't have actually loaded at least in the environment (if I run the same code in the environment it will load)

2b) if it is loaded, and it tries to remove the module, it will error that none exists.

# create a FileSystemWatcher on the currect directory
$filter = '*.psm1'
$folder = $PWD
$watcher = New-object IO.FileSystemWatcher $folder, $filter -Property @{IncludeSubdirectories = $false; EnableRaisingEvents = $true; NotifyFilter = [IO.NotifyFilters]'LastWrite'}
Register-ObjectEvent $watcher Changed -SourceIdentifier FileChanged -Action { 
$name = $Event.SourceEventArgs.Name 
$filename = $name.Remove($name.IndexOf('.'), 5)

$loadedModule = Get-Module | ? { $_.Name -eq $filename }
write-host $filename

if ($loadedModule) {
    write-host "Reloading Module $folder\$($filename)"
    Reload-Module $folder\$filename
} else {
    write-host "Importing Module $folder\$($filename)"
    Import-Module $folder\$filename
}
}

I am of the opinion that though this is being ran in a session, the code block in the event is not associated with this specific environment.

Abbas
  • 6,720
  • 4
  • 35
  • 49
pghtech
  • 3,642
  • 11
  • 48
  • 73
  • I'm not too sure I understand your question. Are you saying, since it's a loop, it's blocking other code from running? If so you can have the file system watcher fire off a script block asynchronously by registering a event handler instead of using a loop. If that sounds like what you need I can give you some example code of that. – Andy Arismendi Dec 23 '11 at 15:07
  • @Andy: Yes, because it is in a loop, its blocking other code. And since reloading/importing modules will only be in the current environment that the File System watcher is running in, I can't rerun scripts that are dependent on the reloaded/imported modules. I am not sure having the watch fire off an event would work because then I have to hard code what that is going to be. But, if it isn't to much, could you post some example? – pghtech Dec 23 '11 at 15:26

1 Answers1

2

Here is an example from some code I have that copies a folder to a shared folder any time something has changed in it. It's kinda my little dropbox implementation :-)

Any time one of the file system watcher event types such as Changed occurs, the code specified in the -Action parameter of the Register-ObjectEvent cmdlet will fire.

In your -Action code you would call Import-Module with the -Force parameter to overwrite the current one in memory.

function Backup-Folder {
    & robocopy.exe "c:\folder" "\\server\share" /MIR /W:10 /R:10
}

$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = "c:\folder"
$watcher.IncludeSubdirectories = $true
$watcher.EnableRaisingEvents = $true

Register-ObjectEvent $watcher "Changed" -Action { Backup-Folder }
Register-ObjectEvent $watcher "Created" -Action { Backup-Folder }
Register-ObjectEvent $watcher "Deleted" -Action { Backup-Folder }
Register-ObjectEvent $watcher "Renamed" -Action { Backup-Folder }
Andy Arismendi
  • 50,577
  • 16
  • 107
  • 124
  • I took your suggestions and went this same route, and my Action basically checks if a module is loaded and either imports or reloads the module (.psm1) file that changed. However, I find that whatever is returned on the execution of the block of code, it doesn't load the module - at least in the environment the script was ran that bound the code block to the event. – pghtech Dec 26 '11 at 16:00
  • I was trying to see if there is a way to pass environment specific information in the code block and load modules against a specific session/environment – pghtech Dec 26 '11 at 16:13
  • If I understand correctly your saying the following. 1. Import-Module loads the initial module. 2. The module file changes. 3. The event handler fires the script block. 4. The script block re-loads the module. 5. The module changes do not take effect. Is that right? – Andy Arismendi Dec 26 '11 at 16:35
  • Only partially. 1) From PS env run module-loader script that monitors current directory, if a .psm1 file changes it checks if its already loaded and import/reload. The code block that is the event is very simple, it just does a get-module where that psm1 file has changed and if loaded, reload, else import. But it evaluates that the module is loaded, but then throughs the error that it isn't loaded when it attempts reload-module. – pghtech Dec 27 '11 at 05:04
  • Could you post your code so I might be able to help you work around the error? – Andy Arismendi Dec 27 '11 at 09:39
  • The problem found with this is that when the event handler is called, it operates within its own encapsulated environment, and doesn't reload in the user's active environment. – pghtech Jan 24 '12 at 15:03