Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

GetLastError() retourne 6 après CreateFile("COM1",...)

8 réponses
Avatar
Bertrand Lenoir-Welter
Salut, désolé c'est ma journée de questions

J'ai une appli qui dialogue avec un automate via un port série ouvert
par CreateFile(). Ca marche bien quel que soit le port, vrai série ou
convertisseur USB -> RS232 (au passage je recommande le convertisseur
Sitecom CN-104 après avoir eu des déboires avec pas mal d'autres).
Lorsqu'on ferme l'appli, elle fait un CloseHandle() pour libérer le
port. Jusqu'ici, tout va bien.

Seulement sur certains PC, heureusement assez rares, et quelle que soit
la version de Windows (en tout cas ça le fait aussi bien avec 98 que
XP), il arrive que CreateFile() échoue lorsque l'appli a été lancée pour
la 3ème ou 4ème fois - parfois plus - avec INVALID_HANDLE_VALUE
retournée. Si je redémarre Windows, ça remarche bien, à nouveau pour 3
ou 4 fois, puis ça replante. GetLastError() me retourne la valeur 6 que
FormatMessage() me transcode en "Descripteur non valide", ce qui m'en
dit pas beaucoup plus.

La communication avec l'automate est simplissime et je ne crée donc pas
un thread pour gérer la communication (la lecture bloquante ne gêne
pas). En simplifiant, mon ouverture du port est à peu près comme suit :

char ComStr[5]="COM1";
HANDLE
ComHandle=CreateFile(ComStr,GENERIC_READ|GENERIC_WRITE,0,0,OPEN_EXISTING,0,0);
if(ComHandle==INVALID_HANDLE_VALUE)
{
DWORD ErrCode=GetLastError(); // récupère alors la valeur 6
...
}

Je présume que soit il y a des paramètres à préciser dans CreateFile()
que je ne connais pas, soit il y a des bricoles à faire au préalable
pour demander à Windows de nettoyer le port - au Kärcher comme dirait
l'autre.

Toute idée bienvenue, once again. Merci d'avance.

A++

8 réponses

Avatar
David MAREC
Bertrand Lenoir-Welter a écrit :

J'ai une appli qui dialogue avec un automate via un port série ouvert
par CreateFile().
Seulement sur certains PC, heureusement assez rares, et quelle que soit
la version de Windows (en tout cas ça le fait aussi bien avec 98 que
XP), il arrive que CreateFile() échoue lorsque l'appli a été lancée pour
la 3ème ou 4ème fois - parfois plus - avec INVALID_HANDLE_VALUE
retournée.




Je dialogue avec des automates de la même manière, par une écriture
légèrement différente :

/***/
// Ici, j'utilise les MFC.
HANDLE h ::CreateFile((LPCTSTR)sPort,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);

if(h == INVALID_HANDLE_VALUE)
{
sPort.Format(_T("\.COM%d"),m_ComPort); //Format sous NT.
h= ::CreateFile((LPCTSTR)sPort,
GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
if(h== INVALID_HANDLE_VALUE)
{
// gestion d'erreur
}
}

if(!::SetupComm(h, // handle of communications device
INPUT_BUFFER_SIZE, // = 1024
OUTPUT_BUFFER_SIZE // = 1024
) )
{
// gestion d'erreur
}


DCB dcb;
// Ici vient la configuration
::memset(&dcb,0,sizeof(dcb));

// TimeOuts
COMMTIMEOUTS cto;
// ...
SetTimeouts(&cto);
// ...
::SetCommState(h,&dcb);
/***/

Utilisez vous ::ClearComError() et ::PurgeComm() ?

Si vous soupçonnez une discordance "matérielle" sur ces machines,
essayez de nettoyer les signaux avec ::EscapeCommFunction();
Avatar
Bertrand Lenoir-Welter
David MAREC:

// Ici, j'utilise les MFC.
HANDLE h > ::CreateFile((LPCTSTR)sPort,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);



Je suis toujours sur mon vieux Borland, mais je pense que mon appel de
CreateFile() est identique. Et c'est là que ça coince, pas dans la suite
des événements. Je pige pas pourquoi CreateFile() retourne
INVALID_HANDLE_VALUE au bout d'un certain nombre de relances du programme.


if(h == INVALID_HANDLE_VALUE)
{
sPort.Format(_T("\.COM%d"),m_ComPort); //Format sous NT.
h= ::CreateFile((LPCTSTR)sPort,
GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
if(h== INVALID_HANDLE_VALUE)
{
// gestion d'erreur
}
}



Pourquoi ce 2ème essai avec les même arguments ? Que fait cette fonction
Format() sous NT ? Bon, de toute façon, mon problème se pose aussi bien
sous NT que 95...


Utilisez vous ::ClearComError() et ::PurgeComm() ?



Non, je m'en suis jamais servi. Ce qui est curieux, c'est que j'ai un
autre soft qui dialogue aussi par port série avec un autre type
d'automate et lui n'a pas ce problème. Bon, faut dire que ça s'adresse
pas aux mêmes utilisateurs.

Apparemment, ClearCommError(), ClearCommBreak(), PurgeComm(), etc. ont
besoin d'un handle, justement. Vous voulez dire qu'il faut les appeler
avant de faire CloseHandle() et fermer le programme ? Bon, je vais
essayer d'ajouter tout ça... Y'a un ordre préférentiel ?


Si vous soupçonnez une discordance "matérielle" sur ces machines,
essayez de nettoyer les signaux avec ::EscapeCommFunction();



Non, c'est vraiment le CreateFile() qui veut pas. J'ai un moniteur
espion sur la communication, et rien d'anormal sur les échanges de
télégrammes précédents.


Grand merci pour ton aide en tout cas.
Avatar
David MAREC
Bertrand Lenoir-Welter a écrit :

// Ici, j'utilise les MFC.
HANDLE h >> ::CreateFile((LPCTSTR)sPort,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);



Je suis toujours sur mon vieux Borland, mais je pense que mon appel de
CreateFile() est identique.



Quasiment, oui. Avec LPCTSTR, je suis sensé géré Unicode, c'est tout.

Et c'est là que ça coince, pas dans la suite
des événements. Je pige pas pourquoi CreateFile() retourne
INVALID_HANDLE_VALUE au bout d'un certain nombre de relances du programme.



Je dois dire que je n'ai jamais constaté ce problème.
Faites vous une configuration complète du port, ::SetCommState,
::SetupComm() et ::SetCommTimeouts() ?



if(h == INVALID_HANDLE_VALUE)
{
sPort.Format(_T("\.COM%d"),m_ComPort); //Format sous NT.
h= ::CreateFile((LPCTSTR)sPort,
GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL);
if(h== INVALID_HANDLE_VALUE)
{
// gestion d'erreur
}
}




Pourquoi ce 2ème essai avec les même arguments ? Que fait cette fonction
Format() sous NT ?



Sous NT, le port COM peut s'adresser de cette façon, < .COM1 >;
Enfin, je crois, puisque je n'en trouve plus la trace dans la MSDN.

Apparemment, ClearCommError(), ClearCommBreak(), PurgeComm(), etc. ont
besoin d'un handle, justement. Vous voulez dire qu'il faut les appeler
avant de faire CloseHandle() et fermer le programme ?



Oui.
Il faut appeler ::ClearCommError() pour basculer les drapeaux après
chaque erreur, sinon, le port n'autorise plus d'opération.
En fait, il est aussi utilisé pour lire l'état du port après une
opération ou attendre la présence d'un signal sur le port.


do
{
::ClearCommError(h,&dw_Err,&s_comstat);
} while(s_comstat.cbOutQue==0 ); // && !TimeOut, le cas échéant.



PurgeCom() devrait être appelé avant la fermeture du port pour vider les
buffers et je ne me suis jamais servi de ClearCommBreak().

Si vous soupçonnez une discordance "matérielle" sur ces machines,
essayez de nettoyer les signaux avec ::EscapeCommFunction();




Non, c'est vraiment le CreateFile() qui veut pas.



Est-ce que vous pouvez utiliser le port avec un autre logiciel, après
les plantage? voire même avec un
$ echo test > com1 .
Avatar
Bertrand Lenoir-Welter
> Je dois dire que je n'ai jamais constaté ce problème.
Faites vous une configuration complète du port, ::SetCommState,
::SetupComm() et ::SetCommTimeouts() ?



SetCommState() -> oui, bien sûr, pour le protocole
SetupComm() -> non (jamais eu besoin)
SetCommTimeouts() -> oui, je règle ici mon timeout de lecture


Sous NT, le port COM peut s'adresser de cette façon, < .COM1 >;
Enfin, je crois, puisque je n'en trouve plus la trace dans la MSDN.



Je crois que je vais négliger cette étape. A mon avis, le problème vient
d'ailleurs, probablement de la façon dont je ferme le port et termine le
programme. Je fais juste un CloseHandle() dans le destructeur de ma
fenêtre d'appli, et rien d'autre. Peut-être qu'il faut y ajouter les
fonctions Kärcher que vous m'avez indiquées. Je vais aller essayer sur site.


Il faut appeler ::ClearCommError() pour basculer les drapeaux après
chaque erreur, sinon, le port n'autorise plus d'opération.
En fait, il est aussi utilisé pour lire l'état du port après une
opération ou attendre la présence d'un signal sur le port.



Avant mon CloseHandle(), j'envoie un dernier télégramme pour remettre
l'automate au repos. Ce télégramme part bien, et il est tout aussi bien
acquitté. Donc il n'y a pas d'erreur à ce moment-là. A la limite, ça se
produirait sur tous les PC et tout le temps ; pas juste sur quelques uns
au bout de 3 ou 4 exécution du programme. Curieux, non ?


do
{
::ClearCommError(h,&dw_Err,&s_comstat);
} while(s_comstat.cbOutQue==0 ); // && !TimeOut, le cas échéant.



Je vais l'ajouter, quoi qu'il en soit. Merci David pour tous ces tuyaux.


Est-ce que vous pouvez utiliser le port avec un autre logiciel, après
les plantage? voire même avec un
$ echo test > com1 .



Ah ouais, bonne question. J'ai même pas essayé l'HyperTerminal, focalisé
que j'étais sur mon programme. Je cours essayer !
Avatar
Johann.D
"Bertrand Lenoir-Welter" a écrit dans le message
de news:42e0e10b$0$25028$


> Est-ce que vous pouvez utiliser le port avec un autre logiciel, après
> les plantage? voire même avec un
> $ echo test > com1 .

Ah ouais, bonne question. J'ai même pas essayé l'HyperTerminal, focalisé
que j'étais sur mon programme. Je cours essayer !



Oui, si le problème survient également avec HT, ça ressemblerait beaucoup à
un problème au niveau physique de la liaison RS232. J'ai déjà remarqué avec
un équipement qui tient la ligne RX (du PC) à 0, tant que l'application
tourne on détecte bien ça comme un break (et on peut l'acquitter par
ClearCommBreak) et si ça se prolonge comme une erreur. Par contre quand
l'application est fermé et que personne ne s'occupe du port, un break
prolongé conduit aux symptomes que tu décris (plus d'accès au port -
CreateFile qui renvoie INVALID_HANDLE_VALUE).

Mes 2 centimes...

--
Johann Dantant / Pro-Active
www.pro-active.fr
Avatar
David MAREC
Bertrand Lenoir-Welter a écrit :

Sous NT, le port COM peut s'adresser de cette façon, < .COM1 >;
Enfin, je crois, puisque je n'en trouve plus la trace dans la MSDN.




Je crois que je vais négliger cette étape. A mon avis, le problème vient
d'ailleurs, probablement de la façon dont je ferme le port et termine le
programme.




Ce qui me gène, c'est que je ne me souviens plus pourquoi je fais ce
deuxième appel.
Je ne trouve pas mention de cette écriture dans la MSDN, du moins par
pour les ports de type série. Pire, d'après ce que je lis, cette
écriture n'est pas valide pour un port de communication.

Je fais juste un CloseHandle() dans le destructeur de ma
fenêtre d'appli, et rien d'autre. Peut-être qu'il faut y ajouter les
fonctions Kärcher que vous m'avez indiquées. Je vais aller essayer sur
site.



Un ::PurgeComm() suivi d'un ::ClearCommError() suffiraient.


Avant mon CloseHandle(), j'envoie un dernier télégramme pour remettre
l'automate au repos. Ce télégramme part bien, et il est tout aussi bien
acquitté. Donc il n'y a pas d'erreur à ce moment-là.



En fait, l'idéal est d'appeler cette fonction lors de chaque erreur sur
le port, ce qui implique de contrôler chaque appel des fonctions de
communication.

A la limite, ça se
produirait sur tous les PC et tout le temps ; pas juste sur quelques uns
au bout de 3 ou 4 exécution du programme. Curieux, non ?



Oui, c'est pourquoi je soupçonne un désagrément matériel;
comme l'indique Johann.D, un signal serait resté 'actif' sur certains
type de cartes série. D'où l'idée d'un appel à ::EscapeCommFunction()
avant de fermer le port.

Il se peut d'ailleurs que les appels de ::PurgeComm() et
ClearCommError() avant le ::CloseHandle() l'invoque implicitement.

A ce propos, constatez vous ce problème lorsque vous communiquez via le
controleur USB ?
Avatar
Bertrand Lenoir-Welter
Bon, j'ai trouvé le bug. Désolé, c'est évidemment une bêtise de ma part.
1ère Loi de Brooks (je crois) : "toute erreur attribuée à l'ordinateur
repose sur deux erreurs humaines au moins, dont celle qui consiste à
accuser l'ordinateur."

Ce qui se passe : dans mon destructeur de fenêtre d'appli, j'envoie un
dernier télégramme pour dire à l'automate de se mettre au repos,
l'automate acquitte la commande, je fais le CloseHandle() et le
programme se termine.

L'erreur, en fait sur tous les PC, mais j'avais pas compris que certains
utilisateurs éteignaient l'automate PUIS quittaient le programme,
l'erreur, donc, c'était que le soft attendait indéfiniment que la
commande soit acquittée pour se terminer (je boucle s'il n'y a pas
d'acquittement, sauf si l'utilisateur fait Esc, plus d'autres cas de
figure). Comme la fenêtre était déjà détruite, donc plus rien à l'écran,
je pensais naïvement que le programme était terminé - grave erreur, il
était toujours vivant et avait toujours le handle sur le port COM. Donc,
quand on relançait, le port était déjà pris par le fantôme du programme
toujours vivant. Evidemment, relancer Windows nettoie tout ça.

On reprend ce que dit l'utilisateur, avec toutes les corrélations qu'il
a faites "au bout du 3ème ou 4ème lancement, sur certains PC seulement,
etc." et on se rend pas compte que, au 3ème ou 4ème lancement, il a
quitté le soft APRES avoir éteint l'automate. Conclusion : faut jamais
se lancer à la poursuite d'un bug sur des rails bien huilés et préparés
d'avance.

Ouais, je sais, c'est du niveau d'un étudiant en 1ère année d'IUT. Je
dois vieillir, que voulez-vous...

Merci quand même pour tous les tuyaux. Au moins, en plus de l'étendue de
mes oeillères, j'ai appris sur ClearCommBreak(), ClearCommError() et
PurgeComm().
Avatar
halfwolf
David MAREC wrote in message news:<dbr0n1$1pnv$...
Ce qui me gène, c'est que je ne me souviens plus pourquoi je fais ce
deuxième appel.
Je ne trouve pas mention de cette écriture dans la MSDN, du moins par
pour les ports de type série. Pire, d'après ce que je lis, cette
écriture n'est pas valide pour un port de communication.



Salut,

Peut être la KB 115831 (port supérieurs à COM9).
Cette écriture est valide pour les devices, y compris les ports de comm.

HalfWolf