Что нового

Как сделать drag & drop файлов на иконку скрипта в трее?

Suppir

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

Скрипт autoit имеет в трее иконку. Можно ли каким-то образом сделать так, чтобы отлавливать события drag & drop файлов на эту иконку? Спасибо
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Пользователь выделяет в проводнике несколько файлов или папку.
Потом он хватает их мышкой и тянет выделенные объекты на иконку AutoIt-скрипта, которая находится в трее. В момент, когда пользователь наводит мышкой на иконку, нужно получить полные пути выделенных пользователем объектов (файлов или директорий).



Добавлено:
Сообщение автоматически объединено:

+ важное примечание:

в скрипте используется

Код:
Opt("TrayOnEventMode", 1)
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Иконка в трее (в отличие от создаваемых AutoIt GUI) принадлежит не процессу AutoIt, а десктопу. Поэтому непонятно как зарегистрировать сообщение WM_DROPFILES, которое нужно отловить.

Возможно есть какой-то способ вроде:

Код:
If нажата левая мышка And мышкой навели на меню в трее Then
     Получить данные о переносимых файлах (может быть есть специальный dllcall для этого?)
Endif
 

Sp01LeR

Знающий
Сообщения
45
Репутация
12
Трей вроде не реагирует на "дроп"-события, хотя могу и ошибатся, но у меня на ХР'юше при перемещении файлов из проводника на трей показывает курсор "Операция невозможна".
 
Автор
S

Suppir

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

да, у меня тоже показывает. Но AutoIt запросто может определить, что левая клавиша мышки зажата и что мышка находится над иконкой в трее.
Нужно только получить в этот момент информацию о перетягиваемых файлах.
 

Sp01LeR

Знающий
Сообщения
45
Репутация
12
Suppir сказал(а):
Но AutoIt запросто может определить, что левая клавиша мышки зажата и что мышка находится над иконкой в трее.

Это конечно автоит может определить, но кроме этого, если например ты перетаскиваешь файл или название ссылки, то нужно еще определить тип и содержимое при "дроп"-операции...
Ты наверно пользовался менеджерами закачек, - так вот у многих из них есть так называемый "квадратик-информер" с возможностью перетаскивать его по рабочей области экрана и поддержкой "дроп"-операций. Это наверно самый лучший способ реализации этой задачи...
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
Эх, жаль, Creator'а нет :( он умеет такие штуки делать.
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
Есть только один выход на мой взгляд.

Отслеживать движение курсора мышки в зажатом состояний, таким образом получая текущий список перетаскиваемых файлов (см. пример с _ExplorerGetSelectedItems), ну и далее отследить отпускание кнопки мышки над иконкой (для этого есть $TRAY_EVENT_MOUSEOVER).
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Suppir
У меня, вроде, так работает:
Код:
#NoTrayIcon
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include '_SysTray.au3'
#include <File.au3>
#include <Array.au3>
#include <GuiListView.au3>
#include <Misc.au3>

Opt('TrayOnEventMode', 1)
Opt('TrayMenuMode', 1)
Opt('WinTitleMatchMode', 2)

$bPress = True

HotKeySet('{ESC}', '_Close')

TraySetClick(16)
$infoitem = TrayCreateItem('Info')
TrayItemSetOnEvent(-1, 'ShowInfo')
TrayCreateItem('')
$exititem = TrayCreateItem('Exit')
TrayItemSetOnEvent(-1, '_Close')
TraySetState()

If Not StringInStr(@OSVersion, 'WIN_XP') Then
	MsgBox(16, '', 'Проверялось только на Windows XP')
	Exit
EndIf

$aPosGUI = _GetIconPos()
If @error Then
	MsgBox(16, '', 'Error')
	Exit
EndIf
$hGUI = GUICreate('Test', $aPosGUI[2], $aPosGUI[2], $aPosGUI[0], $aPosGUI[1], $WS_POPUP, BitOR($WS_EX_ACCEPTFILES, $WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
$Label1 = GUICtrlCreateLabel('', 0, 0, $aPosGUI[2], $aPosGUI[2])
GUICtrlSetState(-1, $GUI_DROPACCEPTED)

GUISetState()
WinSetTrans($hGUI, '', 1)

_WinAPI_EmptyWorkingSet()
While 1
	If _IsPressed('01') Then
		Sleep(50)
		_MoveGUI()
	Else
		If $bPress Then
			WinSetOnTop($hGUI, '', 0)
			$bPress = False
		EndIf
	EndIf
	$nMsg = GUIGetMsg()
	Switch $nMsg
		Case $GUI_EVENT_CLOSE
			Exit
		Case $GUI_EVENT_DROPPED
			$sFolder = StringRegExpReplace(@GUI_DragFile, '\\[^\\]*$', '')
			$sTitle = StringRegExpReplace($sFolder, '^.*\\', '')
			If $sTitle <> 'Рабочий стол' Then
				$hListView = ControlGetHandle('[TITLE:' & $sTitle & '; CLASS:CabinetWClass]', '', '[CLASS:SysListView32; INSTANCE:1]')
			Else
				$hListView = ControlGetHandle('[TITLE:Program Manager; CLASS:Progman]', '', '[CLASS:SysListView32; INSTANCE:1]')
			EndIf
			$aTemp = _GUICtrlListView_GetSelectedIndices($hListView, True)
			If $aTemp[0] Then
				Dim $aSelect[$aTemp[0] + 1]
				$aSelect[0] = $aTemp[0]
				For $i = 1 To $aSelect[0]
					$aSelect[$i] = $sFolder & '\' & _GUICtrlListView_GetItemText($hListView, $aTemp[$i])
				Next
				_ArrayDisplay($aSelect, $sTitle)
			EndIf
			$bPress = True
	EndSwitch
WEnd

Func _GetIconPos()
	Local $aResult[3], $iIndex, $aPos
	$iIndex = _SysTrayIconIndex(@AutoItPID, 2)
	If $iIndex = -1 Then Return SetError(1)
	$aPos = _SysTrayIconPos($iIndex)
	If $aPos = -1 Then Return SetError(1)
	$aResult[0] = $aPos[0]
	$aResult[1] = $aPos[1]
	$aResult[2] = 18
	Return $aResult
EndFunc   ;==>_GetIconPos

Func _MoveGUI()
	Local $aPosNow = _GetIconPos()
	If $aPosGUI[0] <> $aPosNow[0] Or $aPosGUI[1] <> $aPosNow[1] Then
		$aPosGUI[0] = $aPosNow[0]
		$aPosGUI[1] = $aPosNow[1]
		WinMove($hGUI, '', $aPosGUI[0], $aPosGUI[1])
	EndIf
	WinSetOnTop($hGUI, '', 1)
	$bPress = True
EndFunc   ;==>_MoveGUI

Func _Close()
	Exit
EndFunc   ;==>_Close

Func ShowInfo()
	MsgBox(64, 'Info', 'Info')
EndFunc   ;==>ShowInfo

Func _WinAPI_EmptyWorkingSet($pid = 0)

	If Not $pid Then
		$pid = _WinAPI_GetCurrentProcessID()
		If Not $pid Then
			Return SetError(1, 0, 0)
		EndIf
	EndIf
	Local $hProcess = DllCall('kernel32.dll', 'ptr', 'OpenProcess', 'dword', 0x001F0FFF, 'int', _
			0, 'dword', $pid)
	If (@error) Or (Not $hProcess[0]) Then
		Return SetError(1, 0, 0)
	EndIf
	Local $Ret = DllCall(@SystemDir & '\psapi.dll', 'int', 'EmptyWorkingSet', 'ptr', $hProcess[0])
	If (@error) Or (Not $Ret[0]) Then
		$Ret = 0
	EndIf
	_WinAPI_CloseHandle($hProcess[0])
	If Not IsArray($Ret) Then
		Return SetError(1, 0, 0)
	EndIf
	Return 1
EndFunc   ;==>_WinAPI_EmptyWorkingSet

_SysTray.au3
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
madmasles
Неплохо, у меня проскакивала такая идея.

Но вот так вроде корректнее будет:
Код:
#NoTrayIcon
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Constants.au3>
#include <Array.au3>
#include 'SysTray_UDF.au3'
#include 'MouseOnEvent.au3'

HotKeySet('{ESC}', '_Close')

Opt('TrayOnEventMode', 1)
Opt('TrayMenuMode', 1)

Global Const $WM_DROPFILES = 0x233
Global $aDropped_Files[1]
Global $bPrimaryDown = False

TraySetClick(16)
$exititem = TrayCreateItem('Exit')
TrayItemSetOnEvent(-1, '_Close')
TraySetState()

_MouseSetOnEvent($MOUSE_MOVE_EVENT, "MOUSE_MOVE_EVENT", "", "", 0, 0)
_MouseSetOnEvent($MOUSE_PRIMARYDOWN_EVENT, "MOUSE_PDN_EVENT", "", "", 0, 0)
_MouseSetOnEvent($MOUSE_PRIMARYUP_EVENT, "MOUSE_PUP_EVENT", "", "", 0, 0)

$aPosGUI = _GetIconPos()

If @error Then
    MsgBox(16, '', 'Error')
    Exit
EndIf

$hGUI = GUICreate('Test', $aPosGUI[2], $aPosGUI[2], $aPosGUI[0], $aPosGUI[1], $WS_POPUP, BitOR($WS_EX_ACCEPTFILES, $WS_EX_TOOLWINDOW))
$Label1 = GUICtrlCreateLabel('', 0, 0, $aPosGUI[2], $aPosGUI[2])
GUICtrlSetState(-1, $GUI_DROPACCEPTED)

GUISetState()
GUIRegisterMsg($WM_DROPFILES, "WM_DROPFILES_FUNC")
WinSetTrans($hGUI, '', 1)

While 1
    $nMsg = GUIGetMsg()
	
    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $GUI_EVENT_DROPPED
			_ArrayDisplay($aDropped_Files)
			WinSetOnTop($hGUI, '', 0)
	EndSwitch
WEnd

Func MOUSE_MOVE_EVENT()
	If $bPrimaryDown Then
		Local $tPoint = DllStructCreate("int X;int Y")
		DllStructSetData($tPoint, "X", MouseGetPos(0))
		DllStructSetData($tPoint, "Y", MouseGetPos(1))
		
		If _WinAPI_WindowFromPoint($tPoint) = ControlGetHandle("[CLASS:Shell_TrayWnd]", "", "ToolbarWindow321") Then
			_MoveGUI()
		EndIf
	EndIf
EndFunc

Func MOUSE_PDN_EVENT()
	$bPrimaryDown = True
EndFunc

Func MOUSE_PUP_EVENT()
	$bPrimaryDown = False
EndFunc

Func WM_DROPFILES_FUNC($hWnd, $msgID, $wParam, $lParam)
	Local $nSize, $pFileName
	Local $nAmt = DllCall("shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", 0xFFFFFFFF, "ptr", 0, "int", 255)
	
	$aDropped_Files = 0
	Dim $aDropped_Files[$nAmt[0]+1]
	
	For $i = 0 To $nAmt[0] - 1
		$nSize = DllCall("shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", $i, "ptr", 0, "int", 0)
		$nSize = $nSize[0] + 1
		$pFileName = DllStructCreate("char[" & $nSize & "]")
        
		DllCall("shell32.dll", "int", "DragQueryFile", "hwnd", $wParam, "int", $i, "ptr", DllStructGetPtr($pFileName), "int", $nSize)
		
		$aDropped_Files[0] += 1
		
		$aDropped_Files[$aDropped_Files[0]] = DllStructGetData($pFileName, 1)
		$pFileName = 0
	Next
	
	ReDim $aDropped_Files[$aDropped_Files[0]+1]
EndFunc

Func _GetIconPos()
    Local $aResult[3], $iIndex, $aPos
    $iIndex = _SysTrayIconIndex(@AutoItPID)
    If $iIndex = -1 Then Return SetError(1)
    $aPos = _SysTrayIconPos($iIndex)
    If $aPos[0] = -1 Then Return SetError(1)
    $aResult[0] = $aPos[0]
    $aResult[1] = $aPos[1]
    $aResult[2] = 18
    Return $aResult
EndFunc

Func _MoveGUI()
    Local $aPosNow = _GetIconPos()
	
    If $aPosGUI[0] <> $aPosNow[0] Or $aPosGUI[1] <> $aPosNow[1] Then
        $aPosGUI[0] = $aPosNow[0]
        $aPosGUI[1] = $aPosNow[1]
        WinMove($hGUI, '', $aPosGUI[0], $aPosGUI[1])
    EndIf
	
    WinSetOnTop($hGUI, '', 1)
EndFunc

Func _Close()
    Exit
EndFunc


SysTray_UDF.au3
MouseSetOnEvent
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
CreatoR
У меня с Вашим вариантом, при попытке закрыть окно с массивом (_ArrayDisplay), почему-то на несколько секунд мышка зависает. :wacko:
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8,671
Репутация
2,481
madmasles [?]
на несколько секунд мышка зависает
Это из за хука. Как это обойти мне пока неизвестно, но оно и не нужно в этом случае, ведь нам нужен только трей :smile:.
 
Автор
S

Suppir

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

Использовал ваш скрипт на Windows 7. При перетаскивании файла на иконку появляется плюсик, но когда я отпускаю файл, ничего не происходит.
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Если "+" появляется, то значит координаты определяются правильно (я думаю). А файлы не перетаскиваются, так как в Vista/7 нет класса "SysListView32" в Explorer'е.
 
Автор
S

Suppir

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

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

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Ну, черт его знает. Подождем madmasles'а.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
Suppir [?]
Использовал ваш скрипт на Windows 7...
...что этот плюс появляется только для очень небольшой области
В моем скрипте есть проверка на XP:
Код:
;...
If Not StringInStr(@OSVersion, 'WIN_XP') Then
    MsgBox(16, '', 'Проверялось только на Windows XP')
    Exit
EndIf
;...
И Вам ответил
Yashied [?]
А файлы не перетаскиваются, так как в Vista/7 нет класса "SysListView32" в Explorer'е.
За размер области перетаскивания отвечает строка $aResult[2] = 18 в функции _GetIconPos().
Как все это сделать в Windows 7 я пока не знаю. Кстати вопрос про 7 появился только сейчас, вначале даже намека не было на нее.
 
Автор
S

Suppir

Продвинутый
Сообщения
967
Репутация
62
madmasles
Я видел эти строчки, конечно. И удалил их, чтобы посмотреть, будет ли скрипт работать в семерке:
при перетаскивании файла на иконку появляется плюсик, но когда я отпускаю файл, ничего не происходит.
 

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
На основе предыдущих примеров, вроде, вполне нормально работает в Win 7:

Код:
#NoTrayIcon
#include <Array.au3>
#include <Constants.au3>
#include <GuiToolBar.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

#include '_SysTray.au3'
#include 'MouseOnEvent.au3'

HotKeySet('{ESC}', '_Close')

Opt('TrayOnEventMode', 1)
Opt('TrayMenuMode', 1)

Global $aDropped_Files[1]
Global $bPrimaryDown = False

$hToolbar = ControlGetHandle('[CLASS:Shell_TrayWnd]', '', 'ToolbarWindow321')
Global $aToolbar =_GUICtrlToolbar_GetButtonSize($hToolbar)

TraySetClick(16)
$exititem = TrayCreateItem('Exit')
TrayItemSetOnEvent(-1, '_Close')
TraySetState()

_MouseSetOnEvent($MOUSE_MOVE_EVENT, 'MOUSE_MOVE_EVENT')
_MouseSetOnEvent($MOUSE_PRIMARYDOWN_EVENT, 'MOUSE_PDN_EVENT')
_MouseSetOnEvent($MOUSE_PRIMARYUP_EVENT, 'MOUSE_PUP_EVENT')

$aPosGUI = _GetIconPos()

If @error Then
    MsgBox(16, '', 'Error')
    Exit
EndIf

$hGUI = GUICreate('Test', $aPosGUI[3], $aPosGUI[2], $aPosGUI[0], $aPosGUI[1], $WS_POPUP, BitOR($WS_EX_ACCEPTFILES, $WS_EX_TOOLWINDOW))
$Label1 = GUICtrlCreateLabel('', 0, 0, $aPosGUI[2], $aPosGUI[2])
GUICtrlSetState(-1, $GUI_DROPACCEPTED)

GUISetState()
GUIRegisterMsg($WM_DROPFILES, 'WM_DROPFILES_FUNC')
WinSetTrans($hGUI, '', 1)

While 1
    $nMsg = GUIGetMsg()

    Switch $nMsg
        Case $GUI_EVENT_CLOSE
            Exit
        Case $GUI_EVENT_DROPPED
            _ArrayDisplay($aDropped_Files)
            WinSetOnTop($hGUI, '', 0)
    EndSwitch
WEnd

Func MOUSE_MOVE_EVENT()
    If $bPrimaryDown Then
        Local $tPoint = DllStructCreate('int X;int Y')
        DllStructSetData($tPoint, 'X', MouseGetPos(0))
        DllStructSetData($tPoint, 'Y', MouseGetPos(1))
        If _WinAPI_WindowFromPoint($tPoint) = ControlGetHandle('[CLASS:Shell_TrayWnd]', '', 'ToolbarWindow321') Then
            _MoveGUI()
        EndIf
    EndIf
EndFunc

Func MOUSE_PDN_EVENT()
    $bPrimaryDown = True
EndFunc

Func MOUSE_PUP_EVENT()
    $bPrimaryDown = False
EndFunc

Func WM_DROPFILES_FUNC($hWnd, $msgID, $wParam, $lParam)
    Local $nSize, $pFileName
    Local $nAmt = DllCall('shell32.dll', 'int', 'DragQueryFile', 'hwnd', $wParam, 'int', 0xFFFFFFFF, 'ptr', 0, 'int', 255)

    $aDropped_Files = 0
    Dim $aDropped_Files[$nAmt[0]+1]

    For $i = 0 To $nAmt[0] - 1
        $nSize = DllCall('shell32.dll', 'int', 'DragQueryFile', 'hwnd', $wParam, 'int', $i, 'ptr', 0, 'int', 0)
        $nSize = $nSize[0] + 1
        $pFileName = DllStructCreate('char[' & $nSize & ']')

        DllCall('shell32.dll', 'int', 'DragQueryFile', 'hwnd', $wParam, 'int', $i, 'ptr', DllStructGetPtr($pFileName), 'int', $nSize)

        $aDropped_Files[0] += 1

        $aDropped_Files[$aDropped_Files[0]] = DllStructGetData($pFileName, 1)
        $pFileName = 0
    Next

    ReDim $aDropped_Files[$aDropped_Files[0]+1]
EndFunc

Func _GetIconPos()
    Local $aResult[4], $iIndex, $aPos
    $iIndex = _SysTrayIconIndex(@AutoItPID, 2)
    If $iIndex = -1 Then Return SetError(1)
    $aPos = _SysTrayIconPos($iIndex)
    If $aPos = -1 Then Return SetError(1)
    $aResult[0] = $aPos[0]
    $aResult[1] = $aPos[1]
    $aResult[2] = $aToolbar[0]
	$aResult[3] = $aToolbar[1]
    Return $aResult
EndFunc   ;==>_GetIconPos

Func _MoveGUI()
    Local $aPosNow = _GetIconPos()

    If $aPosGUI[0] <> $aPosNow[0] Or $aPosGUI[1] <> $aPosNow[1] Then
        $aPosGUI[0] = $aPosNow[0]
        $aPosGUI[1] = $aPosNow[1]
        WinMove($hGUI, '', $aPosGUI[0], $aPosGUI[1])
    EndIf
    WinSetOnTop($hGUI, '', 1)
EndFunc

Func _Close()
    Exit
EndFunc
 
Верх