VBA 从 Excel 中关闭 PDF 文档

如何解决VBA 从 Excel 中关闭 PDF 文档

我有一个可爱的小程序,它可以关闭带有 Acrobat 显示的窗口或只关闭其中的一个文档。只有设计是我自己制作的,这意味着我不完全理解代码,但我知道它只是部分工作。无论显示多少个文档,它都会完全退出 Adob​​e Acrobat,但它不能只关闭一个(因为转录它的原始文件声称它可以而且应该)。

  question = create(:question,user: user)
  expect { delete question_url(question) }.to change(Question,:count).by(-1)

逻辑是参数 Private Sub CloseReaderDC(Optional ByVal MailIdx As Integer) Dim WinId As String Dim Wnd As LongPtr If MailIdx Then WinId = AcrobatWindowID(Mail(MailIdx)) Wnd = FindWindow(vbNullString,WinId) PostMessage Wnd,WM_CLOSE,ByVal 0& Else WinId = AcrobatWindowID Wnd = FindWindow(WinId,vbNullString) SendMessage Wnd,ByVal 0& End If End Sub 标识足以找到顶部窗口的文件名。如果未给出任何值,则应关闭应用程序。这部分有效。另一部分也可以工作,但前提是打开了一个文档,在这种情况下,不是关闭文档而是关闭整个应用程序。我相信这种关闭可能是由 Acrobat Reader 本身引起的,它没有看到保持打开状态而没有显示文档的原因。我还认为,如果有多个文档,可能找不到窗口句柄,因为 MailIdx 只找到一个顶部窗口,而我想关闭的将是第二个。在实践中,我尝试了两者,在打开另一个之前和之后关闭现有。在一种情况下应用程序被关闭,在另一种情况下什么也没有发生。

我不知道为什么我的导师在一种情况下使用 FindWindow 而在另一种情况下使用 SendMessage。我也不知道我所追求的窗口是否是 子窗口 或者如何处理它。有什么建议吗?

编辑 2021 年 1 月 11 日

我使用以下代码来测试@FaneDuru 的解决方案。

PostMessage

该代码对 1 个或 2 个文件都完美地工作,直到使用 0 参数调用时才关闭应用程序。但在第二次尝试时,它未能找到窗口,因此没有采取任何行动。

我启动了 Acrobat 并从其“文件”>“打开”菜单中选择了 2 个先前打开的文件。 File1 在第一个选项卡中,File2 在第二个选项卡中,处于活动状态。然后我尝试删除失败的 File1。然后我用 2 作为参数调用了关闭顶层文件的代码。此后,代码找到 File1 的窗口并将其关闭。

不过,我认为并没有始终如一地遵循明显的规则。文件的打开方式似乎有所不同。在我的项目中,文件通过超链接打开,一次一个。因此,我的上述测试并不表明 FaneDuru 的建议将如何在我的项目中发挥作用,但它证明了该解决方案有效。

解决方法

关于我关于通过以编程方式按下文件菜单“关闭文件”控件关闭活动文档的评论...

这种关闭方式不会使 Acrobat 应用程序退出。它保持打开状态,即使在运行代码时只打开了一个文档。

所以,请测试下一行代码。您需要 Acrobat Reader DC 处理程序和必要的参数,如下所示:

Const WM_CloseClick = &H111
SendMessage Wnd,WM_CloseClick,6038,ByVal 0&

6038 是“关闭文件”文件菜单控件 ID。 我可以使用下一个函数来确定它:

Private Function findControlID(mainWHwnd As LongPtr,ctlNo As Long) As Long
   Dim aMenu As LongPtr,sMenu As LongPtr
   
   aMenu = GetMenu(mainWHwnd): Debug.Print "Main menu = " & Hex(aMenu)
    sMenu = GetSubMenu(aMenu,0&): Debug.Print "File menu = " & Hex(sMenu)
    mCount = GetMenuItemCount(sMenu): Debug.Print "File menu no of controls: " & mCount 'check if it is 28
     findControlID = GetMenuItemID(sMenu,ctlNo - 1) 'Menu controls are counted starting from 0
End Function

上面的函数是这样调用的:

Sub testFindCloseControlID()
  Dim Wnd As LongPtr
   'Wnd = findWindowByPartialTitle("Adobe Acrobat Reader DC") 'you will obtain it in your way
   Debug.Print findControlID(Wnd,15) '15 means the fiftheenth control of the File menu (0)
End Sub

计算水平控件分隔符也获得了 15 个。

为了找到“Adobe Acrobat Reader DC”窗口处理程序,我使用了上面提到的函数,但这并不重要。你可以用你的方法来确定它...

请测试上述方式并发送一些评论

已编辑

为了提取应用程序菜单的标题,我使用以下声明:

Option Explicit

'APIs for identify a window handler
Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" _
             (ByVal lpClassName As String,ByVal lpWindowName As String) As LongPtr
Private Declare PtrSafe Function GetWindowTextLength Lib "user32" Alias "GetWindowTextLengthA" (ByVal hwnd As LongPtr) As Long
Private Declare PtrSafe Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As LongPtr,_
        ByVal lpString As String,ByVal cch As Long) As Long
Private Declare PtrSafe Function GetWindow Lib "user32" (ByVal hwnd As LongPtr,ByVal wCmd As Long) As Long
'____________________________________________________

'Menu related APIs
Private Declare PtrSafe Function GetMenu Lib "user32.dll" (ByVal hwnd As LongPtr) As LongPtr
Private Declare PtrSafe Function GetSubMenu Lib "user32" (ByVal hMenu As LongPtr,ByVal nPos As Long) As LongPtr
Private Declare PtrSafe Function GetMenuItemID Lib "user32" _
                                   (ByVal hMenu As LongPtr,ByVal nPos As Long) As Long
Private Declare PtrSafe Function GetMenuItemCount Lib "user32" (ByVal hMenu As LongPtr) As Long
        Private Declare PtrSafe Function GetMenuItemInfo Lib "user32" Alias "GetMenuItemInfoA" (ByVal hMenu As LongPtr,_
                                        ByVal Un As Long,ByVal b As Long,lpMenuItemInfo As MENUITEMINFO) As Long

Private Declare PtrSafe Function GetMenuString Lib "user32" Alias "GetMenuStringA" (ByVal hMenu As LongPtr,_
                ByVal wIDItem As Long,ByVal lpString As String,ByVal nMaxCount As Long,ByVal wFlag As Long) As Long
'_____________________________________________________


Private Type MENUITEMINFO
    cbSize As Long
    fMask As Long
    fType As Long
    fState As Long
    wID As Long
    hSubMenu As LongPtr
    hbmpChecked As LongPtr
    hbmpUnchecked As LongPtr
    dwItemData As LongPtr
    dwTypeData As String
    cch As Long
    hbmpItem As LongPtr
End Type

Private Const GW_HWNDNEXT = 2

以及接下来的功能/订阅:

要查找仅知道其部分标题的任何窗口:

Sub testFindWindByPartTitle()
  Debug.Print findWindowByPartialTitle("Notepad")
End Sub

Private Function findWindowByPartialTitle(ByVal sCaption As String,Optional strSecond As String) As LongPtr
  Dim lhWndP As LongPtr
    Dim sStr As String
    findWindowByPartialTitle = CLngPtr(0)
    lhWndP = FindWindow(vbNullString,vbNullString) 'PARENT WINDOW
    Do While lhWndP <> 0
        sStr = String(GetWindowTextLength(lhWndP) + 1,Chr$(0))
        GetWindowText lhWndP,sStr,Len(sStr)
        If Len(sStr) > 0 Then sStr = left$(sStr,Len(sStr) - 1)
        If InStr(1,sCaption) > 0 And _
                IIf(strSecond <> "",InStr(1,strSecond) > 0,1 = 1) Then
            findWindowByPartialTitle = lhWndP
            Exit Do
        End If
        lhWndP = GetWindow(lhWndP,GW_HWNDNEXT)
    Loop
End Function

通过控件标题提取必要 ID 的版本,但它仅适用于记事本

Private Sub TestfindMenuItemsByCaption()
  Const NotePApp As String = "Notepad"
  Debug.Print findMenuIDByString(NotePApp,"Save") 'it does work
  Const pdfApp As String = "Adobe Acrobat Reader DC"
  Debug.Print findMenuIDByString(pdfApp,"Close")  'it does not work
End Sub
Private Function findMenuIDByString(pdfApp As String,searchString As String) As Long
    Dim mainWHwnd As LongPtr,aMenu As LongPtr,mCount As Long
    Dim LookFor As Long,sMenu As LongPtr,sCount As Long
    Dim LookSub As Long,sID As Long,sString As String
    
    mainWHwnd = findWindowByPartialTitle(pdfApp)
    aMenu = GetMenu(mainWHwnd): Debug.Print "Main menu = " & Hex(aMenu)
    sMenu = GetSubMenu(aMenu,0): Debug.Print "File menu = " & Hex(sMenu)
    sCount& = GetMenuItemCount(sMenu)
    For LookSub& = 0 To sCount& - 1
        sID& = GetMenuItemID(sMenu,LookSub&): Debug.Print "ID = " & sID: 'Stop
        sString$ = String$(100," ")
        Call GetMenuString(sMenu,sID&,sString$,100&,1&) ' 1&)
        Debug.Print sString$ ': Stop
        If InStr(LCase(sString$),LCase(searchString$)) Then
            findMenuIDByString = sID
            Exit Function
        End If
    Next LookSub&
End Function

还有第二个版本,不幸的是以完全相同的方式工作。我的意思是,仅为记事本返回 ID:

Private Sub TestfindMenuItemsByCaptionBis()
  Const NotePApp As String = "Notepad"
  Debug.Print findMenuItemIDByCaption(NotePApp,"Save")
  Const pdfApp As String = "Adobe Acrobat Reader DC"
  Debug.Print findMenuItemIDByCaption(pdfApp,"Close")
End Sub
Private Function findMenuItemIDByCaption(strApp As String,strCaption As String)
  Dim appHwnd As LongPtr,hMenu As LongPtr,fMenu As LongPtr,i As Long
  Dim retval As Long,mii As MENUITEMINFO 'mii receives information about each item
  Const WM_SaveClick = &H111,MIIM_STATE = &H1,MIIM_STRING = &H40&,MIIM_ID = &H2&,MIIM_CHECKMARKS = &H8&
  Const MIIM_SUBMENU = &H4&,MIIM_TYPE = &H10,MIIM_FTYPE = &H100&,MIIM_DATA = &H20&
  
    appHwnd = findWindowByPartialTitle(strApp)
     If appHwnd = 0 Then MsgBox "No application window found...": Exit Function

       hMenu = GetMenu(appHwnd)         'application window Menu
       fMenu = GetSubMenu(hMenu,0)     'app window 'File' Submenu

       For i = 0 To GetMenuItemCount(fMenu)
         With mii
            .cbSize = Len(mii)
            .fMask = MIIM_STATE Or MIIM_SUBMENU Or MIIM_TYPE
            .dwTypeData = space(256)
            .cch = 256
                retval = GetMenuItemInfo(fMenu,i,1,mii) '2 = the third menu item
                Debug.Print left(.dwTypeData,.cch)
                If InStr(left(.dwTypeData,.cch),strCaption) > 0 Then
                   findMenuItemIDByCaption = GetMenuItemID(fMenu,i): Exit Function
                End If
        End With
     Next i
End Function

我尝试了我能找到的所有常量,但没有成功......如果我们能找到一种方法,一个子程序也可以读取最近的文件列表并激活需要的,如果不是活动的就是必要的.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-