Quantcast
Channel: Scripting Blog
Viewing all 2129 articles
Browse latest View live

Use PowerShell to Back Up Word Docs

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to make a backup of all Word documents.

Hey, Scripting Guy! Question Hey, Scripting Guy! I need some help. I routinely edit Microsoft Word documents, but because of the changes I make to the file, I like to keep the original file intact. So what I do is open a Word document, then I use the Save As feature in Word to save a copy of the file into a different folder. I am wondering if I can have Windows PowerShell use the Word automation feature to do a Save As for me. Can you help me?

—GB

Hey, Scripting Guy! Answer Hello GB,

Microsoft Scripting Guy, Ed Wilson, is here. I spent nearly a dozen years in consulting work before I became the Scripting Guy. One of the first things I learned to ask was the question “Why?” It is not that I doubted the customer, but rather because sometimes a customer becomes fixated on a specific solution. When I know what the purpose of the activity is, I can often recommend an easier (and most cases, less expensive) solution.

GB, your question falls directly into this category, and it gives me a reason to begin Word Week.

Sure, I could use the Word automation model. I can open a Word document in a folder, call Save As, give it a new name, then close the Word document and open to the next one. But in this case, the question “Why?” really comes into play. Why do you want to do this?

Well, luckily you included the answer: You want to have a backup copy of a Word document before you begin editing. So, this actually makes it pretty easy. All I need is basic Windows PowerShell.

I can use Get-ChildItem to recurse through your directory structure, New-Item to create your backup folder, and Copy-Item to copy the documents to the backup folder. This will be much faster, and easier to do, than using the Word automation model to open and Save As each document in the folder. Much, much easier. So let's get started.

Create the backup folder

The first thing I do is use the New-Item cmdlet to create a new folder that I will use as the backup folder. To do this, I specify the item type as Directory and provide a path. At the same time, I use a couple of variables to specify the destination path for the source files and the backup folder location. I use –WhatIf now so the command does not actually execute. This will help me make sure the script works completely before I actually run it. These commands are shown here:

$bu = "e:\backup"

$source = "E:\Data\ScriptingGuys\2015"

New-Item -ItemType directory -Path $bu -WhatIf

Find the files

Now I need to find the document files. I have specified my directory starting point as a string that I stored in the $source variable. I filter out only files that have an extension of .docx, and I tell the cmdlet to recurse so that it will find all files in subfolders off of the main directory. I end the command with a pipe character ( | ) because I want each of the file objects that I find to pass to the next command. Here is the command:

Get-ChildItem -Path $source -Filter *.docx -Recurse |

Copy the files and give them a new name

I want to copy each file that I find to the backup destination, and I want to add the letters “bu” to the end of the file name. To do this, I use the Foreach-Object cmdlet and the Copy-Item cmdlet. Once again, because I want to see that the command works properly first, I add the –WhatIf parameter to Copy-Item, as shown here:

ForEach-Object {

Copy-Item -path $_.FullName -Destination ("$bu\{0}.bu.docx" -f $_.basename) -whatif }

I create the new file name based on the BaseName property of the file. The BaseName property is simply the file name without the file extension and without the file path. I pick up the backup location from the $bu variable, and I add .bu to the base file name. I then retain the .docx file extension. The complete script is shown here:

$bu = "e:\backup"

$source = "E:\Data\ScriptingGuys\2015"

New-Item -ItemType directory -Path $bu -WhatIf

Get-ChildItem -Path $source -Filter *.docx -Recurse |

ForEach-Object {

Copy-Item -path $_.FullName -Destination ("$bu\{0}.bu.docx" -f $_.basename) -whatif }

I run it, and examine the output in the Windows PowerShell ISE. This output is shown here:

Image of command output

I pay attention to the second line of the output, and then to the third and following lines. They tell me that the script will work as I expect, so I remove the two –WhatIf parameters. The revised script is shown here:

$bu = "e:\backup"

$source = "E:\Data\ScriptingGuys\2015"

New-Item -ItemType directory -Path $bu

Get-ChildItem -Path $source -Filter *.docx -Recurse |

ForEach-Object {

Copy-Item -path $_.FullName -Destination ("$bu\{0}.bu.docx" -f $_.basename) }

This time, the output is less impressive:

Image of command output

Now I check to see my newly created backup folder. As you can see in the following image, it is full of backup documents:

Image of menu

GB, that is all there is to using Windows PowerShell to back up your Word documents. Word Week will continue tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 


PowerTip: Find PowerShell Help for Specific Parameter

$
0
0

Summary: Use Windows PowerShell Help to assist with a specific parameter.

Hey, Scripting Guy! Question I know I can use Get-Help –full to see the complete Help for a Windows PowerShell cmdlet, but how can I
           see the Help for only  a specific parameter?

Hey, Scripting Guy! Answer Use the Get-Help cmdlet, specify the cmdlet name, and then specify the specific parameter, for example:

get-help Start-Process -Parameter wait

Use PowerShell to Create Documents from Template

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to create Word documents from a template.

Hey, Scripting Guy! Question Hey, Scripting Guy! I often need to create a bunch of Word documents. These documents have a number of standard features. Right now, I basically paste the last document I was working on into the folder, rename the file, open the file, delete all of the custom stuff, and leave the boilerplate stuff.

I know there should be an easier way to do this, but to be honest I am doing the work of three people, and I simply do not have time to figure out an easier way to do this. Now the process takes me less than five minutes to get ready for my new report. So anything you come up with would have to take less time than that, or else it is simply not worth my time. You seem to be a clever geek, can you help me? Thanks,

—RC

Hey, Scripting Guy! AnswerHello RC,

Microsoft Scripting Guy, Ed Wilson, is here. I used to try to do a lot of different stuff with Microsoft Word. In fact, I rarely used Normal.dot because I did not like the way it formatted stuff. I never used standard headings, and I seemed to fight with every version of Word until I finally just gave up. No, it just does not seem to be worth the effort. Besides, whenever it seems like I get something going the way that I really want it, everything changes. Instead, I just try to go with the flow.

RC, it seems like your situation would be a good candidate for creating a Word template. The problem is that templates get rather complicated. Instead, I just use a normal Word document. I make changes to that document, and then I give it the name of a template. I do not save this with a template extension—I just call the basic Word document a template.

Here is a Word document that I use as a template. I have adjusted the default font, font size, margins, and the spacing between paragraphs. Each new document has the same information in the upper-left corner of the document.

Image of menu

Suppose that Professor Hasenpfeffer lists the Shakespearean plays that we will read this semester, and the good professor has assigned short reader response papers for each play. Here is the list of plays from the class syllabus:

Image of list

Because I have my class template paper, and because I have my list of paper topics, I can create the seven paper templates that I will use when I write my reader response papers. This does not even require a script. But, because the line of code would wrap several times, I decide to type the command into the Windows PowerShell ISE. Here is the command:

Get-Content C:\Lit\Shakespeare.txt |

ForEach-Object {

Copy-Item -Path C:\lit\PaperTemplate.docx -Destination  `

 ("c:\lit\{0}_ew.docx" -f $_)}

This is a single-line command, but I wrote it on several lines so it would be easier to read. Basically, I read the contents of the text file. For each line in the text file, I copy my PaperTemplate.docx file. I then rename the file to be the name of the play, and I add my initials as part of the name of the document. I use a place holder for the name of the play that comes across the pipeline. Here is the folder after I run my command:

Image of menu

Now all I need to do is read the play, type my reactions, and I am done. Well, I probably still need to figure out the MLA handbook bibliography entry for Shakespearean plays, but other than that, I am done.

Later I will use a CSV file for input and modify my template so that Windows PowerShell gives me a better head start on my papers.

To script or not to script...that is the question.

RC, that is all there is to using Windows PowerShell to create Word documents from a template. Word Week will continue tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell to List Word Autocorrect Entries

$
0
0

Summary: Use Windows PowerShell to list autocorrect entries for Microsoft Word.

Hey, Scripting Guy! Question Microsoft Word keeps messing up what I type, and I suspect it could be an autocorrect issue. How can I use
           Windows PowerShell to list all the entries in Microsoft Word autocorrect?

Hey, Scripting Guy! Answer Use the New-Object cmdlet to create an instance of the Word.Application object. Look at autocorrect
           and Entries, then pipe the results to the Format-Table cmdlet, and look at Name and Value properties:

(New-Object -ComObject word.application).autocorrect.entries | ft name, value

  

Use PowerShell to Add Headers to Word Documents

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to add headers to Microsoft Word documents.

Microsoft Scripting Guy, Ed Wilson, is here. It snowed in Charlotte yesterday. It was not really a surprise because the weather service did a good job of warning that it would happen. Although the Scripting Wife and I were a bit worried that the power would go off, it turned out to be a huge non-event—at least for us. For the thousands of people who lost their power...well, that was a different story. It seems that these sorts of things always pile up—we lose the power, it is cold, there is ice and snow—it all happens at the same time. Why can’t we have a nice snowstorm during the summer when it is over 100 degrees with 98 percent humidity? Oh well.

One of the things that seems to pile on is using Windows PowerShell to automate Microsoft Word. What am I talking about? Well, about the time I am finished with one thing, I remember that there is something else I need to do. For example, if I had added my script in the static header of the Word document template that I created yesterday, I could avoid having to write that script today. The cool thing is that when I know how to automate adding a header, I can customize the header, such as to include the title of the article or some other information that would be helpful.

Begin with the Word.Application object

The place to begin is with the Word.Application object. I have to create that object as my entry point for automating Word. It is a COM object, so I also need to specify that. When I am writing a single script, I like to make the Word object visible. After I get everything working, I do not need to make the object visible, and I do that by setting the Visible property to $false. Here are those two lines of code:

$word = New-Object -comobject word.application

$word.visible = $true

I like to use the standard enumerations because it makes the script more readable. It also facilitates looking stuff up on MSDN. To do this, I create two types, which I will use directly in the script:

$HeaderFooterIndex = "microsoft.office.interop.word.WdHeaderFooterIndex" -as [type]

$alignmentTab = "microsoft.office.interop.word.WdAlignmentTabAlignment" -as [type]

To edit a Word document, I need to open it. I am working with my Template document right now, so I specify my path to the template, and I then call the Open method. A document object returns and I store it in a variable named $doc. This is shown here:

$filePath = "C:\lit\PaperTemplate.docx"

$doc = $word.documents.open($filePath)

I now need to retrieve a Section object. Keep in mind that the sections begin numbering with 1 as opposed to 0. To gain access to the Section object, I use the Item method to retrieve a specific section from the Sections collection. When I call the Sections property from the Document object, it returns a Sections collection. This collection includes the Item method. The code is shown here:

$section = $doc.sections.item(1)

Now I want to edit my first page header. To do this, I use the Headers property from the Section object to return a Headers collection. I then use the Item method to gain access to a specific header. I want the header for the first page, so I use the wdHeaderFooterFirstPage enumeration. This is a static property from the type I loaded earlier in my script.

In the Windows PowerShell ISE, I can use IntelliSense to retrieve the possible values, and therefore, to avoid having to bounce back and forth on MSDN looking stuff up. Here is that code:

$header = $section.headers.item($HeaderFooterIndex::wdHeaderFooterFirstPage)

Now, I need my header to be right-justified on my paper. So to do this, I need to use the wdRight enumeration from the WdAlignmenttabAlignment type that I added earlier. I use this value to specify that I want to insert an alignment tab. There is a method from the Range object that permits me to do this. All I do is call the method, and tell it what kind of alignment tab I want to insert. This is shown here:

$header.range.InsertAlignmentTab($alignmentTab::wdRight)

After I have inserted my alignment tab, I can finally insert my header text. To do this, I need to use the InsertAfter method from the Range object. As shown here, I simply give it a string that represents the text I want to insert:

$header.range.InsertAfter("First Page Header")

Now I need to save my changes, close the Word document, and call it quits:

$doc.save()

$doc.close()

$word.quit()

The complete script is shown here:

$filePath = "C:\lit\PaperTemplate.docx"

$HeaderFooterIndex = "microsoft.office.interop.word.WdHeaderFooterIndex" -as [type]

$alignmentTab = "microsoft.office.interop.word.WdAlignmentTabAlignment" -as [type]

$word = New-Object -comobject word.application

$word.visible = $true

$doc = $word.documents.open($filePath)

$section = $doc.sections.item(1)

$header = $section.headers.item($HeaderFooterIndex::wdHeaderFooterFirstPage)

 

$header.range.InsertAlignmentTab($alignmentTab::wdRight)

$header.range.InsertAfter("First Page Header")

$doc.save()

$doc.close()

$word.quit()

When I run the script, the Word document pops up, and then it closes. I open the file, and I see that the header was added. This is shown here:

Image of menu

That is all there is to using Windows PowerShell to add a header to a document. Word Week continues tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Use PowerShell ISE for Standard Enumerations

$
0
0

Summary: Use the Windows PowerShell ISE to use enumeration values.

Hey, Scripting Guy! Question How can I use the Windows PowerShell ISE to use the standard enumeration types to write a
           Word automation script?

Hey, Scripting Guy! Answer Cast the string as a type, and store the returned value in a variable. Each enumeration value
           will be available as a static property. Here is an example:

$HeaderFooterIndex = "microsoft.office.interop.word.WdHeaderFooterIndex" -as [type]

$HeaderFooterIndex::wdHeaderFooterFirstPage

Add Custom Headers to Folder Full of Word Documents

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about reading a CSV file and adding custom headers to a folder full of Word documents.

Microsoft Scripting Guy, Ed Wilson, is here. The snow continues to befuddle the drivers around here, and as a result, the Scripting Wife and I are staying home. This has given me a lot of time to sit, look at the giant slip-and-slide outside (also called a road), and write Windows PowerShell scripts. When I combine Word and Windows PowerShell, it is guaranteed to produce a lot of fun.

Begin with my CSV file

The other day, I created a CSV file with the titles of various plays that will be covered in a Shakespeare class. I then copied a research paper template, and I created all of the document files that will be required for the class. Yesterday I talked about using Windows PowerShell to add a header to a document file. Today, I add an additional column to the CSV file, and go through it to add the headers to the document files. This illustrates the power of using Windows PowerShell, because I can add custom headers—that is, the title of each research paper as the header for the file. Here is the CSV file:

Image of text

Note  Today’s script builds off of yesterday's script. You should refer to yesterday’s blog post, Use PowerShell to Add Headers to Word Documents, for an explanation of that script.

The first thing I do is specify the path to the folder that contains the various Word documents. I then import the CSV file by using the Import-CSV cmdlet. Because the file does not have a CSV file extension, it does not mean that Windows PowerShell does not know how to properly import it. This code is shown here:

$filePath = "C:\lit"

$topics = import-csv -Path C:\lit\Shakespeare.txt

Now I create my two enumerations, the Word.Application object, and I set the application to be invisible. This script was discussed yesterday, and it is shown here:

$HeaderFooterIndex = "microsoft.office.interop.word.WdHeaderFooterIndex" -as [type]

$alignmentTab = "microsoft.office.interop.word.WdAlignmentTabAlignment" -as [type]

$word = New-Object -comobject word.application

$word.visible = $false

I gather a collection of file objects by using the Get-ChildItem cmdlet to read the location that contains my Word documents. I use the FullName property to specify the file name and the path to the file. As shown here, I use Foreach to walk through the collection of file objects:

Foreach ($file in Get-ChildItem $filepath)

{

 $doc = $word.documents.open($file.FullName)

It is time to obtain my header object and to right-align the header text. This is the same code that I used yesterday:

$header = $section.headers.item($HeaderFooterIndex::wdHeaderFooterFirstPage)

$header.range.InsertAlignmentTab($alignmentTab::wdRight)

I need a way to find the appropriate topic title that I want to add to my header. Because I already imported my CSV file, I did not want to cycle through it. I hit on the idea of using the Where method in Windows PowerShell 4.0, which is added to objects so that I could find the specific play. Then I return the topic associated with that play. The rest of the script is the same as I used yesterday:

$header.range.InsertAfter($($topics.where({$file.BaseName -match $PSItem.play}).topic))

I save and close the document file. I then close the script block from the Foreach:

$doc.save()

 $doc.close() }

The last thing to do is quit the Word.Application object:

  $word.quit()

The complete script is shown here:

$filePath = "C:\lit"

$topics = import-csv -Path C:\lit\Shakespeare.txt

$HeaderFooterIndex = "microsoft.office.interop.word.WdHeaderFooterIndex" -as [type]

$alignmentTab = "microsoft.office.interop.word.WdAlignmentTabAlignment" -as [type]

$word = New-Object -comobject word.application

$word.visible = $false

Foreach ($file in Get-ChildItem $filepath)

{

 $doc = $word.documents.open($file.FullName)

 $section = $doc.sections.item(1)

 $header = $section.headers.item($HeaderFooterIndex::wdHeaderFooterFirstPage)

 $header.range.InsertAlignmentTab($alignmentTab::wdRight)

 $header.range.InsertAfter($($topics.where({$file.BaseName -match $PSItem.play}).topic))

 $doc.save()

 $doc.close() }

 

 $word.quit()

Here is one of my newly modified Word documents:

Image of text

That is all there is to using Windows PowerShell to add custom headers to Word documents. Join me tomorrow when I will talk about more fun Windows PowerShell stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Get Row from CSV File Based On Value

$
0
0

Summary: Use Windows PowerShell to get a row from a CSV file based on a specific value.

Hey, Scripting Guy! Question How can I use Windows PowerShell 4.0 to look up a specific row from a CSV file I imported into a variable?

Hey, Scripting Guy! Answer Use the Where method, and use a match pattern to search on a specific column and value, for example:

$topics = import-csv -Path C:\lit\Shakespeare.txt

$topics.Where({$PSItem.play -eq 'hamlet'}).topic


Use PowerShell to Add Table to Word Doc and Email as Attachment

$
0
0

 

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to create a document in Microsoft Word, add a table, and email the document as an attachment.

Microsoft Scripting Guy, Ed Wilson, is here. One of the cool things I like to do with Windows PowerShell is to combine multiple tasks so that it makes life easier for me. I use what I call the “annoyance meter” quite often to determine whether to write a script or not. Today’s script is one of those things that rates pretty high on my annoyance meter.

Here is the scenario: I need to supply my professor in my Shakespeare class with a list of proposed topics and my critical approach to them. So what is the big deal? I would have to create a table, type out each of the plays, add my topics, and add my critical approach to each topic. I then have to save the file, open email, find the file, and send the email. That is like, I don’t know, maybe a dozen steps.

Getting started

But what do I have? I already have a text file with my plays, and I already came up with a listing of topic ideas for each of the plays. I did that yesterday in Add Custom Headers to Folder Full of Word Documents.

So all I need to do is to add the literary approach as a third column to the CSV file. Here is my modification:

text list 

Now I open the Windows PowerShell ISE, start a new script, and set my initial variables. Because I am going to use the SaveAs method from the Document object, I know that I will need to pass the format by reference. I include this [ref] when I create the WdSaveFormat type. I want to know how many columns I have in my CSV file. Each column will be added as a NoteProperty, so I use the Get-Member cmdlet to find the NoteProperty members, and I count them. I find the number of rows that I will need by counting the number of rows in my CSV file. The code is shown here:

[ref]$SaveFormat = "microsoft.office.interop.word.WdSaveFormat" -as [type]

$topics = Import-Csv C:\Lit\Shakespeare.txt

$path = "C:\lit\ResearchTopics.docx"

$Number_Of_Rows = ($topics.Count +1)

$Number_Of_Columns = ($topics | gm -MemberType NoteProperty).count

$x = 2

Create the Word document 

Now I create the Word.Application object, set it to not be visible, and add a document to the Documents collection object. I then obtain a range object from the document and store the returned objects in the appropriate objects. This is shown here:

$Word = New-Object -comobject word.application

$Word.Visible = $false

$Doc = $Word.Documents.Add()

$Range = $Doc.Range()

Now that I have a Range object, I can add a table to the Tables collection. When I do this, I use the Add method and I specify the Range that will host the table (the range I have stored in the $Range variable), the number of rows, and the number of columns that the table will contain. I pipe the results to the Out-Null cmdlet to avoid cluttering up my output pane. Here is the code:

$Doc.Tables.Add($Range,$Number_Of_Rows,$Number_Of_Columns) | Out-Null

Now I get the table and add my column headings. I have only added a single table to the Tables collection, so I can simply use Item(1) to obtain the table object that represents my new table. I now use the Cell property to add my column headings. I want the columns to begin with the first row and first column. Then I specify a text property for the range associated with each cell. This code is shown here:

$Table = $Doc.Tables.item(1)

 

$Table.Cell(1,1).Range.Text = "Play"

$Table.Cell(1,2).Range.Text = "Topic"

$Table.Cell(1,3).Range.Text = "Approach"

Now I want to add the information to my table. I walk through the information I obtained from my CSV file, and add each appropriate value. Because I want to ensure that play titles appear under the Play column heading, I have specified each property rather than simply walking through the collection of items. This code is shown here:

Foreach($t in $topics)

{

 $Table.Cell($x,1).Range.Text = $t.Play

 $Table.Cell($x,2).Range.Text= $t.Topic

 $Table.Cell($x,3).Range.Text=$t.Approach

 $x++

I just want a nice looking table, but I do not want to fool with a lot of manual formatting. Luckily, Microsoft Word has an automatic format method associated with the Table object. I use a hard-coded number to choose which style I want to use. This is shown here:

$Table.AutoFormat(9)

Now I save my document by using the SaveAs method. I need to specify the path and the format when I call this method. I then close the document and exit the application. Here is the code:

$doc.saveas([ref] $path, [ref]$SaveFormat::wdFormatDocumentDefault)

$doc.close()

$word.quit()

I want to release all of these objects, so I call the ReleaseComObject method. I do this for each of objects I created. I then call garbage collection to scavenge memory, and I remove the variables. This is shown here:

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($doc) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($range) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($table) | Out-Null

Remove-Variable Doc,Word, range, table

[gc]::collect()

[gc]::WaitForPendingFinalizers()

The document is shown here:

 Table listing plays, topics, approaches

Now to email the document to my professor, I use the Send-MailMessage cmdlet. All I need to do is ensure that I am using the right SMTP email server, and that I specify my credentials if required. Here is the code I use:

Send-MailMessage -From "ScriptingGuys@Outlook.com" -To "DrHasenpfeffer@State.Edu" `

 -Attachments C:\Lit\ResearchTopics.docx -Subject "research topics" -Body "attached" `

 -SmtpServer "smtp-mail.outlook.com" -UseSsl -Credential "ScriptingGuys@Outlook.com"

That is all there is to using Windows PowerShell to create a Word document, add a table, and email the document as an attachment. Join me tomorrow when I will talk about more cool stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

 

PowerTip: Release a COM Object in PowerShell

$
0
0

Summary: Learn how to release a COM object in Windows PowerShell. 

 My Windows PowerShell automation script uses a COM object. How can I make sure that it releases?

  Use the ReleaseCOMObject static method from the Marshal class. Pass it the variable that holds the COM object. Here is an example:

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($doc) | Out-Null

Weekend Scripter: Add a Comment to a Word Doc

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to add a comment to a Microsoft Word document.

Microsoft Scripting Guy, Ed Wilson, is here. I spend a lot of time using Microsoft Word. In fact, I spend more time using Word than using Microsoft Outlook—and that is saying something—actually, it is saying a lot. Word is open on my computer nearly every day of the year, and from time to time, at all hours of the day. Because I deal with editors, I also have to read comments that they add to Word documents. On the other hand, when I provide peer review for other writers, I also have to be able to add comments to Word documents. From time to time, I find myself adding exactly the same sort of comments to documents. Here is a sampling of comments that I added to over a dozen documents in the last few months:

“Remember, when writing, it is more important to show something, and not to merely tell about something. In technical writing, this can take the form of a table, a line drawing, or an image showing the GUI.”

“Please don’t mix point of views. Remember, first person can be great for a blog, if you are the main writer. The first person "I" brings the reader into your world, and permits you great flexibility when discussing issues that might arise. The second person is excellent for writing how-to topics, but can break down if you are not careful. The third person tends to add a layer of distance that you might not want to have. The most important thing is consistency.”

“When discussing technical matters, variety of word choice is not necessarily a good thing. Don’t substitute program for process, executable for service, app for application, or storage for hard disk drive. It will confuse the reader who knows the difference, and it will be meaningless for a reader who does not.”

In fact, for the documents I was reviewing, I ended up copying the comments into Notepad, and then just used Cut and Paste. For each comment, I had to make over a dozen keystrokes: switch to Notepad, select a paragraph, copy the paragraph, switch back to Word, find my spot, paste the comment, and so forth and so on. Sounds like I needed a script.

Adding a comment to a Word document … it’s easy

After I decided that I wanted to write a Windows PowerShell script to add a comment to a Word document, I needed to see if it was even possible. I mean, in all the years I have been using Word, and in all the years I have been using Windows PowerShell, I only just now had the need to automate adding comments. But could I do it?

When I have such an idea, I always pop over to MSDN, the Microsoft Developer Network, and look at the Word Object Model Reference documentation for automation. Because it seems there is a new version of Word nearly every year, I always have to ensure that I have the documentation for the version of Word that I am using.

Luckily, I always have the latest version. The object model changes, sometimes in subtle ways, so I always like to look things up. Here is a link to the Object Model Reference (Word 2013 Developer Reference) documentation. I can’t say how long the Comments object has been available, because, like I said, this is the first time I decided to write such a script. I scroll down the list of objects until I see the Comment and the Comments objects. Luckily, the names of the objects make sense. Because I understand the Word object model, I know that a comment will come from a Comments collection. I know that there will be an Add method to add the comment, and I know that such a comment will be attached to a Range object. A Range object specifies a location in a Word document. I can create a specific Range, or I can retrieve a Range object via the Range property from a Document, Section, Paragraph,or other such object. There is a pretty good article on MSDN, Working with Range Objects, that talks about this.

Adding a comment is as simple as calling the Add method from the Comments object, and specifying the Range and the comment text itself. Everything else about it is standard Word automation.

First, the basic Word automation stuff

The first thing I do is specify the path to the file that will receive my comments. In this example, I use the Word document that I created yesterday that holds my Shakespeare research topics. Next, I create the Word.Application object, set the application to not visible, open my Word document, retrieve the first section, and the range that is associated with this section. This code is shown here:

$filePath = "C:\lit\ResearchTopics.docx"

$word = New-Object -comobject word.application

$word.visible = $false

$doc = $word.documents.open($filePath)

$section = $doc.sections.item(1)

$range = $section.Range

Add the comment

After I have my Range object, from the first section of the Word document, and the Comments object, from the Comments property of the document I opened, I can add the comment to the document. It actually makes sense. I am adding a comment to a specific location in a specific document. To specify that location, I need to supply a Range object, from the $range variable, and my comment. Here is the code:

$doc.Comments.Add($range,"This is a great listing of topics")

Close things out and clean stuff up

Now I want to save my changes to the document, close the document, and exit the Word application. I then release all the objects, remove the variables, and call garbage collection to free up the memory. This code is pretty standard for most of my Word automation projects, and it is shown here:

$doc.save()

$doc.close()

$word.quit()

 

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($doc) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($range) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Section) | Out-Null

Remove-Variable Doc,Word, range, section

[gc]::collect()

[gc]::WaitForPendingFinalizers()

 The complete script is shown here:

$filePath = "C:\lit\ResearchTopics.docx"

$word = New-Object -comobject word.application

$word.visible = $false

$doc = $word.documents.open($filePath)

$section = $doc.sections.item(1)

$range = $section.Range

$doc.Comments.Add($range,"This is a great listing of topics")

$doc.save()

$doc.close()

$word.quit()

 

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($doc) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($range) | Out-Null

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($Section) | Out-Null

Remove-Variable Doc,Word, range, section

[gc]::collect()

[gc]::WaitForPendingFinalizers()

 The comment appears in my Word document when I open it. It is shown here:

 Table with plays, topics, and approaches

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Easy Way to Troubleshoot a PowerShell Script

$
0
0

Summary: Learn the easy way to troubleshoot a Windows PowerShell script.

  I receive an error every time I run my Windows PowerShell script. What is an easy way to troubleshoot the script in the
Windows PowerShell ISE?

  Highlight the line of code in the script to select it, either with the mouse, or by using keyboard shortcuts, and then press F-8 to execute only the selected line of code. If no errors arise, select the next line of code and repeat the process until an error arises.

 

Weekend Scripter: Fun Things to Do with PowerShell

$
0
0

Summary: Windows PowerShell MVP, Teresa Wilson, talks about fun things to do with Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Today we have a guest blog post from Microsoft Honorary Scripting Guy and Windows PowerShell MVP, Teresa Wilson, who shares some fun things to do with Windows PowerShell.
Take it away, Teresa …

Hello everyone,

Sometimes when I am out and about, people will ask me what I do and where I work. It is easy to explain when you are talking to an IT Professional, but to explain it to the general public is not so easy. I usually give a pretty simple explanation of using a Windows PowerShell script to read a spreadsheet with new employee names and departments to create new users when you hire a group of people. When I'm working with an IT Pro who has never used Windows PowerShell, it is pretty easy to show the idea of reading log files and setting up scheduled tasks with a script.

The first thing that you need to remember is to use the right tool for the job. Windows PowerShell is not always the right tool, but when it is, it is well worth the time and effort to create a script to automate the task. Today, I thought I could take it a step further and give some examples that also include the script. I hope you think that the examples are useful.

First, I went to the Hey Scripting Guy blog and looked for the most viewed blog posts to see if there was a common theme or idea. I came up with this: List Files in Folders and subfolders with PowerShell. This blog post and its VBScript script version were two of the most viewed. That tells me that listing files in folders and subfolders is a popular task that you can perform with Windows PowerShell.

Next, I went to the Script Repository and found some interesting popular items.

The Windows Update PowerShell Module by MVP Michal Gajda has been downloaded more than 112,000 times. I am an accounting person so that number tells me that this blog post is popular.

One of the great things about Windows PowerShell is how you can change your scripts as time goes by. One popular item that has taken advantage of this capability is the updated and improved version of Create Active Directory Users Based On Excel Input. You know that I had to include this blog post especially because it is close to my generic example of what you can do with Windows PowerShell.

These two are just a couple of examples of more than 5,000 Windows PowerShell entries in the Script Repository.

New and exciting is the PowerShell Gallery. After you go to the home page, be sure to click the Get Started tab because there is a lot of information there. I am not going to reinvent the wheel by restating the words that you will find there.

That is all I have today. Hope you have a wonderful day.

~Teresa

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

PowerTip: Easily Check Syntax Options for a PowerShell Cmdlet

$
0
0

Summary: Use the Get-Command cmdlet to check syntax options for a Windows PowerShell cmdlet.

  I want to know what syntax options (parameter sets) exist for a particular Windows PowerShell cmdlet. How can I find them easily?

  Use the Get-Command cmdlet and check the Definition property. The code is shown here:

(Get-Command Stop-Process).Definition

   

Use PowerShell to Count Comments in Word Docs

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about using Windows PowerShell to count comments in Microsoft Word documents.

Microsoft Scripting Guy, Ed Wilson, is here. The process of writing often includes re-writing. In fact, I would imagine that I spend more time re-writing stuff than I spent writing the original document, especially when I write a book. The book process goes something like this: I write a chapter, I send it to my editor. She looks it over for style and consistency with the series, and then she forwards it to the technical reviewer. The technical reviewer sends it back to the editor, who forwards it to the line editor. The line editor returns it to the editor who then returns it to me. I then review all of the comments, make changes, accept or suggest other changes, and the process completes another iteration. The documents finally go to the publisher, who returns page proofs, and I have one final chance for correction.

I turn in chapters on a regular basis, but sometimes chapters return to me in batches. When that happens, I like to know which chapters are going to require more work to review and to correct stuff in the comments, and which chapters will require the least amount of work. Knowing this information can help me plan my work according to how much time I have available. In the past, that required opening up each document, scrolling through to the end, and making a mental note of how many comments appear. Now I can use Windows PowerShell to do this for me.

Search a folder for documents and count the comments

It dawned on me that I could use Windows PowerShell to tell me which documents in a folder contain the most comments. Armed with this information, I would know where to focus my attention. It would also be useful, if I had a collection of documents that I needed to review and add comments to. It would help me to know if I had missed any particular documents. If a document was perfect, I could at least add a comment that says something like “Great job. No changes needed.”

The first thing I need to do is to specify the folder that contains my document collection, create the Word.Application object, set the Word automation Visible property to $false, and use the Foreach command to walk through the collection. The code is shown here:

$Path = "E:\data\BookDOcs\PS3_StartHere"

$word = New-Object -comobject word.application

$word.visible = $false

Foreach($filepath in (Get-ChildItem $path -Filter *.docx -Recurse))

{

Now I need to open the document. Note that documents that are more complex than one section and range could have comments associated with them.

Here all of the comments are associated with the Document object. This is shown here:

$doc = $word.documents.open($filePath.FullName)

 After I have the document opened, I use the Comments object to retrieve the count that is associated with the document. If the count is greater than or equal to 1, I display the count and the file name. I then close the document, as shown in this code:

$count = $doc.Comments.count

  if( $count -ge 1) {"$count comments in $filepath"}

 $doc.close() 

 I now remove the Document object before I move on to the next file. This code is shown here:

  [System.Runtime.Interopservices.Marshal]::ReleaseComObject($doc) | Out-Null

  Remove-Variable Doc }

 After I have completed working with all of the documents in the folder, I clean up the Word.Application object and call garbage collection to free up memory. This code is shown here:

$word.quit()

 

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null

Remove-Variable Word

[gc]::collect()

[gc]::WaitForPendingFinalizers()

 The complete script is shown here:

$Path = "E:\data\BookDOcs\PS3_StartHere"

$word = New-Object -comobject word.application

$word.visible = $false

Foreach($filepath in (Get-ChildItem $path -Filter *.docx -Recurse))

{

 $doc = $word.documents.open($filePath.FullName)

 $count = $doc.Comments.count

  if( $count -ge 1) {"$count comments in $filepath"}

 $doc.close()

 

 [System.Runtime.Interopservices.Marshal]::ReleaseComObject($doc) | Out-Null

  Remove-Variable Doc }

$word.quit()

 

[System.Runtime.Interopservices.Marshal]::ReleaseComObject($word) | Out-Null

Remove-Variable Word

[gc]::collect()

[gc]::WaitForPendingFinalizers()

 

When I run the script, the following output appears in the Console pane:

 Image of command output

That is all there is to using Windows PowerShell to count the number of comments in a document. Join me tomorrow when I will talk about more cool stuff.

 

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

 

 


PowerTip: Troubleshoot a Word Automation Script

$
0
0

Summary: Learn to troubleshoot a Windows PowerShell script for Microsoft Word automation.

  I have been struggling with a Windows PowerShell script that automates Word. The problem is that every time I run the script I get an error saying the Word document is locked. What can I do?

   When you test or develop your Windows PowerShell script for Word automation, make sure that you quit the Word.Application object and that you close the Word document. Otherwise, the document will be locked. This is especially true if you set the Application.Visible property to $false.

When to Use WMI and PowerShell

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about when to use WMI.

Microsoft Scripting Guy, Ed Wilson, is here. One of the things that Windows PowerShell has done is make WMI very easy to use. Beginning with Windows PowerShell 1.0 and the Get-WmiObject cmdlet, WMI information has become much easier to access. Whereas a simple VBScript script or Perl script would require a minimum of seven lines of rather complicated code to access WMI information, Windows PowerShell made the same information available as a one-liner. Truly a marvelous win for IT pros!

But one problem remains. WMI (even in Windows PowerShell) can be complicated and inconsistent. In fact, when I taught WMI classes, I would say, “The only thing consistent about WMI is that it is inconsistent.” Although that is a bit of an overstatement, anyone who spends much time working with WMI also spends a lot of time researching anomalies on MSDN.

Historically, some of the tasks that required using WMI on local or remote computers are:

  • Working with the registry
  • Working with processes 
  • Working with services 
  • Performing hardware inventory 
  • Working with files and folders 
  • Working with event logs 

Beginning with Windows PowerShell 2.0, you can perform these tasks (except for a hardware inventory) by using Windows PowerShell. There is no need to use WMI to work with files, folders, event logs, the registry, processes and services—either locally or remotely.

Note  There are still some things related to these topics that require WMI, but most of the main tasks related to these topics can be performed without using WMI.

Beginning with Windows PowerShell 3.0 in Windows 8 and Windows Server 2012, many of the hardware-related tasks (such as inventorying hard disks and network adapters) no longer require complex WMI queries.

Note  The underlying technology is still WMI, but the classes are basically wrapped and exposed as functions that behave like Windows PowerShell cmdlets.

So, what is the best way to use WMI?

Avoid using WMI at all.

If I can use a Windows PowerShell command such as:

Get-NetAdapter

instead of typing:

Get-WmiObject Win32_NetworkAdapter

-or-

Get-CimInstance Win32_NetworkAdapter

...then of course, I want to use the Windows PowerShell command.

The other thing that is great about using the Get-NetAdapter cmdlet is that it uses CIM, which uses Windows PowerShell remoting instead of remote procedure call (RPC). This means that I can query for network adapter information from a remote computer through one port in the firewall instead of opening a bunch of ports in the firewall. The other advantage is that it is much faster.

This week, I will talk about when to use Windows PowerShell and when I still have to use WMI. It will be fun.

That is all there is to using Windows PowerShell to find WMI stuff. WMI Week will continue tomorrow when I will talk about more cool stuff. 

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Find WMI Classes with a CIM Cmdlet

$
0
0

Summary: Use a Windows PowerShell CIM cmdlet to find WMI classes.

   How can I find Windows Management Instrumentation (WMI) classes that are related to a particular technology?


  Use the Get-CimClass cmdlet and use a wildcard character for the name, then specify the Dynamic 
           parameter to help filter classes, for example:

Get-CimClass -ClassName *bios* -QualifierName dynamic

Comparing WMI and Native PowerShell

$
0
0

Summary: Microsoft Scripting Guy, Ed Wilson, talks about comparing WMI and using native Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. When Windows PowerShell was still in beta, there was a saying (song?) that said there must be 50 ways to stop a process. Actually, I am not certain it was ever turned into a Windows PowerShell song, but it probably could have been. Anyway, the conversation was a reflection of the many ways of doing things that are available in Windows PowerShell…and that was back in the Windows PowerShell 1.0 days. Today, there are even more ways of doing things. So what is the deal?

Well, the overriding principle of Windows PowerShell is to fade into the background and to permit one to work as one wishes to work. That is why one of my pet peeves is when someone says, “That is not a very PowerShell way of doing things.”

To be honest, there is really no specific “PowerShell way” of doing things. Windows PowerShell is a tool that permits one to work the way that one wants to work. Sure, some ways are easier, but if one is already familiar with C++ programing, and one wishes to work with native Win32 APIs, and if it works in Windows PowerShell, then I guess I can say that it is Windows PowerShell.

For one who has not done much C++ programing in over 15 years, that would not be my first choice. In fact, I prefer to do everything the easiest way possible. But then, what is easy is often what one is comfortable with—or what takes the shortest amount of time.

If on the other hand, one has no programing background and has done no scripting previously, or if one is seriously attempting to explore the boundaries of what Windows PowerShell has to offer, then wrestling with native Win32 APIs may not be the fastest way to get things done. (In fact, I would say it probably is not the fastest way…unless you already have a script that does what you need to get done, and if it works perfectly…well, then, maybe.)

Comparing two ways to do the same thing

There are lots of WMI scripts out there. Often when people search around to find a script to solve a problem, they run across a WMI script. OK. Finding a script is often faster and easier than writing a script. But suppose that I do not have a script or that I am in a locked-down environment, and I do not have access to a script. What then? Well, that is where knowing a bit about Windows PowerShell and WMI comes to the rescue.

Processes

One of the first WMI scripts I ever used was one that retrieved information about processes. And as I said, Windows PowerShell made it easy to use WMI. So if I want information about processes, I use the Win32_Process class.

Note  When you are using the Get-CimInstance cmdlet, don’t forget that Tab completion works for WMI class names (in the Windows PowerShell ISE and the Windows PowerShell console).

In Windows PowerShell 1.0, the WMI cmdlet was Get-WmiObject. It was a huge improvement over writing WMI scripts that required lines and lines of often obscure commands. But beginning with Windows PowerShell 3.0, the CIM cmdlets are even a bigger improvement. As shown here, often the change in command is direct substitution:

Get-WmiObject win32_process

Get-CimInstance Win32_Process

But there are other times when there are changes—even in parameter names. For example, Get-WmiObject uses –Class, and Get-CimInstance uses –ClassName. One of the cool things is the way Get-CimInstance formats the output. Here is the output from Get-WmiObject:

Image of command output

Here is the output from Get-CimInstance:

Image of command output

If I want to find out information about a Notepad process, I need to create a filter. This is shown here:

PS C:\> Get-CimInstance Win32_Process -Filter "name = 'notepad.exe'"

ProcessId          Name            HandleCount       WorkingSetSize    VirtualSize

---------               ----                 -----------             --------------         -----------

5280               notepad.exe            91                    6905856           2199127113728

If I want to use a native Windows PowerShell command to find out information about Notepad, I can use the Get-Process cmdlet. This is shown here:

PS C:\> Get-Process -Name notepad

Handles  NPM(K)    PM(K)      WS(K) VM(M)   CPU(s)        Id      ProcessName

-------        ------        -----        -----     -----      ------        --        -----------

     91           8          1620          6744       99       0.02         5280       notepad

The commands appear here:

Get-Process -Name notepad

Get-CimInstance Win32_Process -Filter "name = 'notepad.exe'"

So, the native Windows PowerShell command appears to be a bit easier to use and understand. And the output is similar.

So there is a bit about comparing WMI and Windows PowerShell. WMI Week will continue tomorrow when I will talk about more cool Windows PowerShell stuff.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

PowerTip: Find “Hidden” Property Values in PowerShell

$
0
0

Summary: Learn how to find some missing property values in Windows PowerShell.

Hey, Scripting Guy! Question How can I find missing property values in the process information I am looking at in Windows PowerShell?

Hey, Scripting Guy! Answer Launch Windows PowerShell with Admin rights (hold down the Shift key, right-click the Windows PowerShell icon,
           select Run as Administrator or Run as different user, and then enter credentials for an account with
           Admin rights).

Note  Windows PowerShell runs in user mode; therefore, it only grants access to information to which
the user has rights.

Viewing all 2129 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>