Книга: Writing Windows WDM Device Drivers
Win32 Device Interface Access
Разделы на этой странице:
Win32 Device Interface Access
We are ready — at last — to access a Wdm1 device. The Wdm1Test program opens a connection to the Wdm1 driver and puts it through its paces. Remember that all the calls in this section are to Win32 routines, not to the kernel.
The source code for the Wdm1Test program is in the Wdm1exe directory. The book software has a Visual Studio WDM Book workspace, which includes a project for the Wdm1Test program. The Wdm1Test code in Wdm1Test.cpp is listed at the end of the chapter.
Wdm1Test is a standard Win32 console application. When run, it appears in a DOS box. As it is a standard Win32 program, you can debug it in Visual Studio as normal (e.g., step through the code).
There are two points to note about the Wdm1Test project. The first is that it includes the c:98ddkincwin98setupapi.h header file. The VC++ 5 version of this file is seriously out of date, so the code specifically includes the Windows 98 DDK version. The second special setting required is to ensure that c:98ddklibi386freesetupapi.lib is listed in the Link property page Output/library modules section of the Project settings.
Getting a Device's Interface Name
The GetDeviceViaInterface routine in Wdm1Test.cpp opens a handle to a device, given the device interface's GUID, as shown in Listing 5.5. GetDeviceViaInterface has a second parameter, instance, which is the zero-based index into the count of available devices. The total number of devices cannot be determined in advance; simply call GetDeviceViaInterface until NULL is returned.
The Wdm1Test main function calls GetDeviceViaInterface with WDM1_GUID and 0 as parameters to open the first available Wdm1 device. Special steps must be taken again to ensure that WDM1_GUID is declared properly. In one module #include "initguid.h" before including the GUID header file.
Listing 5.5 GetDeviceViaInterface
HANDLE GetDeviceViaInterface(GUID* pGuid, DWORD instance) {
// Get handle to relevant device information set
HDEVINFO info = SetupDiGetClassDevs(pGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
if (info==INVALID_HANDLE_VALUE) {
printf("No HDEVINFO available for this GUIDn");
return NULL;
}
// Get interface data for the requested instance
SP_INTERFACE_DEVICE_DATA ifdata;
ifdata.cbSize = sizeof(ifdata);
if (!SetupDiEnumDeviceInterfaces(info, NULL, pGuid, instance, &ifdata)) {
printf("No SP_INTERFACE_DEVICE_DATA available for this GUID instance n");
SetupDiDestroyDeviceInfoList(info);
return NULL;
}
// Get size of symbolic link name
DWORD ReqLen;
SetupDiGetDeviceInterfaceDetail(info, &ifdata, NULL, 0, &ReqLen, NULL);
PSP_INTERFACE_DEVICE_DETAIL_DATA ifDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA)(new char[ReqLen]);
if (ifDetail==NULL) {
SetupDiDestroyDeviceInfoList(info);
return NULL;
}
// Get symbolic link name
ifDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
if (!SetupDiGetDeviceInterfaceDetail(info, &ifdata, ifDetail, ReqLen, NULL, NULL)) {
SetupDiDestroyDeviceInfoList(info);
delete ifDetail;
return NULL;
}
printf("Symbolic link is %sn",ifDetail->DevicePath):
// Open file
HANDLE rv = CreateFile(ifDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
delete ifDetail;
SetupDiDestroyDevicelnfoList(info);
return rv;
}
The Win32 call to SetupDiGetClassDevs opens a "device information set" about devices with the specified GUID. The DIGCF_PRESENT and DIGCF_INTERFACEDEVICE flags ensure that only devices that are present are found.
SetupDiEnumDeviceInterfaces is then called to retrieve a SP_INTERFACE_DEVICE_DATA context structure of information about the device instance in which you are interested. GetDeviceViaInterface tries to retrieve information about only one instance, but you might want to find all instances by incrementing the MemberIndex parameter from 0 until SetupDiEnumDeviceInterfaces fails and GetLastError returns ERROR_NO_MORE_ITEMS.
The next task is to obtain the symbolic link name for the instance that has been found. This string is in the PSP_INTERFACE_DEVICE_DETAIL_DATA ifDetail structure returned by a call to SetupDiGetDeviceInterfaceDetail. SetupDiGetDeviceInterfaceDetail must be called twice. The first call retrieves the required size for ifDetail, while the second actually gets the structure.
Eventually ifDetail->DevicePath contains the filename that is used to open a handle to the relevant Wdm1 device. This filename is passed to CreateFile and then ifDetail is deleted. Do not forget to call SetupDiDestroyDeviceInfoList on all paths to close the device information set.
The call to CreateFile in GetDeviceViaInterface uses a pretty standard set of parameters. As noted before, you can change the share and access modes, if necessary. Further, the device can be opened for overlapped nonblocking access. Overlapped access works for devices in Windows 98, NT, and Windows 2000 and lets an application issue an I/O request and get on with other work while the request is being processed. The detailed explanation of the DebugPrint Monitor program in Chapter 14 gives an example of this technique.
Running Wdm1Test
Simply run Wdm1Test.exe in the Wdm1exeRelease directory. Make sure that the Wdm1 driver is installed. Wdm1Test is a console application running in a DOS box. Press the Enter key to exit the program.
If you run Wdm1Test in W2000, the output should look like that given in Listing 5.6. The first time round, Test 2 is designed to fail; subsequent tests should succeed. In Windows 98, the output is different, as it does not seem to support the SetFilePointer function on devices, such as Wdm1.
The rest of the Wdm1Test main function performs each of these tests in turn.
1. Open the first Wdm1 device.
2. Read the first DWORD stored in the shared memory buffer. This will fail first time round, as there will be nothing in the buffer.
3. Write 0x12345678 to the start of the buffer. 0x78 is written at file pointer zero, 0x56 at one, 0x34 at two, and 0x12 at three.
4. Set the file pointer to position 3.
5. Read one byte from the file. This should be 0x12.
6. Write 0x12345678 to the buffer starting at file position 3.
7. Use an IOCTL to get the buffer size, which should be 7 (i.e., 3+4).
8. Use an IOCTL to get the entire buffer. The first word is printed and should be 0x78345678.
9. Check that issuing an IOCTL with an invalid parameter fails. The IOCTL asks for the entire buffer with a request size that is too big.
10. Use an IOCTL to zero all the bytes in the buffer. Get the entire buffer again. The first word is printed which should be 0x00000000.
11. Use an IOCTL to remove the buffer. Check that the buffer size is now zero.
12. Try to issue an invalid IOCTL code. Confirm that this fails.
13. Write 0xabcdef01 to the start of the buffer. When Wdm1Test is run next, Test 2 should find this value.
Listing 5.6 Wdm1Test output on W2000
Test 1
Symbolic link is ?root#unknown#0003#{c0cf0640-5f6e-11d2-b677-00c0dfe4c1f3}
Opened OK
Test 2
Read successfully read stored value of 0xABC0EF01
Test 3
Write 0x12345678 succeeded
Test 4
SetFilePointer worked
Test 5
Read successfully read stored value of 0x12
Test 6
Write at new file pointer succeeded
Test 7
Buffer size is 7 (4 bytes returned)
Test 8
First DWORD of buffer is 78345678 (7 bytes returned)
Test 9
Too big get buffer failed correctly 87
Test 10
Zero buffer succeeded
First DWORD of buffer is 00000000 (7 bytes returned)
Test 11
Remove buffer succeeded
Buffer size is 0 (4 bytes returned)
Test 12
Unrecognised IOCTL correctly failed 1
Test 13
SetFilePointer worked Write 0xabcdef01 succeeded
Test 14
CloseHandle worked
Press enter please
Wdm1Test exercises all the functions of the Wdm1 driver. It checks that things that should work do work, and things that should fail do fail.
Writing this test program showed that the Wdm1 device does behave differently in Windows 98 and Windows 2000. However, the problem does not lie in the driver itself, but is due to Windows 98 not supporting SetFilePointer for device files.
- Chapter 5 Device Interfaces
- Appendix C Direct Memory Access
- Device Driver Context
- Lesson 2: Implementing a Stream Interface Driver
- Registry Settings to Load Device Drivers
- Практическая работа 53. Запуск Access. Работа с объектами базы данных
- InterBase Super Server для Windows
- Интеграция с платформой Windows NT
- Часы в Windows показывают неправильное время
- Классическая архитектура на Windows NT (Yaffil CS)
- 4.2. Центр уведомлений Windows 10
- Chapter 15. Graphical User Interfaces for Iptables