dynamic hastable key and value

This topic contains 5 replies, has 3 voices, and was last updated by  Grant Harrington 2 months, 4 weeks ago.

  • Author
    Posts
  • #98401

    Grant Harrington
    Participant

    I need to extract the "Last Author" field from several thousand Word files and have put this code together to get the values needed, but am having a difficult time getting the output to CSV. For a single file, it works for outputting the content on the screen.

    I've tried Out-File and that gets me a single column of the $VarPropValue content.

    I work with Custom Objects frequently, but am stuck on getting this converted to Key = $Var, Value = $VarPropValue, such that the key value is what-ever the content at the time the $Var variable passed through the foreach loop.

    Using fixed key names, I often create dynamic value "values", but an not sure how to create a loop with unique key values each time.

    Key | Value
    Title | My Title
    Author | Someone Else
    etc..

    Title: My Title
    Subject:
    Author: Someone Else
    Keywords:
    Comments:
    Template: Notebook Template
    Last author: Harrington, Grant
    Creation date: 01/08/2018 17:43:00
    Last save time: 01/08/2018 17:43:00
    Number of pages: 3
    Company: My Company

    
    
    $wordApplication = New-Object -ComObject word.application
    
    $document = $wordApplication.documents.open("C:\Users\Grant.Harrington\Desktop\TEMPWORD\Test\2018-01-08_22-43-00.DOCX");
    
    $binding = "System.Reflection.BindingFlags" -as [type];
    
    $builtinProperties = $document.BuiltInDocumentProperties
    
    #region Variables
    $WordVariables = 'Title', 'Subject', 'Author', 'Keywords', 'Comments', 'Template', 'Last author', 'Creation date', 'Last save time', 'Number of pages', 'Company'
    foreach ($var in $WordVariables)
    {
    	
    	# This is the Property Name we are trying to find
    	$pVar = "$var"
    	
    	[Array]$VarArgs = $pVar
    	
    	#Get the Property item for the Comments property
    	$VarProp = [System.__ComObject].InvokeMember("Item", $binding::GetProperty, $null, $builtinProperties, $VarArgs)
    	
    	#Get the value of the Comments property, so we can check if it contains a version string.
    	$VarPropValue = [System.__ComObject].InvokeMember("value", $binding::GetProperty, $null, $VarProp, $null);
    	
    	Write-Output "$Var`: $VarPropValue"
    	
    } #end foreach var
    
    #endregion Variables
    
    $document.Close($false) 
    $wordApplication.Quit()
    
  • #98415

    Joel Sallow
    Participant

    If you want to construct a hashtable with key/value pairs, you need to do two things:

    1: instantiate an empty hashtable before the loop:

    $hashtable = @{}

    2: add the items to the hashtable instead of doing Write-Output:

    $hashtable[$var] = $VarPropValue

    That said... are you pulling document properties from the Word document? If I'm not mistaken, you should be able to do this a lot more simply. (Though, granted, I've not had nearly as much experience dealing with Word COM Objects, because Word isn't presently installed on my machine...)

    Tell you what... What does the $BuiltInProperties variable contain before the loop? (Just Write-Output that variable & copy-paste — make sure if it includes any sensitive data you snip that out!) and then also, what does $BuiltInProperties.GetType() give you? I feel like this is a very circuitous method and there might be a far simpler one available to you!

  • #98470

    Grant Harrington
    Participant

    I'm using this method (Word.Application), because the Shell.Application doesn't capture the Last Author Field (snippet from that script is below).

    # Creates the Shell.Application (to capture settings)
    $objShell = New-Object -ComObject Shell.Application
    In that example, I'm able to capture the values I need by $FileAttributesReportAll | export-csv

    I'm trying to accomplish the same results with the word.application data, but get the "Last Author" property.

    PS C:\Users\grant.harrington\Documents\GitHub\PowerShell (master ↑1 +13 ~8 -0 !)
    $ Write-Output $builtinProperties
    System.__ComObject

    PS C:\Users\grant.harrington\Documents\GitHub\PowerShell (master ↑1 +13 ~8 -0 !)
    $ $BuiltInProperties.GetType()
    Object reference not set to an instance of an object.
    At line:1 char:1
    + $BuiltInProperties.GetType()
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OperationStopped: (:) [], NullReferenceException
    + FullyQualifiedErrorId : System.NullReferenceException

    foreach ($IndividualDirectory in $SubDirectory.FullName)
    {
    	$Subfilesdir = Get-ChildItem $IndividualDirectory
    	# Scans files and puts in an array
    	
    	# Creates the Shell.Application (to capture settings)
    	$objShell = New-Object -ComObject Shell.Application
    	
    	# Assigns ParentDirectory to ComObject Namespace
    	$objFolder = $objShell.namespace($IndividualDirectory)
    	
    	
    	$Global:FileAttributesReportAll = @() 
    	
    	if ($Subfilesdir.Name -like "*.doc*")
    	{
    		foreach ($File in $Subfilesdir.Name)
    		{
    			$Item = $objFolder.items().item($File)
    			
    			$ObjFileAttributes = [ordered]@{
    				"Authors"  = $objFolder.getDetailsOf($Item, 20)
    				"Company"  = $objFolder.getDetailsOf($Item, 33)
    				"Folder Path" = $objFolder.getDetailsOf($Item, 186)
    				"Path"	   = $objFolder.getDetailsOf($Item, 189)
    			} #end $ObjFileAttributes
    			
    			$Global:FileAttributesReport = New-Object -TypeName PSObject -Property $ObjFileAttributes
    			$Global:FileAttributesReportAll += $FileAttributesReport
    			
            	$FileAttributesReportAll
    
  • #98479

    Sam Boutros
    Participant
    #region Input
    
    [CmdletBinding(ConfirmImpact='Low')] 
    Param(
        [Parameter(Mandatory=$false)]
            [ValidateScript({Test-Path $_ -PathType 'Container'})]
                [String]$WordFolder = 'c:\data' # Path to folder containing Word Docs
    )
    
    #endregion
    
    #region Initialize
    
    $FileList = Get-ChildItem -Path $WordFolder | where Extension -Match 'doc'
    if (! $FileList) { throw "No word documents found under folder '$WordFolder'" }
    $wordApplication = New-Object -ComObject word.application
    $wordApplication.Visible = $false
    $binding = 'System.Reflection.BindingFlags' -as [type]
    $PropertyList = @('Title', 'Subject', 'Author', 'Keywords', 'Comments', 'Template', 'Last author', 'Creation date', 'Last save time', 'Number of pages', 'Company')
    
    #endregion
    
    #region Process
    
    $myOutput = foreach ($FileName in $FileList.FullName) {
        Write-Verbose "Processing file '$FileName'" 
        $document = $wordApplication.documents.open($FileName)
        $builtinProperties = $document.BuiltInDocumentProperties
        $myHashTable = [Ordered]@{ FileName = $FileName }
        foreach ($Property in $PropertyList) {
            $Temp  = [System.__ComObject].invokemember('Item',$binding::GetProperty,$null,$builtinProperties,$Property)
            $Value = [System.__ComObject].InvokeMember('value', $binding::GetProperty, $null, $Temp, $null)
            $myHashTable += @{ $Property = $Value }
        }
        New-Object -TypeName PSCustomObject -Property $myHashTable
        $document.Close($false)
    }
    $wordApplication.Quit()
    
    #endregion
    
    #region Output
    
    $myOutput | FT -a 
    $myOutput | Export-Csv "$WordFolder\WordDocsSelectMetadata1.csv" -NoType
    $myOutput | Out-GridView
    
    #endregion
    

    Please ask about anything that needs to be explained in the code above

    • #99658

      Grant Harrington
      Participant

      Thank you. That was what I needed to get the data to CSV where I can further work with it (create directories based on Last author, sort, etc..). I was able to take that concept and apply to a similar script I'm working with to obtain Excel metadata. I'm sure this can be expanded to other CustomObject projects as well.

You must be logged in to reply to this topic.