Tech Tips

Installing a USB Filter Driver
Alan MacInnes Accessing IDL ref Types as C++ References
Matthew Wilson

Accessing Old List-View HeadersMatthew Wilson

Avoiding the MIDL Semantic Analysis BugMatthew Wilson



August 01, 2003
URL:http://www.drdobbs.com/tech-tips/184416679

Tech Tips Tips

Download the code for this issue

Installing a USB Filter Driver

Alan MacInnes
[email protected]

Installing a WDM (Windows Driver Model) filter driver for a USB device requires that just a few lines be added to the .INF file, which is used to install its primary device driver. In the example .INF file provided (Listing 1), FILTER.SYS will be installed as an "upper" filter driver to SESUSB.SYS. Three things need to be accomplished by these additional lines to the .INF file. First, the filter driver image itself, FILTER.SYS, must be copied to the %systemroot%\system32\drivers folder. Second, an entry must be added to the registry at HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services to define the filter driver as a service. Finally, a registry value must be added to the entry within HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Enum/USB for these particular USB Vendor ID and Product ID values. This is to specify that there is an upper filter driver that must be loaded into the device driver stack for this particular device. (This .INF file was tested with FILTER.SYS, which is the sample USB filter driver provided in the Microsoft DDK).

Accessing IDL ref Types as C++ References

Matthew Wilson
[email protected]

The Interface Definition Language can, in common with C, manipulate function arguments as by-value or by-reference, where by-reference parameters are expressed in the form of pointers.

When such by-reference parameters cannot be NULL, the parameter is marked with the [ref] attribute, as in the IInterface::NonNullMethod in Listing 2.

This means that the marshaling code, and the implementing class's code, can always rely on a valid nonNULL pointer pss to transmit and use. However, in C/C++ client code, there is nothing to prevent a NULL from being passed (with the attendant crash following shortly thereafter). (Note that [ref] and another pointer attribute [ptr] cannot be applied to interface pointers, as they are always assumed to be [unique], and specifies such leads to the MIDL compiler ignoring the attribute and giving the MIDL2034 warning. However, the concepts described here may still be applied usefully to interface pointers, in so far as providing the convenience of the use of references to interface-implementing objects.)

In C++, the reference is a very useful syntactic construct that provides the programmer with the ability to pass-by-reference while still appearing to be using object instances themselves, as opposed to pointers to them. Furthermore, since it is illegal — and in practice usually takes a deliberate effort — to pass a NULL reference, it is a very useful way for programmers to express to client programmers this semantic in their APIs.

Because reference arguments exhibit the same type conversion and, where applicable, polymorphic abilities as do pointers, it would be very useful to be able to pass reference parameters to interface methods that have been defined as being of [ref] type. This can be achieved with a simple trick on the part of the MIDL compiler's preprocessor commands.

It would be nice when compiling for C++ if the pss and pii2 parameters to NonNullMethod() would be references, without that causing an issue to the MIDL compiler. The technique for this is very simple, and relies on using the MIDL cpp_quote keyword to insert preprocessor code for the C/C++ compile, not for the MIDL compile, as in Listing 3.

The use of cpp_quote to insert post-MIDL compile-time preprocessor instructions for the C/C++ compiler allows IDL and C/C++ to see different definitions of types. Because a C++ reference is equivalent to a pointer (in terms of what happens at the instruction level), the technique allows one to change the parameter type. It should be noted that great care must be taken to get the respective definitions correct, and to make sure they stay in sync as the IDL source evolves, or nasty things can happen.

Despite this technique having an inherent danger in IDL, it can help increase the safety of interface-using C++ code. It is clear how much more convenient this is, as well as its affording an additional level of type safety by enforcing the use of (C++) references to the interface's method's (IDL) reference parameters. For example, if one had wished to wrap SOMESTRUCT into a class SomeStruct and had a class Class2 implementing IOther, the use of the IInterface interface with these types is very simple, as in Listing 4.

The only caveat is that one must ensure that the cpp_quote code is correct, and current, should the interface method change (though I am sure none of our good readers would ever change an interface except prior to its initial release).

Accessing Old List-View Headers

Matthew Wilson
[email protected]

The list-view common control, in report mode (window style contains LVS_REPORT), has a header control. This control is accessed via the LVM_GETHEADER message, or the macro ListView_GetHeader (which wraps a sending of the LVM_GETHEADER message), which takes no parameters and simply returns the window handle of the header control.

Unfortunately, old versions of the common control library (comctl32.lib) do not handle this message, requiring the following function, ListView_GetHeaderCtrl(), which searches for the child header control if the parent list-view does not recognize the LVM_HEADER message.

HWND ListView_GetHeaderCtrl(HWND hwnd)
{
#ifndef LVM_GETHEADER
#define LVM_GETHEADER   (LVM_FIRST + 31)
#endif

    /* Attempt the LVM_GETHEADER message */
    HWND    hwndChild = (HWND)SendMessage(hwnd,       LVM_GETHEADER, 0, 0L);

    if(hwndChild  ==  NULL)
    {
       /* NULL returned so attempt a search */
        HWND    hwndFirst;

        hwndChild = GetWindow(hwnd, GW_CHILD);
        hwndFirst = hwndChild;

        do
        {
            CHAR    szCls[200];

            if( GetClassNameA(hwndChild,                szCls, sizeof(szCls)) &&
                lstrcmpiA(szCls, WC_HEADERA) == 0)
            {
                /* Found it! */
                break;
            }

        } while((hwndChild = GetWindow(hwndChild, GW_HWNDNEXT)) != NULL &&
                hwndChild != hwndFirst);
    }

    return hwndChild;
}

The function has been compiled with Visual C++ 2.0, 4.0, 4.2, 5.0, and 6.0, and Borland C++ 4.52 and 5.5, and tested on Windows 95, 98, NT 4, and Windows 2000.

Avoiding the MIDL Semantic Analysis Bug

Matthew Wilson
[email protected]
.

The Microsoft IDL (MIDL) compiler provides a number of facilities for defining and manipulating types borrowed from C. One of these, typedef, is used to create aliases of existing types (or of previously defined aliases), usually for clarity/brevity or for flexibility.

One would declare such types in the following way:

    typedef ExistingType    NewAliasType;

For example, in wtypes.idl the APPID type alias is typedef'd from the type GUID. (In fact GUID is also an alias for the actual type struct _GUID), as in:

    typedef GUID            APPID;

In C/C++ the ExistingType may be omitted, in which case the type int is assumed. But if the ExistingType is an identifier that is unknown to the compiler, then obviously the compilation will fail at that point.

In MIDL, however, the MIDL compiler appears to treat typedefs in the same way as preprocessor symbol definition replacements, since it is possible to have the following compile without errors or warnings (where NOT_DEFINED is not defined):

    typedef NOT_DEFINED     XYZ_t;

This may not seem like a problem, since how often would one define a type in an IDL file and not use it? Well quite often, when building a complex system with a hierarchy of interfaces, types and, consequently, IDL files. In particular, one often defines types in IDL that are only utilized in C/C++ source code. Therefore, this issue can be the source of a number of subtle bugs, causing big headaches when combined with minimal spelling errors. For example:

     typedef GULD * const    CPGUID;       // Trouble awaits! w::d


George Frazier is a software engineer in the System Design and Verification group at Cadence Design Systems Inc. and has been programming for Windows since 1991. He can be reached at georgefrazier@ yahoo.com.

Tech Tips

Listing 1 USB filter driver example


; This .INF file demonstrates how to install a filter driver
; for a USB device.
;
; The lines marked with the comment "Filter driver install"
; identify those lines that were specifically added in order to install
; the upper filter driver onto the "driver stack" for this USB device
;
; If one were to remove these lines, what remains is the original
; .INF file to install just the one driver for the USB device.
;

[Version]
Signature="$WINDOWS NT$"
Class=USB
ClassGUID={36FC9E60-C465-11CF-8056-444553540000}

[Manufacturer]
%MfgName%=MyDriver

[MyDriver]
%USB\VID_07CC&PID_0003.DeviceDesc%=SESUSB.Dev, USB\VID_07CC&PID_0003

[DestinationDirs]
SESUSB.Files.Ext = 10,System32\Drivers

[SESUSB.Dev.NT]
CopyFiles=SESUSB.Files.Ext

[SESUSB.Files.Ext]
SESUSB.SYS
FILTER.SYS                                   ; "Filter driver install"

[SESUSB.Dev.NT.Services]
Addservice = SESUSB, 0x00000002, SESUSB.AddService
Addservice = FILTER, , SESFILTER.AddService  ; "Filter driver install"

[SESUSB.AddService]
DisplayName    = %SESUSB.SvcDesc%
ServiceType    = 1                  ; SERVICE_KERNEL_DRIVER
StartType      = 3                  ; SERVICE_DEMAND_START
ErrorControl   = 1                  ; SERVICE_ERROR_NORMAL
ServiceBinary  = %12%\SESUSB.SYS
LoadOrderGroup = Base

[SESFILTER.AddService]                       ; "Filter driver install"
DisplayName    = %SESFILTER.SvcDesc%         ; "Filter driver install"
ServiceType    = 1                           ; "Filter driver install"
StartType      = 3                           ; "Filter driver install"
ErrorControl   = 1                           ; "Filter driver install"
ServiceBinary  = %12%\FILTER.SYS             ; "Filter driver install"
LoadOrderGroup = PnP Filter                  ; "Filter driver install"

[SESUSB.Dev.NT.HW]                           ; "Filter driver install"
Addreg=SESFILTER_Filter_Reg                  ; "Filter driver install"

[SESFILTER_Filter_Reg]                       ; "Filter driver install"
HKR,,"UpperFilters",0x00010000,"FILTER"      ; "Filter driver install"

[Strings]
MfgName="Sample Driver"
USB\VID_07CC&PID_0003.DeviceDesc="Sample USB device"
SESUSB.SvcDesc="SESUSB.SYS Sample USB device driver"
SESFILTER.SvcDesc="FILTER.SYS Upper filter driver" ; "Filter driver install"


Tech Tips

Listing 2 Marking a parameter with [ref]


typedef struct SOMESTRUCT
{
    int     i;
    short   s;
} SOMESTRUCT;

interface IOther
{
    HRESULT TellSOMESTRUCT([in] int i, [in] short s);
}

interface IInterface
{
    HRESULT NonNullMethod(  [in, ref] SOMESTRUCT *pss,
                            [in] IOther *pii2);
}


Tech Tips

Listing 3 Using the MIDL cpp_quote keyword


typedef struct SOMESTRUCT
{
    int     i;
    short   s;
} SOMESTRUCT;

cpp_quote("#ifndef __cplusplus")
typedef SOMESTRUCT	*SOMESTRUCT_ref_t;
cpp_quote("#else")
cpp_quote("typedef SOMESTRUCT   &SOMESTRUCT_ref_t;")
cpp_quote("#endif /* !__cplusplus */")

interface IOther
{
    HRESULT TellSOMESTRUCT([in] int i, [in] short s);
}

cpp_quote("#ifndef __cplusplus")
typedef IOther		*IOther_ref_t;
cpp_quote("#else")
cpp_quote("typedef IOther  &IOther_ref_t;")
cpp_quote("#endif /* !__cplusplus */")

interface IInterface
{
    HRESULT NonNullMethod(  [in, ref] SOMESTRUCT_ref_t pss,
                            [in] IOther_ref_t pii2);
}

Tech Tips

Listing 4 Using the IInterface interface


// By defining the following classes ...

class SomeStruct
    : public SOMESTRUCT
{
// Construction
    SomeStruct(int i, short s)
    {
        this->i = i;
        this->s = s;
    }

    ...
};

class Class2
    : public IOther
{
    ...
};

// ... one can write validly, from within C++, the following code.

void funcX(IInterface *pii, Class2 &cls2)
{
    ...

    SomeStruct  some(65536, 256);

    pii->NonNullMethod(some, cls2);

    ...
}

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.