{"id":8491,"date":"2012-08-03T00:01:00","date_gmt":"2012-08-03T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2012\/08\/03\/use-powershell-to-autocomplete-word-built-in-properties\/"},"modified":"2012-08-03T00:01:00","modified_gmt":"2012-08-03T00:01:00","slug":"use-powershell-to-autocomplete-word-built-in-properties","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-powershell-to-autocomplete-word-built-in-properties\/","title":{"rendered":"Use PowerShell to Autocomplete Word Built-in Properties"},"content":{"rendered":"<p><b>Summary<\/b>: Microsoft Scripting Guy, Ed Wilson, shows how to use Windows PowerShell to automatically complete the Microsoft Word built-in properties.\nMicrosoft Scripting Guy, Ed Wilson, is here. Registration for the <a href=\"http:\/\/powershellsaturday.com\/002\/\" target=\"_blank\">Charlotte PowerShell Saturday event<\/a> to be held on September 15, 2012 is underway. (Incidentally, that is the day after my birthday. The speakers dinner at the Scripting Wife&rsquo;s and my house, will actually be a Scripting Guy birthday party). We have all speakers (except for one) identified, and you can <a href=\"http:\/\/powershellsaturday.com\/002\/presentations\/\" target=\"_blank\">see the speakers and the tracks<\/a> on the website. If you are going to be anywhere near Charlotte, North Carolina in the United States on September 15, 2012, you should make plans to attend. With three tracks and over a dozen Microsoft PFEs, Microsoft MVPs, and community leaders speaking, it will be an event too good to pass up. I myself, am doing two or three presentations on the beginner and the advanced tracks. It will be cool. <a href=\"http:\/\/powershellsaturday.com\/002\/register\/\" target=\"_blank\">Here is the link to register for this awesome PowerShell event<\/a>.<\/p>\n<h2>Use PowerShell to add to Word metadata<\/h2>\n<p style=\"padding-left: 30px\"><b>Note<\/b>&nbsp;&nbsp;&nbsp;This week I have been talking about finding files and working with the associated built-in properties of those files (Microsoft Word). On Monday, I wrote <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/07\/30\/use-powershell-to-explore-nested-directories-and-files.aspx\" target=\"_blank\">Use PowerShell to Explore Nested Directories and Files<\/a>. on Tuesday, I wrote <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/07\/31\/use-powershell-to-help-find-all-of-your-images.aspx\" target=\"_blank\">Use PowerShell to Help Find all of Your Images<\/a>. And on Wednesday, we began our deep dive into the Microsoft Word automation model when I wrote <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/08\/01\/find-all-word-documents-that-contain-a-specific-phrase.aspx\" target=\"_blank\">Find All Word Documents that Contain a Specific Phrase<\/a>, which was followed up with <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/08\/02\/use-powershell-to-find-specific-word-built-in-properties.aspx\" target=\"_blank\">Use PowerShell to Find Specific Word Built-in Properties<\/a> on Thursday.\nWhen I figured out how to find specific Microsoft Word documents by using the Microsoft Word built-in properties, I thought it would be a useful technique. Potentially, it could be quicker, and more accurate to use these built-in properties than to try to use regular expressions to search Word documents for specific word patterns. It is easier to search for specific words in specific Microsoft Word styles, but all of my documents do not always use standard Microsoft Word styles. Therefore, that technique does not work so well.\nIf, on the other hand, I have accurately populated the built-in properties in a Microsoft Word document, I know I can search for them via the technique I developed in yesterday&rsquo;s Hey, Scripting Guy! Blog. In a previous blog, <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2009\/12\/29\/hey-scripting-guy-december-29-2009.aspx\" target=\"_blank\">How Can I List All the Properties of a Microsoft Word Document?<\/a>, I talked about manually adding values to the Microsoft Word built-in properties. Today I want to talk more about adding values to the Microsoft Word built-in properties.\nTo be useful as a document management technique, I need to have my script figure out what to add. This can involve lots of regular expressions and other things. Based on my writing of this series, I have decided to modify my Microsoft Word template: the title goes in the Title style, the summary goes in as a Subtitle style; and I use Heading 9 for my tags. But, of course, although this will help in the future, it does not do much for me today. I do not want to overly complicate the script today because my main purpose is to illustrate the complicated task of actually writing to a Microsoft Word built-in property.\nYesterday when I was messing around with my script, I also noticed that because all my <i>Data <\/i>directory was copied from my backup that is stored on my SAN at home, the file creation dates are all messed up. This includes the <i>Content created <\/i>built-in property as well as the <i>Date last saved <\/i>built-in property. In addition, the actual file time stamps, Date created, Date modified, and Date accessed are similarly unreliable.<\/p>\n<p style=\"padding-left: 30px\"><b>Note<\/b> &nbsp;&nbsp;I created a function in the <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2012\/06\/01\/use-powershell-to-modify-file-access-time-stamps.aspx\" target=\"_blank\">Use PowerShell to Modify File Access Time Stamps<\/a><i> <\/i>blog post. By using that function, it is easy to change the file time stamps. That will be the topic for tomorrow&rsquo;s Weekend Scripter blog posting.<\/p>\n<h2>Writing to Microsoft Word built-in properties via PowerShell<\/h2>\n<p><a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/microsoft.office.core.documentproperty\" target=\"_blank\">MSDN details the built-in properties for Microsoft Word<\/a>. Today, I want to assign a value to the <strong>Comments<\/strong> built-in property.\nThe first part of the <b>Set-SpecificDocumentProperties<\/b> script appears similar to the script from yesterday&rsquo;s Hey, Scripting Guy! Blog. The difference is two new variables. The thing to keep in mind here is that the <b>$AryProperties<\/b> and the <b>$newValue<\/b> are specified as an [array], but they are actually singletons. The reason for this is because the <b>SetProperty <\/b>method used to write the values back to the <b>BuiltInDocumentProperties<\/b> collection must receive an array. Other than that, this code is relatively straightforward.<\/p>\n<p style=\"padding-left: 30px\">Param(<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $path = &#8220;C:fso&#8221;, [array]$include = @(&#8220;HSG*.doc*&#8221;,&#8221;WES*.doc*&#8221;))<\/p>\n<p style=\"padding-left: 30px\">[array]$AryProperties = &#8220;Comments&#8221;<\/p>\n<p style=\"padding-left: 30px\">[array]$newValue = &#8220;Scripting Guy blog&#8221;<\/p>\n<p style=\"padding-left: 30px\">$application = New-Object -ComObject word.application<\/p>\n<p style=\"padding-left: 30px\">$application.Visible = $false<\/p>\n<p style=\"padding-left: 30px\">$binding = &#8220;System.Reflection.BindingFlags&#8221; -as [type]<\/p>\n<p style=\"padding-left: 30px\">$docs = Get-childitem -path $Path -Recurse -Include $include\nNow I use the <b>foreach<\/b><i> <\/i>statement to walk through the collection of documents retrieved by the <b><i>Get-ChildItem<\/i><\/b> cmdlet. Inside the script block for the command, the first thing I do is open the document and store the returned <b>Document<\/b><i> <\/i>object in the <b>$document<\/b> variable. Next I retrieve the <b>BuiltInDocumentProperties <\/b>object, and I store it in the <b>$builtinProperties<\/b> variable. Next I use the<i> <\/i><b>GetType<\/b><i> <\/i>method to return the <b>BuiltInDocumentProperties<\/b><i> <\/i>type, and I store that in the <b>$builtinPropertiesType<\/b> variable. I could also use <b>[system.__ComObject]<\/b> like I did yesterday, but I thought I would show you a different technique that is perhaps a bit more readable. Here is the code.<\/p>\n<p style=\"padding-left: 30px\">Foreach($doc in $docs)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;{<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $document = $application.documents.open($doc.fullname)<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $BuiltinProperties = $document.BuiltInDocumentProperties<\/p>\n<p style=\"padding-left: 30px\">&nbsp; $builtinPropertiesType = $builtinProperties.GetType()\nOnce again, (just like in yesterday&rsquo;s script), I use the <b>Try<\/b> \/ <b>Catch<\/b> commands to attempt to write new values for the properties. If an exception occurs, a blue string displays that states the script was unable to set a value for the property.\nIn the <b>Try<\/b><i> <\/i>script block, the first thing I do is get the built-in property and assign it to the <b>$BuiltInProperty<\/b> variable. To do this, I use the <b>InvokeMember<\/b><i> <\/i>method on the <b><i>item<\/i><\/b><i> <\/i>with the <b><i>GetProperty<\/i><\/b><i> <\/i>binding. I also include the <b>$builtinProperties<\/b> variable that contains the <b>BuiltInProperties<\/b><i> <\/i>collection. I store the returned property object in the <b>$BuiltInProperty<\/b> variable. Next I use the <b>GetType<\/b><i> <\/i>method to return the data type, and I store that in the <b>$BuiltInPropertyType<\/b> variable. These two lines of code are shown here (the first line is really long and wraps).<\/p>\n<p style=\"padding-left: 30px\">$BuiltInProperty = $builtinPropertiesType.invokemember(&#8220;item&#8221;,$binding::GetProperty,$null,$BuiltinProperties,$AryProperties)<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $BuiltInPropertyType = $BuiltInProperty.GetType()\nI now call the <b>SetProperty<\/b><i> <\/i>binding for the <b>value<\/b><i> <\/i>by using code that is similar to the previous line of code. Once again, the new value must be supplied in an array.<\/p>\n<p style=\"padding-left: 30px\">$BuiltInPropertyType.invokemember(&#8220;value&#8221;,$binding::SetProperty,$null,$BuiltInProperty,$newValue)}\nInside the loop, it is now time to close the document, release the COM objects, and remove the variables. This code is shown here.<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; $document.close()<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; [System.Runtime.InteropServices.Marshal]::ReleaseComObject($BuiltinProperties) | Out-Null<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; [System.Runtime.InteropServices.Marshal]::ReleaseComObject($document) | Out-Null<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; Remove-Variable -Name document, BuiltinProperties<\/p>\n<p style=\"padding-left: 30px\">&nbsp;&nbsp; }\nWhen the script finishes looping through the documents, the final cleanup occurs. This code is shown here.<\/p>\n<p style=\"padding-left: 30px\">$application.quit()<\/p>\n<p style=\"padding-left: 30px\">[System.Runtime.InteropServices.Marshal]::ReleaseComObject($application) | Out-Null<\/p>\n<p style=\"padding-left: 30px\">Remove-Variable -Name application<\/p>\n<p style=\"padding-left: 30px\">[gc]::collect()<\/p>\n<p style=\"padding-left: 30px\">[gc]::WaitForPendingFinalizers()\nThe default option of the <b>Close<\/b><i> <\/i>method is to save the Word Document. You can use the <b>wdSaveChanges<\/b> value from the <b>WdSaveOptions<\/b> enumeration as well. MSDN documents the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/office\/microsoft.office.interop.word.wdsaveoptions(v=office.11).aspx\" target=\"_blank\">WdSaveOptions enumeration<\/a>, but it is easy to use Windows PowerShell to find this information by using <b>Get-Member &ndash;static<\/b> on the variable that contains the enumeration type. What is really weird is that the interop requires that the save option is passed by reference. This is the reason for the <b>[ref]<\/b> type constraint in front of the <b>$saveOption<\/b> variable.\nI uploaded the complete <a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/Set-specific-word-document-05849e83\" target=\"_blank\">Set-SpecificDocumentProperties.ps1 script<\/a> to the Scripting Guys Script Repository&nbsp; When you download the zip file, make sure to unblock the file prior to attempting to run the script or else the script execution policy will prohibit the script from running. For more information about this, refer to the <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/04\/02\/scripting-wife-learns-about-unblocking-files-in-powershell.aspx\" target=\"_blank\">following Scripting Wife blog<\/a>.\nJoin me tomorrow for the Weekend Scripter when I will talk about parsing file names and creating <b>DateTime<\/b> objects based on the file name. It is cool.\nI invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"http:\/\/blogs.technet.commailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.\n<b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Microsoft Scripting Guy, Ed Wilson, shows how to use Windows PowerShell to automatically complete the Microsoft Word built-in properties. Microsoft Scripting Guy, Ed Wilson, is here. Registration for the Charlotte PowerShell Saturday event to be held on September 15, 2012 is underway. (Incidentally, that is the day after my birthday. The speakers dinner at [&hellip;]<\/p>\n","protected":false},"author":597,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[84,49,3,45],"class_list":["post-8491","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-microsoft-word","tag-office","tag-scripting-guy","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Microsoft Scripting Guy, Ed Wilson, shows how to use Windows PowerShell to automatically complete the Microsoft Word built-in properties. Microsoft Scripting Guy, Ed Wilson, is here. Registration for the Charlotte PowerShell Saturday event to be held on September 15, 2012 is underway. (Incidentally, that is the day after my birthday. The speakers dinner at [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/8491","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/597"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=8491"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/8491\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=8491"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=8491"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=8491"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}