Looks an article for your blog.
Webster From: Michael B. Smith [mailto:mich...@smithcons.com] Subject: RE: Powershell help So.. I replied to him offlist. But the application issue is pretty generic. Note that I am not a master-developer. I just get by. There are 3 things at play here: 1] COM+, while it can be easily used within PowerShell, doesn't really respect the garbage collector (nor will it ever - it's not a managed interface) 2] .Net holds file locks until the object owning the file lock is disposed and its memory is garbage collected 3] NTFS doesn't allow you to actually remove a file while a file lock still exists - however it does allow you to enqueue a single pending file removal operation And I guess a fourth one: 4] Unless you specifically tell PowerShell to clean up, it doesn't clean up until the PowerShell session exits Now, the first three items are not PowerShell specific. They are true in all Windows programming and something that any developer eventually learns to handle (or they just release crappy code, that happens too). PowerShell hides most of this from you because of item (4). You close your PowerShell session - and most things get cleaned up. Most PowerShell scripts do not illustrate proper cleanup because of this. COM doesn't get automatically cleaned up, but that's RELATIVELY low-use. So, the first thing I illustrate is about items 1 - 3 (you need a writable c:\temp directory for this to work): Echo "hi`nhi`nhi`n" > c:\temp\file.txt $imsg = new-object -com cdo.message $iBP = $imsg.AddAttachment( "c:\temp\file.txt" ) ## bp stands for "body part", "I" is for "interface" $iBP.SaveToFile( "c:\temp\file2.txt" ) ## create owner-lock on file2.txt $iBP = $null [GC]::Collect() ## this releases lock on file2.txt but not on file.txt, because iBP is simply an interface on imsg. Erase c:\temp\file2.txt ## does remove file Erase c:\temp\file.txt ## does not remove file - but does create a pending remove operation Dir c:\temp\fil*.txt ## prove it :-) $imsg = $null [GC]::Collect() ## file should be gone Dir c:\temp\fil*.txt ## prove it :-) This example show how NTFS allows you to create a pending delete operation and it will execute that operation when the file lock is removed. But the file doesn't actually get removed until the lock is released. It also illustrates how to clean up these specific COM objects and release their memory and it proves the operation of each of items 1 - 3. The second example is more complicated. In this case, we have a CDO.Message object that has zero or more attachments. So the example can be self-contained, we create the message and it's attachments. Where is message actually comes from is irrelevant to the example. # part A Echo "hi`nhi`nhi`n" > c:\temp\file.txt $imsg = new-object -com cdo.message $iBP1 = $imsg.AddAttachment( "c:\temp\file.txt" ) ## bp stands for "body part", "I" is for "interface" cp c:\temp\file.txt c:\temp\file1.txt ## to have a second file to attach $iBP2 = $imsg.AddAttachment( "c:\temp\file1.txt" ) In the second part of the example, we process the message and split out each attachment and forward that attachment (using email) to someone else. Note that the message itself is a COM+ object, but we use a .Net mechanism for sending email. In order for this to interop between COM and .Net properly, we save the message to a file as an intermediate location, and we want to clean up that file when we are done. # part B $i = 2 Foreach( $attach in $imsg.Attachments ) ## examine each attachment in the message { $attach.SaveToFile( "c:\temp\file$($i.ToString()).txt" ) $fileattachment = "c:\temp\file$($i.ToString()).txt" $i++ $mailer = new-object Net.Mail.SMTPclient( "win2008r2ex2010", 25 ) $msg = new-object Net.Mail.MailMessage( "mich...@theessentialexchange.com", "mich...@smithcons.com", "Attachment test" , "" ) $attachment = new-object Net.Mail.Attachment( $fileattachment ) $msg.attachments.add( $attachment ) $mailer.send($msg) # clean up mailer - but note - I CANNOT fully clean up $attach because $imsg doesn't belong to my scope! # most powershell scripts don't bother to do this $attachment.Dispose() ## we force-dispose of the attachment. Otherwise, because of the interop, we have a hanging file lock $attachment = $null ## set the refCounts on all our objects to zero $msg = $null $mailer = $null $attach = $null [GC]::Collect() ## this will call Dispose() on all .Net base objects where refCount == 0 } In the third part of the example, we finish cleanup. This is the part that is typically missing from most PowerShell scripts. It's also the most finicky part of the script. I output information to prove that it works. Note the COM specific Dispose calls (as opposed to a .Net Dispose() method ccall). # part C ## clean up imsg and attachment records $iBP1.Dispose $iBP2.Dispose $iBP1 = $null $iBP2 = $null $imsg = $null [GC]::Collect() ## all locks are released ## now we can clean up our generated files. show all of them first Dir c:\temp\fil*.txt $null = read-host "press enter to continue (should be 4 files above)" For( $j = 2; $j -lt $i; $j +=1 ) { Erase "c:\temp\file$($j.ToString()).txt" } ## prove it Dir c:\temp\fil*.txt $null = read-host "press enter to continue (should be 2 files above)" ## remove two dummy files Erase c:\temp\file1.txt Erase c:\temp\file.txt ## prove it again Dir c:\temp\fil*.txt $null = read-host "press enter to continue (should be zero files above)" You can combine parts A, B, and C into a single PS1 file and execute it yourself. This is not the only way to handle these issues. But the concepts are the same whether you use this linear methodology or try/catch/finally or a PSObject-based solution. Regards, Michael B. Smith Consultant and Exchange MVP http://TheEssentialExchange.com ~ Finally, powerful endpoint security that ISN'T a resource hog! ~ ~ <http://www.sunbeltsoftware.com/Business/VIPRE-Enterprise/> ~ --- To manage subscriptions click here: http://lyris.sunbelt-software.com/read/my_forums/ or send an email to listmana...@lyris.sunbeltsoftware.com with the body: unsubscribe ntsysadmin