You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用AutoHotkey获取激活的Foxit窗口PDF总页数

获取激活状态下Foxit PhantomPDF打开的PDF总页数(AutoHotkey实现)

问题背景

已实现Adobe Acrobat下获取当前打开PDF总页数的功能,但在Foxit PhantomPDF场景中,无法获取激活窗口对应的PDF文件路径,现有代码仅能在指定固定路径时获取页数,需补充路径获取逻辑以实现完整需求。

解决方案

通过以下步骤实现:

  • 获取当前激活的Foxit PhantomPDF窗口句柄
  • 从窗口标题或进程命令行中提取打开的PDF文件路径
  • 将提取到的路径传入原有页数获取逻辑,计算总页数

完整AutoHotkey代码

F1::
    ; 获取当前激活窗口的句柄
    WinGet, activeWinID, ID, A
    WinGet, activeWinProcess, ProcessName, ahk_id %activeWinID%
    
    ; 验证是否为Foxit PhantomPDF进程
    if (activeWinProcess != "FoxitPhantomPDF.exe") {
        MsgBox 当前激活窗口不是Foxit PhantomPDF
        return
    }

    ; 尝试从窗口标题提取文件路径
    WinGetTitle, activeWinTitle, ahk_id %activeWinID%
    pdfPath := ExtractPathFromTitle(activeWinTitle)
    
    ; 如果标题提取失败,从进程命令行获取
    if (!pdfPath) {
        pdfPath := ExtractPathFromProcess(activeWinID)
    }
    
    if (!pdfPath) {
        MsgBox 无法获取当前打开的PDF文件路径
        return
    }

    ; 原有获取页数的逻辑
    VarSetCapacity(GUID, 16)
    DllCall("ole32\CLSIDFromString", "wstr", IID_RandomAccessStream := "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", &GUID)
    DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", pdfPath, "uint", Read := 0, "ptr", &GUID, "ptr*", IRandomAccessStream)
    CreateClass("Windows.Data.Pdf.PdfDocument", IPdfDocumentStatics := "{433A0B5F-C007-4788-90F2-08143D922599}", PdfDocumentStatics)
    DllCall(NumGet(NumGet(PdfDocumentStatics+0)+8*A_PtrSize), "ptr", PdfDocumentStatics, "ptr", IRandomAccessStream, "ptr*", PdfDocument)   ; LoadFromStreamAsync
    WaitForAsync(PdfDocument)
    DllCall(NumGet(NumGet(PdfDocument+0)+7*A_PtrSize), "ptr", PdfDocument, "uint*", PageCount)   ; get_PageCount
    ObjReleaseClose(IRandomAccessStream)
    ObjReleaseClose(PdfDocumentStatics)
    ObjReleaseClose(PdfDocument)
    MsgBox 当前PDF总页数:%PageCount%
return

; 从窗口标题提取路径(Foxit标题格式:"文件名 - Foxit PhantomPDF")
ExtractPathFromTitle(title) {
    splitPos := InStr(title, " - Foxit PhantomPDF")
    if (splitPos = 0)
        return ""
    ; 处理可能的引号(如果文件名含空格,标题会带引号)
    filePath := SubStr(title, 1, splitPos-1)
    if (SubStr(filePath, 1, 1) = """" && SubStr(filePath, StrLen(filePath)) = """") {
        filePath := SubStr(filePath, 2, StrLen(filePath)-2)
    }
    ; 验证路径是否存在
    if (FileExist(filePath))
        return filePath
    return ""
}

; 从进程命令行提取路径
ExtractPathFromProcess(winID) {
    WinGet, pid, PID, ahk_id %winID%
    hProcess := DllCall("OpenProcess", "UInt", 0x400 | 0x10, "Int", 0, "UInt", pid, "Ptr")
    if (!hProcess)
        return ""
    
    ; 获取命令行缓冲区大小
    cmdLineSize := DllCall("Psapi\GetCommandLine", "Ptr", hProcess, "Ptr", 0, "UInt", 0, "UInt*", 0) + 1
    VarSetCapacity(cmdLine, cmdLineSize * 2, 0)
    DllCall("Psapi\GetCommandLine", "Ptr", hProcess, "Ptr", &cmdLine, "UInt", cmdLineSize, "UInt*", 0)
    
    DllCall("CloseHandle", "Ptr", hProcess)
    
    ; 解析命令行,提取PDF路径(Foxit启动参数中会包含打开的文件路径)
    cmdLineStr := StrGet(&cmdLine, "UTF-16")
    ; 分割命令行中的参数
    params := []
    inQuotes := false
    currentParam := ""
    loop, Parse, cmdLineStr
    {
        char := A_LoopField
        if (char = """") {
            inQuotes := !inQuotes
            continue
        }
        if (!inQuotes && char = " ") {
            if (currentParam != "") {
                params.Push(currentParam)
                currentParam := ""
            }
            continue
        }
        currentParam .= char
    }
    if (currentParam != "")
        params.Push(currentParam)
    
    ; 遍历参数,找到存在的PDF文件路径
    for index, param in params {
        if (SubStr(param, -3) = ".pdf" || SubStr(param, -4) = ".PDF") {
            if (FileExist(param))
                return param
        }
    }
    return ""
}

CreateClass(string, interface := "", ByRef Class := "")
{
   CreateHString(string, hString)
   if (interface = "")
      result := DllCall("Combase.dll\RoActivateInstance", "ptr", hString, "ptr*", Class, "uint")
   else
   {
      VarSetCapacity(GUID, 16)
      DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
      result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   }
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
    DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}

WaitForAsync(ByRef Object)
{
   AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
   loop
   {
      DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status)   ; IAsyncInfo.Status
      if (status != 0)
      {
         if (status != 1)
         {
            DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode)   ; IAsyncInfo.ErrorCode
            msgbox AsyncInfo status error: %ErrorCode%
            ExitApp
         }
         ObjRelease(AsyncInfo)
         break
      }
      sleep 10
   }
   DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult)   ; GetResults
   ObjReleaseClose(Object)
   Object := ObjectResult
}

ObjReleaseClose(ByRef Object)
{
   if Object
   {
      if (Close := ComObjQuery(Object, IClosable := "{30D5A829-7FA4-4026-83BB-D75BAE4EA99E}"))
      {
         DllCall(NumGet(NumGet(Close+0)+6*A_PtrSize), "ptr", Close)   ; Close
         ObjRelease(Close)
      }
      ObjRelease(Object)
   }
   Object := ""
}

代码说明

  1. 路径提取逻辑:优先从窗口标题解析路径,若失败则读取进程命令行参数,覆盖文件名含空格、特殊字符等场景
  2. 页数计算:复用原有基于Windows.Data.Pdf.PdfDocument的COM接口实现,保证页数获取的准确性
  3. 兼容性处理:加入路径有效性验证,避免无效路径导致的报错

内容的提问来源于stack exchange,提问作者Shahin Kachooie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 14:54:19