[Compuware Corporation] [Compuware NuMega home page]                [NuMega Lab]
[teal]

 [DriverStudio]    [Image][Image]
   Home
 [Driver Products]                         MultiType Class for Prototype Woes
                                           A Class for Dealing with Function
   DriverStudio              [Image]      Prototype Changes for
   DriverBundle                           InterlockedCompareExchange() between
   Previews                               DDKs
   Compatibility
 [Downloads]  
                          Sometimes change is for the better. When an API function changes prototypes
 Wizards                  between two DDKs, we certainly hope that the change was made for the better,
   Utilities             because it certainly makes life more difficult for the developer. Such is the
   NT source             case with the kernel API call InterlockedCompareExchange, which has the following
 examples                 prototypes:
   VxD source
 examples
   WDM source            PVOID
 examples                 FASTCALL
 [Resources]             InterlockedCompareExchange(
 Technical papers             IN OUT PVOID *Destination,
   Useful links              IN PVOID ExChange,
   Technical tips            IN PVOID Comperand
 [Support]                   );

 Support
   Knowledge base        In the Windows 2000 DDK, the prototype is
   Problem
 submission
   Product               NTKERNELAPI
 registration             LONG
   Release notes         FASTCALL
 [Shop NuMega]           InterlockedCompareExchange(
 Buy it!                      IN OUT PLONG Destination,
   Price list                IN LONG ExChange,
   How to buy                IN LONG Comperand
   Sales offices             );


 [Y2K Compliance]         There is a new function in the Windows 2000 DDK called
                          InterlockedCompareExchangePointer, which has a prototype similar to that of
                          InterlockedCompareExchange in the NT4 DDK. This function doesn't exist in the NT4
 [More information]       DDK, and from the prototype, it is clear that InterlockedCompareExchange was used
                          on pointer values. The reason behind the change is most likely linked with
                          cleaning up the kernel API for future compatibility with 64-bit addresses. The
                          older NT4 definition was probably used interchangeably for different 32 bit data
                          types by use of cast operators. The fact that it was originally specified using
                          PVOID instead of LONG probably heralds from its most common usage.

                          Whatever the case, the end result is a mess for the developer trying to write
                          code that compiles cleanly using either DDK. The problem arises from the fact
                          that even if the developer knows they are performing a safe operation, for
                          instance using the function to compare and exchange a LONG, the compiler will
                          complain for one DDK or the other depending on how the variables are declared or
                          cast because of type checking. This is a more serious problem for drivers written
                          in C++ (as opposed to C), because C++ is much stricter about type checking.

                          Here is a code snippet that will compile cleanly with NT 2000 DDK, but not NT4
                          DDK:

                          {
                                  LONG * pDestination;
                                  LONG Exchange;
                                  LONG Comperand;
                                  LONG Return;

                                  Return = InterlockedCompareExchange ( pDestination, Exchange,
                                          Comperand);
                          }

                          There are a few possible solutions to this problem, some uglier than others. The
                          primary aim of this solution was to not introduce DDK dependent code.

                          To get past the type checking of the C++ compiler, a template class named
                          MultiType was designed. The class definition follows:

                          template <class T1, class T2> class MultiType
                          {
                          public:
                                  union
                                  {
                                          T1      m_t1;
                                          T2      m_t2;
                                  } u;

                                  MultiType(const T1 t1) { u.m_t1 = t1; }
                                  MultiType(const T2 t2) { u.m_t2 = t2; }

                                  operator T1 () const { return u.m_t1; }
                                  operator T2 () const { return u.m_t2; }
                          };

                          The class is very simple, consisting of two constructors and two cast operators.
                          Each constructor accepts one of the two data types and initializes the union data
                          member. Each of the two cast operators returns the union data member appropriate
                          to the data type being cast. The purpose of the class is to allow inline
                          construction of a class instance using one data type that is capable of being
                          implicitly cast by the compiler to either of the two data types specified in the
                          template parameters.

                          Essentially, we've created an object of neutral type, allowing the compiler to
                          implicitly cast it to agree with its declaration in the DDK we are using.

                          To handle the problem associated with InterlockedCompareExchange, two data types
                          can be defined using the template class definition.

                          typedef MultiType<void**,long*> PpvoidPlong;
                          typedef MultiType<void*, long> PvoidLong;

                          Armed with these new data types, the code snippet from above can be modified to:

                          {
                                  LONG * pDestination;
                                  LONG Exchange;
                                  LONG Comperand;
                                  LONG Return;

                                  Return = PvoidLong( InterlockedCompareExchange(
                                          PpvoidPlong(pDestination), PvoidLong(Exchange),
                                          PvoidLong(Comperand)) );
                           }


                          We have constructed class instances inline using the appropriate MultiType types
                          defined above. The compiler implicitly casts the instance to the desired type
                          depending on which DDK we are compiling with, and no data type errors are
                          encountered. Code performance is maintained, since all of the inline functions
                          will be optimized out in a Release build by the compiler. To improve the
                          readability of the code, a macro can be defined to hide the inline constructors:

                          #define INTERLOCKED_COMPARE_EXCHANGE(Dest, Exch, Comp)  \
                                  PvoidLong(      InterlockedCompareExchange(  PpvoidPlong(Dest),         \
                                          PvoidLong(Exch), PvoidLong(Comp)   )      )
                          The code snippet would then look like this
                          {
                                  LONG * pDestination;
                                  LONG Exchange;
                                  LONG Comperand;
                                  LONG Return;

                                  Return = INTERLOCKED_COMPARE_EXCHANGE(
                                          pDestination, Exchange, Comperand );
                           }


                          which is very close to the look of the original API call, with the significant
                          difference that it compiles for both DDKs. The MultiType class could easily be
                          used to solve the same problem if it arose for other API functions whose
                          prototypes change. There are of course all of the inherent dangers present
                          associated with casting, and it is of course up to the user to make sure that the
                          type conversions are indeed safe.




  DriverCentral  DriverStudio  Free downloads  Resources  Support and
                          Services  Shop NuMega
     Compuware NuMega  Tel: +1 603 578-8400  Updated: 9 August 1999 
                      Problems? Contact our webmaster.
