xlogI125’s blog

パソコン作業を効率化したい

PowerShell練習 Image.PropertyItemsの内容を取得

メモ

  • GetDetailsOfではなくImage.PropertyItemsによる取得を考える

使い捨てスクリプト

  • とりあえず何かの情報を取得する
# PowerShell 5.1, Windows 11 (2023年6月頃)

using namespace System.Collections.Generic

Set-StrictMode -Version Latest

$ErrorActionPreference = "Stop"

Add-Type -AssemblyName @(
  "Microsoft.VisualBasic"  # InputBox
  "System.Drawing"  # Image
)

# 配列をシャローコピーしたものを並べ替えて返す(FileInfoクラスとGroupInfoクラスを想定)
Add-Type -Language CSharp -ReferencedAssemblies Microsoft.CSharp -TypeDefinition @'
namespace MyNS {
  using System.Runtime.InteropServices;

  public static class Array {
    public static dynamic[] Sort(dynamic[] obj) {
      dynamic[] ret = (dynamic[])obj.Clone();
      System.Array.Sort(ret, (x, y) => StrCmpLogicalW(x.Name, y.Name));
      return ret;
    }

    [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
    private static extern int StrCmpLogicalW(
      [MarshalAs(UnmanagedType.LPWStr)] string psz1, 
      [MarshalAs(UnmanagedType.LPWStr)] string psz2
    );
  }
}
'@

try {
  # InputBox
  $str = [Microsoft.VisualBasic.Interaction]::InputBox("パス", $Host.Name, "$HOME\Desktop", -1, -1)

  # Set-Location
  Set-Location -LiteralPath (Split-Path -LiteralPath $str)

  # 並べ替え後の画像ファイル
  $psObjs = @( & {
    # 画像ファイルの拡張子
    $ext = [string]( @(
      '^\.bmp$', '^\.dib$', '^\.jpg$', '^\.jpeg$', '^\.jpe$', '^\.jfif$', '^\.gif$', '^\.tif$', '^\.tiff$', '^\.png$'
    ) -join '|' )

    # Get-ChildItem
    $fs = @(Get-ChildItem -Recurse -Depth 2 -File).Where({$_.Extension -match $ext})

    # GroupInfoクラスのNameプロパティで並べ替え
    $grByDirName = [MyNS.Array]::Sort(@($fs | Group-Object -Property DirectoryName))

    $fList = [List[IO.FileInfo]]::new()

    # FileInfoクラスのNameプロパティで並べ替え
    for ($i = 0; $i -lt $grByDirName.Length; $i++) {
      $fList.AddRange([MyNS.Array]::Sort([IO.FileInfo[]]$grByDirName[$i].Group))
    }

    return $fList
  } )

  $propNameImgPropItemSet = [HashSet[string]]::new()

  # Image.PropertyItemsプロパティ
  foreach ($psObj in $psObjs) {
    if ($psObj.Extension -notmatch '^\.bmp$|^\.gif$|^\.jpg$|^\.png$|^\.tif$') {
      continue
    }

    try {
      $fStream = $null
      $img     = $null

      $fStream = [IO.File]::Open(
        $psObj.FullName, [IO.FileMode]::Open, [IO.FileAccess]::Read, 
        [IO.FileShare]::Delete -bor [IO.FileShare]::ReadWrite
      )
      $img = [Drawing.Image]::FromStream($fStream, $false, $false)

      $propPSObj = [ordered]@{
        ImgColorDepth = [Drawing.Image]::GetPixelFormatSize($img.PixelFormat)
        ImgDpiX       = $img.HorizontalResolution
        ImgDpiY       = $img.VerticalResolution
        ImgWidth      = $img.Width
        ImgHeight     = $img.Height
      }

      foreach ($propItem in $img.PropertyItems) {
        $propName = "0x$($propItem.Id.ToString('X4'))"
        [void]$propNameImgPropItemSet.Add($propName)
        $value = "Type $($propItem.Type) / Len $($propItem.Len)"

        switch ($propItem.Type) {
          1 {
            $value += "`n"
          }

          2 {
            $encoding = [Text.ASCIIEncoding]::new()
            $str = $encoding.GetString($propItem.Value, 0, $propItem.Len - 1)

            if ($propItem.Id -eq 0x0132) {
              $dateString = $str
              $format   = "yyyy:MM:dd HH:mm:ss"  # 年月日の区切り文字がコロンだと都合が悪いため
              $provider = [Globalization.CultureInfo]::InvariantCulture
              $strDate  = [DateTime]::ParseExact($dateString, $format, $provider).ToString()
              $value    += "`n=> $strDate"
            }
            else {
              $value    += "`n=> $str"
            }
          }

          3 {
            $value += "`n=> $([BitConverter]::ToUInt16($propItem.Value, 0))"
          }

          4 {
            $value += "`n=> $([BitConverter]::ToUInt32($propItem.Value, 0))"
          }

          5 {
            $num   = [BitConverter]::ToUInt32($propItem.Value, 0)
            $den   = [BitConverter]::ToUInt32($propItem.Value, 4)
            $value += "`n=> $num / $den"
          }

          10 {
            $num   = [BitConverter]::ToInt32($propItem.Value, 0)
            $den   = [BitConverter]::ToInt32($propItem.Value, 4)
            $value += "`n=> $num / $den"
          }

          default {
            $value += "`n=> $([BitConverter]::ToString($propItem.Value))"
          }
        }

        $propPSObj.Add($propName, $value)
      }

      $img.Dispose()
      $fStream.Dispose()

      $psObj | Add-Member -NotePropertyMembers $propPSObj
    }
    catch {
      if ($null -ne $img    ) { $img.Dispose()     }
      if ($null -ne $fStream) { $fStream.Dispose() }
      throw
    }
  }

  $props = [List[string]]::new()
  $props.AddRange([string[]]@("BaseName", "Extension", "DirectoryName"))
  $props.AddRange([string[]]@("ImgColorDepth", "ImgDpiX", "ImgDpiY", "ImgWidth", "ImgHeight"))
  $props.AddRange([string[]]@($propNameImgPropItemSet | Sort-Object))

  $outObjs = @($psObjs | Select-Object -Property $props)

  $html = $outObjs | ConvertTo-Html -Head ( @(
    '<style>'
    '  table  { border-collapse: collapse; }'
    '  th, td { white-space: pre; word-break: keep-all; border: thin solid black; }'
    '</style>'
  ) -join "`n" )

  # HTMLファイルとして出力
  $html | Out-File -FilePath "${env:USERPROFILE}\Desktop\OutputTest.html"

  $outObjs | Out-GridView
}
catch {
  throw
}