5/5/00 -- VEEWINAPI

A NOTE ABOUT NAME DECORATION IN THE DOCUMENTATION

It's a long story, but variable names started looking really goofy when one
particular type of name decoration started getting used regularly at
Microsoft. Since then it's spread to most anybody who does Windows
programming. The idea is that variable names should be prefixed with letters
that specify their type. Like a pointer is prefixed with a 'p'. An integer is
prefixed with an 'i'. A pointer to an integer is prefixed with 'pi' and so
on. Things aren't helped by the fact that most variable types used
in Windows programming are actually type definitions. Words made up by one
person or another that mean other things, but that look much more familar.

For instance, the Windows type USHORT means 'unsigned short'. Then again
there are those like COLORREF, which actually means DWORD, which actually
means 'unsigned long'. Then there are those like HWND, which actually means
HANDLE, which actually means either a void pointer to a structure with one
unused member or a generic void pointer depending on whether or not STRICT is
defined. In some cases it gets worse than that.

The point is that VEE doesn't understand words like DWORD in a compiled
function import header so words like this can't be used. Likewise, VEE has no
idea what an 'unsigned' type is and so that can't be used either. Nor can
void * (void pointer). Fortunately there's a way around all this: ignore the
differences. The C language, from which both VEE and Windows were born, is
very loosely-typed: you can declare a variable of type 'int' and then use it
as if it were a pointer to char. The compiler will (or rather, should)
complain, but there's nothing really stopping you from doing it. Contrast
that to VEE, a strongly-typed language. If you declare a variable as Text,
you can't store anything but text in it and you can't use it as anything else
but text (whithout first converting it with 'asXXX()' anyway. In many cases,
VEE will actually supply conversion when necessary, and you don't even know).

So basically the differences and shortcomings are ignored, and they show up
as total chaos when reading the function declarations. In all cases, the VEE
import header uses exclusively the simplest types available that parallel the
Windows type definitions. For instance, the Windows type USHORT will be shown
in the VEE import header as 'short' because there's no unsigned, but there is
a short. As long as the data length works out the same, there's no harm done.

On the other hand, the variable name decoration reflects the actual Windows
type. So now if we have a function that takes a variable called Num that's a
USHORT, the VEE import header shows it as 'short usNum'. To make matters
worse I always let the compiler know exactly what it's dealing with for
function entries & exits, so none of the VEE import header declarations match
their corresponding declarations in the source code.

It's awfully confusing to look at but this is the way I do VEE/C. The reason
is it helps me be fluent with these evil type definitions. I simply know that
a COLORREF is actually an unsigned long, and there's a lot to be said for
that in some instances. Having said that, consider yourself warned that I
don't always follow my own advice with respect to type identification or
naming convention. Names of function parameters who's type is char pointer
are usually scribbled off as pName without regard for whether or not they're
zero-terminated (which would otherwise mandate an 'sz' in the name prefix).
In the case of the color setting functions, the red, green and blue
components of color become simply r, g and b.

Anyway, the dictionary of equivalents for this project is:

Win Type    Prefix  VEE Type    In English          Data Size
INT         i       int         Integer             Platform dependent*
UINT        u       int         Unsigned Integer    Platform dependent*
PINT        pi      int *       Integer Pointer     Platform dependent**
BOOL        b       int         Boolean             Platform dependent*
DWORD       dw      long        Double Word         32-bits
PTCHAR      p       char *      TChar Pointer       Platform dependent**
VOID        -       void        Void                None
SHORT       s       short       Short               16-bits
PSHORT      ps      short *     Short Pointer       Platform dependent**

*  The size of an integer on any particular platform is the size of the
   registers designed to deal with arithmetic (that is, if the compiler
   vendor is sane). Building for Intel (or compatible) platforms on a 32-bit
   compiler yields 32-bit integers.

** The size of a pointer on any particular platform is the size of the
   registers designed to deal with memory referencing (with the exception of
   the violently stupid segmentation scheme that escaped some demon at Intel
   some twenty-two years ago). Building for Intel (or compatible) platforms
   on a 32-bit compiler yields 32-bit pointers.

It's not really necessary to think about it all that much. If you think of an
integer as 32-bits and any pointer as 32-bits, you're probably right for
quite a few different combinations of hardware & software.

A NOTE ABOUT IN AND OUT

In the parameter listing for any particularl function, parameter names are
designated IN or OUT. IN means that this parameter supplies some information
to the function. OUT means that parameter is intended to supply return
information back to the caller.

Part I: EXPORTED FUNCTION REFERENCE

Function:
  int ShowSysMenu(int bShow)

Parameters:
  IN bShow - New state of system menu: zero to hide, non-zero to show.

Returns:
  Non-zero if the function succeedes, zero if not.

This function shows or hides the system menu on the VEE main window. This is
the "small icon" on the left side of the title bar. Typically, this menu
includes the commands Restore, Move, Size, Minimize, Maximize and Close. If
the system menu is hidden, these commands are disabled. Hiding this menu also
hides the minimize, restore/maximize and close buttons on the right side of
the title bar.
-----------------------------------------------------------------------------

Function:
  int ShowMinBox(int bShow)

Parameters:
  IN bShow - New state of the window frame's minimize box: zero to disable,
             non-zero to enable.

Returns:
  Non-zero if the function succeeded, zero if not.

This function enables or disables the minimize button on the right side of
the title bar. While it does not actually hide the button when disabled, the
button will be drawn in grey to show it is disabled.
-----------------------------------------------------------------------------

Function:
  int ShowMaxBox(int bShow)

Parameters:
  IN bShow - New state of the window frame's maximize box: zero to disable,
             non-zero to enable.

Returns:
  Non-zero if the function succeeded, zero if not.

See comments for ShowMinBox.
-----------------------------------------------------------------------------

Function:
  int GetSysDir(char *pDir)

Parameters:
  OUT pDir - Pointer to var which will contain the system directory if the
             function succeeds.

Returns:
  If the function succeeds, the return value is the length of the string
  copied to the buffer. If the string will not fit in the supplied buffer,
  the return value is the size of the buffer needed.

This function returns the current Windows system directory. In VEE, define a
text constant or variable long enough to contain the returned string before
calling the function.
-----------------------------------------------------------------------------

Function:
  int GetWinDir(char *pDir)

Parameters:
  OUT pDir - Pointer to var which will contain the Windows directory if the
             function succeeds.

Returns:
  If the function succeeds, the return value is the length of the string
  copied to the buffer. If the string will not fit in the supplied buffer,
  the return value is the size of the buffer needed.

This function returns the current Windows directory. In VEE, define a text
constant or variable long enough to contain the returned string before
calling the function.
-----------------------------------------------------------------------------

Function:
  int GetCurDir(char *pDir)

Parameters:
  OUT pDir - Pointer to var which will contain the current directory if the
             function succeeds.

Returns:
  If the function succeeds, the return value is the length of the string
  copied to the buffer. If the string will not fit in the supplied buffer,
  the return value is the size of the buffer needed.

This function returns the current directory. In VEE, define a text constant
or variable long enough to contain the returned string before calling the
function.
-----------------------------------------------------------------------------

Function:
  int SetCurDir(char *pDir)

Parameters:
  IN pDir - Fully-qualified path name of the directory to set as the current
            directory.

Returns:
  Non-zero if the function succeeds, zero if not.

Call this function to set the current directory.
-----------------------------------------------------------------------------

Function:
  void GetSysInfo(short *psProcArch, int *piPageSize, int *piProcMask,
                  int *piNumProcs, int *piProcType, int *piAllocGran,
                  short *psProcLevel, short *psProcRev)

Parameters:
  OUT psProcArch  - Pointer to var which will receive the system's processor
                    architecture. This can be one of the following values:

                    PROCESSOR_ARCHITECTURE_INTEL   = 0
                    PROCESSOR_ARCHITECTURE_MIPS    = 1
                    PROCESSOR_ARCHITECTURE_ALPHA   = 2
                    PROCESSOR_ARCHITECTURE_PPC     = 3
                    PROCESSOR_ARCHITECTURE_UNKNOWN = 0xFFFFFFFF (-1)

  OUT piPageSize  - Pointer to var which will receive the system's memory
                    page size (in bytes).

  OUT piProcMask  - Pointer to var which will receive a bitmap representing
                    the set of processors configured into the system. Bit 0 =
                    processor 0, bit 31 = processor 31.

  OUT piNumProcs  - Pointer to var which will receive the number of
                    processors in the system.

  OUT piProcType  - On Windows 95, this var will receive the type of
                    processor in the system. It can be one of the following
                    values:

                    PROCESSOR_INTEL_386     = 386
                    PROCESSOR_INTEL_486     = 486
                    PROCESSOR_INTEL_PENTIUM = 586

                    On Windows NT/2000, additional values are defined:
                    PROCESSOR_MIPS_R4000    = 4000
                    PROCESSOR_ALPHA_21064   = 21064

                    Use of this value on NT/2000 systems is discouraged.

  OUT piAllocGran - Pointer to var which will receive the granularity with
                    which virtual memory is allocated (in K bytes).

  OUT psProcLevel - Pointer to var which will receive the processor level.
                    See the Win32 documentation for the SYSTEM_INFO
                    structure. There's too much to set down here.

  OUT psProcRev   - Same comments!

Returns:
 Nothing.

Call this function to get crazy details about the system.
-----------------------------------------------------------------------------

Function:
  int GetBootType(void)

Parameters:
  None.

Returns:
  This function returns what type of boot was performed and can be one of the
  following values:
  0 - Normal boot.
  1 - Fail-safe boot.
  2 - Fail-safe boot with network support.

Call this function to see how the computer started up for this session.
-----------------------------------------------------------------------------

Function:
  int GetScreenSize(int *piSizeX, int *piSizeY)

Parameters:
  OUT piSizeX - Pointer to var that receives the number of horizontal pixels
                on the screen.

  OUT piSizeY - Pointer to var that receives the number of vertical pixels on
                the screen.

Returns:
  Non-zero if the function succeeds, zero if not.

This function returns the total screen area.
-----------------------------------------------------------------------------

Function:
  int GetWorkareaRect(int *piLeft, int *piTop, int *piRight, int *piBottom)

Parameters:
  OUT piLeft   - Pointer to var which will receive the absolute coordinate of
                 the left edge of the screen workarea.

  OUT piTop    - Pointer to var which will receive the absolute coordinate of
                 the top edge of the screen workarea.

  OUT piRight  - Pointer to var which will receive the absolute coordinate of
                 the right edge of the screen workarea.

  OUT piBottom - Pointer to var which will receive the absolute coordinate of
                 the bottom edge of the screen workarea.

Returns:
  Non-zero if the function succeedes, zero if not.

This function returns the screen area that is not occupied by the task bar.
-----------------------------------------------------------------------------

Function:
  int IsSlowMacine(void)

Parameters:
  None.

Returns:
  Non-zero if Windows considers the machine "slow", zero if not.

Beats me, but I guess this could come in handy.
-----------------------------------------------------------------------------

Function:
  int GetLoggedOnUserName(char *pName)

Parameters:
  OUT pName - Pointer to var which will receive the log on name of the
              currently logged on user.

Returns:
  Non-zero if the function succeeds, zero if not.

This function returns the log on name of the currently logged on user. In
VEE, define a text constant or variable large enough to contain the returned
string before calling the function.
-----------------------------------------------------------------------------

Function:
  int GetExtendedVersionInfo(int *piMajor, int *piMinor, int *piBuild,
                             int *piPlatform, char *pSvcPack)

Parameters:
  OUT piMajor    - Pointer to var which will receive the major version number
                   of the operating system.

  OUT piMinor    - Pointer to var which will receive the minor version number
                   of the operating system.

  OUT piBuild    - Pointer to var which will receive the build number of the
                   operating system. On Windows 95, the build number is in
                   the low 16-bits of the returned number. The high 16-bits
                   contain the major and minor version numbers.

  OUT piPlatform - Pointer to var which will receive the platform identifier.
                   This can be one of the following values:
                   VER_PLATFORM_WIN32s        = 0
                   VER_PLATFORM_WIN32_WINDOWS = 1
                   VER_PLATFORM_WIN32_NT      = 2

  OUT pSvcPack   - Pointer to var which will receive extra information about
                   the operating system. On Windows NT, this is the latest
                   service pack installed. On Windows 95, this is arbitrary
                   additional information.

Returns:
  Non-zero if the function succeeds, zero if not.

This function returns detailed information about the operating system.
-----------------------------------------------------------------------------

Function:
  int GetLastWinError()

Parameters:
  None.

Returns:
  The last error recorded by SetLastError.

This function wraps GetLastError. Most API calls use this to communicate
error information to the caller. Calling this function also sets VeeWinapi's
local error information variable.
-----------------------------------------------------------------------------

Function:
  int GetLastWinErrorMsg(int iErr char *pErrMsg)

Parameters:
  IN  iErr    - Error number to retrieve message for.

  OUT pErrMsg - Pointer to var which will receive error message text.

Returns:
  Non-zero if the function succeeds, zero if not.

Call this function to receive an error message describing iErr. Pass 0 for
iErr to use GetLastError as the error input. If 0 is passed for iErr, this
function also sets VeeWinapi's local error information variable.
-----------------------------------------------------------------------------

Function:
  void SetLastWinErr(int iErr)

Parameters:
  IN iErr - Error number.

Returns:
  Nothing.

Call this function to set VeeWinapi's local error information variable. This
error is also passed along to the Windows API via SetLastError.
-----------------------------------------------------------------------------

Function:
  int SubclassButton(char *pText)

Parameters:
  IN pText - Button text to search for. This is the button to subclass.

Returns:
  Non-zero if the function succeeds, zero if not.

This function will search for a button containing the specified text and
subclass it, replacing it's WNDPROC with the function MyWndProc.
-----------------------------------------------------------------------------

Function:
  int SetButtonText(char *pText)

Parameters:
  IN pText - New button text.

Rturns:
  Non-zero if the function succeeds, zero if not.

This function sets the subclassed button's text to that specified.
-----------------------------------------------------------------------------

Function:
  int SetButtonFont(int iSize, char *pFontName)

Parameters:
  IN iSize     - Point size * 10 of the new font.

  IN pFontName - Specifies name of font.

Returns:
  Non-zero if the function succeeds, zero if not.

This function sets the subclassed button's font to that specified. Note that
attributes such as bold, underlined or italic are not supported. To create
fonts that are bold or italic using this function, the base font must exhibit
those attributes. Note also that sizes are in units of 1/10th point. For a 12
pt. font, pass 120 for iSize.
-----------------------------------------------------------------------------

Function:
  void SetButtonBkgndColor(int r, int g, int b)

Parameters:
  IN r - Specifies red component.

  IN g - Specifies green component.

  IN b - Specifies blue component.

Returns:
  Nothing.

Call this function to set the RGB color value of the subclassed button's
background. The inputs, r, g, and b, can be from 0 to 255.
-----------------------------------------------------------------------------

Function:
  void SetButtonTextColor(int r, int g, int b)

Parameters:
  IN r - Specifies red component.

  IN g - Specifies green component.

  IN b - Specifies blue component.

Returns:
  Nothing.

Call this function to set the RGB color value of the subclassed button's
text. The inputs, r, g, and b, can be from 0 to 255.
-----------------------------------------------------------------------------

Function:
  void RedrawButton(void)

Parameters:
  None.

Returns:
  Nothing.

Call this function to redraw the subclassed button.
-----------------------------------------------------------------------------

Function:
  void ClickButton(void)

Parameters:
  None.

Returns:
  Nothing.

Call this function to simulate a mouse click on the subclassed button.
-----------------------------------------------------------------------------

Function:
  int UnsubclassButton(void)

Parameters:
  None.

Returns:
  Non-zero if the function succeeds, zero if not.

Call this function to unsubclass a previously subclassed button. Note that if
this function does not succeed, VEE will probably crash if the VeeWinapi dll
is unloaded (via Delete Library). Any activity at all that causes the
button's WNDPROC to be called will cause either an immediate access violation
or unpredictable results.
-----------------------------------------------------------------------------

Function:
  int VeeBeep(int uType)

Parameters:
  IN uType - Type of beep to issue. This value may be on of the following:
             MB_OK              = 0
             MB_ICONHAND        = 16
             MB_ICONQUESTION    = 32
             MB_ICONEXCLAMATION = 48
             MB_ICONASTERISK    = 64
             0xFFFFFFFF         = -1

Returns:
  Non-zero if the function succeeds, zero if not.

Call this function to create a noise. The documentation states that the value
0xFFFFFFFF issues a beep on the speaker, but as we've seen, this isn't always
the case.
-----------------------------------------------------------------------------

Function:
  int ControlledBeep(int dwFreq, int dwDur)

Parameters:
  IN dwFreq - Frequency of beep.

  IN dwDur  - Duration of beep.

Returns:
  Non-zero if the function succeeds, zero if not.

No comment! This doesn't work on any of the systems I have access to.
-----------------------------------------------------------------------------

PART II: HELPER FUNCTIONS

These functions are basically support functions for the exported functions.
ChangeStyleBit is used to modify VEE's main window style. FindVeeChildWindow
is used to enumerate VEE's child windows and search for a button by it's
contained text for subclassing.
-----------------------------------------------------------------------------

PART III: SUBCLASSING A BUTTON

As far as Windows is concerned, subclassing a window is modifying it's
default behavior by replacing the function that processes messages sent to
that window. By doing this, the replacing function has a chance to respond to
various messages in it's own way. It also has control over whether or not the
old function gets to see these messages.

The thing that suprizes me the most is that this is even possible. It shows
that Agilent is serious about moving VEE to Windows. Subclassing of this
nature was not possible (as far as I know) until Version 5. Until then, VEE
hadn't used Windows' window manager at all, but it's own. Version 5 still
uses it's own window manager, but at least every object on the VEE screen now
has a Windows window handle and is therefore linked to Windows' window
manager.

The major trick about subclassing a window in VEE is finding out what window
to subclass. First off, the method used here only works for confirmation
buttons and toggle buttons. This method has been tested and works on Windows
2000 and Windows 98. I'm sure it's possible to use this method for any other
VEE object, but it's actual utility is probably still somewhat limited. If
anybody is actaully interested in doing this, I have further details and can
modify the subclassing function to handle any VEE object.

This is where good old-fashioned hacking comes in. In a new VEE project, I
placed various objects on the workspace and named them various unique names.
Then I started our own Process Dumper and searched VEE's memory space for
thoes names. In most cases, I came up with three matches per object. Looking
at a memory dump of the first match always revealed what looked like a link
pointer a short distance away from what I had come to assume was the object's
title. Treating this as an absolute pointer and following it was enough to
convince me that I was in the middle of a major VEE data structure that
describes objects. I was pretty sure this was the target I was after, but the
problem was how was this storage linked to the object's Windows window
handle?

When Windows creates a window, it stores various data about that window in a
structure of it's own in it's window manager and returns a "handle" that
identifies that structure to it's window manager. User programs can
optionally allocate extra "window bytes": additional storage associated with
any specific window who's use is determined by the application that allocated
it. In most applications that make use of this mechanism, the scenario is
almost always the same: four bytes are allocated and a pointer is stored
there that points to some structure in the user application that specifies
additional information about the window.

I kind of went on faith that VEE was doing this, and using Microsoft's Spy++
found out that when I investigated the extra window bytes of a particular
object on the VEE workspace, it was a pointer to a link member of this VEE
data structure. When the object in question was a Confirmation button or a
Toggle button, it's title was just 0xd0 bytes beyond the link pointer. So
that's how VeeWinapi finds the requested button.

The process begins by calling SubclassButton with the text of the button to
subclass. This text must be unique, otherwise the function may subclass the
wrong object. SubclassButton uses the helper function FindVeeChildWindow to
find the button by it's text, and then subclasses it by replacing it's
WNDPROC (Window Procedure) with MyWndProc, the function VeeWinap.dll uses to
process window messages.

FindVeeChildWindow first does some housekeeping. VeeWinapi keeps information
on VEE's child windows in a single linked list of structures called WINNODEs.
The root of this chain is checked to see if any of these structures have been
allocated before (this test should never pass because this function always
cleans up after itself). If so, they are deleted with DeleteWinNodes. The
mechanics of single linked lists & recursive de-allocation are probably not
second nature to VEE's target audience, but it's too much to explain in
detail. Besides, this is a more or less ancient technique that's been
replaced these days by far more interesting things in template libraries.
Still, it's cheap, easy & very fast, so I find myself using it a lot.
Anyway...

The Windows API function EnumChildWindows will call a user-defined function
with the handle of every child window owned by the handle passed to it. We
pass VEE's main window handle, and Windows calls VeeWinapi's
EnumChildWindowProc for each window VEE has created as a child of the main
window. Fortunately, this includes all objects in any given VEE program.
Windows also passes a user-defined 32-bit value to the enumeration function.
This 32-bit value is a pointer to our chain of WINNODEs.

VeeWinapi's enumeration function is where it looks for button text. Deciding
whether or not to keep information on the child window here reduces the
memory space needed to find the object we're after. In a multi-process
environment such as Windows, it's only polite to keep one's resource
requirements to a minimum. VeeWinapi retrieves the child window's "VEE Window
Info Pointer" and looks 0xd0 bytes beyond it to see if there's text there. If
so, it adds a WINNODE onto the end of the chain and loads it with the child
window's handle and the text it found. Once Windows is done enumerating child
windows, it returns control to FindVeeChildWindow where we search the
collection of WINNODEs for the text we're looking for.

If an exact match is found, the window's WNDPROC is replaced with the address
of MyWndProc, which from this point forward is called before any other
function (except a potential process-specific or system-wide message filter)
whenever messages are posted or sent to the subclassed button.

Before describing the message processing functions, take a moment to realize
that these functions do not mimic those that would usually be used in any
other Windows application to do these same jobs. VEE is not completely
integrated with Windows yet, and so there are a few differences between
standard Windows practice and VEE's own needs. The only way to proceed is
basically trial & error. Code according to Petzold, and see if it works.

In the subclassing function, we're only looking for the messages
WM_ERASEBKGND and WM_PAINT. When erase background is received, the function
paints the button's rectangle with the color specified by gcrBkgndColor.
GetClientRect is used to get the rectangle. Fortunately, the device context
passed in the message can safely be used to paint with. For the WM_PAINT
message, VeeWinapi does things differently that it should. There should be
more tests here to determine why and with what options this message arrived.
Specifically, BeginPaint / EndPaint shouldn't always be called, but these
differences are more or less trivial for a simple demo.

There's also more to consider. For instance, that dotted line that shows up
around button text is called the focus rectangle. We should be comparing the
subclassed button's hWnd with that returned from GetFocus (which gets the
handle of the window with input focus). If this button has input focus, we
should draw a focus rectangle in the button with DrawFocusRect. This is
another one of the details that basically got dropped for simplicity.

Notice that the WM_ERASEBKGND message handler returns 1 and the WM_PAINT
message handler returns 0. The only explaination I can offer for that is
"'cause the dox say so." Message handlers are expected to return certain
values as signals to Windows (or the application that sent the message) that
communicate information about whether or not the message was processed or was
responded to in some way. Sometimes the return value should be a 1 if the
message was processed, sometimes it's supposed to be 0 if the message was
processed.

Of course, there are a *lot* of messages. We have to provide some sort of
link to the old function if the message we received is of no interest (like
for instance WM_SIZE). If this linkage wasn't provided, the subclassed button
would appear broken when we tried to resize it or move it. So the last task
of subclassing is providing for this linkage. You might think it would be
enough to call the old WNDPROC with the parameters that were passed to
MyWndProc. This isn't the case though. The reason is because there's no
guarantee that what we got back when we called GetWindowLong to subclass the
window is a function address. Using this as a function pointer could possibly
cause an access violation.

The documentation has this to say about it:

  If this value is obtained by calling the GetWindowLong function with the
  nIndex parameter set to GWL_WNDPROC or DWL_DLGPROC, it is actually either
  the address of a window or dialog box procedure, or a handle representing
  that address.

So that's where the CallWindowProc function comes in. It determins what
function to call to pass on unprocessed messages.

So that's about the long and short of it for subclassing a VEE object in
Windows
