Perdon Daniel, no se si entendi bien. Pero tu quieres que salte un error cuando ingresas una letra o quiere que a medida que estas escribiendo el DNI no te deje escribir letras?...
Y el tema de vacio no lo entiendo por que si lees un integer si o si tenes que ingresar algo. Si pones Readln(dni) (suponiendo que dni sea el integer), si vos apretas enter sin escribir nada sigue esperando que ingreses algo, o hasta si pones un espacio... por eso no entendi lo de vacio...
Pero bueno, supongamos que vos querias que cuando escribas tu dni si este tuviese letras deberia saltar un error.
Bueno eso es facil bajo la directiva $I, es un viejo truco que te salva varias veces. Si ocurre un problema en tiempo de ejecucion, sea por excederte en la capacidad de un arreglo, o superar el maxint, etc (cualquier error que te cerraria el programa) y esta activada esta directiva el programa se va a cerrar, en cambio si la desactivas no lo hara, lo que no quiere decir que el programa va a andar bien, la idea de usar esto es detectar ese error y corregirlo inmediatamente.
Cuando hay un error el programa guarda en una variable llamada IOResult (es un integer) el tipo de error detectado, en un buen libro puedes encontrar esa lista de errores, pero no ahce falta que te la sepas, solo debes saber que cuando no hay error esa variable toma el valor de cero. Esta es una explicación para que entiendas el siguiente codigo.
PROGRAM prueba;
USES
Crt;
VAR
dni : INTEGER;
result : INTEGER;
BEGIN
{$I-}
READLN(dni);
result:=IOResult;
{$I+}
IF (result<>0)OR(dni=0)
THEN
WRITELN('Error, se han ingresado caracteres no numericos o no se ha ingresado ningun DNI');
READKEY;
END.
Yo aca tambien tome como "vacio" si se ponia el DNI 0.
Si no es lo que necesitas explica un poco mejor y seguro que te podemos ayudar.