Hidden Treasures of SpyWorks

by Dan Appleman - Desaware

Find Hidden API Bugs with SpyParam

One of the biggest improvements from Windows 3.0 to 3.1 was the reduction in the number of "Unrecoverable Application Errors". One of the ways this was accomplished was by the addition of parameter validation to most Windows API calls. Invalid parameters that would once cause a system to crash are now simply ignored.

While this does not help make programs more stable, it can lead to hidden bugs that are difficult to find - since simply ignoring invalid parameters foes not solve the source of the problem. SpyParam taps into the internal parameter validation of Windows and reports on parameter error that occur. Like other SpyWorks components, this tool is designed specifically for VB programmers. It can even trigger a Visual Basic runtime error on the exact line where you call an API function with invalid parameters. SpyParam also allows you to filter out parameter errors that are caused by components such as VBXs and DLLs so that you can focus in on your own code.

Heres an example of SpyParam in use. The SelectObject API function is used to select a different drawing object such as a brush for a specific device context. After you are finished using a brush, you should restore the previous brush. Most people do not check the return value from SelectObject when restoring the previous object. SpyParam can detect cases where SelectObject fails due to an invalid device context handle or an invalid GDI object.

  Declare Function GetStockObject%Lib "GDI" (ByVal nIndex%)
  Declare Function SelectObject% Lib "GDI" (ByVal hDC%, ByVal hObject%)
  res% = SelectObject(hDC, GetStockObject (WHITE_PEN)) 
                                        ' res now contains the previous pen
  Ires%=MoveTo(hDC,EndX%,EndY%)         ' previous pen is now lost (this is a bug due 
                                          to reuse of a temporary Variable)
  res%=SelectObject(hDC,res%)           ' selects garbage into the device context

In this case, the White Pen was selected into the device context and something was drawn. But, the variable used to hold the previous pen was reassigned. This error could cause drawing problems later that would be very hard to trace to this code. SpyParam would detect this problem instantly.

SpyParam Helps while SubClassing

Windows programmers often do not bother to check the return value from the PostMessage function, which indicates (among other things) whether the window handle used is valid. Windows can be created and destroyed by various actions in the system, and are even sometimes destroyed and recreated immediately. Take the case where you are sending a series of commands to a window, then posting a final command. If one of the previous commands destroys the window, then every subsequent command using that window handle will fail.

SpyParam can be used to trap "invalid" parameters passed to Windows API functions such as an invalid window handle passed to PostMessage.

  Declare Function PostMessage% Lib "user" (ByVal hWnd%, ByVal wMsg%, ByVal wParam%, lParam As Any)
  Declare Function PostMessageByNum&Lib "User" Alias "PostMessage" (ByVal hWnd%, ByVal wMsg%, ByVal wParam%, ByVal lParam&)
  hWnd = windowarray(index)
  ' if the window gets destroyed or is invalid, this will generate an error that SpyParam can trap
  lres& = PostMessageByNum(hWnd,WM_COMMAND, wp, lp)

Keeping your Form TopMost

You can call the SetWindowsPos API function to make your Form the topmost form (will always be "on top" of other windows and forms - unless others have also been set to topmost). But under certain situations, Windows and Forms can lose their topmost status (e.g. when minimizing a Visual Basic Form). A WM_WINDOWPOSCHANGING message is sent to the Window that is losing their topmost status. You can use the SBC control to intercept that message and call the SetWindowPos API function to set the topmost status for that window again.

Written by: Dan Appleman
August 1995

Image of arrow to previous article Image of arrow to next article