Displaying a Configuration Dialog Example

Easy Driver Pro

PC Driver Downloader and Updater Software

Get Instant Access

Many of the multimedia devices on your system have configuration dialogs. The configuration data affects the quality of the device output, as well as its capabilities in many situations. In some cases, you might want the user to configure a device before using it to play a sound or perform some other multimedia task. The configuration is important because you want to provide the user with the best possible multimedia experience. When you want to perform this type of task, you need to work with the driver directly, which means opening a driver handle using the OpenDriver() function.

Obtaining a device handle sounds almost too easy when you first consider it. You use the OpenDriver() function, which accepts three inputs. The first argument is mandatory. It's the device driver name. The second argument is optional and usually unnecessary for modern drivers. It defines the location of the driver information in the registry. The third argument is device-driver specific. Only supply this information if the vendor documentation for the driver requires it.

The main problem with the OpenDriver() function is finding the required device driver name, especially if you aren't privy to the vendor documentation (which few of us are). The Platform SDK documentation provides a hint, but only a small hint when it tells you that the default location is the Drivers32 key. The actual location of this information is the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Drivers32 key in most cases. Figure 11.5 shows that there are two wave devices in this case: wave and wave1. To determine which device to use, you'd need to match the driver to a specific piece of hardware using the entries in the Device Manager.

Figure 11.5: The registry provides the device names you need to use with the OpenDriver() function.

The example application shows how to open a device, display the associated configuration dialog, and then close the device so another application can access it. Listing 11.1 shows the code that you'll need. You'll find the source code for this example in the \Chapter 11\C#\ConfigDlg and \Chapter 11\VB\ConfigDlg folders of the CD.

Listing 11.1: Displaying a Device Configuration Dialog

// This function opens a device for use. There are two forms this // function can take. The first form should only include the driver // name. Set the IntPtr value to IntPtr.Zero and the lParam to 0. The // second form can include just a section name or both a section name // and an lParam.

[DllImportCWinMM.DLL", CharSet=CharSet.Auto, SetLastError=true )] public static extern IntPtr OpenDriver(String lpDriverName,

IntPtr NoSectionName, Int32 NoParam);

[DllImportCWinMM.DLL", CharSet=CharSet.Auto, SetLastError=true )] public static extern IntPtr OpenDriver(String lpDriverName,

String lpSectionName, Int32 lParam);

// This function sends a message to the driver. The message will // normally require one or two lParam values.

[DllImportCWinMM.DLL", CharSet=CharSet.Auto, SetLastError=true )] public static extern Int32 SendDriverMessage(IntPtr hdrvr,

UInt32 msg, Int32 lParaml, Int32 lParam2);

// Use this enumeration to define which driver message to send.

public enum DrvMsg : uint {

DRV LOAD

= 0x0001

DRV ENABLE

= 0x0002

DRV OPEN

= 0x0003

DRV CLOSE

= 0x0004

DRV DISABLE

= 0x0005

DRV FREE

= 0x0006

DRV CONFIGURE

= 0x0007

DRV_QUERYCONFIGURE DRV_INSTALL DRV_REMOVE DRV_EXITSESSION DRV_POWER DRV_RESERVED DRV_USER

// This structure is used with DRV_CONFIGURE the message.

public struct DRVCONFIGINFO {

public Int32 dwDCISize;

public String lpszDCISectionName;

public String lpszDCIAliasName;

// This function closes a driver handle previously opened using the // OpenDriver() function. Don't pass anything for the lParam values // unless the driver documentation requests it.

[DllImport("WinMM.DLL", CharSet=CharSet.Auto, SetLastError=true )] public static extern Int32 CloseDriver(IntPtr hdrvr,

Int32 lParaml, Int32 lParam2);

private void btnTest_Click(object sender, System.EventArgs e) {

IntPtr hDriver; // Handle to the driver.

Int32 Result; // Results of a call.

DRVCONFIGINFO DCI; // The driver configuration data.

IntPtr DCIPtr; // Pointer to the DCI

// Open the driver handle.

hDriver = OpenDriver(txtDevice.Text, IntPtr.Zero, 0);

// Check for errors.

MessageBox.Show("Couldn't obtain driver handle!", "Application Error", MessageBoxButtons.OK, MessageBoxIcon.Error);

return;

// Determine whether the device supports a capabilities // dialog box.

Result = SendDriverMessage(hDriver,

(UInt3 2)DrvMsg.DRV_QUERYCONFIGURE, 0, 0);

// Check for a configuration dialog.

MessageBox.Show("No configuration dialog available!", "Application Result", MessageBoxButtons.OK, MessageBoxIcon.Information); CloseDriver(hDriver,0,0);

0x0008, 0x0009, 0x000A, 0x0 0 0B, 0x0 0 0F, 0x0800, 0x4000

return;

// Display the configuration dialog.

DCI = new DRVCONFIGINFO();

DCI.dwDCISize = Marshal.SizeOf(DCI);

DCIPtr = Marshal.AllocHGlobal(DCI.dwDCISize);

Marshal.StructureToPtr(DCI, DCIPtr, true);

Result = SendDriverMessage(hDriver,

(UInt3 2)DrvMsg.DRV_CONFIGURE, 0,

DCIPtr.ToInt32());

// Free the allocated memory. Marshal.FreeHGlobal(DCIPtr);

// Close the driver handle. CloseDriver(hDriver, 0, 0);

As you can see, the example provides for two forms of the OpenDriver() function—the form you use depends on the needs of the device driver. However, the first form works, in most cases, because the driver names are stored in the Drivers32 key and most drivers don't require any additional input.

The SendDriverMessage() function requires at least two pieces of input: the handle of the device and a message value. The SendDriverMessage() function can also accept one or two lParam values to send as part of the message. We'll see later in the code that these two values are often optional and that you must pay attention to the placement of values that are required. The DrvMsg enumeration contains all of the messages that you can send to a device driver. Generally, it's better to create a single enumeration that covers all possibilities than to create a specific enumeration for each potential use. The SendDriverMessage() function only works with the following messages:

• DRV_CONFIGURE

• DRV_QUERYCONFIGURE

One of the messages we'll work with in this example does require additional information in the form of the DRVCONFIGINFO structure. The DRV_CONFIGURE message tells the device driver to display its configuration dialog. The requestor must provide the size of the data structure as a minimum. The data structure also contains entries for a custom section of the registry (if the driver isn't in the Drivers32 section) and an alias name for the driver. Neither of these two entries is required, even if you supply the structure.

As with all other forms of handle access, you must close the driver before the application exits. The effects of not doing so, in this case, are especially noteworthy because they're so severe. In most cases, the user will lose access to the device. In addition, the application will lose access to the handle memory, causing a small memory leak within Windows. Finally, in several cases, the loss of device access could result in system failure. Closing the handle isn't an option or something to forget in this situation. Unfortunately, the .NET Framework can't recover for you, so it's up to the developer to ensure proper application execution.

The code for this example begins by opening the driver. It examines the handle for a null value. If the value is null, it's more than likely that the system or another application has already opened the device. Of course, you could have supplied a nonexistent device name as well, so it's important to check spelling and capitalization if you receive the same error more than once. The application will display an error message and exit the method when it receives a null value.

Not every device provides a configuration dialog, so the code uses the SendDriverMessage() function to output a DRV_QUERYCONFIGURE message to the driver. In this case, we don't need to supply any additional information. If the driver returns 0, then it doesn't provide support for a configuration dialog and the method exits. Notice that you must free the device handle before the method exits.

The code can finally display the configuration dialog. However, to do this, it must provide a pointer to a DRVCONFIGINFO data structure, even if the structure is blank. Of course, this brings up the question of how to pass the data structure to a message when the message only accepts an integer as input. The code shows one technique for accomplishing this task. It creates the data structure and fills in the one required field that contains the size of the structure. The code then allocates an IntPtr that points to unmanaged memory of the same size of the data structure. At this point, the Marshal.StructureToPtr() method can convert the managed memory into a pointer to unmanaged memory. Finally, during the SendDriverMessage() call, the code converts the pointer to an Int32 value. At this point, the device driver will display a configuration dialog box similar to the one shown in Figure 11.6.

Microsoft IMA ADPCM CODEC Confieurdtton

r?f5<!

MMsuruMKncconvman«*

Decofflpwecn [Sirtfit hâoCoritoM

1 <* 1

Caned |

1 1

Figure 11.6: The device driver will display its configuration dialog box after it receives the DRV_CONFIGURE message.

In this case, I chose the msacm.imaadpcm device to display the Microsoft IMA ADPCM CODEC Configuration dialog box, which adjusts the Interactive Multimedia Association (IMA) Adaptive Differential Pulse Code Modulation (ADPCM) compression/decompression (CODEC) module. The Microsoft Audio Compression Manager (MSACM) actually includes several device entries in the Drivers32 section of the registry and you'll find the associated code in the MSACM32.DLL file. You might wonder why anyone would adjust this feature. In this case, it's a matter of performance versus resource usage. A higher compression rate requires fewer resources and transfers better over slow media such as a modem connection. On the other hand, a lower compression rate performs better on slower machines with connections to a high-speed local network.

The final two steps of the example include freeing the unmanaged memory and freeing the device handle. Both steps are essential to free resources that the Garbage Collector can't work with. We've covered these two steps quite a few times in the book already, so there's no need to go into detail again.

Was this article helpful?

0 0

Post a comment