En Windows XP no se puede acceder a los puertos de forma natural con las funciones C inport, inportb, outpor y outportb; o con asm desde el modo usuario:
__asm{ mov AX,3 // salida utilizando los pines 2 y 3 mov DX,0x378 // puerto paralelo 378h OUT DX,AX }
Para solventar el problema existen drivers y alguna utilidad encerrada en una dll.
Voy a colocar un código que funciona sin librerías ni drivers. El único requisito es que seamos Administradores del sistema. Para instalar un driver también necesitamos privilegios especiales, aunque no para usarlo después.
El truco está en usar una API poco conocida y que se usa para los debuggers, ZwSystemDebugControl que permite cierta comunicación entre el usuario y el Kernel.
Defino dos funciones para escribir y leer puertos, en este caso se leen y escriben BYTES. Las funciones se llaman, por similitud con las clásicas del C, InPortB y OutPortB.
Expongo un programa funcional de consola para ilustrar el tema, en este caso activaremos el Beep utilizando los puertos 42, 43, y 61. Puede utilizarse para leer o escribir el puerto paralelo (0x378), pero todo el mundo tiene altavoz del sistema y no todos disponen de dispositivos en el puerto paralelo tipo circuitos de diodos para comprobar los resultados.
Se debe preparar la aplicación para que enlace la librería advapi32.lib
//--------------------------------------------------------------------------- #pragma hdrstop //--------------------------------------------------------------------------- #pragma argsused //------------------------------------------------------------------------ #include <windows.h> #include <stdio.h> //#pragma comment(lib, "advapi32") //#pragma link "advapi32.lib" #define NTAPI __stdcall typedef int NTSTATUS; typedef enum _SYSDBG_COMMAND { SysDbgSysReadIoSpace = 14, SysDbgSysWriteIoSpace = 15 }SYSDBG_COMMAND, *PSYSDBG_COMMAND; typedef NTSTATUS (NTAPI * PZwSystemDebugControl) ( SYSDBG_COMMAND ControlCode, PVOID InputBuffer, ULONG InputBufferLength, PVOID OutputBuffer, ULONG OutputBufferLength, PULONG ReturnLength ); PZwSystemDebugControl ZwSystemDebugControl = NULL; typedef struct _IO_STRUCT { DWORD IoAddr; // IN: Aligned to NumBYTEs,I/O address DWORD Reserved1; // Never accessed by the kernel PVOID pBuffer; // IN (write) or OUT (read): Ptr to buffer DWORD NumBYTEs; // IN: # BYTEs to read/write. Only use 1, 2, or 4. DWORD Reserved4; // Must be 1 DWORD Reserved5; // Must be 0 DWORD Reserved6; // Must be 1 DWORD Reserved7; // Never accessed by the kernel } IO_STRUCT, *PIO_STRUCT; BOOL EnablePrivilege (PCSTR name) { HANDLE hToken; TOKEN_PRIVILEGES priv = {1, {0, 0, SE_PRIVILEGE_ENABLED}}; LookupPrivilegeValue(0, name, &priv.Privileges[0].Luid); OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken); AdjustTokenPrivileges (hToken, FALSE, &priv, sizeof priv, 0, 0); CloseHandle (hToken); return (GetLastError() == ERROR_SUCCESS); } BOOL Inicializa() { HMODULE hNtdll; hNtdll = LoadLibrary ("ntdll.dll"); if(EnablePrivilege(SE_DEBUG_NAME)==FALSE){ printf("Usted no tiene suficientes privilegios\n"); return FALSE; } if(hNtdll){ ZwSystemDebugControl = (PZwSystemDebugControl)GetProcAddress(hNtdll, "ZwSystemDebugControl"); return TRUE; } return FALSE; } BYTE InPortB(int Port) { BYTE Value; IO_STRUCT io; io.IoAddr = Port; io.Reserved1 = 0; io.pBuffer = (PVOID)(PULONG)&Value; io.NumBYTEs = sizeof(BYTE); io.Reserved4 = 1; io.Reserved5 = 0; io.Reserved6 = 1; io.Reserved7 = 0; ZwSystemDebugControl(SysDbgSysReadIoSpace, &io, sizeof(io), NULL, 0, NULL); return Value; } void OutPortB(int Port, BYTE Value) { IO_STRUCT io; io.IoAddr = Port; io.Reserved1 = 0; io.pBuffer = (PVOID)(PULONG)&Value; io.NumBYTEs = sizeof(BYTE); io.Reserved4 = 1; io.Reserved5 = 0; io.Reserved6 = 1; io.Reserved7 = 0; ZwSystemDebugControl(SysDbgSysWriteIoSpace, &io, sizeof(io), NULL, 0, NULL); }; void BeepOn (int Freq) { BYTE b; if((Freq >= 20) && (Freq <= 20000)){ Freq = 1193181 / Freq; b = InPortB(0x61); if((b & 3) == 0){ OutPortB (0x61, (BYTE) (b | 3)); OutPortB (0x43, 0xb6); } OutPortB (0x42, (BYTE)Freq); OutPortB (0x42, (BYTE)(Freq >> 8)); } } void BeepOff (void) { BYTE b; b = (InPortB(0x61) & 0xfc); OutPortB(0x61, b); } // Sólo funciona en Windows XP y superiores // Se debe ser Administrador del Sistema int main(void) { if(Inicializa()==FALSE){ printf("Error al inicializar los puertos\n"); Sleep (2000); return 0; } // Aquí podemos utilizar las funciones InPortB y OutPortB // Activamos el altavoz del sistema durante 300ms con una frecuencia // de 1000Hz BeepOn(1000); // frecuencia de 1000Hz Sleep (300); BeepOff(); return 0; }
Para el puerto paralelo, pondríamos li siguiente, basado en el anterior código:
Inicializa(); BYTE B = InPortB(0x378); // Lee del puerto paralelo OutPortB(0x378, 3); // Escribe en el puerto paralelo el valor 3
Saludos.