Los puertos COM en Windows se tratan de manera similar a otros dispositivos (archivos en disco, pipes, etc.). Como cualquier otro dispositivo, un puerto necesita un manejador o 'Handle' para ser utilizado. Sería como un identificador del dispositivo. Este manejador te lo proporciona la función del API de Windows CreateFile() que, aunque parezca lo contrario, no se limita a "crear" y no trabaja sólo con "files", sino que permite hacer otras operaciones y con otros dispositivos. A esta función se le debe especificar el nombre del dispositivo a abrir (el archivo de disco, el puerto serie o paralelo, etc.).
Una vez obtenido el manejador, podemos leer y escribir en el dispositivo con las funciones del API de Windows ReadFile() y WriteFile(), respectivamente. Finalmente, se cierra el puerto con la función del API CloseHandle() (como se ve, demasiado 'file' para acabar con un 'handle': ¿porqué no un CloseFile()? Preguntadle a Microsoft :-)
En fin, ahí va un ejemplo de código:
uses
Windows; // Sobretodo
procedure TForm1.AbrirCom_Escribir_y_Leer;
const
NombrePuerto = 'COM2'; // Por decir algo
var
HandlePuerto: THandle; // Manejador para el puerto
Cadena: String; // Para leer del puerto
dwValor: DWORD; // Tamaño de la lectura
Sta: COMSTAT; // Estado o tamaño del buffer de lectura
bResult: Boolean; // Parámetro de resultado
begin
// ~~~~~~~~~~~~~~~~~~~
// Apertura del puerto
// ~~~~~~~~~~~~~~~~~~~
//
// El puerto ha de abrirse con acceso exclusivo. Es una de las razones de que haya
// un sólo "Acceso telefónico a redes" que actúa de servidor del puerto. Generalmente
// también se abre con acceso de lectura y escritura ya que es extraño tener que leer
// o escribir sólo en él. Pero, claro, esto depende de lo que vayas a hacer con el
// mismo. El "solapamiento" se produce sólo si hay varios procesos que pueden acceder
// al puerto a la vez y se utiliza cuando la aplicación tiene, por ejemplo, un hilo
// para leer y otro para escribir. Presupongo que no es el caso y que sólo hay un hilo.
// El último parámetro ('hTemplate') no tiene efecto para los puertos de comunicaciones.
//
HandlePuerto := Windows.CreateFile(
PChar(NombrePuerto), { Nombre del puerto }
GENERIC_READ or
GENERIC_WRITE, { Modo de apertura: Lectura/Escritura }
0, { Acceso exclusivo }
nil, { Sin atributos de seguridad }
OPEN_EXISTING, { El puerto debe existir }
0, { Sin solapamiento de Lectura/Escritura }
0); { No hay "hTemplate" }
// Comprobar que la apertura ha sido correcta
if (INVALID_HANDLE_VALUE <> HandlePuerto) then
begin
// Vamos a ver si hay algo que leer...
if (Windows.ClearCommError(HandlePuerto, dwValor, @Sta)) then
begin
if (0 < Sta.cbInQue) then //... pues sí
begin
// ~~~~~~~~~~~~~~~
// Leer del puerto
// ~~~~~~~~~~~~~~~
// Redimensionar la cadena de lectura
SetLength(Cadena, (Sta.cbInQue + 1));
bResult := Windows.ReadFile(
HandlePuerto, { Handle del puerto }
PChar(Cadena)^, { Puntero a los datos a leer }
Sta.cbInQue, { Tamaño del buffer de lectura }
dwValor, { Número de caracteres leídos }
nil); { No hay lecturas/escrituras solapadas }
if (bResult) then // Lectura correcta
begin
// Dimensionar la cadena al número de bytes leídos
SetLength(Cadena, dwValor);
// Aquí se puede hacer lo que se quiera con 'Cadena'
end;
end;
end;
// ~~~~~~~~~~~~~~~~~~~~~
// Escribir en el puerto
// ~~~~~~~~~~~~~~~~~~~~~
// En general, los puertos de comunicaciones esperan un retorno de carro
// tras lo que se envíe. Tiene que ver con los búferes del puerto: cuando
// se llena el buffer hasta un nivel o se recibe el retorno de carro se
// envía el contenido del buffer al puerto. Así que, para asegurarse, se
// termina la cadena a escribir por esos caracteres:
Cadena := 'Probando la escritura' + #13#10;
if (Windows.WriteFile(
HandlePuerto, { Handle del puerto }
PChar(Cadena)^, { Datos a escribir }
Length(Cadena), { Longitud de los datos }
dwValor, { Número de caracteres escritos }
nil) { Sin solapamiento de lecturas/escrituras }
)
then
begin
// Aquí nos damos con un canto en los dientes:
// la 'Cadena' se ha escrito en el puerto. Podemos comprobarlo si
// miramos 'dwValor' ya que debe ser igual a 'Length(Cadena)'.
// Si no lo es, algo ha ido mal.
end;
// ~~~~~~~~~~~~~~~~
// Cerrar el puerto
// ~~~~~~~~~~~~~~~~
Windows.CloseHandle(HandlePuerto);
end;
end;
|