2010-12-06 11 views
5

J'écris une application en C# qui utilise la classe SerialPort pour communiquer avec quelques périphériques. Maintenant, le gros problème que j'ai rencontré tout le temps est de savoir comment libérer correctement des ressources là-bas, puisque vous obtenez immédiatement une exception en essayant d'utiliser un port série qui est déjà utilisé.Libérer le port série correctement

Depuis normalement le GC doit prendre soin de la plupart du travail que je suis un peu d'idées que d'autre pour essayer ...

Principalement j'ai essayé 2 choses (dans ma logique) devrait faire le travail. J'utilise une communication basée sur une session, donc j'appelle une méthode OpenPort et une méthode ClosePort avant et après chaque communication - donc le port doit être fermé. J'ai également essayé de définir mon objet contenant le port à null par la suite - mais je reçois toujours UnauthorizedAccessExceptions tout le temps - même si je suis sûr à 100% que la méthode SerialPort.Close() a été appelée. Connaissez-vous de meilleurs moyens de libérer les ports afin que j'arrête d'obtenir cette exception?

EDIT: Merci pour les réponses, mais la méthode Dispose() des choses ne fonctionne pas - j'avais essayé avant - peut-être que je fais quelque chose de mal mais est un exemple alors voici ce que mon code ressemble à:

il est en fait tout à fait comme Øyvind suggéré, bien que je viens d'ajouter le IDisposable - mais ne fonctionne pas non plus:

ce serait donc ma classe d'emballage:

class clsRS232 : IDisposable 
{ 
    public void clsRS232() 
    { 
    Serialport port = new Serialport("COM1",9600,Parity.none,8,Stopbits.one); 
    } 
    public void openPort() 
    { 
    port.Open(); 
    } 
    public void sendfunc(string str) 
    { 
    port.Write(str); 
    } 
    public string readfunc() 
    { 
    port.ReadTo("\n"); 
    } 

    public void Dispose() 
    { 
    port.Dispose(); 
    } 

} 

maintenant, chaque fois que je besoin de communication rs232 gique la nouvelle instance comme ceci:

clsRS232 test = new clsRS232; 
    test.openport(); 
    test.sendfunc("test"); 
    test.Dispose(); 

Mais cela ne change rien - je reçois encore beaucoup de UnauthorizedAccessExceptions - et si l'autre gars avait raison (Cédant() de SerialPort classe contient uniquement des SerialPort.Close ()) - eh bien je suppose que je n'ai pas vraiment changé quelque chose par rapport à ma précédente approche, où j'avais une fonction call close();

merci pour vos réponses - toujours dans l'espoir de trouver la solution :)

Répondre

6

Depuis SerialPort outils IDisposable, vous devez écrire votre code comme ceci:

using(SerialPort port = new SerialPort(...)){ 
    //do what you need with the serial port here 
} 

Cela fera en sorte que, à la fin de le bloc using, le port série est libéré, et si une exception se produit dans le bloc using il est libéré, car le bloc using est exactement le même qu'un bloc try/finally, qui ferme/dispose le SerialPort dans le bloc finally.

EDIT

Selon les besoins des OP du SerialPort devrait rester ouvert plus longtemps que le délai d'une méthode.

Dans ce cas, je voudrais envelopper toute la logique qui a à voir avec le port série à l'intérieur de sa propre classe. Dans le constructeur de la classe, ouvrez le port série et écrivez des méthodes pour effectuer les opérations dont vous avez besoin. Ensuite, implémentez cette classe IDisposable et disposez le SerialPort dans votre propre méthode Dispose.Cela vous permettra de mieux contrôler où vous devez ouvrir et fermer/disposer le port série et placer la logique du port série dans une classe appropriée. Si vous voulez garder le port ouvert pendant une période difficile à contenir par un bloc de code, vous devrez le disposer manuellement lorsque vous en avez fini, par exemple lorsque la fonction qui l'utilise fermé ou tout ce qui déclenche la libération du port COM dans votre programme.

EDIT 2

Votre implémentation actuelle est comme ceci:

clsRS232 test = new clsRS232; 
test.openport(); 
test.sendfunc("test"); 
test.Dispose(); 

Le problème ici est que si sendfunc provoque une exception en quelque sorte, il ne sera jamais disposé. Ce que vous gagnez de la mise en œuvre IDisposable en premier lieu, est que vous pouvez changer votre code pour ressembler à ceci:

using(clsRS232 test = new clsRS232){ 
test.openport(); 
test.sendfunc("test"); 
} 

Maintenant, vous êtes assuré que Dispose sera appelé pour votre port com indépendamment de toute exception à l'intérieur du using bloc.

+0

Merci pour la réponse rapide, mais je ne peux pas vraiment le faire de cette façon, car je ont enveloppé beaucoup de méthodes propres dans une classe propre, que j'utilise pour la communication série - ils utilisent tous le même port, qui est initialisé quand la classe est appelée pour la première fois - donc si je voulais le faire de cette façon, je finirais par écrire un bloc comme ça dans chaque méthode qui utilise le port non? – Lorenz

+0

Voulez-vous conserver le port ouvert à partir du moment où vous créez la classe et fermez-le lorsque toutes les opérations sont terminées ou souhaitez-vous ouvrir et fermer la connexion au port avant et après chaque opération? –

+0

le premier - ma classe wrapper démarre une instance de SerialPort via son constructeur - cela peut alors être utilisé via plusieurs méthodes pour envoyer/recevoir, etc mais une fois que j'appelle ma propre fonction close() (qui contient pour le moment SerialPort.Fermer()) Je veux que le Serialport soit complètement libre pour être à nouveau instancié. Ouvrir et fermer ebefore et après chaque opération ne fonctionnerait pas puisque par exemple j'aurais alors fermé le port après avoir écrit quelque chose et l'avoir ouvert à nouveau pour recevoir quelque chose -> le tampon aurait disparu ... – Lorenz

0

La mise en œuvre proposée par Øyvind Bråthen utilise le modèle IDisposable dans .NET. A la fin du bloc using, la fonction Dispose de l'instance SerialPort est appelée, ce qui libérera les ressources non managées associées (c'est-à-dire le port série)

Appelez port.Dispose() vous-même lorsque vous souhaitez le libérer.

+0

yep - avait essayé Dispose() avant - malheureusement il ne change rien à propos de mes exceptions:/Merci bien :) – Lorenz

2

Je sais que c'est très vieux, mais je suis juste tombé sur le même problème et cette solution a fonctionné pour moi, même si c'est un peu hacky. Selon ce fil why is access to com port denied? le problème est avec un bug dans le SerialPortClass. J'ai créé une classe wrapper qui n'ouvre le port qu'une seule fois et crée la classe pour la durée de vie de l'application. Le SerialPort est ensuite disposé dans la méthode Dispose de la classe, mais est ouvert à l'aide de ce qui suit:

private SerialPort KickerPort { get; set; } 
    . 
    . 
    . 
private bool OpenPort() 
     { 
      //https://stackoverflow.com/questions/7219653/why-is-access-to-com-port-denied 
      //due to a bug in the SerialPort code, the serial port needs time to dispose if we used this recently and then closed 
      //therefore the "open" could fail, so put in a loop trying for a few times 
      int sleepCount = 0; 
      while (!TryOpenPort()) 
      { 
       System.Threading.Thread.Sleep(100); 
       sleepCount += 1; 
       System.Diagnostics.Debug.Print(sleepCount.ToString()); 
       if (sleepCount > 50) //5 seconds should be heaps !!! 
       { 
        throw new Exception(String.Format("Failed to open kicker USB com port {0}", KickerPort.PortName)); 
       } 
      } 
      return true; 
     } 
    private bool TryOpenPort() 
       { 
        if (!KickerPort.IsOpen) 
        { 
         try 
         { 
          KickerPort.Open(); 
          return true; 
         } 
         catch (UnauthorizedAccessException) 
         { 
          return false; 
         } 
         catch (Exception ex) 
         { 
          throw ex; 
         } 

        } 
        return true; 
       } 

cela est appelé par:

try 
      { 
       if (OpenPort()) 
       { 
        //do your thing here ! 
       } 
       return false; 
      } 
      catch (Exception ex) 
      { 
       throw ex; 
      } 

Dans mes tests (je l'ai utilisé pour ouvrir un tiroir-caisse sur