Win32 API Programming with Visual Basic

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
   
   
   
  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.  
   
   
   
  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.  
   
   
   
  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
   
   
   
  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
   
   
   
  Figure 21-5.

After

 
   
   
   
  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.  
   
   
   
  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

 

Категории