所需依赖库下载:

Gdip.ahk库

 

#NoEnv
#SingleInstance Force
SetBatchLines, -1
#Include <Gdip_All> 

Gui, Add, ListView,  Grid AltSubmit x10 y10 r20 w330 h400 vMyListView g列表被点击 hwndHLV, 文件名|拍摄日期| 💬
LV_ModifyCol(1, 150)
LV_ModifyCol(2, 130)
LV_ModifyCol(3, 30)
SetHeaderItemCheckBox(hLV, 1)
gui, font, s9, 微软雅黑
gui, Add, Text, x13 y+2 w150 v项目信息
gui, Add, Text, x280 y+-16 w200 v修改信息
gui, font, s12, 微软雅黑
gui, Add, text, x355 y10 w180 vname
gui, Add, Pic, y+10 vPic g打开图片
gui, font, s9, 微软雅黑
gui, Add, text, x+0 y165 , 拍摄日期:
gui, Add, text, y+5 , 创建日期:
gui, Add, text, y+5 , 修改日期:
gui, Add, text, c002b55 x420 y165 w130 v拍摄日期
gui, Add, text, c002b55 y+5 w130 v创建日期
gui, Add, text, c002b55 y+5 w130 v修改日期

gui, Add, Radio, Checked0 x355 y260 v指定时间单选 g单选按钮被点击, 指定日期:
gui, Add, DateTime, y+5 w180 v指定时间
gui, Add, Radio, Checked0 y+5 v匹配文件名单选 g单选按钮被点击, 匹配文件名:
gui, Add, Edit, y+5 w180 v匹配文件名编辑框
Gui, Add, Button, x355 y387 w50 g加载按钮被点击, 打 开
Gui, Add, Button, x+14 w50 +Disabled v移除按钮 g移除按钮被点击, 移 除
Gui, Add, Button, x+14 w50 +Disabled v修改按钮 g修改按钮被点击, 修 改
ImageListID1 := IL_Create(10)  ; 创建图像列表, 这样 ListView 才可以显示图标:
LV_SetImageList(ImageListID1)  ; 关联图像列表到 ListView, 然而它就可以显示图标了:
Gui, Show, , 相片拍摄时间修改
Folder := "D:\picture"
Gosub, 加载文件
return

加载按钮被点击:
Gui +OwnDialogs  ; 强制用户解除此对话框后才可以操作主窗口.
FileSelectFolder, Folder,, 4, 选择要加载的目录
if not Folder  ; 用户取消了对话框.
   return
Else
{
   LV_Delete()
   Gosub, 加载文件 
}
Return

加载文件:
global 加载文件 := 1
GDIPToken := Gdip_Startup()
; 检查文件夹名称的最后一个字符是否为反斜杠, 对于根目录则会如此,
; 例如 C:\. 如果是, 则移除这个反斜杠以避免之后出现两个反斜杠.
LastChar := SubStr(Folder, 0)
if (LastChar = "\")
Folder := SubStr(Folder, 1, -1)  ; 移除尾随的反斜杠.

; 计算 SHFILEINFO 结构需要的缓存大小.
sfi_size := A_PtrSize + 8 + (A_IsUnicode ? 680 : 340)
VarSetCapacity(sfi, sfi_size)

; 获取所选择文件夹中的文件名列表并添加到 ListView:
Loop Files, %Folder%\*.*
{
   ; 建立唯一的扩展 ID 以避免变量名中的非法字符,
   ; 例如破折号. 这种使用唯一 ID 的方法也会执行地更好,
   ; 因为在数组中查找项目不需要进行搜索循环.
   SplitPath, A_LoopFileFullPath,, Dir, FileExt  ; 获取文件扩展名.
   If A_LoopFileExt In bmp,jpg,png,tif
   {
      ExtID := 0  ; 进行初始化来处理比其他更短的扩展名.
      Loop 7     ; 限制扩展名为 7 个字符, 这样之后计算的结果才能存放到 64 位值.
      {
         ExtChar := SubStr(FileExt, A_Index, 1)
         if not ExtChar  ; 没有更多字符了.
         break
         ; 把每个字符与不同的位置进行运算来得到唯一 ID:
         ExtID := ExtID | (Asc(ExtChar) << (8 * (A_Index - 1)))
      }
      ; 检查此文件扩展名的图标是否已经在图像列表中. 如果是,
      ; 可以避免多次调用并极大提高性能,
      ; 尤其对于包含数以百计文件的文件夹而言:
      IconNumber := IconArray%ExtID%

      if not IconNumber  ; 此扩展名还没有相应的图标, 所以进行加载.
      {
         ; 获取与此文件扩展名关联的高质量小图标:
         if not DllCall("Shell32\SHGetFileInfo" . (A_IsUnicode ? "W":"A"), "Str", A_LoopFileFullPath
            , "UInt", 0, "Ptr", &sfi, "UInt", sfi_size, "UInt", 0x101)  ; 0x101 为 SHGFI_ICON+SHGFI_SMALLICON
         IconNumber := 9999999  ; 把它设置到范围外来显示空图标.
         else ; 成功加载图标.
         {
            ; 从结构中提取 hIcon 成员:
            hIcon := NumGet(sfi, 0)
            ; 直接添加 HICON 到小图标和大图标列表.
            ; 下面加上 1 来把返回的索引从基于零转换到基于一:
            IconNumber := DllCall("ImageList_ReplaceIcon", "Ptr", ImageListID1, "Int", -1, "Ptr", hIcon) + 1

            DllCall("DestroyIcon", "Ptr", hIcon)
            ; 缓存图标来节省内存并提升加载性能:
            IconArray%ExtID% := IconNumber
         }
      }

      GDIPImage := Gdip_LoadImageFromFile(A_LoopFileLongPath)
      PropItem := Gdip_GetPropertyItem(GDIPImage, 0x9003) ; ExifDTOrig - Date & time of original
      Gdip_DisposeImage(GDIPImage)
      Datetime := PropItem.Value
      ; 在 ListView 中创建新行并把它和上面的图标编号进行关联:
      LV_Add("Icon" . IconNumber, A_LoopFileName, Datetime)

      if (A_Index = 1)
         显示图像(A_LoopFileName, A_LoopFileLongPath, datetime)  
      GuiControl,Text, 项目信息, % LV_GetCount() . " 个图像"
   }
}
Gdip_Shutdown(GDIPToken)
加载文件 := 0
return

移除按钮被点击:
GuiControl, -Redraw, MyListView
起始行 := 0
RowNumber := 0  ; 这会使得首次循环从顶部开始搜索.
Loop
{
   RowNumber := LV_GetNext(RowNumber - 1)
   if !起始行
   起始行 := RowNumber
   if not RowNumber  ; 上面返回零, 所以没有更多选择的行了.
   break
   LV_Delete(RowNumber)  ; 从 ListView 中删除行.
}
起始行 := (起始行 > 1) ? 起始行-1 : 起始行
LV_GetText(FileName, 起始行, 1) ; 从首个字段中获取文本.
LV_GetText(Datetime, 起始行, 2)
Filepath := Dir "\" FileName
显示图像(FileName, FilePath, datetime)
GuiControl, +Redraw, MyListView 
Return

单选按钮被点击:

if (A_GuiControl = "匹配文件名单选")
{
   GuiControl,, 指定时间单选 , 0
   GuiControl,, 匹配文件名单选 , 1
   global 拍摄时间来源 := "匹配文件"
   GuiControl, Enable, 修改按钮
   tooltip % 拍摄时间
}
Else
{
   GuiControl,, 指定时间单选 , 1
   GuiControl,, 匹配文件名单选 , 0
   global 拍摄时间来源 := "指定时间"
   GuiControl, Enable, 修改按钮
}
Gui, Submit, NoHide 
Return

修改按钮被点击:
GuiControl, -Redraw, MyListView
Loop % LV_GetCount()
LV_Modify(A_Index,,,, "") 
GuiControl, +Redraw, MyListView 
Gui, Submit, NoHide 
GDIPToken := Gdip_Startup()
拍摄时间 := ""
global 修改数组 := {}
RowNumber := 0  ; 这样使得首次循环从列表的顶部开始搜索.
Loop
{
   RowNumber := LV_GetNext(RowNumber)  ; 在前一次找到的位置后继续搜索.
   if not RowNumber  ; 上面返回零, 所以选择的行已经都找到了.
   break
   LV_GetText(FileName, RowNumber, 1) ; 从首个字段中获取文本.
   LV_GetText(Datetime, RowNumber, 2)

   if (拍摄时间来源 = "指定时间")
   FormatTime, 拍摄时间, %指定时间%, yyyy/MM/dd HH:mm:ss  ;yyyy/MM/dd HH:mm:ss
   Else
   拍摄时间 := 匹配文件名(FileName)[1]
   修改数组.insert(RowNumber "|" Dir . "\" . FileName "|" Datetime "|" 拍摄时间)
}

修改项 := []
for index, element in 修改数组 ; 在大多数情况下建议使用枚举的方式.
{
   修改项 := StrSplit(element, "|")
   sCmd := "D:\3m_script\exiftool.exe"
         . " -P"
         . " -ModifyDate="  """" 修改项[4] """"
         . " -DateTimeOriginal=" """" 修改项[4] """"
         . " -overwrite_original " 
         . 修改项[2]
   ret:=CmdRet(sCmd) 

   LV_Modify(修改项[1], "Vis")
   LV_Modify(修改项[1], "-Select")
   GDIPImage := Gdip_LoadImageFromFile(修改项[2])
   PropItem := Gdip_GetPropertyItem(GDIPImage, 0x9003)
   Gdip_DisposeImage(GDIPImage)
   Datetime := StrReplace(PropItem.Value, ":")
   Datetime := StrReplace(Datetime, " ")
   FormatTime, Datetime, %Datetime%, yyyy/MM/dd HH:mm:ss
   if (Datetime = 修改项[4])
      LV_Modify(修改项[1], , , Datetime, " ✔")
}
Gdip_Shutdown(GDIPToken)
Return

列表被点击:
Critical
if (A_GuiEvent = "i")  ;修改动作会触发 i
{
   选择 := 0
   RowNumber := 0  ; 这样使得首次循环从列表的顶部开始搜索.
   Loop
   {
      RowNumber := LV_GetNext(RowNumber)  ; 在前一次找到的位置后继续搜索.
      if not RowNumber  ; 上面返回零, 所以选择的行已经都找到了.
      break
      选择++
   }
   GuiControl,Text, 项目信息, % LV_GetCount() . " 个图像   选择 " 选择 " 个"
   if (选择 > 0)
   GuiControl, Enable, 移除按钮
   Else
   GuiControl, Disable, 移除按钮
   if (选择 > 0 and 拍摄时间来源 != "")
   GuiControl, Enable, 修改按钮
   Else
   GuiControl, Disable, 修改按钮
   if (LV_GetCount() = 0 and 选择 = 0)
   {
      GuiControl,Text, 拍摄日期
      GuiControl,Text, 创建日期
      GuiControl,Text, 修改日期
      GuiControl,, 匹配文件名编辑框
      GuiControl,Text, name, 文件名
   }
}

if (A_GuiEvent = "Normal")  ; 脚本还可以检查许多其他的可能值.
{
   选择 := 0
   RowNumber := 0  ; 这样使得首次循环从列表的顶部开始搜索.
   Loop
   {
      RowNumber := LV_GetNext(RowNumber)  ; 在前一次找到的位置后继续搜索.
      if not RowNumber  ; 上面返回零, 所以选择的行已经都找到了.
      break
      选择++
   }
   if A_EventInfo
   {
      LV_GetText(FileName, A_EventInfo, 1) ; 从首个字段中获取文本.
      LV_GetText(Datetime, A_EventInfo, 2)
      Filepath := Dir "\" FileName
      显示图像(FileName, FilePath, datetime)
   }
}
Return

显示图像(Filename, FilePath, datetime)
{
   if !GDIPToken
   GDIPToken := Gdip_Startup()

   global picpath := FilePath    ; 用于点击图片时 执行Run
   GuiControl,Text, name, %Filename%
   pBitmap := Gdip_CreateBitmapFromFile(FilePath)
   Gdip_GetImageDimensions(pBitmap, Width, Height)
   Gdip_DisposeImage(pBitmap)
   if (Width > Height)
   GuiControl,, Pic, % "*w200 *h-1 " FilePath
   Else
   GuiControl,, Pic, % "*w-1 *h150 " FilePath

   if !datetime
      拍摄时间 := ""
   Else
   {
      Datetime := StrReplace(datetime, ":")
      Datetime := StrReplace(Datetime, " ")
      FormatTime, 拍摄时间, %Datetime%, yyyy/MM/dd  HH:mm
   }
   GuiControl,Text, 拍摄日期, %拍摄时间%

   if (拍摄时间来源 = "匹配文件")
   {
      匹配时间 := 匹配文件名(FileName)
      if (匹配时间[2] = 0)
      {
         Gui, Font, cRed
         GuiControl, Font, 匹配文件名编辑框
      }
      Else
      {
         Gui, Font, c000000
         GuiControl, Font, 匹配文件名编辑框
      }
      GuiControl,, 匹配文件名编辑框, % 匹配时间[1]
   }

   FileGetTime, 创建时间, %FilePath%, C
   FileGetTime, 修改时间, %FilePath%, M
   FormatTime, 创建时间, %创建时间%, yyyy/MM/dd  HH:mm
   FormatTime, 修改时间, %修改时间%, yyyy/MM/dd  HH:mm 
   GuiControl,Text, 创建日期, %创建时间%
   GuiControl,Text, 修改日期, %修改时间%

   if GDIPToken and (加载文件 = 0)
   Gdip_Shutdown(GDIPToken)
}

匹配文件名(FileName)
{
   匹配时间 := []
   y := SubStr(FileName, 1, 4)
   m := SubStr(FileName, 6, 2)
   d := SubStr(FileName, 8, 2)
   时间 := y "/" m  "/" d " " A_Hour ":" A_Min ":" A_Sec
   匹配时间.Insert(时间)
   if !RegExMatch(时间, "(?:(?:(?:(?:1[8-9]|20)(?:04|08|[2468][048]|[13579][26]))|2000)([-/])02(?:\1)29|(?:(?:1[8-9]|20)\d\d)([-/])(?:(?:0[1-9]|1[0-2])(?:\2)(?:0[1-9]|1[0-9]|2[0-8])|(?:0(?:1|[3-9])|(?:1[0-2]))(?:\2)(?:29|30)|(?:0[13578]|1[02])(?:\2)31))")
   匹配时间.Insert(0)
   Else
   匹配时间.Insert(1)
   Return 匹配时间
}

CmdRet(sCmd, callBackFuncObj := "", encoding := "CP0")
{
   static HANDLE_FLAG_INHERIT := 0x00000001, flags := HANDLE_FLAG_INHERIT
   , STARTF_USESTDHANDLES := 0x100, CREATE_NO_WINDOW := 0x08000000

   DllCall("CreatePipe", "PtrP", hPipeRead, "PtrP", hPipeWrite, "Ptr", 0, "UInt", 0)
   DllCall("SetHandleInformation", "Ptr", hPipeWrite, "UInt", flags, "UInt", HANDLE_FLAG_INHERIT)

   VarSetCapacity(STARTUPINFO , siSize :=    A_PtrSize*4 + 4*8 + A_PtrSize*5, 0)
   NumPut(siSize              , STARTUPINFO)
   NumPut(STARTF_USESTDHANDLES, STARTUPINFO, A_PtrSize*4 + 4*7)
   NumPut(hPipeWrite          , STARTUPINFO, A_PtrSize*4 + 4*8 + A_PtrSize*3)
   NumPut(hPipeWrite          , STARTUPINFO, A_PtrSize*4 + 4*8 + A_PtrSize*4)

   VarSetCapacity(PROCESS_INFORMATION, A_PtrSize*2 + 4*2, 0)

   if !DllCall("CreateProcess", "Ptr", 0, "Str", sCmd, "Ptr", 0, "Ptr", 0, "UInt", true, "UInt", CREATE_NO_WINDOW
    , "Ptr", 0, "Ptr", 0, "Ptr", &STARTUPINFO, "Ptr", &PROCESS_INFORMATION)
   {
      DllCall("CloseHandle", "Ptr", hPipeRead)
      DllCall("CloseHandle", "Ptr", hPipeWrite)
      throw Exception("CreateProcess is failed")
   }
   DllCall("CloseHandle", "Ptr", hPipeWrite)
   VarSetCapacity(sTemp, 4096), nSize := 0
   while DllCall("ReadFile", "Ptr", hPipeRead, "Ptr", &sTemp, "UInt", 4096, "UIntP", nSize, "UInt", 0) 
   {
      stdOut := StrGet(&sTemp, nSize, encoding)               ;返回当前信息
      ; sOutput .= stdOut := StrGet(&sTemp, nSize, encoding)  ;返回累积信息
      ( callBackFuncObj && callBackFuncObj.Call(stdOut) )
   }
   DllCall("CloseHandle", "Ptr", NumGet(PROCESS_INFORMATION))
   DllCall("CloseHandle", "Ptr", NumGet(PROCESS_INFORMATION, A_PtrSize))
   DllCall("CloseHandle", "Ptr", hPipeRead)
   Return sOutput
}

打开图片:
run, %picpath%
Return

GuiClose:  ; 当窗口关闭时, 自动退出脚本:
ExitApp

;--------test-----------------改变LV样式,被选条目颜色改变
SetExplorerTheme(HCTL) { ; HCTL : handle of a ListView or TreeView control
   If (DllCall("GetVersion", "UChar") > 5) {
      VarSetCapacity(ClassName, 1024, 0)
      If DllCall("GetClassName", "Ptr", HCTL, "Str", ClassName, "Int", 512, "Int")
         If (ClassName = "SysListView32") || (ClassName = "SysTreeView32")
            Return !DllCall("UxTheme.dll\SetWindowTheme", "Ptr", HCTL, "WStr", "Explorer", "Ptr", 0)
   }
   Return False
}

;---------------------------------------------------------
SetHeaderItemCheckBox(hListView, item) {
   static LVM_GETHEADER := 0x101F, HDS_CHECKBOXES := 0x400, HDI_FORMAT := 0x4
        , HDM_FIRST := 0x1200, HDM_GETITEM := HDM_FIRST + (A_IsUnicode ? 3 : 11)
        , HDM_SETITEM := HDM_GETITEM + 1, HDF_CHECKBOX := 0x40
        , HDITEM_SIZE := 4*6 + A_PtrSize*6, fmt_offset := 4*3 + A_PtrSize*2
        
   SendMessage, LVM_GETHEADER,,,, ahk_id %hListView%
   WinExist("ahk_id" header := ErrorLevel)
   WinSet, Style, +%HDS_CHECKBOXES%
   VarSetCapacity(HDITEM, HDITEM_SIZE, 0)
   NumPut(HDI_FORMAT, HDITEM)
   SendMessage, HDM_GETITEM, --item, &HDITEM
   fmt := NumGet(HDITEM, fmt_offset, "Int")
   NumPut(fmt|HDF_CHECKBOX, HDITEM, fmt_offset)
   SendMessage, HDM_SETITEM, item, &HDITEM
}

 

声明:站内资源为整理优化好的代码上传分享与学习研究,如果是开源代码基本都会标明出处,方便大家扩展学习路径。请不要恶意搬运,破坏站长辛苦整理维护的劳动成果。本站为爱好者分享站点,所有内容不作为商业行为。如若本站上传内容侵犯了原著者的合法权益,请联系我们进行删除下架。