Telemat Lab's home page


Copyrigtht © 1997 Università di Firenze. All rights reserved.

Free license available.


PROGRAMMI DI COMUNICAZIONE CON WINDOWS 95

a cura di Massimo Pollini, Samuele Nasorri, Riccardo Antonelli


Hyperbook Indice Prec. Succ.

Cap.2: Trasferimento dati con le API di Windows 95.(1/3)

Sommario.

In questo capitolo vengono descritte le API di Win32 necessarie per aprire e configurare una porta seriale. Vengono inoltre descritte varie tecniche per leggere e scrivere dati su una porta.


TRASFERIMENTO DATI CON LE API DI COMUNICAZIONE WIN32 

Per realizzare un trasferimento dati seriale in Windows 95, si utilizzano le API DI COMUNICAZIONE Win32 o brevemente Win32c. Tuttavia Win32c non si limita al solo trasferimento dati, ma è in grado di gestire completamente la porta seriale.
Storicamente il modem e la porta seriale sono strettamente correlati: tutti i trasferimenti di dati su linea telefonica coinvolgono la porta seriale. Per comprendere una comunicazione seriale è essenziale capire la comunicazione del modem in Windows.
Il modem tipicamente spedisce i dati in modo sequenziale sulla linea telefonica, ma Win32c può anche spedire dati su un device parallelo come la porta LPT.
E' difficile generalizzare quando si usa Win32. Esso, per esempio, non si adatta bene alla comunicazione su LAN (Local Area Network). Le LAN spesso serializzano i dati da spedire in modo simile al modem, ma dovranno tener conto di tecniche di accesso al canale, routing, protocolli a correzione d'errore che hanno poco in comune con i protocolli di una porta seriale. La complessità dei protocolli richiesti per le LAN fanno si che Win32c sia poco più che un'idea per questo tipo di applicazioni.
Un altro limite di Win32c è quello di non poter lavorare con comunicazioni real-time, nelle quali la qualità della riproduzione è strettamente legata al tempo. Per esempio, se vogliamo digitalizzare l'audio, la qualità del suono dipende dal rate di riproduzione. Quando l'audio è registrato (ad esempio su un CD ROM) viene digitalizzato con un particolare rate, noto come sampling rate. Ovviamente il segnale deve poi essere riprodotto tenendo conto della frequenza di campionamento utilizzata.
Anche i segnali video sono dipendenti dal tempo: la velocità di riproduzione deve essere opportuna. Supponiamo di voler riprodurre un file audio (usando le Windows Multimedia Extensions), contenente, ad esempio, un messaggio registrato, direttamente sulla linea telefonica. Non si può usare Win32c proprio perchè non è in grado di trasmettere l'audio in tempo reale (questo anche se disponiamo di un modem voice). Quello che si può certamente fare, però, è trasmettere come file audio il messaggio e simultaneamente inviare un segnale di controllo al computer locale collegato all'altra estremità invitandolo a riprodurre tale file usando, ad esempio, Microsoft Sound Recorder (accessorio di Windows 95).
Quindi non sono i file di tipo suono o audio come tali che precludono l'utilizzo di Win32c, ma è il fatto che richiedono una trasmissione in tempo reale ad ostacolarne la realizzazione.
Escludendo LAN, TCP/IP e comunicazioni in real-time, ci sono comunque moltissime altre applicazioni adatte a Win32c. Praticamente tutte le applicazioni che richiedono una porta seriale o un modem sono delle possibili candidate; includendo e-mail, fax, file transfer e applicazioni interattive come la terminal emulation.
Applicazioni come quelle appena citate sono felicemente supportate dalle MAPI (Messaging API).
Quindi si consiglia di usare Win32c per tutte le comunicazioni interattive, non in real-time, che non richiedano molti livelli di protocollo. Attualmente questo consiglio non è stato ancora ben recepito. Win32c non ha ancora assunto un ruolo centrale nella trasmissione di e-mail, faxes e di file, anche se, come abbiamo già detto, grazie alle MAPI queste funzioni verrebbero svolte ottimamente.
Ci sono comunque delle applicazioni dove è quasi obbligatorio utilizzare Win32c, come nel caso in cui si debba scrivere drivers MAPI per comunicazioni su area geografica, nel caso dei terminali interattivi, dei servizi online o dei multiplayer games.

 


2.1 CONOSCENZE DI BASE PER LA COMUNICAZIONE SERIALE

 La seguente trattazione presuppone la conoscenza di alcuni concetti basilari quali il baud rate, bits di start e di stop, parità. Si presuppone inoltre una certa familiarità con le operazioni base e i segnali dell'interfaccia seriale RS-232 (EIA RS-232). Di seguito, comunque, riportiamo un breve richiamo dei segnali più importanti.


2.1.1 APRIRE UNA PORTA SERIALE

 In Windows 16-bit, sono previste funzioni speciali per aprire, chiudere, leggere e scrivere da una porta seriale. Queste funzioni sono OpenComm, CloseComm, ReadComm, WriteComm rispettivamente. In Windows 95, invece, le porte seriali e gli altri dispositivi di comunicazione sono trattati allo stesso modo dei files. La porta seriale è aperta, chiusa, letta, scritta con le stesse funzioni usate per i files.
Una sessione di comunicazione inizia con una chiamata alla funzione CreateFile. Tale funzione "apre" una porta seriale per una lettura, per una scrittura o per entrambe contemporaneamente. Come sempre in Windows, CreateFile ritorna un gestore (handle) da usare nelle successive operazioni sulla porta aperta. La funzione come si può vedere sotto è complessa, questo anche perchè è general-purpose. Infatti si utilizza tale funzione sia per aprire un file esistente, sia per crearne uno nuovo, ma anche per aprire una periferica, che non è un file, come una porta seriale o parallela o un modem.

 CreateFile(szDevice, fdwAccess, fdwShareMode, lpsa, fdwCreate, fdwAttrsAndFlags, hTemplateFile);

Il primo parametro szDevic è il nome logico della porta seriale aperta come COM1 o COM2. Il secondo è fdwAccess il quale specifica il tipo di accesso alla porta. Come per un file, la porta può essere aperta in lettura, scrittura o per entrambe. Il flag GENERIC_READ apre la porta in lettura, GENERIC_WRITE apre la porta in scrittura. Per averle entrambe basta collegare con un OR entrambi i flag come sotto:

fdwAccess = GENERIC_READ | GENERIC_WRITE

Il terzo parametro, fdwShareMode, specifica gli attributi di condivisione di una porta. Questo parametro è usato per i file che possono essere condivisi da più applicazioni. Esso deve essere impostato a 0 per la porta seriale, che non deve essere condivisa. Questa è una delle principali differenze tra una porta seriale e un file. Se un'altra applicazione ha già aperto la porta seriale quando la corrente applicazione chiama CreateFile, la funzione ritorna un errore perchè due applicazioni non possono condividere la porta seriale. Tuttavia, più percorsi (threads) della stessa applicazione possono condividere il gestore (handle) della porta ritornato dalla funzione CreateFile.
Il quarto parametro lpsa, punta ad una struttura di attributi sulla sicurezza che definisce come il gestore della porta può essere utilizzato dai "figli" dell'applicazione che ha aperto la porta.
Il quinto parametro fdwCreate, specifica cosa fare se la funzione CreateFile richiama un file già esistente. Quando una porta seriale è già attiva esso deve essere configurato con OPEN_EXISTING. Questo flag indica a Windows di non creare una nuova porta, ma di aprirne una già esistente.
Il sesto parametro, fdwAttrsAndFlags, descrive vari attributi della porta. Per i files sono possibili molti attributi, ma per la porta seriale solo uno è interessante FILE_FLAG_OVERLAPPED. Quando è specificato la porta di I/O lavora in background.
L'ultimo parametro, hTemplateFile, specifica il gestore di un file archiviato e non è usato per aprire una porta, quindi è impostato a NULL.
Quando una porta è stata aperta, possono essere allocati un buffer di trasmissione e ricezione e altre informazioni di inizializzazione, richiamando la funzione SetupComm come mostrato sotto. Non è essenziale richiamare tale funzione, in quanto Windows alloca il buffer in trasmissione e in ricezione di default. Comunque è buona norma assicurarsi che il buffer abbia le dimensioni opportune.

 SetupComm(hComm, dwRxBufSize, dwTxBufSize)

hComm è il gestore della porta aperta rimandato da Createfile. Il secondo parametro è la dimensione del buffer in ricezione e il terzo di quello in trasmissione.

 


2.1.2 CHIUDERE UNA PORTA SERIALE

 Chiudere una porta seriale è molto più semplice che aprirla. Basta semplicemente chiamare la funzione CloseHandle con il gestore rimandato da CreateFiles come unico parametro.

CloseHandle(hComm);

Ricordarsi sempre di chiudere la porta altrimenti le altre applicazioni non saranno in grado di usarla.


2.2. CONFIGURARE UNA PORTA SERIALE

 Configurare una porta seriale è più complicato in Windows 95 rispetto alle precedenti versioni di Windows. Tuttavia ciò consente di avere maggiori potenzialità, permettendo una maggiore integrazione tra porta seriale e modem.
La configurazione inizia determinando la capacità della porta. In questo modo si evita che un'applicazione utilizzi una configurazione non corretta. Le capacità di una porta sono referenziate nella struttura COMMPROP.

 


2.2.1 LA STRUTTURA COMMPROP

 COMMPROP è probabilmente la struttura più interessante della famiglia delle strutture dati di Windows 95 Comunication. Essa contiene le possibili configurazioni per la porta seriale; ad esempio i valori possibili per il baud rate, il numero bits dei dati, il metodo per stabilire la parità. Se la porta è connessa con un modem, essa contiene anche la configurazione del modem. Per avere tali informazioni la porta deve essere aperta in modo opportuno.

 


2.2.1.1 Come ottenere le proprietà della porta

 La funzione GetCommProperties, mostrata sotto, insieme alla COMMPROP consente di ottenere le informazioni desiderate sulla porta seriale. Il loro utilizzo però non si limita alla porta seriale, infatti, ad esempio, possono essere usate anche per ottenere informazioni sulla porta parallela.
Il contenuto di COMMPROP è statico (nessuna applicazione può modificare i dati di questa struttura), quindi non c'è nessuna funzione che consenta di scrivere in tale struttura la configurazione della porta.

 GetCommProperties (hComm,lpCommProp)

I due parametri della funzione sono il gestore della porta e il puntatore alla struttura COMMPROP mostrata sotto:

typedef struct _COMMPROP {

} COMMPROP, *LPCOMMPROP;

 Si può notare che l'ultimo membro della struttura, chiamato wcProvChar è dichiarato come un array di caratteri, anche se consiste di un unico carattere. Questa strana costruzione si può trovare spesso in Windows, per esempio quando si programma Device Indipendent Bitmaps (DIBs). Tale membro deve essere interpretato come un byte di partenza per ulteriori dati che possono essere appesi alla struttura. In alcuni casi esso è utilizzato dall'applicazione per allocare memoria per dati extra. Con COMMPROP, l'applicazione può allocare memoria per dati extra, ma è Windows, non l'applicazione, che stabilisce le dimensioni effettive. Ciò può causare dei problemi perchè l'applicazione deve allocare memoria per la struttura COMMPROP e per gli eventuali dati extra, ma non conosce quanta memoria Windows destinerà ai dati extra.
Ci sono due vie per risolvere questo problema: una via diretta e una "pigra". La via "pigra" che spreca memoria, ma risulta rapida e semplice, è quella di allocare più memoria di quanta ne potrebbe richiedere Windows. COMMPROP di per sé richiede soltanto 64 bytes, quindi se, per esempio, alloco 1000 bytes (come mostrato sotto) sono sicuro di garantire una sufficente area di memoria per ogni plausibile dato extra che Windows mi può rimandare.

 LPCOMMPROP lpCommProp = (LPCOMMPROP)malloc (1000);

 Fortunatamente c'è una via migliore. Essa consiste nel richiamare due volte la funzione GetCommProperties come mostrato sotto. La prima volta si richiama la funzione con il campo wPacketLength contenente il numero totale di bytes richiesti dalla struttura, compresi i dati extra. L'applicazione controlla questo valore per allocare la memoria opportunamente e chiama nuovamente GetCommProperties per ricevere i dati extra.

 LPCOMMPROP lpCommProp = (LPCOMMPROP)malloc (sizeof(COMMPROP));
lpCommProp.wPacketLength = sizeof(COMMPROP);
GetCommProperties(hComm,lpCommProp);
dwSize=lpCommProp.wPacketLength;
free(lpCommProp);
lpCommProp = (LPCOMMPROP)malloc (dwsize);
lpCommprop.wPacketLength = dwSize;
GetCommProperties(hComm, lpCommProp);

 Come si può vedere sopra è necessario, prima di chiamare la funzione la prima volta, impostare wPacketLength con la sizeof(COMMPROP): Questo perchè Windows assumerà proprio il numero di bytes stabiliti da wPacketLength come riferimento per allocare lo spazio di memoria destinato ai dati (se destinassi più memoria del necessario potrei incorrere in problemi di overwrite). Windows successivamente imposta wPacketLength col numero di bytes richiesti per la struttura COMMPROP, compresi i dati extra. Infine l'applicazione rialloca opportunamente lo spazio di memoria per la struttura e richiama nuovamente GetCommProperties.

 


2.2.1.2 Definizione delle propietà della porta

 Finora abbiamo visto come richiamare la sruttura COMMPROP e abbiamo descritto il primo e l'ultimo campo (wPacketLength e wcProvChar) della struttura. Analizziamo adesso gli altri campi.
Il campo wPacketLength specifica la versione della struttura COMMPROP. Versioni differenti di Windows e di altri sistemi operativi Microsoft utilizzano versioni diverse della struttura COMMPROP con differenti interpretazioni per i vari membri. E' quindi opportuno testare questo membro per dare una corretta interpretazione alla struttura. In Windows 95, la versione è configurata a 0xffff.
Il membro dwServiceMask definisce le funzionalità della porta. Per la porta seriale è impostato a SP_SERIALCOMM.
dwMaxTxQueue e dwMaxRxQueue contengono rispettivamente le dimensioni massime consentite (in bytes) per i buffer di trasmissione e ricezione. Se questo membro risulta essere 0 significa che non ci sono limitazioni sulle massime dimensioni.
dwMaxBaud è il massimo data rate che la porta può supportare, in bits per secondo (bps). Questo membro può essere fissato o programmato. Nel caso in cui sia programmabile il campo è impostato a BAUD_USER. Altrimenti è impostato ad uno dei valori mostrati sotto.

dwProvSubType specifica il tipo di periferica di comunicazione in modo più dettagliato rispetto a dwServiceMask. Per la porta seriale questo campo è impostato a PST_RS232. Altre periferiche supportate sono riportate sotto.

dwProvCapabilities descrive la capacità della porta. Vediamole in dettaglio:

L'interfaccia seriale RS-232 e il driver della porta seriale di Windows supportano la maggior parte delle possibilità specificate sopra ad eccezione di PCF_16BITMODE e di PCF_SPECIALCHAR.
Il membro dwSettableParams specifica quali tipi di impostazione per la porta sono configurabili.
I valori dei flags e le rispettive interpretazioni sono mostrate sotto.

dwSettableParams è uno dei membri più importanti da testare per un'applicazione. E' infatti buona norma testare la configurazione esistente prima di apportarne qualunque modifica.
dwSettableBaud, wSettableData e wSettableStopParity contengono le possibili impostazioni per il baud rate, il numero di bits di stop, il parity mode.
dwCurrentTxQueue e dwCurrentRxQueue contengono le dimensioni correnti dei buffer in trasmissione e in ricezione, rispettivamente.
I membri dwProvSpec1, dwProvSpec2 e wcProvChar non sono usati con la porta seriale RS-232. I primi due membri non sono usati neppure dal modem; invece wcProvChar con il modem assume un particolare significato che vedremo più avanti.