[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 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 PpvoidPlong; typedef MultiType 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.