VBA для предотвращения ввода с клавиатуры во время чтения объекта пакета (XML) в поток ADODB?
я разрабатываю приложение, которое открывает и читает XML-документ, ранее встроенный в презентацию PowerPoint, или документ Word. Чтобы прочитать этот объект (xmlFile as Object
) Я должен сделать:
xmlFile.OLEFormat.DoVerb 1
это открывает объект пакета, и у меня есть другая подпрограмма, которая получает открытый экземпляр Notepad.exe и считывает его содержимое в поток ADODB.
пример этой процедуры доступен в Google Docs:
во время этого процесса есть несколько секунд, где Блокнот.exe получает фокус, и непреднамеренное нажатие клавиши может привести к нежелательным результатам или ошибке чтения XML-данных.
Я ищу одну из двух вещей:
- либо метод для предотвращения непреднамеренного ввода пользователем (с помощью клавиатуры / мыши / etc) во время выполнения этой операции. Предпочтительно что-то, что делает не взять под контроль компьютер пользователя как
MouseKeyboardTest
подпрограмма, ниже. Или, - лучший способ извлечения 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, это, похоже, единственный способ сделать это. Полный фон об этом, здесь:
4 ответов
я понимаю, что у вас есть контроль над тем, как XML-файл внедряется в презентацию PowerPoint в первую очередь. Здесь я не совсем понимаю, почему вы решили сохранить необходимые вам данные как содержание встроенного объекта.
чтобы быть уверенным, задача получить это содержимое обратно не кусок пирога. На самом деле, пока нет (простого или даже умеренно сложного) способа вызова QueryInterface
и использовать IPersist*
интерфейсы от VBA, есть только один из способов добраться до содержимого встроенного объекта. Путь включает в себя следующие шаги:
- активация встроенного объекта. Вы использовали
OLEFormat.DoVerb 1
для этого. Лучшим способом было бы позвонитьOLEFormat.Activate
, но это не имеет отношения к вашей конкретной проблеме. - используйте модель программирования встроенного объекта для выполнения полезных операций, таких как получение содержимого, сохранение или что-либо еще.
Notepad.exe
не предоставляет такой модели программирования, и вы прибегли кWinAPI
Это самый лучший выбор доступный.
к сожалению, Ваш текущий подход имеет по крайней мере 2 недостатка:
- тот, который вы определили в вопросе (активация
notepad.exe
водя к возможности взаимодействия потребителя). - если у пользователя есть программа по умолчанию для открытия
.txt
файлы, кромеnotepad.exe
, ваш подход обречен.
если у вас есть контроль над тем, как создается внедренный объект, то лучшим подходом было бы хранить ваши XML-данные в каком-то свойстве
как вы правильно догадались, в комментарии выше, что принимая внимание от Блокнота решит вашу проблему. Приведенный ниже код делает именно это.
логика:
A. Петля через форму и получить его имя. В вашем случае это будет что-то вроде Chart Meta XML_fbc9775a-19ea-.txt
B. Используйте API, как FindWindow
, GetWindowTextLength
, GetWindow
etc, чтобы получить ручку окна Блокнота, используя частичное надпись.
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
Я не думаю, что блокировка пользователя является правильным подходом,
Если вы должны использовать содержимое окна блокнота, я бы предложил использовать метод sendkeys, чтобы отправить такую комбинацию:
SendKeys("^A^C")
что эквивалентно "выбрать все "и"копировать",
и затем вы можете продолжать работать "в автономном режиме" в буфере обмена, не опасаясь вмешательства нажатий клавиш.
Я добавляю:
Public Declare Function _
ShowWindow& Lib "user32" (ByVal hwnd As Long, _
ByVal ncmdshow As Long)
Public Const SW_MINIMIZE = 6
и затем в