#PowerShell, #PowerWiseScripting, #ProjectWise, PWPS_DAB

HowTo: Get ProjectWise Work Area Property Definitions

Be sure to check out my Scripting4Crypto initiative. It’s a fun way to get into using cryptocurrencies all while getting your PowerShell needs met.

When working with ProjectWise Work Area Types, it is often necessary to inspect the attribute definitions assigned to a specific Work Area class. The Get-PWWorkAreaProperties function provides a simple way to retrieve these definitions and return them either as a hashtable or as rows in a referenced DataTable.

This function is part of a larger ProjectWise scripting toolkit and uses the PWWrapper API to query Work Area (Rich Project) metadata.

Overview

Get-PWWorkAreaProperties retrieves the list of attribute definitions for a given ProjectWise Work Area Type. The function supports two output modes:

  1. Hashtable output (default)
    • Returns key/value pairs of:
      • Name — The internal attribute name
      • Label — The user-friendly display label
  2. DataTable output
    • When a [ref] to a DataTable is supplied, the following columns are populated:
      • ID — Attribute ID
      • Name — The internal attribute name
      • Description
      • Label — The user-friendly display label

Features

  • Supports dynamic validation of -WorkAreaType using Get-PWRichProjectTypes.
  • Provides detailed -Verbose logging for tracing API activity
  • Uses PWWrapper to access:
    • Class pointer
    • Attribute definitions
    • Attribute common properties
    • Attribute label strings
  • Supports silent error handling with warnings
  • Lets you choose the return method (hashtable or DataTable)

Syntax

Get-PWWorkAreaProperties [-WorkAreaType <string>] [-dtWorkAreaProperties <ref DataTable>] [-Verbose]

Parameters

-WorkAreaType <string>

Dynamically generated ValidateSet based on available Rich Project Types.
If provided, the function retrieves the attribute definitions for that Work Area class.

-dtWorkAreaProperties <ref DataTable>

Optional.

If supplied, the function populates the referenced DataTable instead of returning a hashtable.

Examples

Example 1: Return attributes as a hashtable

$WorkAreaType = 'MyWorkAreaType'
$waProperties = Get-PWWorkAreaProperties -WorkAreaType $WorkAreaType -Verbose

Example 2: Populate a DataTable

# Datatable to populate with Work Area property definition data.
$dtWorkAreaProperties = [Data.DataTable]::new("Properties")
Get-PWWorkAreaProperties -WorkAreaType MyWorkAreaType -dtWorkAreaProperties ([ref] $dtWorkAreaProperties) `
-Verbose

Script

The following is the entire function definition.

FUNCTION Get-PWWorkAreaProperties{
<#
.SYNOPSIS
Used to obtain the Work Area properties definitions.
.DESCRIPTION
Used to obtain the Work Area properties definitions by the specified Work Area type provided.
There are two ways to return data.
- via a hashtable (Name and Label)
- via a referenced datatable (ID, Name, Description, Label)
.EXAMPLE
# Returns the Work Area properties for the provided Work Area type.
$WorkAreaType = 'MyWorkAreaType'
$waproperties = Get-PWWorkAreaProperties -WorkAreaType MyWorkAreaType -Verbose
.EXAMPLE
# Populates the provided datatable with the Work Area properties for the specified Work Area type.
$dtWorkAreaProperties = [Data.Datatable]::new("Properties")
Get-PWWorkAreaProperties -WorkAreaType MyWorkAreaType -dtWorkAreaProperties ([ref] $dtWorkAreaProperties) -Verbose
#>
[CmdletBinding()]
param(

# Referenced datatable to populate.
# When included, the datatable will be populated with the Work Area properties.
# Otherwise, a list is returned with the PropertyName and Label.
[ValidateScript({
if($_.GetType().Name -eq 'DataTable'){
$true
} else {
$false
}})]
[Parameter(Mandatory = $false)]
[Data.DataTable][ref] $dtWorkAreaProperties

) #end param...

BEGIN {

$CmdletName = $MyInvocation.MyCommand.Name
$StartTime = Get-Date

Write-Verbose -Message "[BEGIN] $StartTime - Entering '$CmdletName' Function..."

$WorkAreaType = $PSBoundParameters[$ParameterName]

if($dtWorkAreaProperties){
$dtWAProperties_Temp = [Data.Datatable]::new('WorkAreaProperties')
$dtWAProperties_Temp.Columns.AddRange(@(
'ID'
'Name'
'Description'
'Label'
))
}

} #end BEGIN...

DynamicParam {
$ParameterName = 'WorkAreaType'
$RuntimeParameterDictionary = New-Object System.Management.Automation.RuntimeDefinedParameterDictionary
$AttributeCollection = New-Object System.Collections.ObjectModel.Collection[System.Attribute]

$ParameterAttribute = New-Object System.Management.Automation.ParameterAttribute
$ParameterAttribute.Mandatory = $false
$AttributeCollection.Add($ParameterAttribute)

$arrset = (Get-PWRichProjectTypes).GetEnumerator() | Select-Object -ExpandProperty ClassName | Sort-Object ClassName
$ValidateSetAttribute = New-Object System.Management.Automation.ValidateSetAttribute($arrset)
$AttributeCollection.Add($ValidateSetAttribute)

$RuntimeParameter = New-Object System.Management.Automation.RuntimeDefinedParameter($ParameterName, [string], $AttributeCollection)
$RuntimeParameterDictionary.Add($ParameterName, $RuntimeParameter)

return $RuntimeParameterDictionary
} # end DynamicParam

PROCESS {

try{
$slAttributes = @{}
$iptr = [pwwrapper]::aaOApi_FindClassPtrByName($WorkAreaType)
if($iptr -eq 0){
throw "Failed to get Class information for '$WorkAreaType'."
}

$iCount = [pwwrapper]::aaOApi_GetClassAttrCount($iptr, $true)
if($iCount -eq -1){
throw "Failed to get Class / Attribute count."
}

for($i = 0; $i -lt $iCount; $i++){ # break

#region VARIABLES

[int] $lAttrId = 0
[int] $lAddonId = 0
[int] $lParentId = 0;
[int] $lVisibility = 0
[int] $lValueType = 0
[int] $iDataLen = 0;
$sbAttrName = [System.Text.StringBuilder]::new(256)
$sbAttrDesc = [System.Text.StringBuilder]::new(256)
$sbAttrLabel = [System.Text.StringBuilder]::new(256)

#endregion VARIABLES

try{

if( -not ([pwwrapper]::aaOApi_GetClassAttributes($iptr, 0, $i, ([ref] $lAttrId), ([ref] $lAddonId), ([ref] $lParentId)))){
throw [pwwrapper]::aaApi_GetMessageByErrorId([pwwrapper]::aaApi_GetLastErrorId())
}

$pAttr = [System.IntPtr]::Zero
if( -not ([pwwrapper]::aaOApi_FindAttribute(([ref] $pAttr), $lAttrId))){
throw [pwwrapper]::aaApi_GetMessageByErrorId([pwwrapper]::aaApi_GetLastErrorId())
}

if ( -not ([PWWrapper]::aaOApi_GetAttributeCmnProps($pAttr, ([ref] $lAttrId), $sbAttrName, $sbAttrDesc,
([ref] $lVisibility), ([ref] $lValueType), ([ref] $iDataLen)))) {
throw [pwwrapper]::aaApi_GetMessageByErrorId([pwwrapper]::aaApi_GetLastErrorId())
}

Write-Verbose -Message "[PROCESS] $($sbAttrName.ToString())"

if([PWWrapper]::aaOApi_GetAttributeStringProperty($pAttr, [PWWrapper+ODSAttributeProperty]::Label, $sbAttrLabel, $sbAttrLabel.Capacity)){
Write-Verbose -Message "[PROCESS] - Label: $($sbAttrLabel.ToString())"
} else {
Write-Warning -Message "[PROCESS] Failed to get label."
}

if( -not ($dtWorkAreaProperties)){
if ( -not ($slAttributes.ContainsKey($sbAttrName.ToString()))){
$slAttributes.Add($sbAttrName.ToString(), $sbAttrLabel.ToString()) | Out-Null
}
} else {
$dr = $dtWAProperties_Temp.NewRow()
$dr.ID = $lAttrId
$dr.Name = $sbAttrName.ToString()
$dr.Description = $sbAttrDesc.ToString()
$dr.Label = $sbAttrLabel.ToString()
$dtWAProperties_Temp.Rows.Add($dr) | Out-Null
}

} catch {
Write-Warning -Message "[PROCESS] $_"
}
} # end for($i = 0; $i -lt $iCount; $i++){...

} catch {
Write-Warning -Message "[PROCESS] $_"
} finally {
if( -not ($dtWorkAreaProperties)){
if($slAttributes.Count -gt 1){
Write-Output $slAttributes
}
} else {
# Add returned data to the referenced datatable.
$dtWorkAreaProperties.Merge($dtWAProperties_Temp)
}
}

} #end PROCESS...

END{

$EndTime = Get-Date
Write-Verbose -Message "[END] It took $($EndTime - $StartTime) to complete the process."
Write-Verbose -Message "[END] $EndTime - Exiting '$CmdletName' Function..."

} #end END...

} #end FUNCTION Get-PWWorkAreaProperties...
Export-ModuleMember -Function Get-PWWorkAreaProperties

 


Experiment with it and have fun.

Hopefully, you find this useful. Please let me know if you have any questions or comments.  If you like this post, please click the Like button at the bottom of the page. And thank you for checking out my blog.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.