当包对象(XML)被读入ADODB流时VBA阻止键盘输入?

时间:2013-05-02 21:41:28

标签: vba ole adodb powerpoint-vba

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

xmlFile.OLEFormat.DoVerb 1

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

Google文档中提供了此过程的示例:

XML_Test.pptm

在此过程中,Notepad.exe会在几秒钟内获得焦点,无意中击键可能会导致意外结果或读取XML数据时出错。

我正在寻找两件事之一:

  1. 在执行此操作时防止用户无意中输入(通过键盘/鼠标/等)的方法。最好不要控制用户机器的东西,如下面的MouseKeyboardTest子程序。或者,
  2. 将XML数据提取为字符串变量的更好方法。
  3. 对于#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技能影响的应用程序(记事本)中未保存的文档,这似乎是唯一的方法。完整的背景,在这里:

    Extracting an OLEObject (XML Document) from PowerPoint VBA

4 个答案:

答案 0 :(得分:2)

正如您在上面的评论中正确猜到的那样,将焦点从记事本上移开将解决您的问题。下面的代码确实如此。

<强> LOGIC

<强> A 即可。循环通过形状并获取它的名称。在您的方案中,它将类似于Chart Meta XML_fbc9775a-19ea-.txt

enter image description here

<强>乙即可。使用FindWindowGetWindowTextLengthGetWindow等API来使用部分字幕来获取记事本窗口的句柄。

<强> C 即可。使用ShowWindow API最小化窗口

代码(在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

答案 1 :(得分:1)

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

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

  1. 激活嵌入对象。您使用了OLEFormat.DoVerb 1。更好的方法是拨打OLEFormat.Activate,但这与您的特定问题无关。
  2. 使用嵌入对象的编程模型执行有用的操作,例如获取内容,保存或暴露的任何内容。 Notepad.exe没有公开这样的编程模型,你使用了WinAPI这是最好的选择。
  3. 不幸的是,您当前的方法至少有两个缺陷:

    1. 您在问题中发现的那个(notepad.exe的激活导致用户干扰的可能性)。
    2. 如果用户拥有用于打开.txt以外的notepad.exe个文件的默认程序,则您的方法注定失败。
    3. 如果您确实可以控制嵌入对象的创建方式,那么更好的方法是将XML数据存储在Shape对象的某个属性中。我会使用Shape.AlternativeText(非常简单易用;如果您将.pptm导出为HTML,或者有AlternativeText重要的不同情况)或Shape.Tags,则不应使用对于那个问题,这个问题可能是最具语义正确性的。

答案 2 :(得分:0)

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

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

SendKeys("^A^C")

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

然后你可以继续在剪贴板上“脱机”工作,而不用担心按键干扰。

答案 3 :(得分: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