Ce code VB.Net permet d'accéder aux données du presse-papiers de Windows, sur la partie fichiers, pour stocker des fichiers à couper ou à copier et pour récupérer (coller) les fichiers stockés dans le presse-papiers, en gérant la copie ou le déplacement.
La fonction isFichiersPresents indique si des fichiers ont été copiés dans le presse-papiers, afin d'activer ou non un éventuel menu "Coller".
' Couper/copier l'objet fichier dans le presse-papier Windows
Public Sub couperCopierFichiersVersPressePapiers(ByVal bCouper As Boolean, ByVal listeFichiers() As String)
Clipboard.Clear()
Dim doFichiers As New DataObject(DataFormats.FileDrop, listeFichiers)
doFichiers.SetData("Preferred Dropeffect", New MemoryStream(BitConverter.GetBytes(CInt(IIf(bCouper, DragDropEffects.Move, DragDropEffects.Copy)))))
Clipboard.SetDataObject(doFichiers, False)
End Sub
' Indique si un fichier a été coupé/copié dans le presse-papiers
Public Function isFichiersPresents() As Boolean
Dim data As IDataObject = Clipboard.GetDataObject
isFichierEnMemoire = data.GetDataPresent(DataFormats.FileDrop)
End Function
' Couper/copier l'objet fichier dans le presse-papier Windows
Public Sub collerFichiersDepuisPressePapiers(ByVal sRepDesti As String)
Dim data As IDataObject = Clipboard.GetDataObject
If Not isFichiersPresents() Then Exit Sub
' Récupère l'effet copier ou couper
Dim stream As MemoryStream = CType(data.GetData("Preferred DropEffect", True), MemoryStream)
Dim iVal As Integer = stream.ReadByte()
If iVal <> 2 AndAlso iVal <> 5 Then Exit Sub
Dim bCouper As Boolean = iVal = 2
' Parcours les fichiers en mémoire
For Each sNomFichier As String In data.GetData(DataFormats.FileDrop)
Dim sNouveauNom As String = Path.Combine(sRepDesti, Path.GetFileName(sNomFichier))
If bCouper Then
File.Move(sNomFichier, sNouveauNom)
Else
File.Copy(sNomFichier, sNouveauNom)
End If
Next
End Sub
Cas des fichiers en bureau à distance
Si le fichier source ou en destination ne provient pas de la même machine, le presse-papiers fonctionne différemment :
- Le presse-papiers du fichier de la machine source contient un lien vers le fichier
- Le presse-papiers de la machine destination contient une structure indiquant un flux de fichier (Stream)
Voici une fonction permettant d'identifier que le fichier à coller provient d'un bureau à distance : le presse-papiers contient l'objet FileGroupDescriptorW :
' Indique si un fichier en RDP a été copié ou coupé dans le presse-papiers (depuis un bureau à distance)
Public Function isFichierRDPEnMemoire() As Boolean
isFichierRDPEnMemoire = Clipboard.GetDataObject.GetDataPresent("FileGroupDescriptorW")
End Function
Il est possible de récupérer la structure des fichiers en mémoire, en utilisant une structure FILEDESCRIPTOR. Cependant, il n'est pas toujours nécessaire de la connaître : le code-ci dessous va permettre de coller le contenu du presse-papiers en faisant intervenir la fonction Coller de Windows. Il ouvre tout d'abord un explorateur sur le chemin cible, appelle la commande Control+V, puis referme l'explorateur une fois la commande terminée.
Malheureusement, il est nécessaire de garder cet explorateur visible : l'envoi de certaines touches (Control, ALT...) ne fonctionne que vers l'application active ouverte avec Sendkeys.
Private Declare Auto Function GetWindowThreadProcessId Lib "user32.dll" (ByVal hwnd As IntPtr, ByRef lpdwProcessId As Integer)As Integer
Private Declare Function GetForegroundWindow Lib "user32.dll" () As IntPtr
' Ouvre un explorateur, colle les fichiers et referme l'explorateur
Public Sub collerAvecExplorateur(ByVal sRepDesti As String)
Dim myProg As New Process
With myProg.StartInfo
.FileName = "explorer.exe"
.WindowStyle = ProcessWindowStyle.Normal
.Arguments = "/separate," & sRepDesti
.CreateNoWindow = False
.UseShellExecute = True
End With
Dim hwndAppliActuelle As System.IntPtr = GetForegroundWindow()
If myProg.Start() Then
' Explorer ouvre un autre processus que le sien, on ne peut donc pas s'appuyer sur l'ID récupéré.
' On va retrouver l'ID à partir du changement de fenêtre active
While GetForegroundWindow() = hwndAppliActuelle
Application.DoEvents()
End While
Dim hwndFenetreExplorer As System.IntPtr = GetForegroundWindow()
' Récupère l'id du process, pour vérifier s'il est bien fermé
Dim iProcessId As Integer
GetWindowThreadProcessId(hwndFenetreExplorer, iProcessId)
Thread.Sleep(200) ' Attend la visibilité de la fenêtre
' Aucune attente malgré le sendwait
' L'utilisation de %{F4} ne ferme pas toujours la fenêtre (si le fichier est volumineux par exemple)
System.Windows.Forms.SendKeys.SendWait("^v") ' CONTROL + V
Thread.Sleep(300)
' Ferme la fenêtre Explorer ouverte
Try
Dim listProcess() As Process = System.Diagnostics.Process.GetProcesses()
For Each proc As Process In listProcess
If (proc.Id = iProcessId) Then
' Demande de fermeture du process Explorer
proc.WaitForInputIdle()
proc.CloseMainWindow()
Exit For
End If
Next proc
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End If
End Sub