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

Reply via email to