The task of developing custom applications that access custom hardware can be daunting for even the most seasoned application developer and especially for someone who has just learned developing applications using Visual Basic and similar RAD tools. Here in this article I present an alternative way that may help even the non-programmers to do what they want. That is to develop a custom UI and then access a specific piece of hardware that is connected to their machine.
Programming Custom Hardware for Windows - The Code (Page 3 of 4 )
Here is the easy part! Once we know what exactly what we need to do, the rest becomes easy. So I'll now take you on a quick walkthrough of the DLL code that I've written. You can always modify it for better performance or anything that you find suitable. For details about the device driver you'll need to read the article that I just mentioned above. The code in the DLL takes some code from the LOADDRV program by Paula Tomlinson.
Even if you're not concerned how it works and you just intend to use the DLL in your application I'd recommend browsing through the code once so that you'd be able to use it better in your own code.
The DLL contains a total of 11 Functions of which only 5 are exported (made available to applications using the DLL). First we'll discuss the functions that are not exported and later the exported ones. I've named the DLL and the workspace as RADHWLib a short name for Rapid Application Development for Hardware Library. The functions that are not exported are listed below with their descriptions.
DllMain This function is special because this allows us to perform DLL specific initialization/cleanup, as the DLL is loaded/unloaded. This is basically the first function that is invoked when the DLL is loaded and we use it to determine if the hosting OS is of NT family by using the GetVersion( ) API. If it is then we install the GiveIO driver and allow for unrestricted I/O which otherwise would not allow applications to execute I/O instructions. But now the task of I/O resides with our DLL. We should allow our DLL to use the I/O instructions by using the GiveIO driver by making a call to CreateFile( ) API. This will allow our DLL to execute privileged instructions and effectively will make the application written on top of it to access hardware directly.
InitSCM This function initializes the Service Control Manager using the OpenSCManager API. This returns a handle (a 32Bit value) that we use later to perform different operations regarding driver installation and service creation/deletion. This return value is stored in a global variable in the DLL.
ShutDownSCM This function just closes the handle we obtained in the InitSCM function using the CloseServiceHandle API.
AttachDrv If Windows NT is hosting the DLL this function is invoked and this opens the giveio device just created by the device driver in it's initialization routine. This will modify the IOPM (Input Output Port Map) so that our application, which is using our DLL, has unrestricted I/O even under Windows NT family of operating systems. This function tries to open the giveio device through the CreateFile( ) API and that in turn allows the driver to do a IoGetCurrentProcess( ) in order to access the address apace of the application using the DLL and modify the IOPM.
DriverInstall This function installs the giveio driver and returns 0(Zero) on success. You'll notice a magic value of 0x00000431 in the DllMain code. That is because of the fact that GetLastError( ) returns this value if the driver is already installed. It returns meaningful values as documented in the header file.
DriverRemove This function removes the giveio driver provided no applications are using our DLL. You'll notice a global variable nNumberOfApplications in the code. I've put this variable in a shared segment in the DLL so that this is not private to any application and holds the total number of applications that use our DLL. This can be useful if you write some code to manipulate the giveio driver.
Now we'll take a look at the functions that are exported from the DLL. These are of real concern to the developers because if a function is not exported it can't be invoked by any application that uses it.
OutPort This function is used to output a byte to a port. It takes two arguments the port number and the value to write to port. It internally uses the C run-time library _outp routine to do the real I/O and returns the value it gets back from it. As per the MSDN, the value that _outp returns is the data written to the port and that there is no error code.
InPort Just as with OutPort( ) this function reads a byte from a port specified in the function argument and returns it. It doesn't return any error code. It uses the CRT function _inp( ) for its implementation. The input value can be any unsigned short integer in the range 0 to 65,535 because the function _inp( ) is capable of reading byte, word, or double word from the specified port. (See points of interest for details)
DoDriverAction This function takes DDA_LOAD or DDA_UNLOAD as it's single argument and does the job of Installing or Uninstalling the giveio driver on request. It returns several meaningful values as documented in the header file. For making it clear I'd list them again here:
Driver is already installed
Driver installed successfully
Driver installation failed
Driver already Uninstalled
Driver uninstalled successfully
Driver uninstallation failed
Unknown ERROR: Driver not installed
Driver is in use and nNumberOfApplications is not zero
Table 1:DoDriverAction API return values
GetDriverStatus This function returns TRUE or FALSE depending on whether it is loaded or not. 0 implies not loaded and 1 implies that it is loaded.
GetDLLStatus This function returns the number of active applications that are using our DLL. As this returns a value that is kept in a shared section in DLL the value is transparent to all applications when this function is called.