Jump to content

How to copy a file to the clipboard for pasting in explorer?


censv
 Share

Go to solution Solved by Andreik,

Recommended Posts

;===============================================================================
;
; Description:      Copy Files to Clipboard Like Explorer does
; Parameter(s):     $sFile      - Full Path to File(s)
;                   $sSeperator - Seperator for multiple Files, Default = '|'
; Requirement(s):   v3.1.1.122+
; Return Value(s):  On Success - True
;                   On Failure - False and
;                                   Sets @ERROR to: 1 - Unable to Open Clipboard
;                                                   2 - Unable to Empty Cipboard
;                                                   3 - GlobalAlloc Failed
;                                                   4 - GlobalLock Failed
;                                                   5 - Unable to Create H_DROP
;                                                   6 - Unable to Update Clipboard
;                                                   7 - Unable to Close Clipboard
;                                                   8 - GlobalUnlock Failed
; Author(s):        Piccaso (Florian Fida)
; Note(s):
;
;===============================================================================
Func _ClipPutFile($sFile, $sSeperator = "|")
    Local $vDllCallTmp, $nGlobMemSize, $hGlobal, $DROPFILES, $i, $hLock
    Local $GMEM_MOVEABLE = 0x0002, $CF_HDROP = 15
    $sFile = $sFile & $sSeperator & $sSeperator
    $nGlobMemSize = StringLen($sFile) + 20 ; 20 = size of DROPFILES whitout buffer
    $vDllCallTmp = DllCall("user32.dll", "int", "OpenClipboard", "hwnd", 0)
    If @error Or $vDllCallTmp[0] = 0 Then
        SetError(1)
        Return False
    EndIf
    $vDllCallTmp = DllCall("user32.dll", "int", "EmptyClipboard")
    If @error Or $vDllCallTmp[0] = 0 Then
        SetError(2)
        Return False
    EndIf
    $vDllCallTmp = DllCall("kernel32.dll", "long", "GlobalAlloc", "int", $GMEM_MOVEABLE, "int", $nGlobMemSize)
    If @error Or $vDllCallTmp[0] < 1 Then
        SetError(3)
        Return False
    EndIf
    $hGlobal = $vDllCallTmp[0]
    $vDllCallTmp = DllCall("kernel32.dll", "long", "GlobalLock", "long", $hGlobal)
    If @error Or $vDllCallTmp[0] < 1 Then
        SetError(4)
        Return False
    EndIf
    $hLock = $vDllCallTmp[0]
    $DROPFILES = DllStructCreate("dword;ptr;int;int;int;char[" & StringLen($sFile) & "]", $hLock)
    If @error Then
        SetError(5)
        Return False
    EndIf
    DllStructSetData($DROPFILES, 1, DllStructGetSize($DROPFILES) - StringLen($sFile))
    DllStructSetData($DROPFILES, 2, 0)
    DllStructSetData($DROPFILES, 3, 0)
    DllStructSetData($DROPFILES, 4, 0)
    DllStructSetData($DROPFILES, 5, 0)
    DllStructSetData($DROPFILES, 6, $sFile)
    For $i = 1 To StringLen($sFile)
        If DllStructGetData($DROPFILES, 6, $i) = Asc($sSeperator) Then DllStructSetData($DROPFILES, 6, 0, $i)
    Next
    $vDllCallTmp = DllCall("user32.dll", "long", "SetClipboardData", "int", $CF_HDROP, "long", $hGlobal)
    If @error Or $vDllCallTmp[0] < 1 Then
        SetError(6)
        $DROPFILES = 0
        Return False
    EndIf
    $vDllCallTmp = DllCall("user32.dll", "int", "CloseClipboard")
    If @error Or $vDllCallTmp[0] = 0 Then
        SetError(7)
        $DROPFILES = 0
        Return False
    EndIf
    $vDllCallTmp = DllCall("kernel32.dll", "int", "GlobalUnlock", "long", $hGlobal)
    If @error Then
        SetError(8)
        $DROPFILES = 0
        Return False
    EndIf
    $vDllCallTmp = DllCall("kernel32.dll", "int", "GetLastError")
    If $vDllCallTmp = 0 Then
        $DROPFILES = 0
        SetError(8)
        Return False
    Else
        $DROPFILES = 0
        Return True
    EndIf
EndFunc   ;==>_ClipPutFile

Or you can use a more compact version of the script above:

#include <Memory.au3>
#include <Clipboard.au3>

$sFilePath = '<path to your file>'
_ClipPutFile($sFilePath)

Func _ClipPutFile($sFile)
    Local $iLen =  StringLen($sFile) + 2
    _ClipBoard_Open(0)
    _ClipBoard_Empty()
    Local $hGlobal = _MemGlobalAlloc($iLen + 20, $GMEM_MOVEABLE)
    Local $hLock = _MemGlobalLock($hGlobal)
    Local $tDROPFILES = DllStructCreate("dword pFiles;long x;long y;int fNC;int fWide;char File[" & $iLen & "]", $hLock)
    $tDROPFILES.pFiles = DllStructGetSize($tDROPFILES) - $iLen
    $tDROPFILES.x = 0
    $tDROPFILES.y = 0
    $tDROPFILES.fNC = 0
    $tDROPFILES.fWide = 0
    $tDROPFILES.File = $sFile & Chr(0) & Chr(0)
    _ClipBoard_SetDataEx($hGlobal, $CF_HDROP)
    _MemGlobalUnlock($hGlobal)
    _ClipBoard_Close()
    _MemGlobalFree($hGlobal)
EndFunc

 

Edited by Andreik
Link to comment
Share on other sites

1 hour ago, Andreik said:
;===============================================================================
;
; Description:      Copy Files to Clipboard Like Explorer does
; Parameter(s):     $sFile      - Full Path to File(s)
;                   $sSeperator - Seperator for multiple Files, Default = '|'
; Requirement(s):   v3.1.1.122+
; Return Value(s):  On Success - True
;                   On Failure - False and
;                                   Sets @ERROR to: 1 - Unable to Open Clipboard
;                                                   2 - Unable to Empty Cipboard
;                                                   3 - GlobalAlloc Failed
;                                                   4 - GlobalLock Failed
;                                                   5 - Unable to Create H_DROP
;                                                   6 - Unable to Update Clipboard
;                                                   7 - Unable to Close Clipboard
;                                                   8 - GlobalUnlock Failed
; Author(s):        Piccaso (Florian Fida)
; Note(s):
;
;===============================================================================
Func _ClipPutFile($sFile, $sSeperator = "|")
    Local $vDllCallTmp, $nGlobMemSize, $hGlobal, $DROPFILES, $i, $hLock
    Local $GMEM_MOVEABLE = 0x0002, $CF_HDROP = 15
    $sFile = $sFile & $sSeperator & $sSeperator
    $nGlobMemSize = StringLen($sFile) + 20 ; 20 = size of DROPFILES whitout buffer
    $vDllCallTmp = DllCall("user32.dll", "int", "OpenClipboard", "hwnd", 0)
    If @error Or $vDllCallTmp[0] = 0 Then
        SetError(1)
        Return False
    EndIf
    $vDllCallTmp = DllCall("user32.dll", "int", "EmptyClipboard")
    If @error Or $vDllCallTmp[0] = 0 Then
        SetError(2)
        Return False
    EndIf
    $vDllCallTmp = DllCall("kernel32.dll", "long", "GlobalAlloc", "int", $GMEM_MOVEABLE, "int", $nGlobMemSize)
    If @error Or $vDllCallTmp[0] < 1 Then
        SetError(3)
        Return False
    EndIf
    $hGlobal = $vDllCallTmp[0]
    $vDllCallTmp = DllCall("kernel32.dll", "long", "GlobalLock", "long", $hGlobal)
    If @error Or $vDllCallTmp[0] < 1 Then
        SetError(4)
        Return False
    EndIf
    $hLock = $vDllCallTmp[0]
    $DROPFILES = DllStructCreate("dword;ptr;int;int;int;char[" & StringLen($sFile) & "]", $hLock)
    If @error Then
        SetError(5)
        Return False
    EndIf
    DllStructSetData($DROPFILES, 1, DllStructGetSize($DROPFILES) - StringLen($sFile))
    DllStructSetData($DROPFILES, 2, 0)
    DllStructSetData($DROPFILES, 3, 0)
    DllStructSetData($DROPFILES, 4, 0)
    DllStructSetData($DROPFILES, 5, 0)
    DllStructSetData($DROPFILES, 6, $sFile)
    For $i = 1 To StringLen($sFile)
        If DllStructGetData($DROPFILES, 6, $i) = Asc($sSeperator) Then DllStructSetData($DROPFILES, 6, 0, $i)
    Next
    $vDllCallTmp = DllCall("user32.dll", "long", "SetClipboardData", "int", $CF_HDROP, "long", $hGlobal)
    If @error Or $vDllCallTmp[0] < 1 Then
        SetError(6)
        $DROPFILES = 0
        Return False
    EndIf
    $vDllCallTmp = DllCall("user32.dll", "int", "CloseClipboard")
    If @error Or $vDllCallTmp[0] = 0 Then
        SetError(7)
        $DROPFILES = 0
        Return False
    EndIf
    $vDllCallTmp = DllCall("kernel32.dll", "int", "GlobalUnlock", "long", $hGlobal)
    If @error Then
        SetError(8)
        $DROPFILES = 0
        Return False
    EndIf
    $vDllCallTmp = DllCall("kernel32.dll", "int", "GetLastError")
    If $vDllCallTmp = 0 Then
        $DROPFILES = 0
        SetError(8)
        Return False
    Else
        $DROPFILES = 0
        Return True
    EndIf
EndFunc   ;==>_ClipPutFile

Or you can use a more compact version of the script above:

#include <Memory.au3>
#include <Clipboard.au3>

$sFilePath = '<path to your file>'
_ClipPutFile($sFilePath)

Func _ClipPutFile($sFile)
    Local $iLen =  StringLen($sFile) + 2
    _ClipBoard_Open(0)
    _ClipBoard_Empty()
    Local $hGlobal = _MemGlobalAlloc($iLen + 20, $GMEM_MOVEABLE)
    Local $hLock = _MemGlobalLock($hGlobal)
    Local $tDROPFILES = DllStructCreate("dword pFiles;long x;long y;int fNC;int fWide;char File[" & $iLen & "]", $hLock)
    $tDROPFILES.pFiles = DllStructGetSize($tDROPFILES) - $iLen
    $tDROPFILES.x = 0
    $tDROPFILES.y = 0
    $tDROPFILES.fNC = 0
    $tDROPFILES.fWide = 0
    $tDROPFILES.File = $sFile & Chr(0) & Chr(0)
    _ClipBoard_SetDataEx($hGlobal, $CF_HDROP)
    _MemGlobalUnlock($hGlobal)
    _ClipBoard_Close()
    _MemGlobalFree($hGlobal)
EndFunc

 

Thanks for the reply, but neither code works.

2023-12-07_151457.thumb.png.13b0cfb40db0856f5eda765c4965cd81.png

2023-12-07_151653.thumb.png.529af06647f23f95e16dbec4dcfb4447.png

Link to comment
Share on other sites

  • Solution

The code works but it's not unicode.

#include <Memory.au3>
#include <Clipboard.au3>
#include <WinAPIConv.au3>

$sFilePath = '<path to your file'>
_ClipPutFile($sFilePath)

Func _ClipPutFile($sFile)
    Local $iLen =  StringLen($sFile)
    _ClipBoard_Open(0)
    _ClipBoard_Empty()
    Local $hGlobal = _MemGlobalAlloc(($iLen + 2) * 2 + 20, $GMEM_MOVEABLE)
    Local $hLock = _MemGlobalLock($hGlobal)
    $tDROPFILES = DllStructCreate("dword pFiles;long x;long y;int fNC;int fWide;wchar File[" & $iLen & "];wchar Null[2]", $hLock)
    $tDROPFILES.pFiles = DllStructGetPtr($tDROPFILES, 'File') - DllStructGetPtr($tDROPFILES, 'pFiles')
    $tDROPFILES.x = 0
    $tDROPFILES.y = 0
    $tDROPFILES.fNC = 0
    $tDROPFILES.fWide = 1
    _WinAPI_MultiByteToWideCharEx($sFile, DllStructGetPtr($tDROPFILES, 'File'))
    $tDROPFILES.Null = Chr(0) & Chr(0)
    _ClipBoard_SetDataEx($hGlobal, $CF_HDROP)
    _MemGlobalUnlock($hGlobal)
    _ClipBoard_Close()
    _MemGlobalFree($hGlobal)
EndFunc

 

Edited by Andreik
Link to comment
Share on other sites

You should set .pFiles to 20, instead of the calculation (in the second script of @Andreik), because of alignment it may be shifted a few bytes.

Now, you should try to debug yourself the script he gave to you.  Seems that you are passing a file name longer than expected.  Put some ConsoleWrite to see value of the variables and the return of functions along with @error, so you can determine what is going on.  Instead of saying it is not working...

Link to comment
Share on other sites

I think it's wrong to free the Mem, as it will be handled by the OS.

Here's a snippet I use in SMF.

#include <ClipBoard.au3>
#include <WinAPISysWin.au3>
Global Const $DROPFILES = "DWORD pFiles; int pt[2]; int fNC; int fWide;"
Global Const $CFSTR_PREFERREDDROPEFFECT = "Preferred DropEffect"
Global Const $CF_PREFERREDDROPEFFECT = _ClipBoard_RegisterFormat($CFSTR_PREFERREDDROPEFFECT)
Global Const $DROPEFFECT_COPY = 1
Global Const $DROPEFFECT_MOVE = 2


_Clipboard_Copy_File(@ScriptFullPath)

; _Clipboard_Move_File(@ScriptFullPath)


Func _Clipboard_Copy_File($sFile)
    Local $hClip = _ClipBoard_Open(_WinAPI_GetDesktopWindow())
    If $hClip Then
        _ClipBoard_Empty()
        Local $HDROP = _CreateDROPFILES($sFile) ; Mem must NOT be freed via_MemGlobalFree(), as script will crash or fail. Mem will be handled by OS.
        Local $DROPEFFECT = _CreatePREFERREDDROPEFFECT($DROPEFFECT_COPY) ; Mem must NOT be freed via_MemGlobalFree(), as script will crash or fail. Mem will be handled by OS.
        _ClipBoard_SetDataEx($HDROP, $CF_HDROP)
        _ClipBoard_SetDataEx($DROPEFFECT, $CF_PREFERREDDROPEFFECT)
        _ClipBoard_Close()
    EndIf
EndFunc   ;==>_Clipboard_Copy_File

Func _Clipboard_Move_File($sFile)
    Local $hClip = _ClipBoard_Open(_WinAPI_GetDesktopWindow())
    If $hClip Then
        _ClipBoard_Empty()
        Local $HDROP = _CreateDROPFILES($sFile) ; Mem must NOT be freed via_MemGlobalFree(), as script will crash or fail. Mem will be handled by OS.
        Local $DROPEFFECT = _CreatePREFERREDDROPEFFECT($DROPEFFECT_MOVE) ; Mem must NOT be freed via_MemGlobalFree(), as script will crash or fail. Mem will be handled by OS.
        _ClipBoard_SetDataEx($HDROP, $CF_HDROP)
        _ClipBoard_SetDataEx($DROPEFFECT, $CF_PREFERREDDROPEFFECT)
        _ClipBoard_Close()
    EndIf
EndFunc   ;==>_Clipboard_Move_File





;===============================================================================
;
; Function Name:   _CreateDROPFILES
; Description::    Creates a DROPFILES-structure
; Parameter(s):    $Files - Pipe-separated list of files to copy: example: C:\File1.bin|D:\AnotherFile.dat
; Requirement(s):  COM/OLE UDF
; Return Value(s): HGLOBAL-Pointer to a DROPFILES structure
; Author(s):       prog@ndy
;
;===============================================================================
; http://www.autoitscript.com/forum/index.php?showtopic=91046&view=findpost&p=654849
; http://www.autoit.de/index.php?page=Thread&postID=81909&highlight=hdrop#post81909

Func _CreateDROPFILES($Files)
    $Files = String($Files)
    Local $hMem = _MemGlobalAlloc(_DragDrop_SIZEOF($DROPFILES) + ((StringLen($Files) + 2) * 2), $GPTR)
    $Files = StringSplit($Files, "|")
    Local $stDROPFILES = DllStructCreate($DROPFILES, $hMem)
    Local $hPtr = $hMem + DllStructGetSize($stDROPFILES)
    DllStructSetData($stDROPFILES, "fWide", 1)
    DllStructSetData($stDROPFILES, 1, DllStructGetSize($stDROPFILES))
    For $i = 1 To $Files[0]
        $next = DllStructCreate("wchar[" & StringLen($Files[$i]) + 1 & "]", $hPtr)
        DllStructSetData($next, 1, $Files[$i] & ChrW(0))
        $hPtr += (StringLen($Files[$i]) + 1) * 2
    Next
    Local $next = DllStructCreate("wchar[1]", $hPtr)
    DllStructSetData($next, 1, ChrW(0))
    Return $hMem
EndFunc   ;==>_CreateDROPFILES

; Prog@ndy
Func _CreatePREFERREDDROPEFFECT($dwEffect)
    Local $pMem = _MemGlobalAlloc(4, $GPTR) ; 4 bytes - 1 DWORD
    If Not $pMem Then Return SetError(1, 0, 0)
    DllStructSetData(DllStructCreate("DWORD", $pMem), 1, $dwEffect)
    Return $pMem
EndFunc   ;==>_CreatePREFERREDDROPEFFECT

; Author: Prog@ndy
Func _DragDrop_SIZEOF($tagStruct)
    Return DllStructGetSize(DllStructCreate($tagStruct, 1))
EndFunc   ;==>_DragDrop_SIZEOF

 

Link to comment
Share on other sites

@KaFu That's true. I read now on MSDN:

Quote

If SetClipboardData succeeds, the system owns the object identified by the hMem parameter. The application may not write to or free the data once ownership has been transferred to the system, but it can lock and read from the data until the CloseClipboard function is called. (The memory must be unlocked before the Clipboard is closed.) If the hMem parameter identifies a memory object, the object must have been allocated using the function with the GMEM_MOVEABLE flag.

So it should be something like this:

#include <Memory.au3>
#include <Clipboard.au3>
#include <WinAPIConv.au3>

$sFilePath = '<path to your file'>
_ClipPutFile($sFilePath)

Func _ClipPutFile($sFile)
    Local $iLen =  StringLen($sFile)
    _ClipBoard_Open(0)
    _ClipBoard_Empty()
    Local $hGlobal = _MemGlobalAlloc(($iLen + 2) * 2 + 20, $GMEM_MOVEABLE)
    Local $hLock = _MemGlobalLock($hGlobal)
    $tDROPFILES = DllStructCreate("dword pFiles;long x;long y;int fNC;int fWide;wchar File[" & $iLen & "];wchar Null[2]", $hLock)
    $tDROPFILES.pFiles = DllStructGetPtr($tDROPFILES, 'File') - DllStructGetPtr($tDROPFILES, 'pFiles')
    $tDROPFILES.x = 0
    $tDROPFILES.y = 0
    $tDROPFILES.fNC = 0
    $tDROPFILES.fWide = 1
    _WinAPI_MultiByteToWideCharEx($sFile, DllStructGetPtr($tDROPFILES, 'File'))
    $tDROPFILES.Null = Chr(0) & Chr(0)
    _ClipBoard_SetDataEx($hGlobal, $CF_HDROP)
    _MemGlobalUnlock($hGlobal)
    _ClipBoard_Close()
EndFunc

 

Link to comment
Share on other sites

Hello,

use _ClipPutFile()

 

If you place multiple FullFilePathNames, separated by "|" into one string, you can place them all-in-one to the clipboard, regardless of the folders these files are stored in. In your screenshot you already used this function, I didn't get, why you are not happy with that one?

 

I used it in several "Collect files across many differnt folders" tools, no problems.

 

$put2clip="C:\temp\File-1.txt|D:\OtherDrive\anotherfolder\file-2.txt|\\server\share\path\file-3.txt"

_ClipPutFile($put2clip)

 

Edited by rudi

Earth is flat, pigs can fly, and Nuclear Power is SAFE!

Link to comment
Share on other sites

17 hours ago, rudi said:

In your screenshot you already used this function, I didn't get, why you are not happy with that one?

Awesome, thank you.

I wasn't aware of this function in standard udf before. The _ClipPutFile function you see was written by kind-hearted Andreik

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...