Apertura
di una porta seriale |
Il
primo passo da compiere per la gestione della comunicazione
seriale consiste nell'indicare all'applicazione la porta che
si desidera prendere in considerazione (ad esempio COM1, COM2,
LPT1 e così via). Tutti i programmi orientati verso
la comunicazione seriale dovrebbero quindi cominciare con
la funzione CreateFile.
Attraverso questa funzione infatti non solo viene aperta la
porta di comunicazione indicata, ma viene restituito il riferimento
all'oggetto (porta di comunicazione) che potrà essere
poi utilizzato dalle funzioni successive (nella fattispecie
ReadFile e WriteFile che vedremo nel corso dello speciale).
La dichiarazione della CreateFile è la seguente:
Declare
Function
CreateFile Lib "kernel32"
Alias "CreateFileA"
(ByVal _
lpFileName As String, ByVal
dwDesiredAccess As Long,
ByVal dwShareMode As
Long, _ lpSecurityAttributes As
SECURITY_ATTRIBUTES, ByVal
dwCreationDisposition As _
Long, ByVal
dwFlagsAndAttributes As Long,
ByVal hTemplateFile As
Long) As Long |
Come
si può notare il valore ritornato dalla funzione è
un intero proprio perchè il riferimento alla porta
viene espresso come Long.
Prima di continuare bisogna però specificare che la
CreateFile estende il proprio utilizzo anche a file e directory.
Per questo motivo alcuni parametri e costanti assegnabili
a questi sono del tutto inutili allo scopo di gestire una
comunicazione seriale e quindi molti di essi verranno ignorati.
La tabella sottostante spiega nel dettaglio il significato
di ogni parametro da associare alla CreateFile:
lpFileName
|
indica
il nome della porta da aprire. Sarà quindi sufficiente
indicare un nome come "COM1" (naturalmente racchiuso
tra le virgolette); |
dwDesiredAccess
|
come
il nome indica, è il tipo di accesso all'oggetto
porta di comunicazione. I tipi di accesso da indicare
sono espressi sotto forma di costanti:
0 acesso di tipo 'interrogazione della porta'. In pratica
l'applicazione richiede lo stato della porta senza accedervi
direttamente
GENERIC_READ accesso in lettura
GENERIC_WRITE accesso in scrittura
mentre gli ultimi due parametri sono associabili (specificando
GENERIC_READ Or GENERIC_WRITE per un accesso sia in lettura
che in scrittura), la costante 0 non permette alcun accesso
diretto; |
dwShareMode
|
indica,
sempre con l'utilizzo di costanti, se è possibile
un accesso multiplo alla porta. In caso positivo, dopo
aver aperto la porta con la funzione CreateFile, è
possibile accedervi senza limitazioni. In caso negativo
un ulteriore accesso è possibile soltanto dopo
la chiusura della porta oppure sotto alcune condizioni
indicate dalle costanti.
Le costanti sono:
0 non è possibile accedere alla porta se già
aperta
FILE_SHARE_READ sono possibili accessi successivi
alla porta solo a scopo di lettura
FILE_SHARE_WRITE sono possibili accessi successivi
alla porta solo a scopo di scrittura
Anche in questo caso è possibile combinare le costanti
limitando l'accesso alla porta a scopo di lettura-scrittura
in questo modo:
FILE_SHARE_READ Or FILE_SHARE_WRITE ; |
lpSecurityAttributes
|
è
un puntatore ad una struttura del tipo SECURITY_ATTRIBUTES
che indica se il riferimento alla porta è ereditabile
da altri sottoprocessi. E' possibile indicare la non ereditarietà
del riferimento e quindi indicare comodamente 0& oppure
NULL; |
dwCreationDisposition
|
indica
le operazioni che è possibile compiere sulla porta
di comunicazione. L'unica operazione possibile nel caso
di comunicazione seriale è:
OPEN_EXISTING che apre la porta solo se esistente.
In realtà essendo la funzione riferibile anche
ai file e alle directory, esistono altre costanti che
rappresentano ulteriori operazioni. Sarebbe però
del tutto inutile indicare ad esempio CREATE_NEW perchè
significherebbe chiedere all'applicazione di creare una
nuova porta di comunicazione. |
dwFlagsAndAttributes
|
permette
di indicare il tipo di comunicazione alla porta. Come
già abbiamo visto è possibile una comunicazione
sincrona o asincrona. Nel primo caso si potrà indicare
0, nel secondo FILE_FLAG_OVERLAPPED. In questa serie di
articoli si tratterà solamente la comunicazione
sincrona (non overlapped) quindi si potrà assegnare
0 al parametro; |
hTemplateFile
|
parametro
non riferibile alla comunicazione seriale. Indicare 0.
|
Risulta
così piuttosto semplice fare un primo esempio di apertura
di una porta di comunicazione e recupero del riferimento alla
stessa.
Dopo aver dichiarato la funzione indichiamo il nome della
porta da aprire ad esempio "LPT1" (primo parametro),
poi indichiamo il tipo di accesso ossia come vogliamo operare
una volta avuto accesso alla porta: GENERIC_READ Or GENERIC_WRITE
(secondo parametro). Non c'interessa nè la condivisione
della porta nè l'ereditarietà del riferimento
per cui possiamo indicare come terzo parametro 0 e come quarto
0&. Per il quinto parametro l'unica possibilità
è OPEN_EXISTING mentre per il sesto e settimo indichiamo
0 (comunicazione sincrona e parametro non necessario).
Prima di mostrare il codice per intero è però
necessario notare un particolare: nella dichiarazione della
CreateFile è indicata una struttura, la SECURITY_ATTRIBUTES:
Private
Type
SECURITY_ATTRIBUTES
nLength As Long
lpSecurityDescriptor As Long
bInheritHandle As Long
End Type |
Siccome
per la gestione della comunicazione seriale non si fa riferimento
a tale struttura è meglio non indicarla nella dichiarazione
della CreateFile cambiando il tipo di dato rappresentato dal
parametro lpSecurityAttributes da SECURITY_ATTRIBUTES a Long.
In conclusione, per utilizzare la CreateFile per la comunicazione
seriale utilizzare la seguente dichiarazione:
Declare
Function
CreateFile Lib "kernel32"
Alias "CreateFileA" (ByVal
_
lpFileName As String, ByVal
_
dwDesiredAccess As Long,
ByVal dwShareMode As
Long, lpSecurityAttributes As
_ Long, ByVal
dwCreationDisposition As Long,
ByVal dwFlagsAndAttributes
As Long, _ ByVal
hTemplateFile As Long) As
Long |
Ecco
quindi come aprire LPT1 e come ottenere in una finestra di
messaggio il riferimento alla porta:
Private
Declare Function
CreateFile Lib "kernel32"
Alias "CreateFileA"
(ByVal _ lpFileName As
String, _
ByVal dwDesiredAccess As
Long, ByVal dwShareMode
As Long, lpSecurityAttributes
_ As Long,
ByVal dwCreationDisposition
As Long,
ByVal dwFlagsAndAttributes As
_ Long, ByVal hTemplateFile
As Long) As
Long
Private
Sub Form_Load()
Dim nPorta As
Long 'l'intero di riferimento
della porta
nPorta = CreateFile("LPT1", GENERIC_READ Or
GENERIC_WRITE, 0, 0&, _ OPEN_EXISTING, 0, 0)
MsgBox nPorta
End Sub |
il
risultato ottenuto sarà di questo tipo:
Adesso
provare ad avviare nuovamente l'applicazione. La finestra
di messaggio visualizzerà il seguente risultato:
Perchè?
Non si deve dimenticare che abbiamo impostato il terzo parametro
della funzione ossia dwShareMode su 0 ossia il blocco di qualsiasi
tentativo successivo di accedere ad una porta già aperta.
E questo caso rappresenta un efficace esempio per comprendere
il significato del parametro. Per evitare problemi del genere
è necessario chiudere la porta alla chiusura dell'applicazione,
richiamando la funzione CloseHandle, la cui dichiarazione
è la seguente:
Declare
Function
CloseHandle Lib "kernel32"
(ByVal hObject As
Long) As Long |
E'
facile notare che qui l'unico parametro da associare alla
funzione è proprio il riferimento alla porta aperta
con la CreateWindow ossia, nell'esempio rappresentato dal
codice sopra, nPorta.
In questo caso non è necessario assegnare il valore
di ritorno della CloseHandle ad una variabile. Se quindi integriamo
il codice con questa nuova funzione per aprire e chiudere
la porta COM2:
Private
Declare Function
CreateFile Lib "kernel32"
Alias "CreateFileA"
(ByVal _
lpFileName As String,
ByVal dwDesiredAccess As
Long, ByVal dwShareMode
As Long, _ lpSecurityAttributes
As Long, ByVal
dwCreationDisposition As Long,
ByVal _ dwFlagsAndAttributes
As Long, ByVal
hTemplateFile As Long) As
Long
Private Declare Function
CloseHandle Lib "kernel32"
(ByVal hObject As
Long) As _ Long
Private
Sub Form_Load()
Dim nPorta As
Long 'l'intero di riferimento
della porta
nPorta = CreateFile("COM2", GENERIC_READ Or
GENERIC_WRITE, 0, 0&, _ OPEN_EXISTING, 0, 0)
MsgBox nPorta
CloseHandle nPorta
End Sub |
Adesso
ad ogni apertura dell'applicazione la finestra di messaggio
mostrerà lo stesso valore ossia il riferimento a COM2.
|