21. Bitmaps

Page 353
  IV. THE WINDOWS GDI (GDI32.DLL PROGRAMMING)  
Page 355
  21. Bitmaps  
   
  So far, we have discussed in some detail the rather low-level concepts of processes and threads, memory, PE files, and windows messages. In this chapter, we begin our look at graphics and text, which is much closer to the user level than the material that we have been discussing.  
   
  Windows exposes its graphics-related functions in the Windows Graphical Device Interface, or GDI library. The GDI functions are implemented primarily in the GDI32.DLL library.  
 
  Rectangles  
   
  We have had some occasion to use the RECT structure previously in the book. Let us repeat the declaration:  
 
  typedef struct _RECT {
   LONG left;
   LONG top;
   LONG right;
   LONG bottom;
} RECT;
 
   
  The GDI makes extensive use of rectangles and the RECT structure, and implements a variety of functions for manipulating rectangles:  
 
  CopyRect
Makes a copy of a rectangle.
 
 
  EqualRect
Returns
True if two rectangles are equal.
 
 
  InflateRect
Enlarges a rectangle by increasing the value of one or more members.
 
Page 356
 
  IntersectRect
Gets the intersection of two rectangles.
 
 
  IsRectEmpty
Returns
True if the rectangle has no area.
 
 
  OffsetRect
Moves a rectangle.
 
 
  PtInRect
Returns
True if a specified point lies within a specified rectangle.
 
 
  SetRect
Sets the members of a
RECT structure.
 
 
  SetRectEmpty
Sets the members of a
RECT structure to 0.
 
 
  SubtractRect
Subtracts one rectangle from another, provided that the difference is another rectangle.
 
 
  UnionRect
Returns the smallest rectangle that contains the two specified rectangles. (In case you are interested, this is not the union, but rather the convex hull, of the two rectangles.)
 
   
  We will get a chance to use some of these functions in an upcoming example.  
 
  Bitmaps  
   
  The term bitmap (or bitmap image) refers to a graphical image that consists of a rectangular array of picture elements, or pixels (although transparent pixels may make the bitmap appear to be nonrectangular). Each bitmap pixel corresponds to a pixel on the display device, which we will assume to be a display monitor. (Unfortunately, many software programs like to make unauthorized changes to a bitmap in order to print the image. Thus, it is often not the case that a bitmap pixel corresponds to a single printer ''pixel" or printer dot. Life would be so much simpler if the user had easily accessible control over the mapping from bitmap pixels to printer dots but I digress here.)  
   
  The term Windows bitmap file (these files have extension .BMP) refers to a specific set of file formats that Windows uses to render bitmaps on a display device. Each pixel in the bitmap image corresponds to a certain number of bits in the bitmap file, as shown in Figure 21-1.  
   
  The number of bits per pixel in the bitmap file is called the bitmap's color depth and specifies the number of colors supported by the bitmap. A monochrome bitmap, for instance, requires only one bit per pixel, whereas a 256-color bitmap  
Page 357
   
  0357-01.gif  
   
  Figure 21-1.
Bitmap images and bitmap files
 
   
  will require 8 bits per pixel (color depth is 8). In general, a bitmap with color depth n supports 2n colors.  
   
  As we will see, the pixel values in the bitmap file may or may not describe the actual colors themselves. To express a color requires 3 bytes for each primary color one byte each for red, green, and blue. Thus, it is often the case that each pixel value specifies an offset into a color table that is also contained in the bitmap file.  
   
  Microsoft has enhanced the BMP file format several times over the years, which is why there is more than one bitmap file format extant. Files of the original BMP format are referred to as device-dependent bitmaps, or DDBs. This format is now generally considered obsolete, although files of this type still seem to exist in abundance. All versions of the BMP file format since the first are referred to as device-independent bitmaps, or DIBs. Unfortunately, the term bitmap is often used for these file types as well. For clarity, we will use the term bitmap file when we are referring to the file, and bitmap image when we are referring to the actual graphics image.  
   
  To confuse matters even more, when Windows loads a bitmap file into memory, it removes the file header, thus creating a Windows bitmap memory image. In summary, the term bitmap can refer to a bitmap graphics image, a bitmap file, or a bitmap memory image.  
   
  Scan Lines  
   
  In order to render a bitmap image on a display, the pixels (dot trios) on the inside surface of the display are scanned by electron guns, one row at a time. Hence,  
Page 358
   
  each row of the bitmap file's pixel array (see Figure 21-1) corresponds to a portion of one of the display's scan lines. Sometimes a row of the bitmap file itself is referred to as a scan line. (For more on hardware raster scanning, let me suggest my book Understanding Personal Computer Hardware, published by SpringerVerlag, New York.)  
   
  In this regard, bitmaps can be classified into two categories, distinguished by whether the rows of pixels appearing in the bitmap file are scanned in normal order or in reverse order. In the top-down bitmap file, the first row of pixels in the bitmap file corresponds to the top row of the bitmap image. This means that the origin of the bitmap array, that is, the pixel array entry (0,0) represents the upper-left corner of the bitmap image. In a bottom-up bitmap file, the first row of pixels in the bitmap file corresponds to the last (bottom) row of the bitmap image. Hence, the origin represents the lower-left corner of the bitmap image.  
   
  Simply put, a top-down bitmap file builds the bitmap image from the top down, whereas a bottom-up bitmap file builds the bitmap image from the bottom up.  
   
  Device-Independent Bitmaps  
   
  The main compatibility problem related to bitmaps is the rendering of color. The original device-dependent bitmap (DDB) format, used in Windows 3.0 and earlier, uses a very simple method for representing colors. Each pixel value in the bitmap's data array is an index into a color table that is maintained by Windows itself. In this case, the color table is not part of the bitmap file. Hence, if a bitmap is created on one system and displayed on another system with a different color table, the original colors may be replaced. This is not good.  
   
  To mitigate this problem, the device-independent bitmap, or DIB, was invented. A DIB file contains all of the color information used by the bitmap, thus making it portable.  
   
  To understand how this is done, we must first note that colors are generally represented in a bitmap by the RGB color model, in which each color is described by a 3-byte (24-bit) number 8 bits to specify the intensity of red, 8 bits to specify the intensity of green, and 8 bits to specify the intensity of blue. (The colors red, green, and blue are the primary colors for the model.) Thus, for instance, the color &HFFFFFF indicates full intensity for each color, thus producing white; &H0 produces black; and &H00FF00 produces full intensity (bright) green. You can check this yourself by using a VB picturebox control and executing the code:  
 
  Picture1.BackColor = RGB(0, &HFF, 0)  
   
  Incidentally, there are other color models. For instance, the CYMK color model uses the primary colors cyan, yellow, magenta, and black. The RGB color model is used by display monitors, and the CYMK model is used by color printers. This is one reason why it is so difficult to get screen and printer colors to match.  
Page 359
   
  To understand the format of a DIB file, the place to start is with the way in which the color of each pixel is represented in the file. This depends on how many colors the bitmap supports. The common color depths for a bitmap are:  
   
  1 bit: 2 colors (called monochrome)  
   
  4 bits: 16 colors  
   
  8 bits: 256 colors  
   
  16 bits: 65,536 colors (called high color)  
   
  24 bits: 16,777,216 colors (called true color)  
   
  There is also a 32-bit color bitmap. These bitmaps actually use 24 bits for the primary RGB colors. The extra byte is devoted to the so-called alpha channel, which carries information about the degree of opacity for each pixel.  
   
  Bitmaps with color depth 1, 4, or 8 always use a color table, also called a color palette. This is a table of 3-byte RGB color values, equal in size to the number of supported colors. Each pixel value in the bitmap's data array is simply an index into this color table. Figure 21-2 illustrates the concept of a color table.  
   
  0359-01.gif  
   
  Figure 21-2.
A color table
 
   
  The real complexity comes with 16-bit color bitmaps, which fall into the gray area between those bitmaps that should obviously use a color table (depth 1, 4, and 8) and those that do not need a color table (depth 24 and 32). A variety of schemes are used to deal with 16-bit color bitmaps, but they basically fall into two categories. Either the pixel value is an offset into a color table or the pixel value is masked into three parts one for each primary color. Of course, each part is less than the full 8 bits that would be used to represent all possible color intensities. For instance, the simplest color mask is to use 5 bits for each color, as in:  
 
  xrrrrrgggggbbbbb  
Page 360
   
  where the most significant bit x is unused. Thus, the low-order 5 bits determines the intensity of blue, for instance. Note that when this 16-bit word is stored in memory, the bytes are reversed (little-endian), so it will appear as gggbbbbbxrrrrrgg.  
   
  It is important to note that even 24-bit and 32-bit color bitmap files may contain color tables. This is because they may need to be rendered on devices that do not support the full range of colors supported by the bitmap itself. To render a 24-bit color bitmap on a monitor that is set to display only 256 colors requires the use of a color table or some other means for replacing each of potentially over 16 million colors by one of 256 colors!  
   
  The BMP file format has evolved through at least four versions and a new version is being implemented for Windows 2000. Since it is not essential to our discussion of the Win32 GDI, we will not discuss bitmap file formats.  
 
  Bitmap Functions  
   
  The Win32 API provides a slew of functions for creating and manipulating bitmaps, and you will no doubt want to take a close look at these functions before programming your next bitmap editing application. However, our discussion will be confined to just two bitmap-related functions: LoadImage and BitBlt.  
   
  BitBlt  
   
  The BitBlt function is one of the workhorses of the Windows GDI. This term BitBlt is shorthand for Bitmap Block Transfer, which tells us that the purpose of BitBlt is to move graphical data from one location to another. However, it is also possible to specify how the source data should interact with the destination data.  
   
  The syntax for BitBlt is:  
 
  BOOL BitBlt(
  HDC hdcDest,  // handle to destination device context
  int nXDest,  // x-coordinate of destination rectangle's upper-left corner
  int nYDest,  // y-coordinate of destination rectangle's upper-left corner
  int nWidth,  // width of destination rectangle
  int nHeight,  // height of destination rectangle
  HDC hdcSrc,   // handle to source device context
  int nXSrc,   // x-coordinate of source rectangle's upper-left corner
  int nYSrc,  // y-coordinate of source rectangle's upper-left corner
  DWORD dwRop  // raster operation code
);
 
   
  Among other things, this function requires handles to device contexts for both the source and the destination. We shall discuss device contexts in detail in Chapter 22. Device Contexts I: Overview. Suffice it to say now that a device context.  
Page 361
   
  provides a means to use the GDI drawing functions for a particular device (which might be the display, a printer, a window, or even a chunk of memory). You may be familiar with the fact that most VB controls (including forms) have an hDC property that returns a handle to a device context. This property is all we need for this chapter.  
   
  The only other non-self-explanatory parameter is the raster operation code dwRop. Table 21-1 shows some of the possible values. The operators AND, OR, and NOT stand for bitwise operations. Thus, for instance,  
 
  1100 AND 1010 = 1000
1100 OR 1010 = 1110
1100 XOR 1010 = 0110
NOT 10 = 01
 
Table 21-1. Some Raster Operation Codes
Code Description
BLACKNESS Fills the destination rectangle using the color associated with index 0 in the physical palette. (This color is black for the default physical palette.)
DSTINVERT Inverts the destination rectangle.
NOTSRCCOPY Destination = NOT Source
NOTSRCERASE Destination = NOT (Source OR Destination)
SRCAND Destination = Source AND Destination
SRCCOPY Copies the source rectangle directly to the destination rectangle. The original destination pixels are lost.
SRCERASE Destination = Source AND (NOT Destination)
SRCINVERT Destination = Source XOR Destination
SRCPAINT Destination = Source OR Destination
WHITENESS Fills the destination rectangle using the color associated with index 1 in the physical palette. (This color is white for the default physical palette.)


   
  Example: Moving Playing Cards  
   
  Before illustrating the BitBlt function, let me digress briefly to explain how I first encountered this function, since it is at least partially relevant to the discussion.  
   
  Sometime in 1994 (I believe), the first continuous speech recognition software became available for the PC. At that time, I was in the process of creating a large database of several thousand high-level mathematics books for a newsletter that I publish. For this, I needed to supply each book with up to three subject classifications out of more than a hundred different subjects (I'll bet you didn't know that there were more than a hundred different subject areas of mathematics). The first few subjects are shown in Table 21-2.  
Page 362
Table 21-2. Subject Classification
Code Subject
AA Abstract Algebra
AGEO Algebraic Geometry
ALG Algorithms
APPL Applied Math
APPROX Approximation Theory
ARITH Arithmetic
ATOP Algebraic Topology


   
  The idea that perhaps this could be done by voice was very exciting to me, since the alternative typing in these subjects was sickening. I wanted to be able to speak words such as:  
  Next Book, Algorithms, Next Subject, Applied Math, Next Subject, Approximation, Next Book  
   
  As it turned out, this worked quite well certainly much better than I was expecting, and it got me very interested in voice recognition.  
   
  In any case, the demo program that came with the voice recognition software was a pointless little video poker game,  and it occurred to me that the company might be able to better demonstrate its software by voice-enabling the traditional solitaire game that comes with Windows.  
   
  Unfortunately, there is no way to voice-enable that particular version because it does not accept keyboard input. So I set out to write a clone of Windows Solitaire using Visual Basic one that could be completely controlled through the keyboard, with commands like JH.QS, meaning Put the Jack of Hearts on the Queen of Spades. This would make it a simple matter to voice-enable the program.  
   
  As it happens, this turned out to be quite a challenge.  
   
  First, I started by placing 52 picture boxes on a VB form. I didn't get very far before VB complained about being out of resources. Next, I tried using image controls instead, but there seemed to be no way to eliminate the major flicker problem when moving these controls. (There were other problems as well, but I can't remember what they were now.)  
   
  The only solution that would not require more resources than were available and would not produce noticeable flicker when the cards were moved was to use BitBlt.  
Page 363
   
  The idea is to add a separate form with a single picture box called Deck. In that picture box, I place d the set of 52 cards, as in Figure 21-3. Whenever a card needed to be drawn somewhere on the gaming table, I just used BitBlt to copy it from the picture box to the main form.  
   
  0363-01.gif  
   
  Figure 21-3.
The cards for a solitaire program
 
   
  To illustrate the process, the CD includes a small BitBlt example project called rpiBitBlt. The program just draws randomly chosen cards from a small deck of 20 cards onto the main form frmTable. You can use the mouse to drag these cards around the main window without interfering with the other cards. Figure 21-4 and Figure 21-5 show sample before and after pictures.  
   
  The bulk of the action in this program takes place in the MouseMove event.  
   
  When a card is moved, we first identify the rectangles that are uncovered by this move (UnRect1 and UnRect2). Figure 21-6 shows these uncovered rectangles when the card is moved down and to the right. Of course, the location of the rectangles will depend upon the direction of movement of the card.  
   
  Here is the code that corresponds to Figure 21-6:  
 
  ElseIf CardLocation (iCurrentCard).Left > CardPrevRect.Left And _
   CardLocation(iCurrentCard).Top > CardPrevRect.Top Then
   ' Moved right and down
   UnRect1.Top = CardPrevRect.Top
   UnRect1.Bottom = CardLocation(iCurrentCard).Top
   UnRect1.Left = CardPrevRect.Left
   UnRect1.Right = CardPrevRect.Right

   UnRect2.Top = CardLocation(iCurrentCard).Top
   UnRect2.Bottom = CardPrevRect.Bottom
   UnRect2.Left = CardPrevRect.Left
   UnRect2.Right = CardLocation(iCurrentCard).Left
 
Page 364
   
  0364-01.gif  
   
  Figure 21-4.
Before
 
   
  Once the rectangles UnRect1 and UnRect2 have been identified, we next fill them with the color of the tabletop (the form) using the FillRect GDI function:  
 
  FillRect Me.hdc, UnRect1, COLOR_BTNFACE + 1
FillRect Me.hdc, UnRect2, COLOR_BTNFACE + 1
 
   
  (In case you are wondering about the +1, here is what the documentation says:  
  If specifying a color value for the hbr parameter, it must be one of the standard system colors (the value 1 must be added to the chosen color).  
   
  Next, we copy the card being moved to its new location using a BitBlt:  
 
  ' Place current card at new location
i = CardIndex(iCurrentCard)
BitBlt Me.hdc, _
   CardLocation(iCurrentCard). Left, _
   CardLocation(iCurrentCard). Top, _
   CARD_WIDTH, _
   CARD_HEIGHT, _
   frmCards.Deck.hdc, _
   DECK_X_SPACING * (i Mod DECK_COL_COUNT), _
   DECK_Y_SPACING * (i \ DECK_COL_COUNT), _
   SRCCOPY
 
Page 365
   
  0365-01.gif  
   
  Figure 21-5.
After
 
   
  0365-02.gif  
   
  Figure 21-6.
Card movement
 
   
  Of course, the uncovered rectangles may intersect some of the other cards. To check that, we use the IntersectRect function. Here is an excerpt:  
 
  ' Get rectangle for card
SetRect Card, CardLocation(i).Left, CardLocation(i).Top, _
   CardLocation(i).Left + CARD_WIDTH, CardLocation(i).Top + CARD_HEIGHT
 
 

Page 366
 
  ' Get intersection of Unrect1 and card
IntersectRect FixupRect, UnRect1, Card

' If not empty then redraw FixupRect
If IsRectEmpty(FixupRect) = 0 Then
   BitBlt Me.hdc, _
      FixupRect.Left, _
      FixupRect.Top, _
      FixupRect.Right - FixupRect.Left, _
      FixupRect.Bottom - FixupRect.Top, _
      frmCards.Deck.hdc, _
      DECK_X_SPACING * (CardIndex(i) Mod DECK_COL_COUNT) _
      + (FixupRect.Left - CardLocation(i).Left), _
      DECK_Y_SPACING * Int(CardIndex(i) / DECK_COL_COUNT) _
      + (FixupRect.Top - CardLocation(i).Top), _
      SRCCOPY
End If
 
   
  The entire MouseMove code is shown here:  
 
  Private Sub Form_MouseMove(Button As Integer, Shift As Integer, _
x As Single, y As Single)

Dim i As Integer

Dim UnRect1 As RECT
Dim UnRect2 As RECT
Dim FixupRect As RECT
Dim Card As RECT

If Not bProcessMouseMove Then Exit Sub

' Adjust card location
CardLocation(iCurrentCard).Left = x - iCardOffsetx
CardLocation(iCurrentCard).Top = y - iCardOffsety
CardLocation(iCurrentCard).Bottom = CardLocation(iCurrentCard).Top + CARD_HEIGHT
CardLocation(iCurrentCard).Right = CardLocation(iCurrentCard).Left + CARD_WIDTH

' Get uncovered rectangles
If CardLocation(iCurrentCard).Left = CardPrevRect.Left Then
   ' Vertical move
   SubtractRect UnRect1, CardPrevRect, CardLocation(iCurrentCard)
   SetRectEmpty UnRect2

ElseIf CardLocation(iCurrentCard).Top = CardPrevRect.Top Then
   ' Horizontal move
   SetRectEmpty UnRect1
   SubtractRect UnRect2, CardPrevRect, CardLocation(iCurrentCard)

ElseIf CardLocation(iCurrentCard).Left > CardPrevRect.Left And _
   CardLocation(iCurrentCard).Top > CardPrevRect.Top Then
   ' Move right and down
   UnRect1.Top = CardPrevRect.Top
   UnRect1.Bottom = CardLocation(iCurrentCard).Top
   UnRect1.Left = CardPrevRect.Left
   UnRect1.Right = CardPrevRect.Right
 
 

Page 367
 
     UnRect2.Top = CardLocation(iCurrentCard).Top
   UnRect2.Bottom = CardPrevRect.Bottom
   UnRect2.Left = CardPrevRect.Left
   UnRect2.Right = CardLocation(iCurrentCard).Left

ElseIf CardLocation(iCurrentCard).Left > CardPrevRect.Left And _
   CardLocation(iCurrentCard).Top < CardPrevRect.Top Then
   ' Move right and up
   UnRect1.Top = CardLocation(iCurrentCard).Bottom
   UnRect1.Bottom = CardPrevRect.Bottom
   UnRect1.Left = CardPrevRect.Left
   UnRect1.Right = CardPrevRect.Right

   UnRect2.Top = CardPrevRect.Top
   UnRect2.Bottom = CardLocation(iCurrentCard).Bottom
   UnRect2.Left = CardPrevRect.Left
   UnRect2.Right = CardLocation(iCurrentCard).Left

ElseIf CardLocation(iCurrentCard).Left < CardPrevRect.Left And _
   CardLocation(iCurrentCard).Top > CardPrevRect.Top Then
   ' Move left and down
   UnRect1.Top = CardPrevRect.Top
   UnRect1.Bottom = CardLocation(iCurrentCard).Top
   UnRect1.Left = CardPrevRect.Left
   UnRect1.Right = CardPrevRect.Right

   UnRect2.Top = CardLocation(iCurrentCard).Top
   UnRect2.Bottom = CardPrevRect.Bottom
   UnRect2.Left = CardLocation(iCurrentCard).Right
   UnRect2.Right = CardPrevRect.Right

ElseIf CardLocation(iCurrentCard).Left < CardPrevRect.Left And _
   CardLocation(iCurrentCard).Top < CardPrevRect.Top Then
   ' Move left and up
   UnRect1.Top = CardLocation(iCurrentCard).Bottom
   UnRect1.Bottom = CardPrevRect.Bottom
   UnRect1.Left = CardPrevRect.Left
   UnRect1.Right = CardPrevRect.Right

   UnRect2.Top = CardPrevRect.Top
   UnRect2.Bottom = CardLocation(iCurrentCard).Bottom
   UnRect2.Left = CardLocation(iCurrentCard).Right
   UnRect2.Right = CardPrevRect.Right

End If

' Color the uncovered rectangles with the table color
FillRect Me.hdc, UnRect1, COLOR_BTNFACE + 1
FillRect Me.hdc, UnRect2, COLOR_BTNFACE + 1

' Place current card at new location
i = CardIndex(iCurrentCard)
BitBlt Me.hdc, _
   CardLocation(iCurrentCard).Left, _
   CardLocation(iCurrentCard).Top, _
 
 

Page 368
 
     CARD_WIDTH, _
   CARD_HEIGHT, _
   frmCards.Deck.hdc, _
   DECK_X_SPACING * (i Mod DECK_COL_COUNT), _
   DECK_Y_SPACING * (i \ DECK_COL_COUNT), _
   SRCCOPY

' Do the uncovered rectangles intersect any cards?
' If so, repaint cards
For i = 0 To DECK_CARD_COUNT - 1

   ' Skip the moving card
   If i = iCurrentCard Then GoTo NotThisCard

   ' Get rectangle for card
   SetRect Card, CardLocation(i).Left, CardLocation(i).Top, _
      CardLocation(i).Left + CARD_WIDTH, CardLocation(i).Top + CARD_HEIGHT

   ' Get intersection of Unrect1 and card
   IntersectRect FixupRect, UnRect1, Card

   ' If not empty then redraw FixupRect
   If IsRectEmpty(FixupRect) = 0 Then
      BitBlt Me.hdc, _
         FixupRect.Left, _
         FixupRect.Top, _
         FixupRect.Right - FixupRect.Left, _
         FixupRect.Bottom - FixupRect.Top, _
         frmCards.Deck.hdc, _
         DECK_X_SPACING * (CardIndex(i) Mod DECK_COL_COUNT) + _
            (FixupRect.Left - CardLocation(i).Left), _
         DECK_Y_SPACING * Int(CardIndex(i) / DECK_COL_COUNT) + _
            (FixupRect.Top - CardLocation(i).Top), _
         SRCCOPY
   End If

   ' Get intersection of Unrect2 and card
   IntersectRect FixupRect, UnRect2, Card

   ' If not empty then redraw FixupRect
   If IsRectEmpty(FixupRect) = 0 Then
      BitBlt Me.hdc, _
         FixupRect.Left, _
         FixupRect.Top, _
         FixupRect.Right - FixupRect.Left, _
         FixupRect.Bottom - FixupRect.Top,
         frmCards.Deck.hdc, _
         DECK_X_SPACING * (CardIndex(i) Mod DECK_COL_COUNT) + _
            (FixupRect.Left - CardLocation(i).Left), _
         DECK_Y_SPACING * Int(CardIndex(i) / DECK_COL_COUNT) + _
            (FixupRect.Top - CardLocation(i).Top), _
         SRCCOPY
   End If

NotThisCard:
Next
 
 

Page 369
 
  ' Save for next time
CardPrevRect = CardLocation(iCurrentCard)

End Sub
 
 
  Using Bitmaps in Menus  
   
  If anyone asks you why he or she would want to program the Win32 API under Visual Basic, you can always cite the following example as a good reason. In this example, we will place a bitmap in a VB menu, as shown in Figure 21-7.  
   
  0369-01.gif  
   
  Figure 21-7.
A bitmap in a menu
 
   
  The key to doing this is the ModifyMenu function:  
 
  BOOL ModifyMenu(
  HMENU hMnu,       // handle to menu
  UINT uPosition,   // menu item to modify
  UINT uFlags,      // menu item flags
  UINT uIDNewItem,  // menu item identifier or handle to drop-down menu/submenu
  LPCTSTR lpNewItem // menu item content
);
 
   
  When the uFlags parameter is set to MF_BITMAP, the lpNewItem parameter is expected to contain the handle to a bitmap. Accordingly, we declare the ModifyMenu function in VB as follows (note that the last parameter is declared as a long):  
 
  Declare Function ModifyMenu Lib "user32" Alias "ModifyMenuA" ( _
   ByVal hMenu As Long, _
   ByVal nPosition As Long, _
   ByVal wFlags As Long, _
   ByVal wIDNewItem As Long, _
   ByVal lpNewItem As Long _
) As Long
 
   
  To get a handle to a bitmap, we use the LoadImage function:  
 
  HANDLE LoadImage(
  HINSTANCE hinst,   // handle of the instance containing the image
  LPCTSTR lpszName,  // name or identifier of image
  UINT uType,        // type of image
  int cxDesired,     // desired width
 
 

Page 370
 
    int cyDesired,     // desired height
  UINT fuLoad        // load flags
);
 
   
  By setting hInst to NULL and uLoad to LR_LOADFROMFILE, we can put the path/ filename of a bitmap in lpszName.  
   
  Here is the entire code to do the job:  
 
  Sub MenuBitmap()

Dim hMenu As Long
Dim hSubMenu As Long
Dim lMenuID As Long
Dim hBitmap As Long
Dim hImage As Long

' Get handle of top menu
hMenu = GetMenu(Me.hwnd)

' Do we have a valid menu handle
If IsMenu(hMenu) = 0 Then
   MsgBox "Menu handle invalid", vbInformation
   Exit Sub
End If

' Get handle of submenu 0 (File menu)
hSubMenu = GetSubMenu(hMenu, 0)

' Do we have a valid submenu handle
If IsMenu(hSubMenu) = 0 Then
   MsgBox "Submenu handle invalid", vbInformation
   Exit Sub
End If

' Need menu item ID (item 1 is second item)
lMenuID = GetMenuItemID(hSubMenu, 1)

' Load bitmap
hImage = LoadImage(0, "d:\bkapi\0code\atten.bmp", IMAGE_BITMAP, 0, 0, LR_
LOADFROMFILE)

' Stick it in menu
ModifyMenu hSubMenu, 1, MF_BITMAP Or MF_BYPOSITION, lMenuID, hImage

End Sub
 


WIN32 API Programming with Visual Basic
Win32 API Programming with Visual Basic
ISBN: 1565926315
EAN: 2147483647
Year: 1999
Pages: 31
Authors: Steven Roman

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net