Что нового

перемещение файлов/папок на 1 уровень вверх

Alexey

Новичок
Сообщения
171
Репутация
0
Версия AutoIt: 3.3.0.0 ( win xp pro sp 2 )

Описание: скрипт, при запуске которого выделенные в проводнике файлы/папки без подтверждения перемещались бы на 1 уровень вверх
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
максимально упростил свою просьбу в первом сообщении, оставив только самое главное
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5 379
Репутация
2 711
Я не буду писать полностью скрипт, дам только самое основное. Надеюсь непосредственно перемещение файлов/папок ты сожешь сделать сам (FileMove() / DirMove() + пара функций String...). Ну или кто-нибудь из форумчан поможет. Вот сам код:

MoveUp.au3

Код:
#NoTrayIcon

#Include <WindowsConstants.au3>

Opt('MustDeclareVars', 1)
Opt('WinTitleMatchMode', 3)
Opt('WinWaitDelay', 0)

Global Const $GUI_NAME = 'MoveUp#ZdwRqp10'

Global $aList[1] = [0], $aTemp, $tCOPYDATA, $tData, $hWnd

$hWnd = WinGetHandle($GUI_NAME)
If $CmdLine[0] Then
	If $CmdLine[1] = '/startup' Then
		If $hWnd Then
			Exit
		EndIf
	Else
		If $hWnd Then
			$tCOPYDATA = DllStructCreate('ulong_ptr;dword;ptr')
			$tData = DllStructCreate('wchar[' & StringLen($CmdLine[1]) + 1 & ']')
			DllStructSetData($tData, 1, $CmdLine[1])
			DllStructSetData($tCOPYDATA, 2, DllStructGetSize($tData))
			DllStructSetData($tCOPYDATA, 3, DllStructGetPtr($tData))
			DllCall('user32.dll', 'lresult', 'SendMessage', 'hwnd', $hWnd, 'uint', $WM_COPYDATA, 'ptr', 0, 'ptr', DllStructGetPtr($tCOPYDATA))
;			If @error Then
;
;			EndIf
		EndIf
		Exit
	EndIf
Else
	Exit
EndIf

Opt('TrayIconHide', 0)

$hWnd = GUICreate($GUI_NAME)
GUIRegisterMsg($WM_COPYDATA, 'WM_COPYDATA')

While 1
	Sleep(100)
	If $aList[0] Then
		$aTemp = $aList
		$aList[0] = 0
		For $i = 1 To $aTemp[0]
			MsgBox(0, '', $aTemp[$i])

			; Здесь копируем очередной файл $aTemp[$i] на уровень выше!

		Next
	EndIf
WEnd

Func WM_COPYDATA($hWnd, $iMsg, $wParam, $lParam)

	Local $tCOPYDATA = DllStructCreate('ulong_ptr;dword;ptr', $lParam)

	$aList[0] += 1
	ReDim $aList[$aList[0] + 1]
	$aList[$aList[0]] = DllStructGetData(DllStructCreate('wchar[' & DllStructGetData($tCOPYDATA, 2) & ']', DllStructGetData($tCOPYDATA, 3)), 1)
	Return 1
EndFunc   ;==>WM_COPYDATA


Как это должно работать:

  • Компилируешь скрипт.
  • Копируешь полученный .exe файл в папку "...\System32".
  • Добавляешь контекстное меню для файлов ("*") и папок ("Folder") с помощью следующего .reg файла:

    Windows Registry Editor Version 5.00

    [HKEY_CLASSES_ROOT\*]

    [HKEY_CLASSES_ROOT\*\shell]

    [HKEY_CLASSES_ROOT\*\shell\MoveUp]
    @="Переместить на уровень выше"

    [HKEY_CLASSES_ROOT\*\shell\MoveUp\command]
    @="MoveUp.exe \"%1\""

    [HKEY_CLASSES_ROOT\Folder]

    [HKEY_CLASSES_ROOT\Folder\shell]

    [HKEY_CLASSES_ROOT\Folder\shell\MoveUp]
    @="Переместить на уровень выше"

    [HKEY_CLASSES_ROOT\Folder\shell\MoveUp\command]
    @="MoveUp.exe \"%1\""


  • Запускаешь программу с параметром "/startup" (можешь внести это дело в автозагрузку):

    MoveUp.exe /startup

Вот и все. Теперь выделяй один или несколько файлов/папок, щелкай правой кнопкой мыши и выберай пункт "Переместить на уровень выше".

:smile:
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
Yashied, благодарю за отзывчивость

однако, к сожалению, всё это не подходит. хотя бы потому что есть гораздо более простой способ с использованием контекстного меню:
создать, например, move-up.bat со строкой
Код:
@for %%f in (%*) do @move /-Y %%f "%%~dpf..\"
и закинуть его в папку Send To

но для меня этот способ не особо удобен, поэтому и нужен скрипт, exe-файл из которого я бы повесил на определённую комбинацию клавиш. скрипт запускался бы только на какую-то секунду и только тогда, когда это бы требовалось. при этом достигалось бы максимальное удобство, контекстное меню не засорялось бы, реестр бы не трогался

так что придётся мне ждать, пока
кто-нибудь из форумчан поможет
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 471
Репутация
2 401
Alexey [?]
скрипт запускался бы только на какую-то секунду и только тогда, когда это бы требовалось.
Наврядли получится, скрипт должен будет висеть в процессах и ждать вызова г.клавиши.
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
CreatoR
хочу понять разницу, почему тогда вот в этом случае скрипту не нужно висеть в процессах?
всё дело только в том, что тут требуется перемещение, а в той теме - только выделение?
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 471
Репутация
2 401
Alexey [?]
почему тогда вот в этом случае скрипту не нужно висеть в процессах?
Ну ты же написал, что там оно не нужно:
[?]
по горячей клавише почти всегда всё проще (к слову, я их себе понастраивал не один десяток), но не в этот раз
:smile:

Вот тоже самое без г. клавиши.

Код:
$hExplorer = WinGetHandle("[CLASS:ExplorerWClass]")
If @error Then $hExplorer = WinGetHandle("[CLASS:CabinetWClass]")

$aSelected_Files = _ExplorerGetSelectedItems($hExplorer)

If @error Then
    MsgBox(48, "Error", "It seems that there is no selected files, but also it can be an internal function error.")
    Exit
EndIf

$sExistingFiles = ""
$sParentPath = StringRegExpReplace($aSelected_Files[1][2], "\\[^\\]*\\[^\\]*$", "")

For $i = 1 To $aSelected_Files[0][0]
    $sFileName = StringRegExpReplace($aSelected_Files[$i][2], "^.*\\", "")
	
	If FileExists($sParentPath & "\" & $sFileName) Then
		$sExistingFiles &= $sFileName & @CRLF
		ContinueLoop
	EndIf
	
    If $aSelected_Files[$i][1] = 1 Then
        DirMove($aSelected_Files[$i][2], $sParentPath)
    Else
        FileMove($aSelected_Files[$i][2], $sParentPath)
    EndIf
Next

If $sExistingFiles <> "" Then
	MsgBox(48, "Attention", StringFormat("The following objects already exists in the parent folder\n[%s]\nMoving was not performed:\n\n%s", $sParentPath, $sExistingFiles))
EndIf

;[0][0] = Total items count
;[N][0] = Selected element Index
;[N][1] = Selected element is Directory
;[N][2] = Selected element string (full path to the file/dir)
Func _ExplorerGetSelectedItems($hWnd="[CLASS:CabinetWClass]")
    Local $aRetInfo[1][1]
    Local $aIndexes, $iIndex, $iCount, $sSelected, $sSelected_Path
    Local $hSearch, $sCurrentFile
    
    If Not IsHWnd($hWnd) Then
        $hWnd = WinGetHandle($hWnd)
    EndIf
    
    $sSelected_Path = _GetWindowsExplorerPath($hWnd)
    $sSelected_Path = StringRegExpReplace($sSelected_Path, "\\+$", "")
    $aIndexes = StringSplit(ControlListView($hWnd, "", "SysListView321", "GetSelected", 1), "|")
    
    If $aIndexes[1] = "" Then
        Return SetError(1, 0, 0)
    EndIf
    
    Dim $aRetInfo[$aIndexes[0]+1][3]
    
    For $i = 1 To $aIndexes[0]
        $sSelected = ControlListView($hWnd, "", "SysListView321", "GetText", $aIndexes[$i])
        $sCurrentFile = $sSelected_Path & "\" & $sSelected
        
        If Not FileExists($sCurrentFile) Then ;Search the extension for file...
            $hSearch = FileFindFirstFile($sCurrentFile & ".*")
            
            If $hSearch <> -1 Then
                $sCurrentFile = $sSelected_Path & "\" & FileFindNextFile($hSearch)
                FileClose($hSearch)
            EndIf
        EndIf
        
        $aRetInfo[0][0] += 1
        $aRetInfo[$aRetInfo[0][0]][0] = $aIndexes[$i] ;Index
        $aRetInfo[$aRetInfo[0][0]][1] = Number(StringInStr(FileGetAttrib($sCurrentFile), "D") > 0) ;Is Directory
        $aRetInfo[$aRetInfo[0][0]][2] = $sCurrentFile ;Selected file / dir
    Next
    
    If $aRetInfo[0][0] = 0 Then
        Return SetError(2, 0, 0)
    EndIf
    
    ReDim $aRetInfo[$aRetInfo[0][0]+1][3]
    Return $aRetInfo
EndFunc

; ==================================================================================================
; Func _GetWindowsExplorerPath($hWnd)
;
; Function to get the path currently being explored by a Windows Explorer window
;
; $hWnd = Handle to the Windows Explorer window
;
; Returns:
;   Success: String - Path being explored by this window
;   Failure: "" empty string, with @error set:
;      @error = 1 = This is not a valid explorer window
;      @error = 2 = DLL call error, use _WinAPI_GetLastError()
;
; Author: WideBoyDixon
; ==================================================================================================
Func _GetWindowsExplorerPath($hWnd)
    Local $pv, $pidl, $return = "", $ret, $hMem, $pid, $folderPath = DllStructCreate("char[260]"), $className
    Local $bPIDL = False
    Local Const $CWM_GETPATH = 0x400 + 12;

    ; Check the classname of the window first
    $className = DllCall("user32.dll", "int", "GetClassName", "hwnd", $hWnd, "str", "", "int", 4096)
    If @error Then Return SetError(2, 0, "")
    If ($className[2] <> "ExploreWClass" And $className[2] <> "CabinetWClass") Then Return SetError(1, 0, "")
    
    ; Retrieve the process ID for our process
    $pid = DllCall("kernel32.dll", "int", "GetCurrentProcessId")
    If @error Then Return SetError(2, 0, "")

    ; Send the CWM_GETPATH message to the window
    $hMem = DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hWnd, "int", $CWM_GETPATH, "wparam", $pid[0], "lparam", 0) 
    If @error Then Return SetError(2, 0, "")
    If $hMem[0] = 0 Then Return SetError(1, 0, "")
    
    ; Lock the shared memory
    $pv = DllCall("shell32.dll", "ptr", "SHLockShared", "uint", $hMem[0], "uint", $pid[0])
    If @error Then Return SetError(2, 0, "")
    If $pv[0] Then
        $pidl = DllCall("shell32.dll", "ptr", "ILClone", "uint", $pv[0]) ; Clone the PIDL
        If @error Then Return SetError(2, 0, "")
        $bPIDL = True
        DllCall("shell32.dll", "int", "SHUnlockShared", "uint", $pv) ; Unlock the shared memory
    EndIf
    DllCall("shell32.dll", "int", "SHFreeShared", "uint", $hMem, "uint", $pid) ; Free the shared memory
    
    If $bPIDL Then
        ; Retrieve the path from the PIDL
        $ret = DllCall("shell32.dll", "int", "SHGetPathFromIDList", "ptr", $pidl[0], "ptr", DllStructGetPtr($folderPath))
        If (@error = 0) And ($ret[0] <> 0) Then $return = DllStructGetData($folderPath, 1) ; Retrieve the value
        DllCall("shell32.dll", "none", "ILFree", "ptr", $pidl[0]) ; Free up the PIDL that we cloned
        Return SetError(0, 0, $return) ; Success
    EndIf
    
    Return SetError(2, 0, "") ; Failed a WinAPI call
EndFunc
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
CreatoR
внеси, пожалуйста, изменения:
нужно, чтобы в случае, если в целевой папке объекты с аналогичными названиями ужé существуют, никаких перемещений не происходило, а отображалось окно с надписью "at least one object with the same title already exists, moving was not performed" и одной кнопкой "close" (то есть, безо всяких предложений заменить)

на данный момент:
находящийся в целевой папке файл (not "read only") заменяется перемещаемым, что недопустимо
если же у файла атрибут "read only" есть, то просто ничего не происходит
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 471
Репутация
2 401
Alexey [?]
нужно, чтобы в случае, если в целевой папке объекты с аналогичными названиями ужé существуют, никаких перемещений не происходило
Впринципе достаточно убрать последний параметр у функций Dir/FileCopy.

отображалось окно с надписью
Тогда нужна проверка, поправил.
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
CreatoR
поначалу мне показалось, что чуть ли не можно пользоваться скриптом, но всё же прошу исправить вот что:

1) если у каких-то файлов названия совпадают, а у других - нет, то те, у которых названия не совпадают, перемещаются

должно быть:
- если совпадает название хотя бы одного файла/папки, то ничего не перемещается
- при этом в окне упоминаются и перечисляются совпадения и файлов, и папок

2) почему-то папки не перемещаются вообще
 

CreatoR

Must AutoIt!
Команда форума
Администратор
Сообщения
8 471
Репутация
2 401
Alexey [?]
если у каких-то файлов названия совпадают, а у других - нет, то те, у которых названия не совпадают, перемещаются
Это нужно уточнять сразу, в первом сообщений.

Есть ещё какие то условия которые нужно учесть?

если совпадает название хотя бы одного файла/папки, то ничего не перемещается
Тогда нужно сначала проверять файлы...

при этом в окне упоминаются и перечисляются совпадения и файлов, и папок
Так и есть.

почему-то папки не перемещаются вообще
Потому что не было указано название папки назначения.

Код:
$hExplorer = WinGetHandle("[CLASS:ExplorerWClass]")
If @error Then $hExplorer = WinGetHandle("[CLASS:CabinetWClass]")

$aSelected_Files = _ExplorerGetSelectedItems($hExplorer)

If @error Then
    MsgBox(48, "Error", "It seems that there is no selected files, but also it can be an internal function error.")
    Exit
EndIf

$sExistingFiles = ""
$sParentPath = StringRegExpReplace($aSelected_Files[1][2], "\\[^\\]*\\[^\\]*$", "")

For $i = 1 To $aSelected_Files[0][0]
    $sFileName = StringRegExpReplace($aSelected_Files[$i][2], "^.*\\", "")
    
    If FileExists($sParentPath & "\" & $sFileName) Then
        $sExistingFiles &= $sFileName & @CRLF
    EndIf
Next

If $sExistingFiles <> "" Then
    MsgBox(48, "Move Selected Files - Attention", _
		StringFormat("The following objects already exists in the parent folder\n(%s)\nMoving was not performed:\n\n%s", $sParentPath, $sExistingFiles))
	
	Exit
EndIf

For $i = 1 To $aSelected_Files[0][0]
    $sFileName = StringRegExpReplace($aSelected_Files[$i][2], "^.*\\", "")
    
    If $aSelected_Files[$i][1] = 1 Then
        DirMove($aSelected_Files[$i][2], $sParentPath & "\" & $sFileName)
    Else
        FileMove($aSelected_Files[$i][2], $sParentPath & "\" & $sFileName)
    EndIf
Next

;[0][0] = Total items count
;[N][0] = Selected element Index
;[N][1] = Selected element is Directory
;[N][2] = Selected element string (full path to the file/dir)
Func _ExplorerGetSelectedItems($hWnd="[CLASS:CabinetWClass]")
    Local $aRetInfo[1][1]
    Local $aIndexes, $iIndex, $iCount, $sSelected, $sSelected_Path
    Local $hSearch, $sCurrentFile
    
    If Not IsHWnd($hWnd) Then
        $hWnd = WinGetHandle($hWnd)
    EndIf
    
    $sSelected_Path = _GetWindowsExplorerPath($hWnd)
    $sSelected_Path = StringRegExpReplace($sSelected_Path, "\\+$", "")
    $aIndexes = StringSplit(ControlListView($hWnd, "", "SysListView321", "GetSelected", 1), "|")
    
    If $aIndexes[1] = "" Then
        Return SetError(1, 0, 0)
    EndIf
    
    Dim $aRetInfo[$aIndexes[0]+1][3]
    
    For $i = 1 To $aIndexes[0]
        $sSelected = ControlListView($hWnd, "", "SysListView321", "GetText", $aIndexes[$i])
        $sCurrentFile = $sSelected_Path & "\" & $sSelected
        
        If Not FileExists($sCurrentFile) Then ;Search the extension for file...
            $hSearch = FileFindFirstFile($sCurrentFile & ".*")
            
            If $hSearch <> -1 Then
                $sCurrentFile = $sSelected_Path & "\" & FileFindNextFile($hSearch)
                FileClose($hSearch)
            EndIf
        EndIf
        
        $aRetInfo[0][0] += 1
        $aRetInfo[$aRetInfo[0][0]][0] = $aIndexes[$i] ;Index
        $aRetInfo[$aRetInfo[0][0]][1] = Number(StringInStr(FileGetAttrib($sCurrentFile), "D") > 0) ;Is Directory
        $aRetInfo[$aRetInfo[0][0]][2] = $sCurrentFile ;Selected file / dir
    Next
    
    If $aRetInfo[0][0] = 0 Then
        Return SetError(2, 0, 0)
    EndIf
    
    ReDim $aRetInfo[$aRetInfo[0][0]+1][3]
    Return $aRetInfo
EndFunc

; ==================================================================================================
; Func _GetWindowsExplorerPath($hWnd)
;
; Function to get the path currently being explored by a Windows Explorer window
;
; $hWnd = Handle to the Windows Explorer window
;
; Returns:
;   Success: String - Path being explored by this window
;   Failure: "" empty string, with @error set:
;      @error = 1 = This is not a valid explorer window
;      @error = 2 = DLL call error, use _WinAPI_GetLastError()
;
; Author: WideBoyDixon
; ==================================================================================================
Func _GetWindowsExplorerPath($hWnd)
    Local $pv, $pidl, $return = "", $ret, $hMem, $pid, $folderPath = DllStructCreate("char[260]"), $className
    Local $bPIDL = False
    Local Const $CWM_GETPATH = 0x400 + 12;

    ; Check the classname of the window first
    $className = DllCall("user32.dll", "int", "GetClassName", "hwnd", $hWnd, "str", "", "int", 4096)
    If @error Then Return SetError(2, 0, "")
    If ($className[2] <> "ExploreWClass" And $className[2] <> "CabinetWClass") Then Return SetError(1, 0, "")
    
    ; Retrieve the process ID for our process
    $pid = DllCall("kernel32.dll", "int", "GetCurrentProcessId")
    If @error Then Return SetError(2, 0, "")

    ; Send the CWM_GETPATH message to the window
    $hMem = DllCall("user32.dll", "lparam", "SendMessage", "hwnd", $hWnd, "int", $CWM_GETPATH, "wparam", $pid[0], "lparam", 0) 
    If @error Then Return SetError(2, 0, "")
    If $hMem[0] = 0 Then Return SetError(1, 0, "")
    
    ; Lock the shared memory
    $pv = DllCall("shell32.dll", "ptr", "SHLockShared", "uint", $hMem[0], "uint", $pid[0])
    If @error Then Return SetError(2, 0, "")
    If $pv[0] Then
        $pidl = DllCall("shell32.dll", "ptr", "ILClone", "uint", $pv[0]) ; Clone the PIDL
        If @error Then Return SetError(2, 0, "")
        $bPIDL = True
        DllCall("shell32.dll", "int", "SHUnlockShared", "uint", $pv) ; Unlock the shared memory
    EndIf
    DllCall("shell32.dll", "int", "SHFreeShared", "uint", $hMem, "uint", $pid) ; Free the shared memory
    
    If $bPIDL Then
        ; Retrieve the path from the PIDL
        $ret = DllCall("shell32.dll", "int", "SHGetPathFromIDList", "ptr", $pidl[0], "ptr", DllStructGetPtr($folderPath))
        If (@error = 0) And ($ret[0] <> 0) Then $return = DllStructGetData($folderPath, 1) ; Retrieve the value
        DllCall("shell32.dll", "none", "ILFree", "ptr", $pidl[0]) ; Free up the PIDL that we cloned
        Return SetError(0, 0, $return) ; Success
    EndIf
    
    Return SetError(2, 0, "") ; Failed a WinAPI call
EndFunc
 
Автор
A

Alexey

Новичок
Сообщения
171
Репутация
0
Это нужно уточнять сразу, в первом сообщений.
извиняюсь, просто бывает, что не всё требуемое от скрипта сразу осознаёшь чётко, а вот когда он обрёл форму и его можно ужé пробовать, разные нюансы могут стать значимыми

Есть ещё какие то условия которые нужно учесть?
похоже, что нет. потратил я некоторое время на проверку - всё работает как надо, благодарю ;)

кое-какие сущие мелочи для себя изменил. не привнёс ли я каких-нибудь ошибок в третью строку?:
было:
Код:
If $sExistingFiles <> "" Then
    MsgBox(48, "Move Selected Files - Attention", _
        StringFormat("The following objects already exists in the parent folder\n(%s)\nMoving was not performed:\n\n%s", $sParentPath, $sExistingFiles))
   
    Exit
EndIf


стало:
Код:
If $sExistingFiles <> "" Then
    MsgBox(262144, "", _
        StringFormat("folder %s\n\nalready contains objects:\n\n%s", $sParentPath, $sExistingFiles))
   
    Exit
EndIf
 

Matias

Новичок
Сообщения
1
Репутация
0
Alexey сказал(а):
однако, к сожалению, всё это не подходит. хотя бы потому что есть гораздо более простой способ с использованием контекстного меню:
создать, например, move-up.bat со строкой
Код:
@for %%f in (%*) do @move /-Y %%f "%%~dpf..\"
и закинуть его в папку Send To
Скрипт отлично справляется с переносом файлов
@for %%f in (%*) do @move /Y %%f "%%~dpf..\"
но если названия папок совпадают (которую переносишь и в папке выше), то выдает сообщение отказано в доступе...
Как после перемещения содержимого удалить текущую папку если она пуста?
пробовал CD /D ..\ & RD /S /Q "%~dp0"
не работает
 

Garrett

Модератор
Локальный модератор
Сообщения
3 999
Репутация
964
OffTopic:
Matias
Вы на дату последнего поста смотрите?
 
Верх