IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Framework .NET Discussion :

CallbackOnCollectedDelegate a été détecté


Sujet :

Framework .NET

  1. #1
    Invité
    Invité(e)
    Par défaut CallbackOnCollectedDelegate a été détecté
    Bonjour à tous !

    J'ai trouvé ici un KeyLogger fait avec Windows Form et j'ai voulu le transposer pour WPF. J'ai donc simplement conservé le fichier globalKeyboardHook.cs, et j'ai créé le fichier MainWindow.xaml qui est très simple :

    Code XAML : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    <Window x:Class="KeyLogger.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid>
            <ListView x:Name="events"></ListView>
        </Grid>
    </Window>
    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    using System.Windows;
     
    namespace KeyLogger
    {
        public partial class MainWindow : Window
        {
            globalKeyboardHook gkh = new globalKeyboardHook();
     
            public MainWindow()
            {
                InitializeComponent();
     
                gkh.HookedKeys.Add(System.Windows.Forms.Keys.A);
                gkh.HookedKeys.Add(System.Windows.Forms.Keys.B);
     
                gkh.KeyDown += new System.Windows.Forms.KeyEventHandler(gkh_KeyDown);
            }
     
     
            void gkh_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
            {
                events.Items.Add("Key: " + e.KeyCode);
            }
        }
    }

    Pour être tout à fait complet je remets le code de globalKeyboardHook.cs :

    Code C# : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
     
    namespace KeyLogger
    {
        /// <summary>
        /// A class that manages a global low level keyboard hook
        /// </summary>
        class globalKeyboardHook
        {
            #region Constant, Structure and Delegate Definitions
            /// <summary>
            /// defines the callback type for the hook
            /// </summary>
            public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
     
            public struct keyboardHookStruct
            {
                public int vkCode;
                public int scanCode;
                public int flags;
                public int time;
                public int dwExtraInfo;
            }
     
            const int WH_KEYBOARD_LL = 13;
            const int WM_KEYDOWN = 0x100;
            const int WM_KEYUP = 0x101;
            const int WM_SYSKEYDOWN = 0x104;
            const int WM_SYSKEYUP = 0x105;
            #endregion
     
            #region Instance Variables
            /// <summary>
            /// The collections of keys to watch for
            /// </summary>
            public List<Keys> HookedKeys = new List<Keys>();
            /// <summary>
            /// Handle to the hook, need this to unhook and call the next hook
            /// </summary>
            IntPtr hhook = IntPtr.Zero;
            #endregion
     
            #region Events
            /// <summary>
            /// Occurs when one of the hooked keys is pressed
            /// </summary>
            public event KeyEventHandler KeyDown;
            /// <summary>
            /// Occurs when one of the hooked keys is released
            /// </summary>
            public event KeyEventHandler KeyUp;
            #endregion
     
            #region Constructors and Destructors
            /// <summary>
            /// Initializes a new instance of the <see cref="globalKeyboardHook"/> class and installs the keyboard hook.
            /// </summary>
            public globalKeyboardHook()
            {
                hook();
            }
     
            /// <summary>
            /// Releases unmanaged resources and performs other cleanup operations before the
            /// <see cref="globalKeyboardHook"/> is reclaimed by garbage collection and uninstalls the keyboard hook.
            /// </summary>
            ~globalKeyboardHook()
            {
                unhook();
            }
            #endregion
     
            #region Public Methods
            /// <summary>
            /// Installs the global hook
            /// </summary>
            public void hook()
            {
                IntPtr hInstance = LoadLibrary("User32");
                hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
            }
     
            /// <summary>
            /// Uninstalls the global hook
            /// </summary>
            public void unhook()
            {
                UnhookWindowsHookEx(hhook);
            }
     
            /// <summary>
            /// The callback for the keyboard hook
            /// </summary>
            /// <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param>
            /// <param name="wParam">The event type</param>
            /// <param name="lParam">The keyhook event information</param>
            /// <returns></returns>
            public int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
            {
                if (code >= 0)
                {
                    Keys key = (Keys)lParam.vkCode;
                    if (HookedKeys.Contains(key))
                    {
                        KeyEventArgs kea = new KeyEventArgs(key);
                        if ((wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN) && (KeyDown != null))
                        {
                            KeyDown(this, kea);
                        }
                        else if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
                        {
                            KeyUp(this, kea);
                        }
                        if (kea.Handled)
                            return 1;
                    }
                }
                return CallNextHookEx(hhook, code, wParam, ref lParam);
            }
            #endregion
     
            #region DLL imports
            /// <summary>
            /// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
            /// </summary>
            /// <param name="idHook">The id of the event you want to hook</param>
            /// <param name="callback">The callback.</param>
            /// <param name="hInstance">The handle you want to attach the event to, can be null</param>
            /// <param name="threadId">The thread you want to attach the event to, can be null</param>
            /// <returns>a handle to the desired hook</returns>
            [DllImport("user32.dll")]
            static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
     
            /// <summary>
            /// Unhooks the windows hook.
            /// </summary>
            /// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
            /// <returns>True if successful, false otherwise</returns>
            [DllImport("user32.dll")]
            static extern bool UnhookWindowsHookEx(IntPtr hInstance);
     
            /// <summary>
            /// Calls the next hook.
            /// </summary>
            /// <param name="idHook">The hook id</param>
            /// <param name="nCode">The hook code</param>
            /// <param name="wParam">The wparam.</param>
            /// <param name="lParam">The lparam.</param>
            /// <returns></returns>
            [DllImport("user32.dll")]
            static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
     
            /// <summary>
            /// Loads the library.
            /// </summary>
            /// <param name="lpFileName">Name of the library</param>
            /// <returns>A handle to the library</returns>
            [DllImport("kernel32.dll")]
            static extern IntPtr LoadLibrary(string lpFileName);
            #endregion
        }
    }

    Et pourtant une erreur se produit à l'exécution, lorsque j'appuie sur A ou sur B :

    CallbackOnCollectedDelegate a été détecté
    Message: Un rappel a été effectué sur un délégué récupéré par le garbage collector de type 'KeyLogger!KeyLogger.globalKeyboardHook+keyboardHookProc::Invoke'. Cela peut entraîner un arrêt brutal de l'application, des dysfonctionnements et des pertes de données. Lors du passage de délégués à du code non managé, ils doivent être maintenus actifs par l'application managée jusqu'à ce qu'il soit garanti qu'ils ne seront jamais appelés.
    J'ai trouvé beaucoup de discussions sur internet au sujet de cette erreur, mais j'avoue ne pas y avoir compris grand chose...

    Quelqu'un aurait-il une piste ?

    Merci infiniment

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Quand tu fais ça dans la méthode hook :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    hhook = SetWindowsHookEx(WH_KEYBOARD_LL, hookProc, hInstance, 0);
    Ca crée implicitement une instance de keyboardHookProc, comme si tu avais écrit ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    hhook = SetWindowsHookEx(WH_KEYBOARD_LL, new keyboardHookProc(hookProc), hInstance, 0);
    Le problème, c'est que tu ne gardes pas de référence à ce delegate, donc rien n'empêche qu'il soit collecté par le GC.

    Il faut donc garder une référence à ce delegate, comme ça par exemple :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
            private keyboardHookProc _hookProc;
     
            public void hook()
            {
                IntPtr hInstance = LoadLibrary("User32");
                _hookProc = new keyboardHookProc(hookProc);
                hhook = SetWindowsHookEx(WH_KEYBOARD_LL, _hookProc, hInstance, 0);
            }
    Le problème ne se pose pas avec du code managé, parce que le code qui appelle le callback garde une référence sur le delegate ; mais comme ici tu passes le delegate à du code non managé, le GC ne sait pas que le delegate est encore utilisé, et donc il le collecte.

  3. #3
    Invité
    Invité(e)
    Par défaut
    Ça marche, effectivement

    Merci beaucoup ! Et merci aussi pour ton explication, bien plus claire que ce que j'ai pu lire auparavant

  4. #4
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par jo_le_coco Voir le message
    Ça marche, effectivement

    Merci beaucoup ! Et merci aussi pour ton explication, bien plus claire que ce que j'ai pu lire auparavant
    Sauf qu'en fait y avait une erreur dans mon explication : c'est avec du code managé que ça ne pose pas de problème, et pas avec du code non managé... j'ai corrigé

  5. #5
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2006
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 37
    Points : 37
    Points
    37
    Par défaut
    Salut

    J'utilise la même classe de gestion du clavier pour intercepter dans une winform la touche entrée (ça, ça fonctionne quand je prends la maquette trouvée sur codeguru)... Mais à l'exécution, j'ai le même message!

    Je me demande ce que je n'ai pas compris dans l'explication ci-dessus mais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    globalKeyboardHook gkh = new globalKeyboardHook();
    En déclaration (pour instancier)
    ensuite:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    private void Fenetre_Load(object sender, EventArgs e)
             {
                     gkh.HookedKeys.Add(Keys.Return);
                     gkh.KeyDown += new KeyEventHandler(gkh_KeyDown);
    }
    Qu'est-ce que j'ai loupé?
    Le code de la classe, dois-je l'intégrer à la fenêtre elle-même?

  6. #6
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Euh... là je vois pas trop

    Tu as fais la modif que j'avais suggéré plus haut ?

  7. #7
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2006
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 37
    Points : 37
    Points
    37
    Par défaut
    Je me demande justement si je l'ai bien positionnée. En gros, je l'ai placée dans la classe en lieu et place de la déclaration de la méthode, et cela persiste.

    Ensuite, en cherchant, j'ai trouvé des astuces complémentaires, mais de là ça ne fonctionne plus correctement (l'interception de la touche n'est plus valide).

    Donc je m'interroge sur ce que j'ai bien pu rater.

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2006
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 37
    Points : 37
    Points
    37
    Par défaut
    Je crois que mon pb est assez tordu.
    La classe de gestion est "conforme", vu que je l'utilise sans anomalie dans un TP à part (une frame et les init sans souci, pas de problème MDA).

    Je crois que mon souci est que l'application globale est tellement bien conçue qu'elle "bascule" d'objet général, d'où le souci.

    Comment faire en sorte de démarrer le processus d'écoute dès le démarrage de l'application (ça, je sais où elle démarre), et de faire "vivre" le thread quelque soit la frame active? Mieux encore: de là, comment de tenir compte de l'état de la saisie sur une frame spécifique à partir du keyEvent mentionné précédemment?
    [Edit]
    Je veux dire: comment faire en sorte que l'interception du keydown puisse être, par exemple, localisé sur une frm A, alors que la méthode d'écoute soit commune à n'importe quelle frame?

  9. #9
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2006
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 37
    Points : 37
    Points
    37
    Par défaut
    Nouveau point: J'ai résolu le problème (mauvais positionnement du démarrage)...

    mais quand j'intercepte la touche (logique!), celle-ci se révèle ensuite inexploitable dans d'autres applications. Typiquement, je gère la touche ENTER, chose quand même utile ailleurs.

    Une idée pourquoi j'ai ce comportement?

  10. #10
    Rédacteur/Modérateur


    Homme Profil pro
    Développeur .NET
    Inscrit en
    Février 2004
    Messages
    19 875
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2004
    Messages : 19 875
    Points : 39 749
    Points
    39 749
    Par défaut
    Citation Envoyé par magellan94 Voir le message
    mais quand j'intercepte la touche (logique!), celle-ci se révèle ensuite inexploitable dans d'autres applications. Typiquement, je gère la touche ENTER, chose quand même utile ailleurs.

    Une idée pourquoi j'ai ce comportement?
    Bizarre... pourtant la méthode hookProc appelle bien CallNextHookEx
    Tu ne mets pas Handled à true dans ton handler de l'évènement KeyDown ?

  11. #11
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Août 2006
    Messages
    37
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 37
    Points : 37
    Points
    37
    Par défaut
    En fait, ce n'est pas bizarre du tout!

    Le projet initial a une petite "tare" idiote: il intercepte les keyup ET keydown, il intercepte donc TOUTES les saisies... les "volant" aux autres applis.

    La solution? Elle est si simple que j'en ai honte en fait
    Petit rappel (au cas où quelqu'un lirait ce post sans savoir de quoi je parle)
    - Le keyDown est la "pression" sur la touche
    - Le keyUp est quand on "libère" la touche

    Donc... Pour que l'autre appli puisse "voir" la touche, il faut lui laisser le loisir de recevoir l'instruction... en n'interceptant pas le Keydown (c'est l'interception par défaut). Le handler de mon appli va donc "intercepter" le keyUp, ce qui permet donc aux DEUX applis (quelque soit l'autre en fait!) de recevoir l'action sur la touche ENTREE!

    La modification par l'exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    private void Fenetre_Load(object sender, EventArgs e)
             {
                     gkh.HookedKeys.Add(Keys.Return);
                     gkh.KeyUp += new KeyEventHandler(gkh_KeyUp);
    }
    et dans la classe initiale!
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public int hookProc(int code, int wParam, ref keyboardHookStruct lParam)
            {
                if (code >= 0)
                {
                    Keys key = (Keys)lParam.vkCode;
                    if (HookedKeys.Contains(key))
                    {
                        KeyEventArgs kea = new KeyEventArgs(key);
                        if ((wParam == WM_KEYUP || wParam == WM_SYSKEYUP) && (KeyUp != null))
                        {
                            KeyUp(this, kea);
                        }
                        if (kea.Handled)
                            return 1;
                    }
                }
                return CallNextHookEx(hhook, code, wParam, ref lParam);
            }
    Voilà voilà!

    PS: merci pour ton écoute, elle m'a été précieuse!

    EDIT: Pour le nom j'ai aussi rectifié (pour lisibilité du code uniquement
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     void gkh_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
            {
                events.Items.Add("Key: " + e.KeyCode);
            }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Mise à jour de la liste des ports COM détectées
    Par chourmo dans le forum Composants VCL
    Réponses: 2
    Dernier message: 23/12/2005, 15h11
  2. Carte réseau non détectée sous Win XP
    Par maddog2032 dans le forum Hardware
    Réponses: 10
    Dernier message: 20/10/2005, 11h54
  3. détected si .net est present sur la machine
    Par calfater dans le forum Delphi .NET
    Réponses: 1
    Dernier message: 08/09/2005, 22h11
  4. [JMF] webcam non détectée
    Par Goldocrack dans le forum Multimédia
    Réponses: 3
    Dernier message: 17/07/2005, 00h31
  5. Comment détecté automatiquement Une base erronée.
    Par ada_b dans le forum Débuter
    Réponses: 4
    Dernier message: 13/01/2005, 07h56

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo