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

API, COM et SDKs Delphi Discussion :

Problème avec la création d'un hook souris global


Sujet :

API, COM et SDKs Delphi

  1. #1
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 364
    Billets dans le blog
    1
    Par défaut Problème avec la création d'un hook souris global
    Je sous sous W10, toutes mises à jour faites.
    J’utilise Delphi 6 Personal Edition pour réaliser une DLL avec des fonctions de service, utilisée par un programme réalisé dans un autre langage.

    Je tente d’implémenter un Hook des évèmenents souris concernant la roue de la souris – rotation vers le haut/bas et clic sur la roue. Pour cela, je commence par installer un hook du type WH_MOUSE_LL . Je mémorise l’adresse de l’ancienne WndProc dans une variable globale. Comme je suis dans une DLL et je n’ai pas accès à hInstance du progamme appelant (bien que j’aie le handle de sa fenêtre principale), je tente d’installer un hook global de la manière suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Const
      WH_MOUSE_LL = 14;
    var
      GenHookMouseWndProc : integer ;
     
    Function SetMyHook : integer ; stdcall ; export ;
    begin
      GenHookMouseWndProc := SetWindowsHookEx(WH_MOUSE_LL, @GenHookMouse_LL_FUNC, 0, 0);   // tenter le hook
      Result :=  0 ;
    End ;
    Cela « marche », car GenHookMouseWndProc est différent de zéro.

    Ma fonction callback pour le hook est la suivante :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function GenHookMouse_LL_FUNC(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
    begin
      Result := CallNextHookEx(GenHookMouseWndProc[i], nCode, wParam, lParam);
    End ;
    Je sais, elle ne fait rien – c’est juste pour m’assurer que le principe du hook fonctionne.

    Problème : dès que le hook est installé, le programme part dans une boucle infinie (le sablier s’affiche) puis disparaît finalement purement et simplement.

    Question : Qu’est-ce que je n’ai pas compris dans l’installation d'un hook souris global ?

  2. #2
    Expert éminent
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 56
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Par défaut
    ton code me parait douteux...GenHookMouseWndProc[i], ça ne doit même pas compiler ?! et ce paramètre est de tout façon ignoré.

    j'utilise les Hook dans ce projet
    https://github.com/tothpaul/Delphi/t.../MacroRecorder

    il semblerait que SetWindowsHookEx() attende une fonction et non un pointer (mais ça peut dépendre de la version de Delphi, les déclarations des API sont parfois modifiées)
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  3. #3
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 364
    Billets dans le blog
    1
    Par défaut
    Oui, évidemment - l'indiçage est en trop. C'est le piège du copier/coller puisque ce code est extrait d'un ensemble plus vaste. Voici le code réel, pour tester:

    Pour installer le hook:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Const
      WH_MOUSE_LL = 14;
    var
      GenHookMouseWndProc : integer ;
     
    Function SetMyHook : integer ; stdcall ; export ;
    begin
      GenHookMouseWndProc := SetWindowsHookEx(WH_MOUSE_LL, @GenHookMouse_LL_FUNC, 0, 0);   // tenter le hook
      Result :=  0 ;
    End ;
    La fonction hook elle-même:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    function GenHookMouse_LL_FUNC(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
    begin
      Result := CallNextHookEx(GenHookMouseWndProc, nCode, wParam, lParam);
    End ;
    J'ai vérifié: on entre bien dans la fonction lors du moinde mouvement de la souris, d'un clic ou d'une action quelconque sur la molette. Et j'ai constaté que le paramètre nCode donne les valeurs suivantes concernant la molette:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
      WM_MOUSEWHEEL_ROLL  = $020A;   // si la molette est tournée vers le haut ou le bas
      WM_MOUSEWHEEL_UP  = $0208    // si la molette a été enfoncée, lors du relâchement;
    Ceci me permet ensuite de gérer spécifiquement ces actions. Mais je n'en arrive même pas là - le programme entre dans une boucle infinie. Ce phénomère est "occulté" si je place des SHOWMESSAGE dans le code pour contrôler l'avancement - il n'y a plus de bloquage. Mais ce n'est évidemment pas le but.

  4. #4
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 364
    Billets dans le blog
    1
    Par défaut
    Bon, j'ai trouvé. Comme souvent, le problème se situait entre la chaise et le clavier...

    Je n'avais pas vraiment compris la logique du hook global, et à cause de cela, j'avais mal déterminée l'adresse de la fonction à passer pour CallNextHookEx.

    Donc, lé problème est résolu et mes fonctions marchent parfaitement. Merci d'avoir regardé mon problème.

  5. #5
    Expert éminent
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 56
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Par défaut
    ce serait sympa de préciser plus clairement l'erreur
    Developpez.com: Mes articles, forum FlashPascal
    Entreprise: Execute SARL
    Le Store Excute Store

  6. #6
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 364
    Billets dans le blog
    1
    Par défaut
    En fait, j'avais copié une séquence de ma DLL gérant un autre type de hook (keuboard hook), spécifiquement pour un contrôle créé apr moi en dérivant un TListView. Et comme l'utilisateur peut en avoir plusieurs dans son projet, j'avais créé des tables mémorisant pour chacun de ces objets s'il y a un hook actif, pour le thread actif et le handle de l'objet générant le hook.

    Je ne me suis pas rendu compte que pour un hook global, la notion de "thread" n'a pas de sens. De ce fait, je me suis enmêlé dans mes tables et je n'ai pas pu trouver la bonne adresse pour CallNextHookEx.

    La solution a consisté à virer, pour mon Global Mouse Hook, toute la gestion de tables et de ne mémoriser qu'une seule adresse issue de SetWindowsHookEx lors de l'installation du hook, et un handle de l'objet à surveiller. Et lorsqu'un hook survient, je vérifie 2 choses:
    - est-ce un hook pour l'évènement WHEEL ? Si ce n'est pas le cas, j'écarte par CallNextHookEx avec la bonne adresse.
    - si le handle de l'objet sous le curseur n'est pas le handle d'objet mémorisé lors de l'installation du hook, j'écarte de la même manière.
    Et si c'est donc un hook WHEEL pour l'objet ciblé, je génère un USER_EVENT et signalant dans WPARAM la nature de l'évènement (1=molette vers le haut, 2=molette vers le bas, 3=molette cliquée), et dans LPARAM, je place le delta (décalage de la molette) en cas de roration demolette, ou zéro en cas de clic.
    J'appelle également CallNextHookEx après cela, afin de laisser Windows prendre en charge son traitement normal.
    Il est ensuite la tâche du programme application de prendre ces informations en charge.

    Voici la description du problème, de la solution et de l'utilité de ces fonctions:
    function InitGlobalMouseHook():integer; stdcall; export;
    assurer qu'un hook n'est pas déjà actif, éventuellement initialiser les variables de mémorisation
    function CreateGlobalMouseHook(frm: integer):integer; stdcall; export;
    installer le hook et mémoriser le handle d'une form (ou tout autre objet ayant un handle) à surveiller
    function KillGlobalMouseHook():integer; stdcall; export;
    supprimer le hook et restaurer le fonctionnement normal

  7. #7
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 932
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 932
    Par défaut
    Citation Envoyé par KlausGunther Voir le message
    Je ne me suis pas rendu compte que pour un hook global, la notion de "thread" n'a pas de sens.
    Bien sûr qu'elle a du sens et puisque tu sembles surveiller une fenêtre précise c'est même par là que tu devrais passer. Tu surcharges inutilement le système complet sans cela.

    Citation Envoyé par KlausGunther Voir le message
    je me suis enmêlé dans mes tables et je n'ai pas pu trouver la bonne adresse pour CallNextHookEx.
    CallNextHookEx doit invariablement être utilisé ainsi CallNextHookEx(0, Code, wParam, lParam) quelque soit le type de hook. Le premier paramètre est ignoré au moins depuis NT...


    Cela dit, les hook de bas niveau (LL) sont a proscrire, Windows n'aime plus ça du tout !
    Si pour une raison quelconque il prend trop de temps à s'exécuter, l'OS va purement et simplement le considérer comme planté et le désinstaller afin d'éviter un dead-lock du système. Tu n'en sauras rien et pourras juste constater que ça ne fonctionne plus.

    Ce n'est pas la cas d'un hook "standard" puisque la DLL est injectée dans les processus distants et que s'il y a un problème c'est juste l'application concernée qui en souffrira.

    On peut supposer que l'utilisation d'un hook de bas niveau était dû à tes "tables" puisqu'elles n'ont de sens que dans le contexte de l'application qui a installé le hook. Perso je passerais illico sur un hook standard puisqu'elles ne sont pas utiles.

    Sinon l'approche actuelle est plutôt de se voir notifier par le système des événements clavier/souris et non de s’intercaler entre OS et applications. Cela se fait assez simplement par RawInput.
    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
    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
    type
      TForm1 = class(TForm)
        Memo1: TMemo;
        procedure FormCreate(Sender: TObject);
      protected
        procedure WndProc(var Message: TMessage); override;
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    uses JwaWinUser;
     
    procedure TForm1.FormCreate(Sender: TObject);
    var
      RID: TRawInputDevice;
     
    begin
      //Messages souris
      RID.usUsagePage := $01;
      RID.usUsage     := $02;
      RID.dwFlags     := RIDEV_INPUTSINK;
      RID.hwndTarget  := Handle;
     
      RegisterRawInputDevices(@RID, 1, SizeOf(TRawInputDevice));
    end;
     
    procedure TForm1.WndProc(var Message: TMessage);
    var
      Data :PRawInput;
      Size :dword;
     
    begin
      if Message.Msg = WM_INPUT then
      begin
        GetRawInputData(Message.LParam, RID_INPUT, nil, Size, SizeOf(TRawInputHeader));
        GetMem(Data, Size);
     
        try
          if GetRawInputData(Message.LParam, RID_INPUT, Data, Size, SizeOf(TRawInputHeader)) = Size then
          begin
            case Data.mouse.union.usButtonFlags of
              RI_MOUSE_WHEEL         : Memo1.Lines.Add(Format('Molette %d', [SmallInt(Data.mouse.union.usButtonData)]));
              RI_MOUSE_BUTTON_3_DOWN : Memo1.Lines.Add('Clic milieu');
            end;
     
            Message.Result := 0;
          end;
     
        finally
          FreeMem(Data);
        end;
      end
     
      else inherited;
    end;

  8. #8
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 364
    Billets dans le blog
    1
    Par défaut
    Merci pour ces informations, Andnotor. Je vais regarder cela de près.

    Es-tu sûr que ces lignes sont appliquables en Delphi 6 PersonalEdition ?
    Puis, dans le code que tu as posté, je vois qu'il s'applique dans un projet VCL, au niveau de la Form1 avec une WndProc personnalisée. Or, dans mon cas, il s'agit d'une DLL appelée par une application externe non écrite en Delphi. Au niveau de la DLL, je dispose uniquement du handle de la Form1 ainsi que du handle d'un objet de type TWinControl à surveiller. C'est pour cette contrainte que j'avais choisi un hook de bas niveau, toujours documenté dans MSDN sans avertissement particulier...

  9. #9
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 932
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 932
    Par défaut
    Citation Envoyé par KlausGunther Voir le message
    C'est pour cette contrainte que j'avais choisi un hook de bas niveau, toujours documenté dans MSDN sans avertissement particulier...
    Source :

    The hook procedure should process a message in less time than the data entry specified in the LowLevelHooksTimeout value in the following registry key:

    HKEY_CURRENT_USER\Control Panel\Desktop

    The value is in milliseconds. If the hook procedure times out, the system passes the message to the next hook. However, on Windows 7 and later, the hook is silently removed without being called. There is no way for the application to know whether the hook is removed.
    Je te parle d'expérience pour avoir moi-même rencontré ce problème par le passé. Et même si le timeout de 5 secondes paraît long il est dépendant du système complet. D'autres apps sans rapport avec la tienne consommant beaucoup de temps CPU et retardant ainsi ton traitement suffit à le déconnecter.

    Citation Envoyé par KlausGunther Voir le message
    Es-tu sûr que ces lignes sont appliquables en Delphi 6 PersonalEdition ?
    Il n'y a pas de raison
    Il te faudra juste la JEDI API Library pour les déclarations (et c'est toujours le cas sous Sydney).

    Citation Envoyé par KlausGunther Voir le message
    il s'agit d'une DLL
    Tu peux sans problème créer une fenêtre message-only dans la DLL pour traiter spécifiquement ce message.
    C'est d'ailleurs ainsi que je procède mais pour d'autres raisons ; RawInput à un inconvénient (si c'en est un), il ne peut être utilisé que par une fenêtre à la fois par processus et j'avais évidemment besoin d'être notifié et dans une app et dans une DLL. Résultat, une DLL dédiée qui dispatch ensuite aux récepteurs finaux.

    Au niveau hook : tu as des handles, tu peux donc déterminer à quelle tâche ils appartiennent par GetWindowThreadProcessId.

  10. #10
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 364
    Billets dans le blog
    1
    Par défaut
    Merci, Andnotor. J'ai bien pris note de tout cela, et je vais revoir mon code dans ce sens.

    Par rapport au délai imparti pour le traiteme t du hook, je suis assez serein, car il consiste essentiellement à déterminer si le handle de l'objet sous le curseur fait partie d'une liste de handles imposées, et ensuite envoyer un SEND_MESSAGE avec un USER_EVENT. Le traitement proprement-dit est ensuite efectué par le programme appelant à réceptiondu USER_EVENT, mais c'est en-dehors du temps de traitement du hook qui est terminé depuis longtemps, à ce moment.

    Voici le code de ma fonction hool actuelle:
    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
    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
    const
      WH_MOUSE_LL = 14;
      WM_MOUSEWHEEL_ROLL  = $020A;
      WM_MOUSEWHEEL_UP  = $0208;
     
      UserEventMouseWheel          = $11000000; // identifiant dans WParam
        UserEventWheelUp                          = $00010000; // la roue a été tournée vers le haut
        UserEventWheelDown                        = $00020000; // la roue a été tournée vers le bas
        UserEventWheelClick                       = $00030000; // la roue a été cliquée
     
     
    var
    // variables pour hook général de fenêtres pour gérer des évènements MOUSE
      GenHookMouseHandles:  TStringList;                          // handle de la fenêtre hookée
      GenHookMouseWndProc:  integer;                              // WNDPROC ancienne du process hooké
     
    ...
     
    // fonction HOOK pour une form en général
    function GenHookMouse_LL_FUNC(nCode: Integer; wParam: WPARAM; lParam: LPARAM): LRESULT; stdcall;
    var
      hTemp  : HWND;
      s      : String;
      i      : integer;
      delta: integer;
      sHandle: string;
    begin
      if wParam=WM_MOUSEWHEEL_ROLL  then begin
        delta :=   (PMSLLHOOKSTRUCT(lParam)^.mouseData and $FFFF0000) shr 16;
        if (delta and $8000)<>0 then delta := delta or $FFFF0000;
        hTemp := WindowFromPoint(PMSLLHOOKSTRUCT(lParam)^.POINT);
        sHandle := inttostr(hTemp);
        if GenHookMouseHandles.IndexOf(sHandle)>=0 then begin
          // générer le USER_EVENT
          if delta>0 then SendMessage(MainFormHandle,CM_PANORAMIC_USER,UserEventMouseWheel or UserEventWheelUp,delta)
                     else SendMessage(MainFormHandle,CM_PANORAMIC_USER,UserEventMouseWheel or UserEventWheelDown,0-delta);
          Result := CallNextHookEx(GenHookMouseWndProc, nCode, wParam, lParam);
          result := 0;
          exit;
        end;
      end;
     
      if wParam=WM_MOUSEWHEEL_UP  then begin
        hTemp := WindowFromPoint(PMSLLHOOKSTRUCT(lParam)^.POINT);
        sHandle := inttostr(hTemp);
        if GenHookMouseHandles.IndexOf(sHandle)>=0 then begin
          // générer le USER_EVENT
          SendMessage(MainFormHandle,CM_PANORAMIC_USER,UserEventMouseWheel or UserEventWheelClick,0);
          Result := CallNextHookEx(GenHookMouseWndProc, nCode, wParam, lParam);
          result := 0;
          exit;
        end;
      end;
     
      Result := CallNextHookEx(GenHookMouseWndProc, nCode, wParam, lParam);
    end;
    La variable GenHookMouseWndProc est chargée lors de l'installation du hook par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        GenHookMouseWndProc := SetWindowsHookEx(WH_MOUSE_LL, @GenHookMouse_LL_FUNC, 0, 0);   // tenter le hook
        if GenHookMouseWndProc=0 then exit;                                                  // échoué ?
    Ensuite, la TStringList GenHookMouseHandles est créée et chargée par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
        GenHookMouseHandles := TStringList.Create;                                           // créer la liste des handles
        handles := GetStringFromPanoramic(sHandles);                                         // récupérer la liste des handles
        GenHookMouseHandles.CommaText := handles;                                            // et mémoriser
    la variable handles est un string contenant la liste des handles des objets à surveiller, séparés par des virgules.

    Donc, je pense que les contraintes de délais de traitement sont très largement respectées.

  11. #11
    Rédacteur/Modérateur
    Avatar de Andnotor
    Inscrit en
    Septembre 2008
    Messages
    5 932
    Détails du profil
    Informations personnelles :
    Localisation : Autre

    Informations forums :
    Inscription : Septembre 2008
    Messages : 5 932
    Par défaut
    Citation Envoyé par KlausGunther Voir le message
    Par rapport au délai imparti pour le traiteme t du hook, je suis assez serein, car il consiste essentiellement à déterminer si le handle de l'objet sous le curseur fait partie d'une liste de handles imposées, et ensuite envoyer un SEND_MESSAGE avec un USER_EVENT. Le traitement proprement-dit est ensuite efectué par le programme appelant à réceptiondu USER_EVENT, mais c'est en-dehors du temps de traitement du hook qui est terminé depuis longtemps, à ce moment.
    Ben non SendMessage est synchrone. Ce que tu dis serait correct pour PostMessage

  12. #12
    Membre éclairé

    Homme Profil pro
    Informaticien retraité
    Inscrit en
    Mars 2010
    Messages
    364
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Informaticien retraité
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2010
    Messages : 364
    Billets dans le blog
    1
    Par défaut
    En effet. Je n'avais pas pensé à cela et je vais changer cela.

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

Discussions similaires

  1. Réponses: 2
    Dernier message: 04/12/2006, 23h02
  2. Réponses: 3
    Dernier message: 12/01/2006, 09h16
  3. probléme avec la création de table Mysql 5
    Par developpeur_mehdi dans le forum Outils
    Réponses: 3
    Dernier message: 19/10/2005, 19h08
  4. Problème avec la création d'un composant
    Par jeromelef dans le forum Composants VCL
    Réponses: 6
    Dernier message: 01/10/2005, 00h51
  5. Réponses: 2
    Dernier message: 29/03/2004, 18h29

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