Страницы: 1
RSS
Excel, VBA, clipboard столкнулся с проблемой: ошибка run time error -2147221040 (800401d0), Пишу в Excel очень нужную полезняху
 
Приветствую. Это третий форум, на котором я ищу помощь и поддержку. "Автоматизирую" рабочее место секретаря. Каждый день она получает пачку корреспонденции. И регистрирует ее в таблице путем внесения реквизитов документов в Excel. Процесс не быстрый. Можно было бы заплатить одному из многочисленных облачных сервисов, выгружать туда сканы и получать результат, но это не наш выбор). В итоге я придумал следующий алгоритм обработки входящей корреспонденции, который может существенно ускорить и сократить время работы секретаря. Во первых, пачка корреспонденции сканируется в PDF с распознаванием (OCR), так что в PDF файле присутствует текстовый слой. Во вторых секретарь открывает Excel таблицу с поддержкой макросов, в которой выполняется программный код. Программа следит за буфером обмена Windows (Clipboard) и как только видит изменения в текстовой составляющей буфера обмена, текст из буфера обмена помещается в ячейку таблицы Excel. Не нужно переключаться между приложениями. Просто быстро копировать нужные реквизиты документов и получать результат в виде таблицы Excel. Идея хороша. Ее можно распространить и на другие виды работ. Серфите в интернете, что то понравилось, скопировали и все уже там в таблице, вы не отвлекаетесь и дальше работаете с текстом. Просто подумайте об этом как много времени экономиться на многочисленных операциях копи паст с переключением между приложениями, выбора места вставки и сама вставка с последующим возвратом к исходному приложению, вот уже и ход мысли потерял.
В общем написал пример, в котором каждую секунду читаю буфер обмена, как только буфер поменялся, вставляю в таблицу. Вроде ничего сложного, но при одном из последующих попыток прочтения буфера обмена лезет мутная ошибка (толкового решения найти не смог). Если увеличить интервал обращения к буферу обмена до двух секунд, то ошибка чтения буфера обмена становиться очень редкой. Но это не вариант, потому как за две секунды м буфер обмена можно обновить несколько раз, и тогда какой то фрагмент текста будет пропущен(. Какие есть варианты? Первое, исправить код в представленном примере, что бы не было ошибки. Второе, поменять алгоритм и методы обращения к буферу обмена (можно как то отлавливать событие изменения clipboard, что бы не дергать его каждую секунду?).
В представленном примере, нажмите на кнопку, запустится скрипт мониторинга буфера обмена, переключитесь на любое другое приложение и копируйте фрагменты текста, они будут последовательно попадать в таблицу Excel. Что бы остановить выполнение скрипта, скопируйте фрагмент текста длинной в два символа. Что бы вызвать ошибку чтения буфера обмена, исправьте в тексте скрипта таймаут с 2 на 1 секунду (там помечено)
Помогайте братцы!  
Изменено: Юрий М - 23.04.2023 14:45:23
 
Не помогло. Переписал функцию чтения из буфера обмена. Результат тот же, 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 То же, через какое то время ошибка, но уже другая.
Изменено: Xalyf - 23.04.2023 14:15:08
 
Цитата
Xalyf написал: столкнулся с проблемой,
Что за проблема? Это нужно  кратко формулировать в названии темы.  Предложите новое - модераторы поменяют.
 
Xalyf,  Код следует оформлять соответствующим тегом: ищите кнопку <...> и исправьте своё сообщение.
 
Проблема - ошибка run time error -2147221040 (800401d0)
поменяйте пож в заголовке
 
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, пробовал переносить в новый файл, не взлетело. Проблема переноситься то же.
Тему можно закрыть.
Всем спасибо за участие и положительный результат!  
 
Игорь, благодарю - забрал  ;)
Цитата
Игорь: DataObject нестабильно работает
не замечал, но учту. А что с HTML.parentWindow.clipboardData (.ClearData/.SetData/.GetData)? Есть опыт работы?
Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄
Страницы: 1
Читают тему
Наверх