8

我正在开发一个应用程序,它可以打开并读取以前嵌入在 PowerPoint 演示文稿或 Word 文档中的 XML 文档。为了读取这个对象 ( xmlFile as Object) 我必须这样做:

xmlFile.OLEFormat.DoVerb 1

这将打开包对象,我有另一个子例程,它获取 Notepad.exe 的打开实例,并将其内容读入 ADODB 流。

Google Docs 上提供了此过程的示例:

XML_Test.pptm

在此过程中,Notepad.exe 会在几秒钟的窗口中获得焦点,并且无意的击键可能会导致不希望的结果或读取 XML 数据时出错。

我正在寻找以下两件事之一:

  1. 一种防止用户在执行此操作时无意输入(通过键盘/鼠标/等)的方法。最好是不控制用户机器的东西,比如MouseKeyboardTest下面的子程序。或者,
  2. 一种将 XML 数据提取到字符串变量中的更好方法。

对于#1:这是我发现的功能,我对使用它持怀疑态度。我对采取这种对用户系统的控制持谨慎态度。##我还有其他方法可以使用吗?##

Private Declare Function BlockInput Lib "USER32.dll" (ByVal fBlockIt As Long) As Long
Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)

Sub MouseKeyboardTest() 'both keyboard and mouse blocked

    BlockInput True ' Turns off Keyboard and Mouse
'   Routine goes here
    Sleep 5000 ' Optional coding
    BlockInput False ' Turns on Keyboard and Mouse

End Sub

对于#2:一些背景,但问题似乎是无法使用除DoVerb 1. 由于我正在处理不受我的 VBA Skillz 影响的应用程序(记事本)中未保存的文档,因此这似乎是执行此操作的唯一方法。完整的背景,在这里:

从 PowerPoint VBA 中提取 OLEObject(XML 文档)

4

4 回答 4

2

正如您在上面的评论中正确猜到的那样,将注意力从记事本上移开将解决您的问题。下面的代码正是这样做的。

逻辑

一个。循环遍历形状并得到它的名字。在你的场景中,它会是这样的Chart Meta XML_fbc9775a-19ea-.txt

在此处输入图像描述

。使用 , 等 APIFindWindow使用GetWindowTextLength部分标题GetWindow获取记事本窗口的句柄。

C. _ 使用ShowWindowAPI 最小化窗口

代码(在 VBA-Powerpoint 中测试)

将此代码粘贴到上述 PPTM 中的模块中

Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" _
(ByVal lpClassName As String, ByVal lpWindowName As String) As Long

Private Declare Function GetWindowText Lib "User32" Alias "GetWindowTextA" _
(ByVal hWnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Private Declare Function GetWindowTextLength Lib "User32" Alias _
"GetWindowTextLengthA" (ByVal hWnd As Long) As Long

Private Declare Function GetWindow Lib "User32" (ByVal hWnd As Long, _
ByVal wCmd As Long) As Long

Private Declare Function ShowWindow Lib "User32" (ByVal hWnd As Long, _
ByVal nCmdShow As Long) As Long

Private Const GW_HWNDNEXT = 2
Private Const SW_SHOWMINIMIZED = 2

Sub Sample()
    Dim shp As Shape
    Dim winName As String
    Dim Ret As Long

    For Each shp In ActivePresentation.Slides(1).Shapes
        If shp.Type = msoEmbeddedOLEObject Then
            winName = shp.Name
            shp.OLEFormat.Activate
            Exit For
        End If
    Next

    If winName <> "" Then
        Wait 1

        If GetHwndFromCaption(Ret, Replace(winName, ".txt", "")) = True Then
           Call ShowWindow(Ret, SW_SHOWMINIMIZED)
        Else
            MsgBox "Window not found!", vbOKOnly + vbExclamation
        End If
    End If
End Sub

Private Function GetHwndFromCaption(ByRef lWnd As Long, ByVal sCaption As String) As Boolean
    Dim Ret As Long
    Dim sStr As String

    GetHwndFromCaption = False

    Ret = FindWindow(vbNullString, vbNullString)

    Do While Ret <> 0

        sStr = String(GetWindowTextLength(Ret) + 1, Chr$(0))
        GetWindowText Ret, sStr, Len(sStr)
        sStr = Left$(sStr, Len(sStr) - 1)
        If InStr(1, sStr, sCaption) > 0 Then
            GetHwndFromCaption = True
            lWnd = Ret
            Exit Do
        End If
        Ret = GetWindow(Ret, GW_HWNDNEXT)
    Loop
End Function

Private Sub Wait(ByVal nSec As Long)
    nSec = nSec + Timer
    While nSec > Timer
        DoEvents
    Wend
End Sub
于 2013-05-07T21:59:06.473 回答
1

我的理解是,您首先可以控制 XML 文件如何嵌入到 PowerPoint 演示文稿中。在这里,我不太明白您为什么选择将所需的数据保留为嵌入对象的内容。

可以肯定的是,取回这些内容的任务不是小菜一碟。实际上,只要没有(简单甚至中等难度)从 VBA 调用QueryInterface和使用IPersist*接口的方法,就只有一种方法可以获取嵌入对象的内容。该方法涉及以下步骤:

  1. 激活嵌入对象。你用过OLEFormat.DoVerb 1那个。更好的方法是调用OLEFormat.Activate,但这与您的特定问题无关。
  2. 使用嵌入式对象的编程模型来执行有用的操作,例如获取内容、保存或任何公开的内容。Notepad.exe没有公开这样的编程模型,你求助于WinAPI哪个是最好的选择。

不幸的是,您当前的方法至少有两个缺陷:

  1. 您在问题中确定的那个(激活notepad.exe导致用户干扰的可能性)。
  2. .txt如果用户有打开文件以外的默认程序notepad.exe,那么你的方法注定失败。

如果您确实可以控制嵌入对象的创建方式,那么更好的方法是将 XML 数据存储在Shape对象的某些属性中。我会使用(非常简单易用;如果您将您的导出到 HTML 或有一些重要的不同场景,Shape.AlternativeText则不应使用)或(这可能是该任务在语义上最正确的)。.pptmAlternativeTextShape.Tags

于 2013-05-07T22:29:09.450 回答
0

根据 Sid 的建议,我的方法是找到一种方法来最小化 Notepad.exe。由于我已经找到了获取该对象并关闭它的方法,因此我认为这不应该那么难。

我添加这些:

Public Declare Function _
     ShowWindow& Lib "user32" (ByVal hwnd As Long, _
                        ByVal ncmdshow As Long)
Public Const SW_MINIMIZE = 6

然后,在FindNotepad函数中,就在之前Exit Function(所以,在找到记事本之后)我最小化窗口:

ShowWindow TopWnd, SW_MINIMIZE
于 2013-05-07T22:00:21.197 回答
0

我不认为阻止用户是正确的方法,

如果您必须使用记事本窗口的内容,我建议使用 SendKeys 方法,以便发送此组合:

SendKeys("^A^C")

相当于“全选”和“复制”,

然后您可以继续在剪贴板上“离线”工作,而不必担心按键干扰。

于 2013-05-07T01:45:18.287 回答