One of the biggest problems encountered when using the Win32 API is a lack of good low level support for devices. DOS allows you to do anything you want, but Win32 runs in protected mode and will trap any access to device I/O instructions. Sure, you can use a new computer interface card, but only if the manufacturer has created a driver for the card. There are many specialized card companies out there that don't have 95 or NT drivers yet. Worse yet, suppose you can't obtain a commercial card and must design your own. The only solution in this case is to send about $800 to Microsoft for their specialized compiler and device development kits and hopefully you can figure out the documentation. Drivers written for Windows 95 won't work with Windows NT and Windows 98 and NT 5.0 are moving to a completely new driver model.
The excuse for this mess is protection of the operating system from the user. We are told that if the programmer can read and write any I/O port then they can operate the hard disk controller and either erase disks or read files that they shouldn't. While most of us use Windows 95 and could care less, this argument is mostly aimed at Windows NT users and server systems where security is an issue. While the ordinary Windows NT user will not be able to crash the system (in theory at least - the author has actually seen the impossible happen) they can still erase all their files. The assumption is also made that every device manufacturer will create bug free drivers that never crash. Real protection requires not only hardware and software protection features but proper implementation by the system operator.
It is our opinion that the difficulty involved in creating drivers could be simplified without decreasing security. Perhaps the only reason it is so complicated is to help sell expensive device development kits. Some of the solutions provided in the links at the end of this page indicate that device drivers can be easy to write if the support software is structured properly. However, we have found an even easier solution for programs that involve simple I/O operations. If you don't need to service interrupts or perform DMA operations, we have a solution for you.
There are many interface cards that can be completely controlled using I/O instructions. The Opto-22 PAMUX interface card is one such card. It consists of 64 I/O addresses that directly map to the field relay cards. This allows very fast scanning of your field I/O. The PIO, CTM and M5312 cards from Keithley-Metrabyte can be used this way too provided you don't need the interrupt functions.
There is a DriverLINX Port I/O driver available that allows you to read and write I/O ports in Windows 95 and NT Win32 programs. You will need to download the PORT95NT.EXE self-extracting file from the Keithley-Metrabyte FTP site here (ftp://ftp.keithley.com/pub/metrabyte/unsupport/). Scientific Software Tools created the program and can be found here (http://www.sstnet.com/). Don't expect support for this product from anyone, it is provided "as is." Run the file and read the instructions to load it onto your system. The software is freely distributable with your systems provided you follow the guidelines in the read-me file.
Because the driver model is changing in Windows 98 and NT 5.0, the DriverLINX Port I/O driver may not function in these releases. Future releases of Windows should provide backward compatibility, but only Microsoft knows for sure.
Some other helpful web sources:
Device Driver Development Information Page (http://www.chsw.com/ddk/)
Vireo Software, Inc. (http://www.vireo.com/)
The following source code provides a Delphi interface to the driver (note - you may use this source under the DriverLINX license restrictions):
unit DlPortIO_INTF; interface Function DlPortReadPortUchar(Port:LongInt):byte; stdcall; Function DlPortReadPortUshort(Port:LongInt):SmallInt; stdcall; Function DlPortReadPortUlong(Port:LongInt):LongInt; stdcall; Procedure DlPortReadPortBufferUchar(Port:LongInt; var Buffer:Pointer; Count:LongInt); stdcall; Procedure DlPortReadPortBufferUshort(Port:LongInt; var Buffer:Pointer; Count:LongInt); stdcall; Procedure DlPortReadPortBufferUlong(Port:LongInt; var Buffer:Pointer; Count:LongInt); stdcall; Procedure DlPortWritePortUchar(Port:LongInt; Value:byte); stdcall; Procedure DlPortWritePortUshort(Port:LongInt; Value:SmallInt); stdcall; Procedure DlPortWritePortUlong(Port:LongInt; Value:LongInt); stdcall; Procedure DlPortWritePortBufferUchar(Port:LongInt; var Buffer:Pointer; Count:LongInt); stdcall; Procedure DlPortWritePortBufferUshort(Port:LongInt; var Buffer:Pointer; Count:LongInt); stdcall; Procedure DlPortWritePortBufferUlong(Port:LongInt; var Buffer:Pointer; Count:LongInt); stdcall; implementation Function DlPortReadPortUchar; external 'DLPortIO.DLL'; Function DlPortReadPortUshort; external 'DLPortIO.DLL'; Function DlPortReadPortUlong; external 'DLPortIO.DLL'; Procedure DlPortReadPortBufferUchar; external 'DLPortIO.DLL'; Procedure DlPortReadPortBufferUshort; external 'DLPortIO.DLL'; Procedure DlPortReadPortBufferUlong; external 'DLPortIO.DLL'; Procedure DlPortWritePortUchar; external 'DLPortIO.DLL'; Procedure DlPortWritePortUshort; external 'DLPortIO.DLL'; Procedure DlPortWritePortUlong; external 'DLPortIO.DLL'; Procedure DlPortWritePortBufferUchar; external 'DLPortIO.DLL'; Procedure DlPortWritePortBufferUshort; external 'DLPortIO.DLL'; Procedure DlPortWritePortBufferUlong; external 'DLPortIO.DLL'; end.