Powershell 错误“GDI+ 中发生一般错误。”更改图像属性后尝试覆盖图像时

问题描述 投票:0回答:1

我正在尝试批量编辑我保存在硬盘上的大量照片。基本上我需要更改拍摄日期的属性,以便我可以对它们进行索引。我将使用文件名来获取实际日期。

我正在使用 Powershell 编写脚本,但最后无法使其与 .save() 命令一起使用。

这是我到目前为止所写的

[reflection.assembly]::LoadWithPartialName("System.Drawing")

$filePath = 'imagePath,jpg'

# Load the image as a copy
$originalPic = New-Object System.Drawing.Bitmap($filePath)
$pic = $originalPic.Clone()

# Dispose the original bitmap object to release the file handle
$originalPic.Dispose()
$originalPic = $null

$props = @("306", "20531", "36867", "36868")

$defaultDateTime = Get-Date "2018-06-28"
$dateString = $defaultDateTime.ToString("yyyy:MM:dd HH:mm:ss`0")
$byteArray = [System.Text.Encoding]::ASCII.GetBytes($dateString)

foreach($prop in $props){
    
    try{
        $propertyItem = $pic.GetPropertyItem($prop)
    }
    catch{
        $propertyItem = [System.Runtime.Serialization.FormatterServices]::GetUninitializedObject([System.Drawing.Imaging.PropertyItem])
        $propertyItem.Id = $prop
    }
    
    $propertyItem.Type = 2
    $propertyItem.Len = $byteArray.Length
    $propertyItem.Value = $byteArray

    
    $pic.SetPropertyItem($propertyItem)
}

$pic.Save($filePath)  # Save the modified image with the new property

除了最后一步我覆盖它之外,一切都工作正常。 我知道我可以给出不同的名称,这确实有效,但我不想为每个图像创建一个副本。

我以为通过 .clone 我会获得一个副本,在处理后,它只会关闭该对象,但它似乎总是在那里。

这就是我得到的错误

Exception calling "Save" with "1" argument(s): "A generic error occurred in GDI+."
At C:\Users\user\Desktop\Random Tools\Powershell\FixMyPic.ps1:40 char:1
+ $pic.Save($filePath)  # Save the modified image with the new property
+ ~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : ExternalException

谢谢大家。

我还在处置后将其设置为 $null ,因为它似乎只是删除了属性,但对象仍然存在(我猜这是正常的),但我认为将其设置为 null 会更好。 Object still loaded after dispose

我尝试关注一些 .NET 相关问题,他们使用了“using”,但我认为它不会有太大改变。

我尝试按照这个问题

打开和关闭 ISE

编辑:

我还可以看到,即使使用了 dispose,图像仍然由 Powershell 保存,这就是它阻止覆盖的原因

enter image description here

powershell metadata dispose exif
1个回答
0
投票

您当前的方法 - 将文件路径传递给

[Bitmap]
构造函数,然后克隆生成的对象 - 似乎还克隆创建的文件句柄,导致当您调用
$pic.Save
时发生共享冲突,就像现在
$pic
本身一样对同一文件有读锁。

$originalPic = New-Object System.Drawing.Bitmap($filePath) # exclusive read handle count: 1
$pic = $originalPic.Clone()                                # exclusive read handle count: 2

$originalPic.Dispose()                                     # exclusive read handle count: 1
$originalPic = $null                                       # still not in the clear...

在幕后,所有文件系统 I/O 均由 Windows 的本机 GDI+ API 处理,因此您会收到“通用 GDI+ 错误”,因为它无法完成文件写入,并且不知道这是因为调用应用程序有文件的独占句柄。

要解决此问题,请使用

[Image]::FromFile
加载原始图片,然后将生成的图像对象传递给
[Bitmap]
构造函数 - 此时构造函数将从内存中对象复制图像数据,而不是重新创建文件句柄,在处理原始文件后,您应该可以自由地获取该文件的写入句柄:

$originalPic = [System.Drawing.Image]::FromFile($filePath) # exclusive read handle count: 1
$pic = [System.Drawing.Bitmap]::new($originalPic)          # exclusive read handle count: 1
$originalPic.Dispose()                                     # exclusive read handle count: 0
© www.soinside.com 2019 - 2024. All rights reserved.