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

Réseau C Discussion :

Client/serveur : ouvrir une deuxième connexion


Sujet :

Réseau C

  1. #1
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 56
    Points : 36
    Points
    36
    Par défaut Client/serveur : ouvrir une deuxième connexion
    Bonjour,

    Je suis en train d'écrire un chat. Pour le moment plusieurs clients peuvent se connecter et discuter : tout va bien. Mais les utilisateurs doivent aussi pouvoir créer des groupes de discussion avec une commande creategroup nom_du_gpe membre1 membre2 membre3. Le serveur doit alors demander à membre1 membre2 et membre3 s'ils veulent entrer dans le groupe, et c'est là que ça se corse pour moi. Ce que j'ai fait pour le moment :

    • Côté serveur : Pour chaque membre, je crée un thread. Dans chaque thread j'envoie un message "voulez vous faire partie du groupe ? (o/n)". Mais pour récupérer la réponse, je ne peux pas utiliser la socket client déjà connectée au serveur parce qu'elle est déjà lue périodiquement dans le main() de mon programme... Il faut donc que j'ouvre une nouvelle connexion spécialement pour cette requête. J'ouvre donc une nouvelle socket et j'attends que le client se connecte.
    • Côté client : quand le client reçoit "voulez-vous faire partie du groupe ? (o/n)", il crée une nouvelle socket pour envoyer sa réponse. Problème : comment faire pour que cette socket se connecte précisément à celle que vient d'ouvrir le serveur ?

    La question se pose aussi côté serveur : j'ouvre trois thread dans lesquels j'ouvre une socket. Comment être sûr que la socket à laquelle est destiné un client précis acceptera bien une connexion de ce client et non d'un autre ?

    J'espère que ça ne parait pas trop confus. Peut-être aussi que je fais fausse route et qu'il y a un moyen plus simple de réaliser cette demande de confirmation.

    Merci d'avance pour vos réponses.

  2. #2
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 372
    Points : 23 628
    Points
    23 628
    Par défaut
    Tu peux effectivement faire de la discrimination sur les adresses serveur et cliente lorsque tu gères tes sockets mais, d'une manière générale, l'exercice consiste normalement à ouvrir d'abord la connexion, à dialoguer avec le client pour lui permettre éventuellement de s'authentifier et, enfin, de l'orienter vers le bon groupe ou mettre fin à la connexion s'il y a lieu.

    Tu peux également lire cette discussion, qui traitait d'un sujet similaire : http://www.developpez.net/forums/d10...ert-lutiliser/

  3. #3
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 56
    Points : 36
    Points
    36
    Par défaut
    Bonjour,

    Merci pour la réponse. Je vais regarder le sujet en question. Sinon l'exercice n'est pas tout à fait l'exercice classique en fait : les clients qui se connectent ne sont pas affectés à un groupe. Ils sont dans le "groupe par défaut" si je puis dire, et c'est ensuite qu'ils peuvent créer de nouveaux groupes ou intégrer des groupes existants. Ils peuvent même intégrer plusieurs groupes différents. Pour communiquer avec un groupe ils doivent taper la commande nom_du_groupe message[...]

    Je vous tiens au courant

  4. #4
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 56
    Points : 36
    Points
    36
    Par défaut
    J'avoue que je ne comprends pas trop en quoi le fil que tu m'as donné en lien pourrait m'aider :s Sinon j'ai peut-être trouvé une solution : ouvrir une connexion sur un nouveau port à chaque fois que le serveur veut demander une confirmation... Mais ça me parait un peu crade, si j'ai beaucoup de clients le serveur va être obligé d'ouvrir temporairement des connexion sur un paquet de ports non ? Pourrais-tu m'en dire plus sur ce que tu appelles la discrimination d'adresse stp ? Ça à l'air intéressant mais j'ai pas trouvé de doc à ce sujet...

  5. #5
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 372
    Points : 23 628
    Points
    23 628
    Par défaut
    Citation Envoyé par silma Voir le message
    J'avoue que je ne comprends pas trop en quoi le fil que tu m'as donné en lien pourrait m'aider :s
    Il faut la lire jusqu'au bout : le fil démarre sur la meilleure manière de vérifier si un programme est déjà ouvert et se termine par l'écriture d'un mini-serveur IRC-like.

    C'est intéressant également parce qu'il gère n clients en n'utilisant qu'un seul thread.

    Sinon j'ai peut-être trouvé une solution : ouvrir une connexion sur un nouveau port à chaque fois que le serveur veut demander une confirmation... Mais ça me parait un peu crade, si j'ai beaucoup de clients le serveur va être obligé d'ouvrir temporairement des connexion sur un paquet de ports non ? Pourrais-tu m'en dire plus sur ce que tu appelles la discrimination d'adresse stp ? Ça à l'air intéressant mais j'ai pas trouvé de doc à ce sujet...
    C'est effectivement très sale.

    En plus, c'est faire l'hypothèse que tu travailles forcément en TCP/IP, ce qui est généralement le cas aujourd'hui, mais pas forcément.

    Ensuite, il faut éviter de recourir systématiquement aux threads et à la multiplication des processus, car ils sont coûteux en ressources et, souvent, le signe que quelque chose n'est pas maîtrisé en amont (en tout cas, dans le genre de cas de figure qui nous intéresse aujourd'hui).

    La meilleure manière de procéder est de créer un objet « connexion » (une simple structure suffit) et en créer une instance par client entrant. Cette structure contiendrait alors toutes les infos relatives à une connexion, comme son descripteur de socket, mais également, par exemple, le numéro du groupe dont le client fait partie, en considérant par exemple que le groupe « zéro » existerait toujours et contiendrait justement tous les clients qui ne sont pas affectés à un autre groupe.

    De là, tu continues à exploiter ton code habituel. J'imagine qu'en l'état actuel des choses, chaque fois que quelqu'un poste un message, tu passes en revue toutes les connexions ouvertes pour leur envoyer, une par une, le message en question. Il te suffit donc de faire exactement la même chose mais en vérifiant si le numéro du groupe du connecté est le même que celui de la personne qui a envoyé le message.

  6. #6
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 56
    Points : 36
    Points
    36
    Par défaut
    La meilleure manière de procéder est de créer un objet « connexion » (une simple structure suffit) et en créer une instance par client entrant. Cette structure contiendrait alors toutes les infos relatives à une connexion, comme son descripteur de socket, mais également, par exemple, le numéro du groupe dont le client fait partie, en considérant par exemple que le groupe « zéro » existerait toujours et contiendrait justement tous les clients qui ne sont pas affectés à un autre groupe.
    Yeaaah ! C'est plus ou moins ce que j'ai fait : une structure "Client" avec un numéro de groupe, une socket "normale" qu'il utilise lors de sa connexion, et un pseudo.

    J'imagine qu'en l'état actuel des choses, chaque fois que quelqu'un poste un message, tu passes en revue toutes les connexions ouvertes pour leur envoyer, une par une, le message en question. Il te suffit donc de faire exactement la même chose mais en vérifiant si le numéro du groupe du connecté est le même que celui de la personne qui a envoyé le message.
    Oui effectivement c'est plus joli comme ça. Mais comme au départ le client appartient au groupe 0, il faut bien que le serveur lui demande s'il veut rejoindre le group n par exemple. Et pour ça je suis obligé de créer un nouveau thread et une nouvelle connexion avec le client, que je clos dès que le serveur à reçu la confirmation, non ? (au passage j'ai ajouté un champs "socket temporaire" dans la structure client pour réaliser cette seconde connexion). En tout cas c'est comme ça que je suis en train de faire et ça avance plutôt pas trop mal.

    EDIT : ça avance pas trop mal, mais je bloque quand même sacrément là, sur la fonction FD_SET. J'ai l'impression qu'au lieu d'ajouter une socket à mon ensemble rdfs, elle écrase la précédente socket...

    Je mets le code, j'espère que ce sera compréhensible bien que ce ne soit qu'un "bout de code" du programme...
    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
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
     
    La fonction thread qui me permets de créer mes groupes
    void * groupCreationThreadFun(void* data){
     
      Data * pData = (Data*)data;
      char buffer[BUF_SIZE];
      int nbOfAnswers = 0; //compte le nombre de clients qui ont déjà répondu à la requete
      int max=0; // pour la selection de socket
     
      SOCKET sock = init_connection(PORT+1); // création de la socket de connexion du serveur
      fd_set readset;
      FD_ZERO(&readset);
     
     
      for (int i=0 ; i< pData->nbOfGuests ; i++){ //j'établis une connexion avec chacun des clients concernés
     
        SOCKET csock;
        SOCKADDR_IN csin = { 0 };
        socklen_t csinsize = sizeof(csin);
     
        /* création de la demande au client */
        char * question = malloc(200);
        strcpy(question,"joingroup voulez-vous rejoindre le groupe "); //le mot-clé "joingroup me permet de détecter qu'il s'agit d'une requete pour rejoindre un groupe côté client
        strcat(question,pData->group.name);
        strcat(question," ?");
        /* envoi de la demande au client */
        write_client(pData->guests[i]->sock,question); 
        /* attente de la connexion du client */
        csock = accept(sock, (SOCKADDR *)&csin, &csinsize); 
        if(csock == SOCKET_ERROR)
          perror("accept()");
        /* on mémorise la socket pour la suite */
        pData->guests[i]->tempSock = csock;
     
     
       /* On ajoute la socket du client à readset,
    mais j'ai l'impression que ça foire un peu... */
        FD_SET(pData->guests[i]->tempSock,&readset); 
        max = csock > max ? csock : max; // calcul du nouveau max, pour le select
      }
     
      while(nbOfAnswers < pData->nbOfGuests){ // On attend que tous les clients aient répondu à la requete
     
    /* ci ça bloque au deuxième passage dans la boucle, car readset ne contient plus rien... 
    En tout cas quand je débug avec gdb, print readset renvoit {__fds_bits = {0 <repeated 16 times>}
    J'ai l'impression que c'est FD_SET qui écrase la première socket au lieu d'en ajouter une nouvelle.*/
        printf("ca bloque au select...\n)"); 
        if(select(max , &readset, NULL, NULL, NULL) == -1){
          perror("select()");
          exit(errno);
        }
     
        for(int i = 0; i < pData->nbOfGuests; i++){
          /* on test quel client a répondu */
          if(FD_ISSET(pData->guests[i]->tempSock, &readset)){
     
    	read_client(pData->guests[i]->tempSock, buffer);
    	  if( strcmp(buffer,"yes") == 0){
    	    write_client(pData->guests[i]->tempSock,"You have been added to the group\n");
    	    strcpy(buffer,pData->guests[i]->name);
    	    strcat(buffer," accepted the invitation to your group");
    	    write_client(pData->creator.sock,buffer);
    	  }
    	  if( strcmp(buffer,"no") == 0 ){
    	    strcpy(buffer,pData->guests[i]->name);
    	    strcat(buffer," refused the invitation to your group");
    	    write_client(pData->creator.sock,buffer);
    	  }
    	  /* Je déconnecte le client qui vient de répondre */
    	  FD_CLR(pData->guests[i]->tempSock,&readset);
    	  closesocket(pData->guests[i]->tempSock);nbOfAnswers++;
    	  printf("On ferme la socket du client %s\n",pData->guests[i]->name);
          }
        }
     }
      closesocket(sock);
      return NULL;
    }
    Merci beaucoup pour toutes ces précisions

  7. #7
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 372
    Points : 23 628
    Points
    23 628
    Par défaut
    Oui effectivement c'est plus joli comme ça. Mais comme au départ le client appartient au groupe 0, il faut bien que le serveur lui demande s'il veut rejoindre le group n par exemple. Et pour ça je suis obligé de créer un nouveau thread et une nouvelle connexion avec le client, que je clos dès que le serveur à reçu la confirmation, non ?
    Non, et heureusement. Dans l'absolu, tu n'es même pas obligé d'utiliser les threads du tout. La gestion de tous les clients, qu'ils viennent d'arriver ou qu'ils appartiennent déjà à un groupe, pourrait se faire dans un seul et même fil. Comment faisait-on lorsque les threads n'existaient pas sur les machines grand public ?

    EDIT : ça avance pas trop mal, mais je bloque quand même sacrément là, sur la fonction FD_SET. J'ai l'impression qu'au lieu d'ajouter une socket à mon ensemble rdfs, elle écrase la précédente socket... Je mets le code, j'espère que ce sera compréhensible bien que ce ne soit qu'un "bout de code" du programme...
    Je n'ai pas eu le temps de revoir ton programme en détail mais, à première vue, ce n'est pas « max » mais « max+1 » qu'il faut passer à select().

    Bon courage.

  8. #8
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 56
    Points : 36
    Points
    36
    Par défaut
    Effectivement, en réfléchissant je crois voir comment faire sans thread. Mais bon, maintenant que j'ai commencé...

    EDIT : j'ai trouvé la parade. Avant le selct, je vide readset, et je le re-remplis avec les sockets que je n'ai pas fermées grâce à une petite boucle for. En fait mon prof fait la même chose (il nous a donné quelques morceaux de code), mais je ne comprends pas en quoi c'est nécessaire, et pourquoi ma solution précédente ne fonctionne pas...

    Sinon j'aimerais un précision sur getaddrinfo, dont le man est carrément obscur (merci pour les précisions que tu apportes ici d'ailleurs http://www.developpez.net/forums/d87...e-getaddrinfo/ ). Dans l'exemple d'utilisation que tu donnes, ta node est une chaine de caractère "développez.net", et la fonction va récupérer l'adresse directement (au passage je pensais pas que c'était aussi facile de faire un truc pareil en C). Mais si je veux aller récupérer l'adresse depuis laquelle un client est connecté, comment puis-je utiliser cette fonction ?

    Bien sûr j'ai réglé ce problème en utilisant le contexte d'addressage du client, mais juste par curiosité j'aimerais savoir si c'est faisable avec getaddrinfo.

    Merci de partager ta science en tout cas

    EDIT 2 : j'ai d'autres questions auxilaires concernant mon programme (oui je débute en programmation réseau, c'est mon tout premier programme...), puis-je le poser ici ou bien je crée un nouveau topic ?

  9. #9
    Modérateur
    Avatar de Obsidian
    Homme Profil pro
    Développeur en systèmes embarqués
    Inscrit en
    Septembre 2007
    Messages
    7 372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2007
    Messages : 7 372
    Points : 23 628
    Points
    23 628
    Par défaut
    Citation Envoyé par silma Voir le message
    EDIT : j'ai trouvé la parade. Avant le selct, je vide readset, et je le re-remplis avec les sockets que je n'ai pas fermées grâce à une petite boucle for. En fait mon prof fait la même chose (il nous a donné quelques morceaux de code), mais je ne comprends pas en quoi c'est nécessaire, et pourquoi ma solution précédente ne fonctionne pas...
    C'est parce que select() utilise le ou les mêmes fd_sets pour savoir, en amont, quels sockets il faut surveiller et pour, en aval, t'indiquer lesquels se sont débloqués. Donc, il faut les réinitialiser à chaque tour, effectivement.


    Sinon j'aimerais un précision sur getaddrinfo, dont le man est carrément obscur (merci pour les précisions que tu apportes ici d'ailleurs http://www.developpez.net/forums/d87...e-getaddrinfo/ ). Dans l'exemple d'utilisation que tu donnes, ta node est une chaine de caractère "développez.net", et la fonction va récupérer l'adresse directement (au passage je pensais pas que c'était aussi facile de faire un truc pareil en C).
    C'est parce qu'il s'agit d'une « couche d'abstraction », c'est-à-dire d'une fonction qui encapsule plusieurs procédés distincts et qui les unifie sous un même appel. Si tu arrives à architecturer tes programmes de la même façon, tu vas énormément te simplifier la vie. Et ce sera encore plus flagrant quand tu passeras à la programmation orientée objet.

    N'oublie pas, cependant, de nettoyer la mémoire allouée avec freeaddrinfo().

    Mais si je veux aller récupérer l'adresse depuis laquelle un client est connecté, comment puis-je utiliser cette fonction ?
    « getaddrinfo() » interroge le D.N.S., c'est-à-dire l'annuaire d'Internet. C'est complètement indépendant du fait que tu sois ou non en relation avec un correspondant à ce moment-là. L'adresse d'un client distant t'es fournie par accept(), donc dans « csin » dans ton exemple.

    EDIT 2 : j'ai d'autres questions auxilaires concernant mon programme (oui je débute en programmation réseau, c'est mon tout premier programme...), puis-je le poser ici ou bien je crée un nouveau topic ?
    Si tes questions concernent le même programme, il faut les déposer ici. Sinon, il faut ouvrir un autre fil.

  10. #10
    Nouveau membre du Club
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    56
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 56
    Points : 36
    Points
    36
    Par défaut
    Edit : j'ai résolu le problème que j'expose ici. Il s'agissait d'une sombre histoire de scanf qui laisse trainder des "\n" dans stdin... Nettoyer le stdin au bon endroit (ie juste apres le scanf et non avant ma saisie) a suffit à régler le problème.

    Je clos aussi le sujet car mon chat fonctionne du feu de dieu Merci encore obsidian

    Salut,

    Désolé d'avoir un peu laissé tomber le fil, j'avais d'autres assignments à côté... J'ai presque terminé ce chat. Il est pas hyper sexy mais je suis content d'avoir pu découvrir les threads et les sockets, et ça m'a permis de trouver un peu d'autonomie en programmation (lire les man, tout ça tout ça).

    Il me reste une dernière chose embêtante, toujours lors de la demande de confirmation à un groupe. Je crois que le problème ce situe côté client cette fois.

    Côté client donc, quand une demande de groupe arrive, on crée un nouveau thread, dans lequel on établit une nouvelle connexion avec le serveur afin de lui envoyer une réponse. Le fait de créer un nouveau thread permet d'attendre la réponse du client, tout en continuant d'afficher les autres messages qui arrivent.

    Le problème, vient de ma fonction get_msg_from_kb(), qui récupère ce que le client tape. Lors de la première demande d'ajour à un groupe, tout va bien. Mais dès la seconde, la fonction est "sautée" malgré son caratère bloquant (elle contient un fgets). J'en ai déduit que c'est parce que stdin n'était pas vide. Je crois, sans aucune certitude, qu'il contient le caractère \n car j'ai une ligne qui est sautée si je fais "puts(buffer)" après "get_msg_from_kb()". Pour vider stdin j'ai d'abord tenté un fflush(stdin) qui n'a rien changé. J'ai donc ajouté une boucle qui nettoie stdin : c'est un peu mieux mais pas parfait car à la seconde demande de groupe, il faut que j'appuie sur "entrée", puis que je tape ma réponse et que je retape sur entrée. Je suis bien coincé avec ce problème depuis quelques temps...
    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
     
    static void * groupCreationThreadFun(void * data){
      Data * pData = (Data *)data;
      char * buffer = malloc(sizeof(char)*BUF_SIZE);
      int c;
      SOCKET sockTmp = init_connection(pData->address, pData->port + 1);
      /* ça c'est pour vider stdin, sinon l'instruction suivante est tout bonnement "sautée" */
        while ((c = getchar()) != '\n' && c != EOF){
          //    do nothing
        }
      get_msg_from_kb(buffer);
      write_server(sockTmp,buffer);
      end_connection(sockTmp);
     
      *(pData->answeringServerRequest) = 0;
      return NULL;
    }
     
     
    static void get_msg_from_kb(char* buffer){
     
      char* p = NULL;
     
      fgets(buffer, BUF_SIZE - 1, stdin);
     
      /* on vire le \n à la fin de la chaine */
      p = strchr(buffer, '\n');
      if (p != NULL){
        *p = 0;
      }
      else{
        printf("attention : le message est tronqué\n");
        buffer[BUF_SIZE - 1] = 0;
        /* Pour purger stdin si jamais il reste des trucs dedans */ 
        int c;
        while ((c = getchar()) != '\n' && c != EOF){
          //    do nothing
        }
      }
    }

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

Discussions similaires

  1. Réponses: 3
    Dernier message: 18/09/2016, 12h46
  2. Client/Serveur en Local sans connexion réseau
    Par ramoud dans le forum C++Builder
    Réponses: 8
    Dernier message: 05/07/2007, 14h44
  3. Problème lors d'une deuxième connexion à l'applet
    Par luckyvae dans le forum Applets
    Réponses: 3
    Dernier message: 16/03/2007, 10h21
  4. abstraction de client /serveur dans une application
    Par Pegaz dans le forum Développement
    Réponses: 2
    Dernier message: 24/01/2007, 09h30
  5. [VB2005]obliger windows à ouvrir une deuxième instance d'excel
    Par Gilles_37 dans le forum Windows Forms
    Réponses: 5
    Dernier message: 12/10/2006, 05h23

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