Hey, Scripting Guy! How Can I Create a Microsoft Word Document from WMI Information?

ScriptingGuy1

Hey, Scripting Guy! Question

Hey Scripting Guy! I would like to be able to use Windows PowerShell to obtain some information from WMI and then write that same information to a Microsoft Word document. I know that I can use Out-File to create a text file, but text files are boring. I want to get the information into Word where I can later add colors, borders, and pictures if I wish. Help me? I’m beggin’ you here.

– GS

SpacerHey, Scripting Guy! Answer

Hi GS,

Boring text files? Dude, I love text files. They use almost no disk space, consume nearly no memory, and are portable across platforms. Personally, I think that Notepad.exe is the best program that Microsoft ever wrote. Hey, it was feature complete nearly 20 years ago, and it has never really had a bug in it. I do, however, agree that colors, borders, and pictures do make a document more interesting.

The coral picture just below, taken by Ed while scuba diving in the Cayman Islands a couple of weeks ago, has nothing to do with this article. But as you can see, it does make the article more interesting. This is particularly true because the water in Little Cayman was 84 degrees, and it is 29 degrees with frost on the ground this morning in Charlotte, NC, USA). Or the water there was 28.89 and it is now -1.69 Celsius here (by using the temperature conversion HTA).

Image of coral off Cayman Islands--ah, vacation!

 

GS, despite my strong affinity for Notepad.exe (also known as the Microsoft script editor), I am going to help you because I love WMI (I [Ed] actually wrote the best-selling book on the subject), and I love scripting Microsoft Word. Okay, enough playing around, here is the WriteBiosInfoToWord.ps1 script. If you would like to see a VBScript that writes WMI information to a Word document, there is one in the Script Repository. That script, however, does not save the data; it just displays the data in a Word document. To see a VBScript example of saving a Word document, follow me.

WriteBiosInfoToWord.ps1

$class = "Win32_Bios"
$path = "C:\fso\bios"

[ref]$SaveFormat = "microsoft.office.interop.word.WdSaveFormat" -as [type]
$word = New-Object -ComObject word.application
$word.visible = $true
$doc = $word.documents.add()
$selection = $word.selection
$selection.typeText("This is the bios information")
$selection.TypeParagraph()
Get-WmiObject -class $class | 
Out-String |
ForEach-Object { $selection.typeText($_) }
$doc.saveas([ref] $path, [ref]$saveFormat::wdFormatDocument)
$word.quit()

The first thing we do in the WriteBiosInfoToWord.ps1 script is create two variables and assign values to them. The first variable is $class (in Windows PowerShell all variables begin with a dollar sign), and it holds the name of the WMI class we wish to query. In this script we are using the Win32_Bios WMI class, but there are many other WMI classes that could be used. They are all detailed in the MSDN WMI documentation. The second variable we use is the $path, which holds the drive, folder, and name of the document we wish to create. Note that it does not contain the file extension. We will use Word to generate the appropriate file extension for us when we save the file. Here are these two variables:

$class = "Win32_Bios"
$path = "C:\fso\bios"

We now use the -as operator to turn the string “Microsoft.Office.Interop.Word.WdSaveFormat” into a type. We use [ref] so that we can pass this type by reference, which is a requirement of the Word saveas method. By creating the WdSaveFormat type we gain access WdSaveFormat enumerations, which makes our script easier to write, easier to maintain, and easier to read. This line of code is seen here:

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

When we want to work with Word, we need to create an instance of the Word.Application object. The methods and properties (members) of the Word.Application object are documented in MSDN at this location. To create the Word.Application object, we use the New-Object cmdlet and the -comobject parameter. We give it the program ID of Word.Application, and store the resulting object in the $word variable. Now we want to see what we are doing, so we set the Word applications visible property to $true. This code starts Word and makes it visible, but does not add any documents to it:

$word = New-Object -ComObject word.application
$word.visible = $true

Once we have made the Word application visible, we are presented with an empty Word application. This is something that is seldom seen because when you start Word normally, it opens a blank document. Gaze upon this rare event:

Image of an empty Word application

 

Now we need to add a document to Word. To do this, we use the documents object and call the add method. What is interesting with this is that we obtain the documents object (which is a collection) by querying the documents property from the application object. When we have the documents object, we can call the add method. We could easily write this code in two lines as shown here:

$collectionDocuments = $word.documents
$doc = $collectionDocuments.add()

If I were to write the code as shown above, it would in fact be more readable. However, it could also make the code twice as long and using all those extra dollars for variables really adds up. So instead, we use a “double dotted” notation. As we read from left to right, we have the Application object stored in $word. Next we have the Documents collection returned from the documents property. Then we wall the add method. It is all done in one nice, neat sentence. We take the new document that is returned from the add method and store it in the $doc variable:

$doc = $word.documents.add()

We create a selection object by querying the selection property of the Word Application object. A selection object is used to represent a selected range of text such as a highlighted section of text or an insertion point in a document. The object will represent the insertion point in the text if nothing in the text is highlighted. In our example, there is nothing in the text at all, so it would be really really hard to select something. So guess what? It refers to the insertion point. Pretty cool, huh?

$selection = $word.selection

This one is hard. Close your eyes, relax, and think. If I want to type text in a Word document, what method do I use? I will give you three guesses, but the first two don’t count. We use the…TypeText method! Now, the next one is a bit harder, but what if I want to type a blank paragraph on the page? You are right: We use the TypeParagraph method. These two lines of code, then, are no surprise. They are shown here:

$selection.typeText("This is the bios information")
$selection.TypeParagraph()

We use the Get-WmiObject cmdlet in Windows PowerShell to query from WMI. We give our class which we stored in the $class variable to the -class parameter. The results are seen here:

PS C:\Documents and Settings\ed> Get-WmiObject -Class win32_bios
SMBIOSBIOSVersion : A01
Manufacturer      : Dell Computer Corporation
Name              : Default System BIOS
SerialNumber      : 9HQ1S21
Version           : DELL   - 6

The cool thing about Windows PowerShell is the large amount of information we can obtain with very little typing.

Personally, I hate to type (bad thing for a writer I know). But I keep hoping that we will get voice recognition working to the point that I can talk my way through these “Hey, Scripting Guy!” articles. We are nearly there with Windows Vista and Word 2007, but not quite enough for daily heavy usage. It makes a cool demo, and is fun to play around with, but… I digress. Sorry. The sun came out, and it is making me dizzy.

We take the information we obtained from the Get-WmiObject cmdlet, and send the resulting object to the Out-String cmdlet. The reason is that everything in Windows PowerShell is an object. If we were to try to write the results of the Get-WmiObject cmdlet directly to Word, we would come up with something that simply said System.Object[] or words to that effect. So we need to change it to a string. When we get the string representation of our data, we pipeline it to the Foreach-object cmdlet. This is seen here:

Get-WmiObject -class $class | 
Out-String |

The Foreach-Object cmdlet is like the Foreach statement, except that it is smart enough to deal with pipelined data. As the data comes across the pipeline, we want to take each of the pieces and use the TypeText method from the Selection object to write that text to the Word document. The $_ is an automatic variable that is used to refer to the current item on the pipeline. This is shown here:

ForEach-Object { $selection.typeText($_) }

We want to save the newly created Word document. Because it has not been saved before and we would like to specify a name for the file, we use the SaveAs method from the Document object. Now we are back to our [ref] trick. These values need to be passed to the method call as reference objects. So we use the [ref] in front of our values. The wdFormatDocument enumeration tells Word to save the document using the default Word document type. The cool thing about this is that it works the same for Word 2007 or Word 2003. In Word 2007, it will save a .docx file. In Word 2003, we get a .doc file:

$doc.saveas([ref] $path, [ref]$saveFormat::wdFormatDocument)

Now that we are finished creating and saving our Word document, we want to exit from Word. To do this, we use the quit method as seen here:

$word.quit()

If we were watching closely when we ran our script, we would see the Word document flash before our eyes. I took a speed reading class, but I can’t read that fast. So let’s open up our newly created document. This is seen here:

Image of the newly created Word document

 

GS, I hope this script will be useful for you. I love this technique, and when applied to Windows PowerShell it becomes easy and powerful because of the automatic expansion of the WMI properties. Word out.

Ed Wilson and Craig Liebendorfer, Scripting Guys

0 comments

Discussion is closed.

Feedback usabilla icon