README.TXT - Read-me file for SUBCLS.ZIP ------------------------------------------------------------------------ NOTE: Use -d when unzipping SUBCLS.ZIP to have subdirectories recreated. ------------------------------------------------------------------------ Subclassing Examples, Copyright (c) 1995 Jonathan Wood and Karl Peterson Redistributed by Permission INTRODUCTION: The accompanying files provide several examples of using a subclassing control with Visual Basic to intercept Windows messages. Emphasis is placed on those messages that result in functionality that cannot be obtained through Visual Basic alone. These files include complete source code for an article in the September 1995 issue of Visual Basic Programmer's Journal (tentatively entitled "Subclass your way around VB's limitations"). Refer to that article for additional information on the techniques used. Also included here are four additional examples not mentioned in the article. In addition, the subclassing control MSGHOOK.VBX is also included. Although other subclassing controls are available, the examples presented here are written to work with this control. See MSGHOOK.TXT for more information regarding the Message Hook custom control. If you'd like to contact the authors of the demo programs and article, they can be reaced on CompuServe. Jonathan Wood at 72134,263 and Karl Peterson at 72000,1751. EXAMPLES: The article contains 8 tips. They are described briefly below and reference is made to the project that corresponds to the tip. Following these 8 tips are 4 additional ones. Since these were not covered in the article, a discussion of them is provided. 1. SHOWING STATUSBAR INFORMATION FOR MENU COMMANDS Refer to the article and the MENUSEL project. 2. ADDING A COMMAND TO A FORM'S SYSTEM MENU Refer to the article and the SYSCMD project. 3. SUPPORTING DRAG-AND-DROP FROM FILE MANAGER Refer to the article and the DROPFILE project. 4. LEFT JUSTIFYING CAPTION TEXT Refer to the article and the LEFTCAP project. 5. RESTRICTING A WINDOW SIZE RANGE Refer to the article and the GETMINMX project. 6. PAINTING THE BACKGROUND OF AN MDIFORM Refer to the article and the MDIPAINT project. 7. DRAWING CUSTOM MENU COMMANDS Refer to the article and the OWNRDRAW project. 8. HOOKING INTO THE CLIPBOARD VIEWER CHAIN Refer to the article and the CLIPVIEW project. 9. CHANGING MENU TEXT (INITMENU) Visual Basic allows you to modify the text of a menu command by setting the Caption property of a menu object. However, it often makes more sense to be able to set this caption just before the drop down menu is displayed. For example, let's say you had a spreadsheet with names and, when a menu was dropped down, you wanted the currently selected name to appear in the menu as part of a command. Rather than changing the menu caption every time the current selection changes, wouldn't it be easier to simply set the caption to the currently selected name just before the menu is displayed? Visual Basic does allow you to do this. For example, if you have an Edit menu that contains a Copy command, you can modify the Copy command's Caption property in the Edit menu's Click event. The Edit menu's Click event occurs before the menu is displayed. Unfortunately, Windows has already calculated the width of the menu at this point. If the new text for the caption is longer that the width of the menu, the text will be cut off. We can solve this problem by using a subclassing control to intercept the WM_INITMENUPOPUP message. This message is sent before a drop down menu is displayed and, significantly, before the width of the menu has been calculated. The INITMENU project demonstrates this technique. The value you assign to the Result parameter, which MsgHook passes to the Message event handler, will be returned to Windows. Like the wParam and lParam paramters, the meaning of the value returned depends on the actual message being sent. However, most messages, including WM_MENUSELECT, simply use a return value of 0 to indicate that your program processed the message. 10. DETECTING SYSTEM COLOR CHANGES (SYSCOLOR) Often when painting custom elements on your forms, you want to use the default system colors so they will blend in nicely with the appearance of the rest of the screen. Windows offers the GetSysColor API call to retrieve any of the colors which the user can set in Control Panel for items such as buttons, titlebars, borders, and so on. But, what if the user alters their color preferences while your program is running? For performance reasons, you may decide that it's best to store the system colors in an array, so you can draw immediately with them rather than calling the GetSysColor API repeatedly. Hooking the WM_SYSCOLORCHANGE provides notification whenever the system colors change. This message is closely followed with a WM_PAINT message which will fire the Form_Paint event in your project. By retrieving each of the system colors during the hooked Message event (see the SYSCOLOR project), the color array is refreshed prior to the painting. There is no need to invoke the original window procedure as Visual Basic has never reacted to this message. One other point of interest is the way the array of colors is initially filled. Rather than duplicate the looping code, SendMessage is employed to fire the Message event just set up during the Form_Load event. 11. FORCING AN APP TO REMAIN MINIMIZED (MINAPP) Some applications don't need a windowed user interface. These are typically written to process large quantities of data in the background, or to sit idly by monitoring and reacting to system events. If you've written such an application, you may decide that it’s best for it to remain minimized either as an icon or in the Windows 95 taskbar, but Visual Basic doesn't provide a convenient means of doing so. If a form's WindowState is reset to Minimized whenever a Form_Resize event occurs, there's still an ugly flash as the form first restores to Normal or Maximized. Hooking the WM_QUERYOPEN message (see the MINAPP project) provides a means for your form to be notified before its state is altered. An application can notify Windows that the icon may be opened by returning a non-zero value, or prevent the icon from opening by returning zero. To implement this technique in Visual Basic, simply return zero in the Result parameter when the Message event is fired. There is no need to invoke the original window procedure. One precaution to observe is to not perform any actions which would cause an activation or alter the focus while processing this message. To provide some degree of user interaction with your iconized application, implement the code presented in the SYSCMD project to add one or several menu options to the form's system menu. 12. PREVENTING A WINDOW FROM BEING SIZED OR MOVED (WINPOSCH) Just before a window gets sized or moved, Windows sends it a WM_WINDOWPOSCHANGING message that includes information about the new window position. A window procedure can inspect the information about the new position and even modify it. The WM_WINDOWPOSCHANGING message uses the lParam parameter to hold the address of (or pointer to) a WINDOWPOS user-defined type (see the WINPOSCH project). While Visual Basic doesn't support pointers, the Windows API provides a function called hmemcpy which can be used to copy data from one location to another. Using hmemcpy, this example copies the data from the address specified by lParam to a Visual Basic variable of type WINDOWPOS. Note that the code declares two hmemcpy parameters As Any. This is done to provide maximum flexibility but also means that Visual Basic is not able to verify that the correct data types are sent. Care should be taken when incorporating hmemcpy in your own applications, as it can be a little tricky. After using hmemcpy, we end up with a copy of the WINDOWPOS data structure in a Visual Basic variable. This data structure contains information about the new window position. If this information is modified, the changes will affect how the window is moved. Setting the SWP_NOSIZE and SWP_NOMOVE bits of the flags element of the WINDOWPOS structure prevents any moving or sizing at all. This example, sets these bits and then calls hmemcpy once again to copy the data structure back. This project contain some additional statements to detect if the window is minimized. If the window is minimized, the program allows the window to be sized normally. Apparently, when a window gets this message as the result of a window becoming unminimized, the form's WindowState will already indicate that the form is not an icon. Therefore, the code also keeps track of the WindowState for the last time the WM_WINDOWPOSCHANGING message was received. If you feel like experimenting, you can take these conditional statements out and see how the form behaves when you minimize it.