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;
Ważne artykuły