Что нового

Как узнать, что в копирование в буфер обмена завершено?

Suppir

Продвинутый
Сообщения
967
Репутация
62
Добрый день!

Мне нужно получить в переменную текст, который выделил пользователь. Я использую следующий метод, однако он срабатывает нормально только в том случае, если пользователь копирует небольшой по размеру текст:

Код:
Send("^{INS}")
	
	local $i
	
	While True
		$i = $i + 1
		if $i > 10 Then 
			ConsoleWrite("Не удалось скопировать в буфер" & @CRLF)
			Return 0 
		EndIf
		
		if StringLen(Clipget()) > 1 Then ExitLoop
		
		sleep (50)
	WEnd
	
	Return 1



Если же идет копирование большого куска, то StringLen(Clipget()) > 1 будет уже через несколько секунд, но на самом деле копирование буфер не завершено. Если ли способ точно узнать, что весь текст скопирован в буфер и туда больше ничего не пишется?
 

Yuri

AutoIT Гуру
Сообщения
737
Репутация
282
Так попробуй:
Код:
$Var1 = "0"
For $i = 1 To 1111111
	$Var1 &=$i
Next

RunWait(ClipPut($Var1))
$Var2 = ClipGet()
;на моем ПК примерно Time: 1.900
;MsgBox(64, "$Var2", $Var2)


Код:
$Var1 = "0"
For $i = 1 To 10
	$Var1 &=$i
Next

RunWait(ClipPut($Var1))
$Var2 = ClipGet()
;на моем ПК примерно Time: 0.254
;MsgBox(64, "$Var2", $Var2)
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Suppir [?]
Если ли способ точно узнать, что весь текст скопирован в буфер и туда больше ничего не пишется?
Примерно так:

Код:
ClipPut("")
$sClip = ""

Send("^{INS}")

While $sClip = ""
	$sClip = ClipGet()
	Sleep(50)
WEnd


Yuriy [?]
Или я не так тему понял, или... :blink:
 

Yuri

AutoIT Гуру
Сообщения
737
Репутация
282
Или тогда я протупил.
Ну, вроде - "Ожидать (Копирование в буфер)" - т.е. ждем пока не скопируется в буфер
и уже только потом выполнить следующую команду.
Как-то так.
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Yuriy
меня интересует не ClipPut, а ClipGet().

CreatoR
Ваш метод практически полностью повторяет мой (но у меня есть еще проверка и выход из цикла). Этот способ не работает. При нажатии хоткея CTRL+INS данные начинают копироваться в буфер обмена. И через определенное время буфер уже не пустой (выход из вашего цикла). Однако копирование в буфер еще не завершено. Необходимо, наверное, с помощью WinApi узнавать, копируется ли в данный момент в буфер что-либо или нет.

Т.е. при копировании в буфер данные там появляются не сразу после завершения копирования, а частями (так же как и запись в файлы). Буфер уже не пуст, но не все данные скопированы. В этом и проблема.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Suppir [?]
Ваш метод практически полностью повторяет мой
Каким образом?! :blink:

у меня есть еще проверка и выход из цикла
А в моём примере нет выхода из цикла?

При нажатии хоткея CTRL+INS данные начинают копироваться в буфер обмена. И через определенное время буфер уже не пустой (выход из вашего цикла). Однако копирование в буфер еще не завершено.
Что то очень странно, раз ClipGet возвращает текст, значит буфер заполнен, мне кажется буфер не может заполняться по частям.

Как воспроизвести это?
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
CreatoR
"Каким образом?!"

и там и там идет в цикле проверка содержимого буфера. Но у вас один выход из цикла - когда в буфере что-то появилось. А у меня два выхода из цикла: когда в буфере что-то появилось и по истечении определенного времени.


CreatoR [?]
мне кажется буфер не может заполняться по частям.

Запись в буфер очень похож на FileWrite (только запись в память, а не на винт). При записи больших файлов видно, как они растут в размере. Однако их открыть на запись в этот момент нельзя, потому что дескриптор занят. Я вот думаю, можно ли для буфера как-то проверять, идет ли в него запись или уже нет.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Suppir
Сколько не пытался, так у меня и не получилось проверить, что в буфер информация попадает по частям. У меня она сразу вся появляется:
Код:
#include <GuiEdit.au3>

$sFile = @ScriptDir & '\Test.txt'
$sTestString = ''
$iLen_1 = 0
$iLen_2 = 0
$fYes = True
$iMB = 40
ClipPut('')
If Not FileExists($sFile) Or FileGetSize($sFile) <> 1024 * 1024 * $iMB Then
	For $i = 1 To 1024 * 1024 * $iMB
		$sTestString &= '0'
	Next
	$hFile = FileOpen($sFile, 2)
	FileWrite($hFile, $sTestString)
	FileClose($hFile)
EndIf
Run(@SystemDir & '\notepad.exe "' & $sFile & '"')
$hWin = WinWait('[Class:Notepad]', '', 5)
If Not $hWin Then Exit
Sleep(20000)
$hEdit = ControlGetHandle($hWin, '', '[CLASS:Edit; INSTANCE:1]')
If Not $hEdit Then Exit
_GUICtrlEdit_SetSel($hEdit, 0, -1)
Sleep(20000)
WinActivate($hWin)
WinWaitActive($hWin, '', 5)
Send('^{INS}')
$iStart = TimerInit()
While 1
	If TimerDiff($iStart) > 30000 Then
		$fYes = False
		ExitLoop
	EndIf
	$iLen_1 = StringLen(ClipGet())
	ConsoleWrite($iLen_1 & @CR)
;	If $iLen_1 Then
;		If $iLen_1 > $iLen_2 Then
;			$iLen_2 = $iLen_1
;			ConsoleWrite($iLen_2 & @CR)
;		ElseIf $iLen_1 = $iLen_2 Then
;			ExitLoop
;		EndIf
;	Else
		Sleep(1)
;	EndIf
WEnd

$iTime = Round(TimerDiff($iStart), 2)
If $fYes Then
	MsgBox(64, 'Info', 'OK' & ' - ' & $iTime)
Else
	MsgBox(16, 'Info', 'TimeOut' & ' - ' & $iTime)
EndIf
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Информация в буфер записывается не частями, а сразу целым блоком данных, за один вызов _ClipBoard_SetData(). Во время записи этого блока, естественно, вы не сможете ничего получить из буфера, т.к. он, теоретически, будет еще пустой.
 

dwerf

Использует ArchLinux
Сообщения
478
Репутация
219
Код:
Send("^{INS}")
	
	Local $iClipLenNew = 0, $iClipLenOld
	
	Do
		Sleep(50)
		$iClipLenOld = $iClipLenNew
		$iClipLenNew = StringLen(Clipget())
	Until $iClipLenNew = $iClipLenOld
	
	If $iClipLenNew = 0 Then
		ConsoleWrite("Не удалось скопировать в буфер" & @CRLF)
		Return 0
	Else
		Return 1
	EndIf
 

e-Mike

Новичок
Сообщения
15
Репутация
1
Допустим, при копировании большого объема в буфер, в нем через несколько секунд уже будет какая-то часть копируемых данных. Тогда имеет смысл определять размер данных в буфере и сравнивать этот размер через каждые "несколько секунд + 1". Но это будет зависеть от скорости компа, и внесет дополнительную задержку в работу.
Может быть лучше сделать заполнение буфера через ClipPut() и проверять возвращаемое функцией значение?
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
CreatoR
Да попробовал, встроил его в свою функцию.

Сижу, разбираюсь целый день. Выяснить удалось следующее:

1) если мы копируем текст из блокнота, то он появляется сразу одним куском (даже если несколько мегабайт) и проблем не возникает
2) если мы копируем текст из специального редактора (с которым работает моя программа), то все сложнее. Информация в буфере обмена содержится в нескольких форматах: plain text, rtf, html, binary data, OEM test, OLE что-то там, objects и т.д. Вся эта информация не попадает в буфер одномоментно. Наверное, первым идет запись plain text (т.е. обычный текст, который содержится в блокноте), а потом дописываются данные в других форматах. Формально выходит, что ClipGet уже содержит данные, но, скажем, RTF-данных там еще нет, или только часть их присутствует (половина файла).
Если мы копируем в буфер небольшой фрагмент (несколько страниц), то проблем не возникает. Достаточно поставить sleep(500). Но пользователи часто копируют большие файлы (по 200 - 400 страниц), при этом копирование в буфер идет 20 - 60 секунд! Буфер уже не пустой через несколько секунд после начала копирования.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Suppir
Я так понял воспроизвести это не суждено, или оно настолько засекречено что на форум выкладывать нельзя?

[?]
копирование в буфер идет 20 - 60 секунд!
Первый раз такое слышу.
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
CreatoR
не то чтобы засекречено, но для установки редактора нужно поднимать сервер, заводить пользователей и т.п. Это как бы редактор+БД.


CreatoR [?]
Первый раз такое слышу.
на больших документах легко. Там даже Word подвисает. OpenOffice Writer частенько не может вообще открыть. Возникает вопрос - а в чем тогда создавался этот большой документ? - В Excel'е. А потом сохранили в docx.

p.s. В понедельник я выложу одно предложение (пару десятков слов) из реального документа. Если это предложение ставить его в word, то он виснет наглухо.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Вот небольшой пример, посмотри как будет менятся размер данных в буфере при их записи туда.

Код:
#Include <Clipboard.au3>
#Include <WindowsConstants.au3>
#Include <WinAPIEx.au3>

GUICreate('', 200, 50, 50, 50, -1, $WS_EX_TOPMOST)
$Label = GUICtrlCreateLabel('', 10, 18, 80, 14)
GUISetState()

While 1
	_ClipBoard_Open(0)
	$hMem = _ClipBoard_GetDataEx($CF_TEXT)
	If $hMem Then
		$Text = _WinAPI_StrFormatKBSize(_MemGlobalSize($hMem))
	Else
		$Text = '0 KB'
	EndIf
	_ClipBoard_Close()
	If GUICtrlRead($Label) <> $Text Then
		GUICtrlSetData($Label, $Text)
	EndIf
	Switch GUIGetMsg()
		Case -3
			Exit
	EndSwitch
WEnd
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Yashied
Понятно. Вы проверяете данные в формате $CF_TEXT. Но в буфере могут содержатся данные форматов:

$CF_TEXT - Text format
$CF_BITMAP - Handle to a bitmap (HBITMAP)
$CF_METAFILEPICT - Handle to a metafile picture (METAFILEPICT)
$CF_SYLK - Microsoft Symbolic Link (SYLK) format
$CF_DIF - Software Arts' Data Interchange Format
$CF_TIFF - Tagged image file format
$CF_OEMTEXT - Text format containing characters in the OEM character set
$CF_DIB - BITMAPINFO structure followed by the bitmap bits
$CF_PALETTE - Handle to a color palette
$CF_PENDATA - Data for the pen extensions to Pen Computing
$CF_RIFF - Represents audio data in RIFF format
$CF_WAVE - Represents audio data in WAVE format
$CF_UNICODETEXT - Unicode text format
$CF_ENHMETAFILE - Handle to an enhanced metafile (HENHMETAFILE)
$CF_HDROP - Handle to type HDROP that identifies a list of files
$CF_LOCALE - Handle to the locale identifier associated with text in the clipboard
$CF_DIBV5 - BITMAPV5HEADER structure followed by bitmap color and the bitmap bits
$CF_OWNERDISPLAY - Owner display format
$CF_DSPTEXT - Text display format associated with a private format
$CF_DSPBITMAP - Bitmap display format associated with a private format
$CF_DSPMETAFILEPICT - Metafile picture display format associated with a private format
$CF_DSPENHMETAFILE - Enhanced metafile display format associated with a private format

+ специфичные форматы, зарегистрированные для данного приложения.

Более чем уверен, что данные не попадают в буфер моментально. Сначала пишется тот самый $CF_TEXT (он идет первый по нумерации, если использовать _ClipBoard_EnumFormats()), а потом остальные данные. При наличии $CF_TEXT функция Clipget() уже будет что-то возвращать...
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Suppir сказал(а):
При наличии $CF_TEXT функция Clipget() уже будет что-то возвращать...

Ну так используйте _ClipBoard_GetData().

:smile:
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Спасибо, нужно попробовать.
 
Верх