Like the storage of a C++ class can be extended in subsequent inheritances, gwst structures also allow to extend its storage by inheritance. This allow to reuse the previous functionality.
See the following example:
// Tut08.prg
BEGIN STRUCTURE OSVERSIONINFO
MEMBER DWORD dwOSVersionInfoSize
MEMBER DWORD dwMajorVersion
MEMBER DWORD dwMinorVersion
MEMBER DWORD dwBuildNumber
MEMBER DWORD dwPlatformId
MEMBER SZSTR szCSDVersion SIZE 128
First as usual, we have declared all the members of our structure, and now we will add some properties to make easy the interpretation of the values.
DYNAMIC PROPERTY lIs9x READ ( (::dwMajorVersion == 4 ) .and. ;
(::dwPlatformId == 1 ) )
// ---------------
DYNAMIC PROPERTY lIsXp READ ( (::dwMajorVersion == 5 ) .and. ;
(::dwMinorVersion > 0 ) )
// ---------------
DYNAMIC PROPERTY lIsVista READ (::dwMajorVersion == 6 )
We will also add the method GetVersionEx() that will fill our structure with the info provided by the Windows API.
DYNAMIC METHOD GetVersionEx BLOCK {|Self| ::_zeromemory_() ,;
::dwOSVersionInfoSize := OSVERSIONINFO():_mc__size_ ,;
(@kernel32:GetVersionExA( Self ) != 0)}
END STRUCTURE
The gwst class var _mc__size_ contain the default size for every gwst class, usually we will use the instance method ::_sizeof_(), but in this case we need that the member dwOSVersionInfoSize contain always the size of the OSVERSIONINFO() structure, even in subclasses.
We have now a fully functional structure with some extra properties and a method for automatically fill its members from the info provided by the Windows API.
Since Windows NT 4. SP6 we can use an extended version of this structure that provides some extra info. This time instead of rewrite all the code to manage our extended structure, we will write our new class reusing the previous code.
BEGIN STRUCTURE OSVERSIONINFOEX EXTENDING OSVERSIONINFO
We add the clause EXTENDING that allow us to add new members to the base structure class, so we just will add the new members to our structure.
MEMBER WORD wServicePackMajor
MEMBER WORD wServicePackMinor
MEMBER WORD wSuiteMask
MEMBER BYTE wProductType
MEMBER BYTE wReserved
Now we will add some properties for easy interpretation of the bit flags stored inside the WORD value wSuiteMask
DYNAMIC PROPERTY lBackOffice IS MASK 0x0004 ;
OF wSuiteMask READONLY
DYNAMIC PROPERTY lWebEdition IS MASK 0x0400 ;
OF wSuiteMask READONLY
DYNAMIC PROPERTY lDataCenter IS MASK 0x0080 ;
OF wSuiteMask READONLY
DYNAMIC PROPERTY lEnterprise IS MASK 0x0002 ;
OF wSuiteMask READONLY
DYNAMIC PROPERTY lEmbeddedNT IS MASK 0x0040 ;
OF wSuiteMask READONLY
DYNAMIC PROPERTY lPersonal IS MASK 0x0200 ;
OF wSuiteMask READONLY
DYNAMIC PROPERTY lSingleUserTS IS MASK 0x0100 ;
OF wSuiteMask READONLY
DYNAMIC PROPERTY lSmallBusiness IS MASK 0x0001 ;
OF wSuiteMask READONLY
DYNAMIC PROPERTY lSmallBusinessRestricted IS MASK 0x0020 ;
OF wSuiteMask READONLY
DYNAMIC PROPERTY lTerminal IS MASK 0x0010 ;
OF wSuiteMask READONLY
And now we will add also more properties to help us interpreting the value wProductType.
DYNAMIC PROPERTY lDomainController READ ( ::wProductType == 0x02 )
DYNAMIC PROPERTY lNtServer READ ( ::wProductType == 0x03 )
DYNAMIC PROPERTY lNtWorstation READ ( ::wProductType == 0x01 )
When we was defined the GetVersionEx() method in our base class I was explain that the common way to get the size of a gwst structure is using the instance method ::_sizeof_(). If we was used this instance method we will not need to overload the ::GetVersionInfoEx() method now, because ::_sizeof_() will report the current size of our new structure.
As you will see in the next block of code the reason to hardcode the size of the structure is because the call of the WINAPI function GetVersionExA() can fail under some older Windows platforms if we specify the size of our new extended structure.
DYNAMIC METHOD GetVersionEx BLOCK {|Self| ::_zeromemory_() ,;
::dwOSVersionInfoSize := OSVERSIONINFOEX():_mc__size_ ,;
iif( @kernel32:GetVersionExA( Self ) == 0 ,;
::OSVERSIONINFO:GetVersionEx() ,;
.T.)}
END STRUCTURE
So if GetVersionExA() fail using the extended size, we will call the same method from the parent object, that will meet the OS requirements for this API call.