Laden...

Erfassen, welches Object sich gerade im Drag und Drop befindet außerhalb der Form

Erstellt von 123thomas vor 5 Jahren Letzter Beitrag vor 5 Jahren 3.454 Views
1
123thomas Themenstarter:in
124 Beiträge seit 2012
vor 5 Jahren
Erfassen, welches Object sich gerade im Drag und Drop befindet außerhalb der Form

Hallo zusammen,

ich bin gerade dabei ein Progamm zuschreiben mit Drag ans Drop. Das Windows Forms Programm läuft im Hintergrund (notifyIcon). Wenn nun irgendwo (HookCallback der user32.dll) die Maus länger als 2 Sekunden betätigt wird öffnet sich die Form und der Anwender kann das Objekt in die Form ziehen und dort "droppen".

Das Problem/Störende an dem Programm ist, dass auch bei anderen Maus Aktionen wie z.B. das Scrollen mit der linken Maus Taste oder das markieren eines Textes sich die Form öffnet.

Gibt es eine Möglichkeit zu prüfen, ob die Maus aktuell ein Object/Datei bewegt außerhalb der Form? (Drag)

Gruß
Thomas

3.170 Beiträge seit 2006
vor 5 Jahren

Hallo,

eine Lösung habe ich jetzt nicht für Dich.
Aber Du solltest vielleicht bedenken:

Wenn nun irgendwo (HookCallback der user32.dll) die Maus länger als 2 Sekunden betätigt wird öffnet sich die Form und der Anwender kann das Objekt in die Form ziehen und dort "droppen"

Ist das eine Anforderung, oder eine Idee von Dir?
Ich würde eher den User das Fenster vorher selbst öffnen lassen - ich würde mich jedenfalls herzlich bedanken, wenn ich ein Objekt irgendwo anders hin draggen will, dann plötzlich eine Form aufploppt und sich über mein eigentliches Zielfenster legt...

Gruß, MarsStein

Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca

G
74 Beiträge seit 2018
vor 5 Jahren

ich hab auch keine Lösung, habe mich mit Mausbedienung in C# noch nicht beschäftigt.

Aber aus alten Actionscriptzeiten, hatten wir hier einige Implementationen die vielleicht dir helfen können.

In flash gab es eine "MouseDown" Funktion, die z.bsp. einen Timer triggern könnte, um dann besagte Form zu öffnen, um dort droppen zu können.

Da die Dragfunktion in flash nur möglich war, wenn man einen bestimmten ausgewählten bereich (dragbereich) mit MouseDown berührte, könntest du ja deine 2 Sekunden abfrage damit koppeln, und sogar evtl, abfragen, was das gerade für ein "gedraggtes" objekt ist.

Wenn das natürlich (und leider kann ich nicht einschätzen, ob man das überhaupt abfragen kann) möglich ist, könntest du ja z.bsp. klären, ob du gerade einen "Scrollbalken" draggst, und in dem fall nichts machen.

In diesem fall, müßte, das so gehen wie du dir das vorstellst.

1
123thomas Themenstarter:in
124 Beiträge seit 2012
vor 5 Jahren

Hallo,

@MarsStein
Das ist eine Anforderung. Das ist natürlich kein großes Fenster, sondern unten rechts am Bildschirmrand und ganz klein (150 x 100) ohne Form Border und Transparent. Es geht darum, dass das Fenster nicht immer manuell geöffnet werden muss.

@Glowhollow
Also das Drag soll in dem kompletten Bildschirmbereich möglich sein, das kann ich nicht Einschränken. Und genau das ist meine Frage.

Wie ich Abfragen kann, was das für ein "gedraggtes" Objekt ist.

Gruß

C
1.214 Beiträge seit 2006
vor 5 Jahren

Du musst wohl die DoDragDrop Funktion hooken. Wenn ich das richtig sehe, ist es nicht direkt vorgesehen, müsste man also mit den üblichen WinApi Hooking Methoden machen.

709 Beiträge seit 2008
vor 5 Jahren

Das erinnert mich irgendwie an Yoink für den Mac.

1
123thomas Themenstarter:in
124 Beiträge seit 2012
vor 5 Jahren

Hallo zusammen,

danke für die Antworten.

@Coder007
Kannst du mir einen kleinen Tipp geben mit welcher dll und Funktion ich dies machen muss. Ich habe nichts brauchbares gefunden.

@pinki
Die Funktionen könnte man auch noch einbauen. =)

Gruß

1
123thomas Themenstarter:in
124 Beiträge seit 2012
vor 5 Jahren

Hallo,

@Th69
Danke für Links, Hooks sind mir bekannt und wende ich bereits erfolgreich an. Mein Problem liegt darin, dass ich keinen Hook finde, der das "Drag" angiebt. (Drop gibt es, aber dann ist es für mich schon zu spät.) Da ich schon beim Ziehen der Datei herrausfinden möchte, ob die Datei im "Drag" für das Programm in Frage kommt.
In den Links konnte ich dazu auch nichts finden. Oder habe ich etwas übersehen?

Gruß

4.931 Beiträge seit 2008
vor 5 Jahren

Nein, sorry, hatte nicht gesehen, daß du ja schon in deinem Eingangsbeitrag "Hooking" erwähnt hast.

Ich habe nur How to hook drag and drop functionality of windows dazu gefunden, weiß aber nicht, ob dir dies (bes. die letzte Antwort) weiterhilft.
Und zum Thema IAT-Hooking ("Import Adress Table") gibt es ein C++-Projekt: IAT-Hooking-Revisited (ich weiß aber nicht, inwiefern sich IAT-Hooking vom normalen Hooking unterscheidet).

Edit: OK, in API hooking revealed wird unter "2e Spying by altering of the Import Address Table" dieses näher erklärt.

C
1.214 Beiträge seit 2006
vor 5 Jahren

Ich meinte die Funktion hier:

DoDragDrop function

1
123thomas Themenstarter:in
124 Beiträge seit 2012
vor 5 Jahren

Hallo zusammen,

ich habe die letzten Wochen alle Seiten zu Drag & Drop studiert, doch leider immer noch keine Lösung für mein Problem gefunden. Daher wende ich mich nochmal an euch.

Das Registrieren des RegisterDragDrop Verweis funktioniert für den aktuellen Prozess, Wenn ich aber nun das Handle des Explorers nehme, dann wird das Drag Drop Event nicht ausgelöst.

Des Weiteren habe ich es noch über die Nuget von EasyHook versucht, leider auch ohne Erfolg. Das Event wird nie ausgelöst.

Ich habe ein Probeprojekt angehangen.(Anhängen ging nicht. eventuell wegen der Größe? Es sind Fehler beim Hochladen aufgetreten. Bitte überprüfe Deine Eingaben.)
<dropbox link entfernt>

Beschreibung:
Aktuell wird das RegisterDragDrop auf das Handle der aktuellen Form angewendet. Wird nun das Projekt ausgeführt wird auch das Event OleDragEnter in der Klasse MyDropTarget ausgelöst. Allerdings möchte ich dies gerne entweder für alle Prozesse oder mindestens für den Explorer.

Für Dokus oder Denkanstöße bin ich Dankbar

Gruß
Thomas

16.807 Beiträge seit 2008
vor 5 Jahren

Bitte keine Projekt auf DropBox verlinken.
Wenn das notwendig ist, dann verwende den Forenanhang
[Hinweis] Wie poste ich richtig?

1
123thomas Themenstarter:in
124 Beiträge seit 2012
vor 5 Jahren

Ich kenne die Regel, nur leider kann ich das nicht anhängen meine Zip wird nicht genommen.
Siehe mein Post.

Wer das Projekt haben möchte soll sich dann bei mir melden.

Form1.cs

using EasyHook;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Threading;
using System.Windows.Forms;
using IDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;

namespace DoDragDrop
{
    public partial class Form1 : Form, IEntryPoint
    {
        public LocalHook dragDropHook;

        public Form1()
        {
            InitializeComponent();

            panel1.AllowDrop = true;
            label1.Text = "";
            TopMost = true;

            //Maus Hook zumerfassen der linken Maus Taste
            MouseHook.MouseActionLeftDown += MouseHook_MouseActionLeftDown;
            MouseHook.Start();

            //Mit der hWnd des Explorers funktioniert das RegisterDragDrop, aber das Event in der MyDropTarget Klasse wird nicht ausgelöst
            //Explorer Prozess holen
            //Process[] processes = Process.GetProcesses();
            //IntPtr hWnd = this.Handle;
            //foreach (var process in processes)
            //{
            //    if (process.ProcessName.Equals("explorer"))
            //    {
            //        hWnd = process.Handle;
            //    }
            //}

            //Aktuelles Fenster holen
            IntPtr hWnd = this.Handle;

            //Prüfen ob es auch ein Fenster ist
            bool iswindow = IsWindow(hWnd);

            //DragDrop registieren
            HRESULT_TYPEDEF hRESULTRegister = new HRESULT_TYPEDEF(RegisterDragDrop(hWnd, new MyDropTarget()));
            label1.Text += "RegisterDragDrop = " + iswindow + "|" + hRESULTRegister.GetName() + Environment.NewLine;

            //Revoke meldet auch Okay nachdem RegisterDragDrop ausgeführt wurde. Wird erst der Revoke ausgeführt meldet er korrekter Weise, dass noch kein DragDrop registiert ist.
            //HRESULT_TYPEDEF hRESULTRevoke = new HRESULT_TYPEDEF(RevokeDragDrop(hWnd));
            //label1.Text += "RevokeDragDrop = " + iswindow + "|" + hRESULTRevoke.GetName() + Environment.NewLine;


            try
            {
                //DoDragDrop "anmelden"
                dragDropHook = LocalHook.Create(LocalHook.GetProcAddress("Ole32.dll", "DoDragDrop"), new DragDropDelegate(DoDragDropHook), this);

                // Alle Processe "hooken"
                dragDropHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }
        }
                

        private void MouseHook_MouseActionLeftDown(object sender, EventArgs e)
        {
            label1.Text += "Maus links Klick" + Environment.NewLine;
        }

        private void panel1_DragEnter(object sender, DragEventArgs e)
        {
            label1.Text += "Drag Enter - Form 1" + Environment.NewLine;
            e.Effect = DragDropEffects.All;
        }
        private void panel1_DragDrop(object sender, DragEventArgs e)
        {
            label1.Text += "DragDrop - Form 1" + Environment.NewLine;
        }

                   
        //DLL's laden bzw. Verweis erstellen
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool IsWindow(IntPtr hWnd);

        [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern int RegisterDragDrop(IntPtr hwnd, IOleDropTarget target);

        [DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern int RevokeDragDrop(IntPtr hwnd);

        [DllImport("Ole32.dll", CharSet = CharSet.Unicode, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
        static extern int DoDragDrop(IDataObject pDataObj, IDropSource pDropSource, uint dwOKEffects, uint[] pdwEffect);

        [UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi, SetLastError = true)]
        delegate int DragDropDelegate(IDataObject pDataObj, IDropSource pDropSource, uint dwOKEffects, uint[] pdwEffect);

        [ComImport, Guid("00000121-0000-0000-C000-000000000046"),
        InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        public interface IDropSource
        {
            [PreserveSig]
            uint QueryContinueDrag([MarshalAs(UnmanagedType.Bool)] bool fEscapePressed, uint grfKeyState);

            [PreserveSig]
            uint GiveFeedback(uint dwEffect);
        }


        //DoDragDrop Event
        int DoDragDropHook(IDataObject pDataObj, IDropSource pDropSource, uint dwOKEffects, uint[] pdwEffect)
        {
            //Der BreakPoint wird nie erreicht

            MessageBox.Show("DoDragDropHook");

            //Format abfragen, wenn was kommen würde
            FORMATETC format = new FORMATETC();
            format.cfFormat = (short)DataFormats.GetFormat("FileContents").Id;
            format.dwAspect = DVASPECT.DVASPECT_CONTENT;
            format.lindex = 0;
            format.ptd = new IntPtr(0);
            format.tymed = TYMED.TYMED_HGLOBAL | TYMED.TYMED_ISTREAM | TYMED.TYMED_ISTORAGE;
            STGMEDIUM medium = new STGMEDIUM();
            pDataObj.GetData(ref format, out medium);
            pdwEffect = new uint[] { 0, 0 };
            return 0;
        }
       
    }

    //Interface für die Ereignisse
    [ComImport, Guid("00000122-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IOleDropTarget
    {
        [PreserveSig]
        int OleDragEnter([In, MarshalAs(UnmanagedType.Interface)] object pDataObj, [In, MarshalAs(UnmanagedType.U4)] int grfKeyState, [In, MarshalAs(UnmanagedType.U8)] long pt, [In, Out] ref int pdwEffect);
        [PreserveSig]
        int OleDragOver([In, MarshalAs(UnmanagedType.U4)] int grfKeyState, [In, MarshalAs(UnmanagedType.U8)] long pt, [In, Out] ref int pdwEffect);
        [PreserveSig]
        int OleDragLeave();
        [PreserveSig]
        int OleDrop([In, MarshalAs(UnmanagedType.Interface)] object pDataObj, [In, MarshalAs(UnmanagedType.U4)] int grfKeyState, [In, MarshalAs(UnmanagedType.U8)] long pt, [In, Out] ref int pdwEffect);
    }

    //Klasse die die Ereignisse des Interfaces aussführen sollte
    internal class MyDropTarget : IOleDropTarget
    {

        public int OleDragEnter(object pDataObj, int grfKeyState, long pt, ref int pdwEffect)
        {
            throw new NotImplementedException();
        }

        public int OleDragOver(int grfKeyState, long pt, ref int pdwEffect)
        {
            throw new NotImplementedException();
        }

        public int OleDragLeave()
        {
            throw new NotImplementedException();
        }

        public int OleDrop(object pDataObj, int grfKeyState, long pt, ref int pdwEffect)
        {
            throw new NotImplementedException();
        }
    }

    //Klasse für die Statis des Register und RevokeDragDrop
    public class HRESULT_TYPEDEF
    {
        private int value = 1;
        public HRESULT_TYPEDEF(int value)
        {
            this.value = value;
        }

        public HRESULT_TYPEDEF(string name)
        {
            this.value = GetValue(name);
        }

        public const int DRAGDROP_E_FIRST = -2147221248;
        public const int DRAGDROP_E_NOTREGISTERED = -2147221248;
        public const int DRAGDROP_E_ALREADYREGISTERED = -2147221247;
        public const int DRAGDROP_E_INVALIDHWND = -2147221246;
        public const int DRAGDROP_E_LAST = -2147221233;

        public const int NOERROR = 0;
        public const int S_OK = 0;
        public const int SEC_E_OK = 0;
        public const int S_FALSE = 1;

        public static int GetValue(string name)
        {
            switch (name)
            {
                case "DRAGDROP_E_FIRST":
                    return DRAGDROP_E_FIRST;
                case "DRAGDROP_E_NOTREGISTERED":
                    return DRAGDROP_E_NOTREGISTERED;
                case "DRAGDROP_E_ALREADYREGISTERED":
                    return DRAGDROP_E_ALREADYREGISTERED;
                case "DRAGDROP_E_INVALIDHWND":
                    return DRAGDROP_E_INVALIDHWND;
                case "DRAGDROP_E_LAST":
                    return DRAGDROP_E_LAST;
                case "S_FALSE":
                    return S_FALSE;
                case "S_OK":
                    return S_OK;
                default:
                    return 1;
            }
        }
        public static string GetName(int value)
        {
            switch (value)
            {
                case DRAGDROP_E_NOTREGISTERED:
                    return "DRAGDROP_E_NOTREGISTERED";
                case DRAGDROP_E_ALREADYREGISTERED:
                    return "DRAGDROP_E_ALREADYREGISTERED";
                case DRAGDROP_E_INVALIDHWND:
                    return "DRAGDROP_E_INVALIDHWND";
                case DRAGDROP_E_LAST:
                    return "DRAGDROP_E_LAST";
                case S_OK:
                    return "S_OK";
                case S_FALSE:
                    return "S_FALSE";
                default:
                    return "S_FALSE";
            }
        }

        public int GetValue()
        { return this.value; }

        public string GetName()
        { return GetName(this.value); }
    }

}


P
5 Beiträge seit 2018
vor 5 Jahren

Hallo 123thomas,

wir hatten ja bereits kurz geschrieben um die Anforderungen noch einmal deutlich zu machen.
Die Applikation soll im Hintergrund laufen und unter folgenden Bedingungen in
den Vordergrund kommen:

  1. Applikation ist im Systray
  2. Linke Maustaste muss X Sekunden gedrückt sein
  3. Es wurde ein Ordner / Datei geklickt
  4. Drag Event wurde ausgelöst

Die 1. Bedingung ist natürlich einfach umzusetzen und Bedarf keine weitere Erklärung.

Für die 2. Bedingung benötigen wir einen globalen Mouse Hook um den Linken Mausklick abzufangen.
In .NET gibt es ingesamt nur 2 Globale Hooks die wir anwenden können. (Mouse und Keyboard)
Siehe dazu "How to set a windows hook in c# .net"

Das ganze ergänzen wir mit Timer/Stopwatch und können so die Zeit abfangen.

Als nächstes kommt die Überprüfung ob eine Datei / Ordner angeklickt wurde.
Stackoverflow hatte zum Glück bereits eine entsprechende Methode parat.
how-to-get-windows-explorers-selected-files-from-within-c


        private bool IsFileOrFolderSelected()
        {
            IntPtr handle = User32Interop.GetForegroundWindow();

            var shell = new Shell32.Shell();
            foreach (SHDocVw.InternetExplorer window in shell.Windows())
            {
                if (window.HWND == (int)handle)
                {
                    Shell32.FolderItems items = ((Shell32.IShellFolderViewDual2)window.Document).SelectedItems();
                    if (items.Count > 0)
                        return true;
                }
            }

            return false;
        }

Durch diese Methode wird dann z.B. auch der Scrollbalken ignoriert.

Kommen wir nun zum letzten und schwierigsten Punkt. Das Drag Event abfangen.
Das Drag Event wird im Explorer nicht über WM_* Messages umgesetzt. Aus diesem
Grund ist es auch nicht möglich das ganze über SetWindowHookEx umzusetzen.

Der Explorer benutzt intern die uralte OLE Schnittstelle. Um diese Schnittstelle
zu hooken benötigen wir IAT Hooking (Siehe Links von Th69) oder Detouring.
EasyHook bietet hier zum Glück alles was wir brauchen.

Dein letztes Beispiel hat aus folgenden Grund nicht funktioniert:
Du setzt den lokalen Hook nur für deine Applikation. Wir benötigen aber einen Remote Hook,
nämlich im Explorer. Da der Code den Rahmen sprengen würde, habe ich im Anhang mein Projekt
angehängt.

Paar offene Punkte:

Das ganze ist von mir nur eine Quick&Dirty Solution. Bitte achte bei der Verwendung in Produktion
auf das Freigeben sämtlicher Ressourcen. Die explorer.exe PID wird im Code gesetzt. (Siehe Form1.cs Funktion InjectIntoExplorer).
Die Solution muss unter x64 gebaut werden und über NuGet Restore die EasyHook Dll's/exe nachgeladen werden.

Cheers 😃

1
123thomas Themenstarter:in
124 Beiträge seit 2012
vor 5 Jahren

Hallo,

erstmal Vielen Dank für deine Bemühungen und entschuldige das ich jetzt erst an dem Projekt weiter gearbeitet habe.

Ich hab mir dein Beispiel Projekt angesehen. Und die Einstellungen eingestellt. Also auf x64 konfiguriert. Die PID des Explorers erst manuell eingestellt und dann automatisch gesucht. Und NuGet Paket geladen.

Aber in der Zeile:

EasyHook.RemoteHooking.Inject(targetPID, "", injectionLibrary, channelName);

Bekomme ich die Fehlermeldung:> Fehlermeldung:

System.ApplicationException
HResult=0x80131600
Nachricht = STATUS_INTERNAL_ERROR: Unknown error in injected C++ completion routine. (Code: 15)
Quelle = EasyHook
Stapelüberwachung:
at EasyHook.NativeAPI.Force(Int32 InErrorCode)
at EasyHook.RemoteHooking.InjectEx(Int32 InHostPID, Int32 InTargetPID, Int32 InWakeUpTID, Int32 InNativeOptions, String InLibraryPath_x86, String InLibraryPath_x64, Boolean InCanBypassWOW64, Boolean InCanCreateService, Boolean InRequireStrongName, Object[] InPassThruArgs)
at EasyHook.RemoteHooking.Inject(Int32 InTargetPID, String InLibraryPath_x86, String InLibraryPath_x64, Object[] InPassThruArgs)
at GlobalDragHook.Ui.Form1.InjectIntoExplorer() in C:\Users\meyerdt\Downloads\GlobalDragHook\GlobalDragHook\GlobalDragHook.Ui\Form1.cs:line 79
at GlobalDragHook.Ui.Form1..ctor() in C:\Users\meyerdt\Downloads\GlobalDragHook\GlobalDragHook\GlobalDragHook.Ui\Form1.cs:line 24
at GlobalDragHook.Ui.Program.Main() in C:\Users\meyerdt\Downloads\GlobalDragHook\GlobalDragHook\GlobalDragHook.Ui\Program.cs:line 19

Ich habe schon bei den Issus von EasyHook geschaut, allerdings keine Änderung gefunden die noch nicht ind dem Beispiel Projekt umgesetzt war.

Gruß Thomas

P
5 Beiträge seit 2018
vor 5 Jahren

Hallo Thomas,

ich habe mir das Projekt gerade nochmal auf Arbeit geladen und ausprobiert. Hier funktioniert es ebenfalls. Sowohl auf Arbeit als auch Privat ist Windows 10 im Einsatz. Folgende Möglichkeiten fallen mir auf Anhieb ein.

  • Antiviren Programm könnte es blockieren
  • Programm als Admin starten
  • EasyHook selber durchkompilieren
  • sicherstellen das alle benötigten EasyHook DLL's/Exe vorhanden sind
    (EasyHook64.dll, EasyHook64Svc.exe, EasyLoad64.dll)

Edit: In den Github Issues wird der Fehler 15 beschrieben

code 15 means it was unable to install the EasyLoad32/64.dll and then call the Loader.Load export for some reason. Usually this is because the target is incompatible with the .NET version you are using (i.e. try .NET 3.5 instead of .NET 4 or vice versa).

oder

There is another situation where Code 15 occurs, which is if the AppDomain.CreateDomain fails within EasyLoad32/64.dll.

I have found that AppDomain.CreateDomain will fail with an OutOfMemoryException when the executable stack commit size is larger than or equal to approximately 0x40000 (on my machine anyway). This can occur because native linking allows the developer to specify the stack reserve and commit size.

Link zum Issue: Injecting assembly into application using EasyHook

1
123thomas Themenstarter:in
124 Beiträge seit 2012
vor 5 Jahren

Hallo,
ich weiß noch nicht ganz genau was es ist aber:

VS 2017 Fehler des Programms wie oben genannt
VS 2013 Funktioniert

Obwohl ich in den Projekteinstellungen das selbe .Net Framework eingestellt habe.

Danke 😃