简体   繁体   中英

Add Version Info to Excel file properties with VBA, read with PowerShell

My goal is to add a version number to the file properties of an Excel file that can then be read externally with PowerShell.

If I run (Get-Item "example.xls").VersionInfo I get blank ProductVersion and FileVersion.

ProductVersion   FileVersion      FileName
--------------   -----------      --------
                                  example.xls

I cannot find a way to set these attributes from VBA. I did find a way to get\\set a Revision Number:

Public Function FileVersion() As String
    With ThisWorkbook.BuiltinDocumentProperties
        FileVersion = .Item("Revision Number").Value
    End With
End Function

Public Sub UpdateFileVersion()
    With ThisWorkbook.BuiltinDocumentProperties
        .Item("Revision Number").Value = .Item("Revision Number").Value + 1
    End With
End Sub

However, I can't find a way to read the Revision Number from PowerShell. I either need to read Revision Number from PowerShell or I need to set ProductVersion and FileVersion from VBA. I would accept any combination of things that results in setting a file version in Excel that is visible outside of Excel, ideally I would like to be able to use all of these properties.

You can see the Revision Number I am trying to get from PowerShell and also the Version Number that I cannot set from VBA here:

在此处输入图片说明

If you right-click a file and hit properties in the Details tab, you see all that is available.

If you don't want to have to COM into the EOM (Excel Object Model), then you need to assign these in the EOM first, then hit them via PowerShell just as Windows Explorer shows them or enum metadata.

So, something like...

### Get file properties
## 
Get-ItemProperty -Path 'D:\Temp' -filter '*.xl*' | 
Format-list -Property * -Force

Or

### Enumerate file properties in PowerShell

# get the first file
(
$Path = ($FileName = (Get-ChildItem -Path 'D:\Temp' -Filter '*.xl*').FullName ) | 
Select-Object -First 1
)

$shell = New-Object -COMObject Shell.Application
$folder = Split-Path $path
$file = Split-Path $path -Leaf

$shellfolder = $shell.Namespace($folder)
($shellfile = $shellfolder.ParseName($file))


<#
You'll need to know what the ID of the extended attribute is. 
This will show you all of the ID's:
#>

0..287 | 
Foreach-Object { '{0} = {1}' -f $_, $shellfolder.GetDetailsOf($null, $_) }

# Once you find the one you want you can access it like this:
$shellfolder.GetDetailsOf($shellfile, 216)

As for this...

Thanks but your list, and the one I got from running this on my Excel file, do not contain Revision

... try it this way.

Gleened from here:

Hey, Scripting Guy! How Can I List All the Properties of a Microsoft Word Document?

and here:

# Getting specific properties fomr MS Word
$Path = "D:\Temp"
$ObjectProperties = "Author","Keywords","Revision number"

$Application = New-Object -ComObject Word.Application
$Application.Visible = $false
$Binding = "System.Reflection.BindingFlags" -as [type]

$Select = "Name","Created"
$Select += $ObjectProperties

ForEach ($File in (Get-ChildItem $Path -Include '*.docx' -Recurse))
{   $Document = $Application.Documents.Open($File.Fullname)

    $Properties = $Document.BuiltInDocumentProperties

    $Hash = @{}
    $Hash.Add("Name",$File.FullName)
    $Hash.Add("Created",$File.CreationTime)

    ForEach ($Property in $ObjectProperties)
    {   $DocProperties = [System.__ComObject].InvokeMember("item",$Binding::GetProperty,$null,$Properties,$Property)
        Try {$Value = [System.__ComObject].InvokeMember("value",$binding::GetProperty,$null,$DocProperties,$null)}
        Catch {$Value = $null}

        $Hash.Add($Property,$Value)
    }

    $Document.Close()

    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($Properties) | 
    Out-Null

    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($Document) | 
    Out-Null

    New-Object PSObject -Property $Hash | 
    Select $Select
}
$Application.Quit()

# Results
<#
Name            : D:\Temp\Test.docx
Created         : 06-Feb-20 14:23:55
Author          : ...
Keywords        : 
Revision number : 5
#>


# Getting specific properties fomr MS Excel
$Path = "D:\Temp"
$ObjectProperties = "Author","Keywords","Revision number"

$Application = New-Object -ComObject excel.Application
$Application.Visible = $false
$Binding = "System.Reflection.BindingFlags" -as [type]

$Select = "Name","Created"
$Select += $ObjectProperties

ForEach ($File in (Get-ChildItem $Path -Include '*.xlsx' -Recurse))
{   $Document = $Application.Workbooks.Open($File.Fullname)
    $Properties = $Document.BuiltInDocumentProperties

    $Hash = @{}
    $Hash.Add("Name",$File.FullName)
    $Hash.Add("Created",$File.CreationTime)

    ForEach ($Property in $ObjectProperties)
    {   $DocProperties = [System.__ComObject].InvokeMember("item",$Binding::GetProperty,$null,$Properties,$Property)

        Try {$Value = [System.__ComObject].InvokeMember("value",$binding::GetProperty,$null,$DocProperties,$null)}
        Catch {$Value = $null}

        $Hash.Add($Property,$Value)
    }

    $Document.Close()

    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($Properties) | 
    Out-Null

    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($Document) | 
    Out-Null

    New-Object PSObject -Property $Hash | 
    Select $Select
}
$Application.Quit()

# Results
<#
Name            : D:\Temp\Test.xlsx
Created         : 25-Nov-19 20:47:15
Author          : ...
Keywords        : 
Revision number : 2
#>

Point of note: I meant to add sources:

Regarding setting properties, see this Word example from the MS PowerShellgallery.com, which can be tweaked of course for other Office docs.

Set specific word document properties using PowerShell

The attached script uses the Word automation model to set a specific BuiltIn Word document property. It is provided as an example of how to do this. You will need to modify the pattern used to find the files, as well as the built-in Word property and value you wish to assign.

As note above, getting is the same thing...

Get Word built-in document properties

This script will allow you to specify specific Word built-in document properties. It returns an object containing the specified word document properties as well as the path to those documents. Because a PowerShell object returns, you can filter and search different information fr

Thanks to @postanote for pointing me in the right direction. None of the code offered worked out of the box for me.

This is what I ended up doing to pull the Revision Number from my Excel document:

<#  Get-Excel-Property.ps1 v1.0.0 by Adam Kauffman 2020-02-03
    Returns the property value from an Excel File
 #>
param(
    [Parameter(Mandatory=$true, Position=0)][string]$FilePath,
    [Parameter(Mandatory=$true, Position=1)][string]$ObjectProperties
)



Function Get-Property-Value {
    [CmdletBinding()]Param (
        [Parameter(Mandatory = $true)]$ComObject, 
        [Parameter(Mandatory = $true)][String]$Property
    )

    $Binding = "System.Reflection.BindingFlags" -as [type]
    Try {
        $ObjectType = $ComObject.GetType()
        $Item = $ObjectType.InvokeMember("Item",$Binding::GetProperty,$null,$ComObject,$Property)
        return $ObjectType.InvokeMember("Value",$Binding::GetProperty,$null,$Item,$null)
    }
    Catch {
        return $null
    }
}



# Main
$Application = New-Object -ComObject Excel.Application
$Application.Visible = $false

$Document = $Application.Workbooks.Open($FilePath)
$Properties = $Document.BuiltInDocumentProperties

$Hash = @{}
$Hash.Add("Name",$FilePath)

ForEach ($Property in $ObjectProperties)
{   
    $Value = Get-Property-Value -ComObject $Properties -Property $Property

    $Hash.Add($Property,$Value)
}

# COM Object Cleanup
if ($null -ne $Document) {
    $Document.Close($false)
    Remove-Variable -Name Document
}

if ($null -ne $Properties) {
    Remove-Variable -Name Properties
}

if ($null -ne $Application) {
    $Application.Quit()
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject($Application) | Out-Null
    Remove-Variable -Name Application
}

[gc]::collect()
[gc]::WaitForPendingFinalizers()

# Show collected information
New-Object PSObject -Property $Hash

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM