Opening and Closing a Stream Driver by Using the File API
To access a stream driver, an application can use the CreateFile function and specify the desired device name. The following example illustrates how to open a driver called SMP1: for reading and writing. It is important to note, however, that Device Manager must already have loaded the driver, such as during the boot process. Lesson 3 later in this chapter provides detailed information about configuring and loading device drivers.
// Open the driver, which results in a call to the SMP_Open function
hSampleDriver = CreateFile(L'SMP1:',
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hSampleDriver == INVALID_HANDLE_VALUE) {
ERRORMSG(1,(TEXT('Unable to open the driver.
'));
return FALSE;
}
// Access the driver and perform read,
// write, and seek operations as required.
// Close the driver
CloseHandle(hSampleDriver);
Dynamically Loading a Driver
As mentioned earlier in this lesson, an application can also communicate with a stream device driver after calling the ActivateDevice or ActivateDeviceEx function. ActivateDeviceEx offers more flexibility than ActivateDevice, yet both functions cause Device Manager to load the stream driver and call the driver's XXX_Init function. In fact, ActivateDevice calls ActivateDeviceEx. Note, however, that ActivateDeviceEx does not provide access to an already loaded driver. The primary purpose of the ActivateDeviceEx function is to read a driver-specific registry key specified in the function call to determine the DLL name, device prefix, index, and other values, add the relevant values to the active device list, and then load the device driver into the Device Manager process space. The function call returns a handle that the application can later use to unload the driver in a call to the DeactivateDevice function.
ActivateDeviceEx replaces the older RegisterDevice function as a method to load a driver on demand, as illustrated in the following code sample:
// Ask Device Manager to load the driver for which the definition
// is located at HKLMDriversSample in the registry.
hActiveDriver = ActivateDeviceEx(L'\Drivers\Sample', NULL, 0, NULL);
if (hActiveDriver == INVALID_HANDLE_VALUE) {
ERRORMSG(1, (L'Unable to load driver'));
return -1;
}
// Once the driver is lodaded, applications can open the driver
hDriver = CreateFile (L'SMP1:',
GENERIC_READ| GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hDriver == INVALID_HANDLE_VALUE) {
ERRORMSG(1, (TEXT('Unable to open Sample (SMP) driver')));
return 0; //Insert code that uses the driver here
}
// Close the driver when access is no longer needed
if (hDriver != INVALID_HANDLE_VALUE) {
bRet = CloseHandle(hDriver);
if (bRet == FALSE) {
ERRORMSG(1, (TEXT('Unable to close SMP driver')));
}
}
// Manually unload the driver from the system using Device Manager
if (hActiveDriver != INVALID_HANDLE_VALUE) {
bRet = DeactivateDevice(hActiveDriver);
if (bRet == FALSE) {
ERRORMSG(1, (TEXT('Unable to unload SMP driver ')));
}
}
Calling ActivateDeviceEx to load a driver has the same result as loading the driver automatically during the boot process through parameters defined in the HKEY_LOCAL_MACHINEDriversBuiltIn key. The BuiltIn registry key is covered in more detail in Lesson 3 later in this chapter.
Lesson Summary
Stream drivers are Windows Embedded CE drivers that implement the stream interface API. The stream interface enables Device Manager to load and manage these drivers, and applications can use standard file system functions to access these drivers and perform I/O operations. To present a stream driver as a file resource accessible through a CreateFile call, the name of the stream driver must follow a special naming convention that distinguishes the device resource from ordinary files. Legacy names (such as COM1:) have a limitation of ten instances per driver because they include only a single-digit instance identification. If you must support more than ten driver instances on a target device, use a device name instead (such as $deviceCOM1).
Because Device Manager can load a single driver multiple times to satisfy the requests from different processes and threads, stream drivers must implement context management. Windows Embedded CE knows two