Приветствую. Это третий форум, на котором я ищу помощь и поддержку. "Автоматизирую" рабочее место секретаря. Каждый день она получает пачку корреспонденции. И регистрирует ее в таблице путем внесения реквизитов документов в Excel. Процесс не быстрый. Можно было бы заплатить одному из многочисленных облачных сервисов, выгружать туда сканы и получать результат, но это не наш выбор). В итоге я придумал следующий алгоритм обработки входящей корреспонденции, который может существенно ускорить и сократить время работы секретаря. Во первых, пачка корреспонденции сканируется в PDF с распознаванием (OCR), так что в PDF файле присутствует текстовый слой. Во вторых секретарь открывает Excel таблицу с поддержкой макросов, в которой выполняется программный код. Программа следит за буфером обмена Windows (Clipboard) и как только видит изменения в текстовой составляющей буфера обмена, текст из буфера обмена помещается в ячейку таблицы Excel. Не нужно переключаться между приложениями. Просто быстро копировать нужные реквизиты документов и получать результат в виде таблицы Excel. Идея хороша. Ее можно распространить и на другие виды работ. Серфите в интернете, что то понравилось, скопировали и все уже там в таблице, вы не отвлекаетесь и дальше работаете с текстом. Просто подумайте об этом как много времени экономиться на многочисленных операциях копи паст с переключением между приложениями, выбора места вставки и сама вставка с последующим возвратом к исходному приложению, вот уже и ход мысли потерял. В общем написал пример, в котором каждую секунду читаю буфер обмена, как только буфер поменялся, вставляю в таблицу. Вроде ничего сложного, но при одном из последующих попыток прочтения буфера обмена лезет мутная ошибка (толкового решения найти не смог). Если увеличить интервал обращения к буферу обмена до двух секунд, то ошибка чтения буфера обмена становиться очень редкой. Но это не вариант, потому как за две секунды м буфер обмена можно обновить несколько раз, и тогда какой то фрагмент текста будет пропущен(. Какие есть варианты? Первое, исправить код в представленном примере, что бы не было ошибки. Второе, поменять алгоритм и методы обращения к буферу обмена (можно как то отлавливать событие изменения clipboard, что бы не дергать его каждую секунду?). В представленном примере, нажмите на кнопку, запустится скрипт мониторинга буфера обмена, переключитесь на любое другое приложение и копируйте фрагменты текста, они будут последовательно попадать в таблицу Excel. Что бы остановить выполнение скрипта, скопируйте фрагмент текста длинной в два символа. Что бы вызвать ошибку чтения буфера обмена, исправьте в тексте скрипта таймаут с 2 на 1 секунду (там помечено) Помогайте братцы!
Не помогло. Переписал функцию чтения из буфера обмена. Результат тот же, 6я строка валится в ошибку через какой то интервал времени, если буфер обмена дергать каждую секунду.
Код
Function ClipboardText()
Dim MyData As DataObject
Set MyData = New DataObject
With MyData
.GetFromClipboard
ClipboardText = .GetText(1)
.Clear
End With
Set MyData = Nothing
End Function
Попробовал использовать .PasteSpecial То же, через какое то время ошибка, но уже другая.
DataObject нестабильно работает Попробуйте использовать WinAPI (вызов функции GetClipboard):
Код
Option Explicit
Option Private Module
#If VBA7 Then
Declare PtrSafe Function OpenClipboard Lib "USER32" (ByVal hWnd As LongPtr) As Long
Declare PtrSafe Function EmptyClipboard Lib "USER32" () As Long
Declare PtrSafe Function CloseClipboard Lib "USER32" () As Long
Declare PtrSafe Function IsClipboardFormatAvailable Lib "USER32" (ByVal wFormat As Long) As Long
Declare PtrSafe Function GetClipBoardData Lib "USER32" Alias "GetClipboardData" (ByVal wFormat As Long) As LongPtr
Declare PtrSafe Function SetClipboardData Lib "USER32" (ByVal wFormat As Long, ByVal hMem As LongPtr) As LongPtr
Declare PtrSafe Function GlobalAlloc Lib "kernel32" (ByVal wFlags As Long, ByVal dwBytes As LongPtr) As LongPtr
Declare PtrSafe Function GlobalLock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Declare PtrSafe Function GlobalUnlock Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Declare PtrSafe Function GlobalSize Lib "kernel32" (ByVal hMem As LongPtr) As LongPtr
Declare PtrSafe Function lstrCpy Lib "kernel32" Alias "lstrcpyW" (ByVal lpString1 As LongPtr, ByVal lpString2 As LongPtr) As LongPtr
#Else
Declare Function OpenClipboard Lib "user32.dll" (ByVal hwnd As Long) As Long
Declare Function EmptyClipboard Lib "user32.dll" () As Long
Declare Function CloseClipboard Lib "user32.dll" () As Long
Declare Function IsClipboardFormatAvailable Lib "user32.dll" (ByVal wFormat As Long) As Long
Declare Function GetClipboardData Lib "user32.dll" (ByVal wFormat As Long) As Long
Declare Function SetClipboardData Lib "user32.dll" (ByVal wFormat As Long, ByVal hMem As Long) As Long
Declare Function GlobalAlloc Lib "kernel32.dll" (ByVal wFlags As Long, ByVal dwBytes As Long) As Long
Declare Function GlobalLock Lib "kernel32.dll" (ByVal hMem As Long) As Long
Declare Function GlobalUnlock Lib "kernel32.dll" (ByVal hMem As Long) As Long
Declare Function GlobalSize Lib "kernel32" (ByVal hMem As Long) As Long
Declare Function lstrcpy Lib "kernel32.dll" Alias "lstrcpyW" (ByVal lpString1 As Long, ByVal lpString2 As Long) As Long
#End If
Public Sub SetClipboard(sUniText As String)
Dim iStrPtr, iLen As Long, iLock
Const GMEM_MOVEABLE As Long = &H2
Const GMEM_ZEROINIT As Long = &H40
Const CF_UNICODETEXT As Long = &HD
OpenClipboard 0&
EmptyClipboard
iLen = LenB(sUniText) + 2&
iStrPtr = GlobalAlloc(GMEM_MOVEABLE Or GMEM_ZEROINIT, iLen)
iLock = GlobalLock(iStrPtr)
lstrCpy iLock, StrPtr(sUniText)
GlobalUnlock iStrPtr
SetClipboardData CF_UNICODETEXT, iStrPtr
CloseClipboard
End Sub
Public Function GetClipboard() As String
Dim iStrPtr, iLen, iLock
Dim sUniText As String
Const CF_UNICODETEXT As Long = 13&
OpenClipboard 0&
If IsClipboardFormatAvailable(CF_UNICODETEXT) Then
iStrPtr = GetClipBoardData(CF_UNICODETEXT)
If iStrPtr Then
iLock = GlobalLock(iStrPtr)
iLen = GlobalSize(iStrPtr)
sUniText = String$(iLen \ 2& - 1&, vbNullChar)
lstrCpy StrPtr(sUniText), iLock
GlobalUnlock iStrPtr
End If
GetClipboard = sUniText
End If
CloseClipboard
End Function
Private Sub TestClip()
Range("A2").value = Empty
SetClipboard Range("A1").value
Range("A2").value = GetClipboard
End Sub
Недели две назад сотрудники обратились, по моему макросу, с такой же ошибкой. Смотрел и вдоль и поперек. Потом вычитал на одном из форумов, что нужно перенести в новый файл. Создал листы с таким же названием скопировал код в модули. В новом файле все заработало. А так может и памяти не хватать еще 1.Application.Wait 2. DoEvents 3. Отключение функции буфера обмена 4. Скрытые листы? И т.д. и т.п.
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Игорь, спасибо! На WinAPI все залетало без проблем. Ham13, пробовал переносить в новый файл, не взлетело. Проблема переноситься то же. Тему можно закрыть. Всем спасибо за участие и положительный результат!
не замечал, но учту. А что с HTML.parentWindow.clipboardData (.ClearData/.SetData/.GetData)? Есть опыт работы?
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄