PowerShell修改图片拍摄日期时覆盖保存出现GDI+通用错误
解决GDI+保存图片覆盖原文件失败问题
需要批量修改硬盘照片的拍摄日期属性(计划从文件名提取日期),使用PowerShell脚本修改属性后执行Save()方法覆盖原文件时,抛出**"A generic error occurred in GDI+."**错误。尝试过克隆图片、释放原对象、调用Dispose()、设为null、重启ISE等方法,文件仍被PowerShell占用,仅能保存为新文件,希望实现直接覆盖原文件。
原脚本代码
[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
错误信息
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
解决方案
核心问题是Bitmap.Clone()方法仍会保留对原文件的隐式引用,导致文件句柄未完全释放。正确做法是通过内存流中转加载图片,彻底脱离原文件依赖:
修改后的脚本
[reflection.assembly]::LoadWithPartialName("System.Drawing") $filePath = 'imagePath.jpg' # 读取文件到内存流,彻底释放原文件句柄 $fileStream = [System.IO.File]::OpenRead($filePath) $memoryStream = New-Object System.IO.MemoryStream $fileStream.CopyTo($memoryStream) $fileStream.Dispose() # 从内存流创建Bitmap对象,与原文件完全解耦 $pic = New-Object System.Drawing.Bitmap($memoryStream) $memoryStream.Dispose() # 属性ID直接用数字,避免类型转换问题 $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) # 最后释放Bitmap资源,避免内存泄漏 $pic.Dispose()
关键说明
- 用内存流加载图片后,原文件不再被占用,可直接执行覆盖保存。
- 将属性ID从字符串改为数字,减少不必要的类型转换操作。
- 操作完成后调用
Dispose()释放Bitmap对象,避免内存泄漏。
内容的提问来源于stack exchange,提问作者AndyMcMars
相关产品推荐
相关产品推荐

