EXTRACTING ICONS AND SAVING THEM AS BITMAPS
an esoteric graphics tutorial
copyright Alyce Watson, 2002
intermediate - advanced skill level

Home

Maintaining checkbox states
Tidbits from the community
Liberty Basic 4
New Alternate Forum
Using the Tsunami Database
Wizard Framework
Links to LB Sites
Update on 10th Anniversary Contest
Extracting Icons And Saving Them As Bitmaps
Applying Symbolic Logic
QuadClicks
Simple Math For Moving Objects
Event Driven Programming - Part 2
The Beginners Series - Part 1

 

This tutorial expects the reader to have a fair understanding of the Windows API and Liberty BASIC graphics functions. [ed: the demo file is part of saveicon.zip]

We can load icons into memory with an API call, LoadImageA. This allows us to access single icon files with the extension *.ico. If we want to access icons from other types of files, such as executables and libraries, we can use ExtractIconA. To use the function, we need to have an instance handle to our program's window, which we obtain with the function GetWindowLongA, and a type flag of _GWL_HINSTANCE. The function returns the instance handle. Here is the function, wrapped in a Liberty BASIC function:

Function GetWindowLong(hWindow, type)
    CallDLL #user32, "GetWindowLongA",_
    hWindow As Long, type As Long,_
    GetWindowLong As Long
    End Function

The function to extract the icon requires the instance handle, the filename of the file containing the icon on disk, and a flag indicating the zero-based index of the desired icon. If this flag is -1, the function returns the number of icons in the file. An icon library file (*.icl) or a dynamic link library (*.dll) may contain dozens of icons.

    hInst=GetWindowLong(hWindow, _GWL_HINSTANCE)
    CallDLL #shell32, "ExtractIconA",_
        hInst As Long,_    'instance handle of window
        file$ As Ptr,_     'disk file name
        index As Long,_    'index of desired icon
        result As Long     'returns icon handle or number of icons

To simplify our demo, we are passing an index of 0 to extract the first icon in the disk file. We could instead, use an index of -1 to obtain the number of icons in the file, then allow the user to choose the desired icon in a preview window. Perhaps some intrepid soul will modify the demo to do just that, and share it with the group?

Here is the ExtractIconA function wrapped in a Liberty BASIC function with an icon index of 0, so it will return the handle of the first icon in the file:

Function ExtractIcon(hWindow, file$)
    hInst=GetWindowLong(hWindow, _GWL_HINSTANCE)
    CallDLL #shell32, "ExtractIconA",hInst As Long,_
    file$ As Ptr, 0 As Long, ExtractIcon As Long
    End Function

When we no longer need the icon, and before we end our program, we must remove the icon from memory with a call to DestroyIcon. Here it is, wrapped in a Liberty BASIC function:

Sub DestroyIcon hIcon
    CallDLL #user32, "DestroyIcon",_
    hIcon As Long,_ 'handle of icon
    ret As Boolean
    End Sub

Once we have a handle to an icon, we can display the icon. We use the DrawIcon function to display the icon on the specified Device Context, at the x, y location of our choice. Here it is, wrapped in a Liberty BASIC function:

Function DrawIcon(hdc,hIcon,x,y)
    CallDLL #user32, "DrawIcon",hdc As Long, x As Long,_
    y As Long, hIcon As Long, DrawIcon As Long
    End Function

We could use the device context of a window or graphicbox when drawing the icon. We first get the handle of the window with LB's hwnd(#window) function, then we can get the Device context of a window with GetDC:

Function GetDC(hWindow)
    CallDLL #user32, "GetDC",_
    hWindow As Long,_	'handle of window
    GetDC As Ulong      'handle of device context
    End Function

'Before the program ends, we must ReleaseDC:

Sub ReleaseDC hWindow, hDC
    CallDLL#user32,"ReleaseDC",_
    hWindow As Long,_   'window handle
    hDC As Long,_       'handle of device context
    result As Long
    End Sub

We aren't drawing the icon on our window in this demo, however. We are going to create a bitmap in memory and draw the icon on a memory device context that contains the created bitmap. This way, we can save the bitmap easily with the BMPSAVE command. Before we can create a memory device context, we must use the GetDC function to get a device context handle to our window, as outlined above. With this, we can create a memory device context with CreateCompatibleDC. When it is no longer needed we must delete this memory device context with DeleteDC. Here are the functions:

Function CreateCompatibleDC(hDC)
    CallDLL #gdi32,"CreateCompatibleDC",_
     hDC As Long,_    'device context of window
    CreateCompatibleDC As Ulong 'returns memory device context
    End Function

Sub DeleteDC hDC
    CallDLL #gdi32, "DeleteDC",_
    hDC As Long,_    'handle of memory device context
    r As Boolean
    End Sub


When it is created, the memory DC has a default bitmap that is one pixel wide and one pixel high. We'll create our own bitmap in memory and make it 32x32, the size of a standard icon. It is important to note that we must use a device context handle for our window in this function and NOT the handle of the memory device context:

Function CreateCompatibleBitmap(hDC,wide,high )
    CallDLL #gdi32, "CreateCompatibleBitmap",_
    hDC As Long,_       'device context of WINDOW
    wide As Long,_      'desired width of bitmap
    high As Long,_      'desired height of bitmap
    CreateCompatibleBitmap As Ulong 'returns handle of memory bitmap
    End Function

Once we select the memory bitmap into the memory device context with SelectObject, all functions that write to the memory device context will write on the memory bitmap. The SelectObject function returns the handle to the previous (default) bitmap. We'll save this in our program, so that we can access it again later:

Function SelectObject(hDC,hObject)
    CallDLL #gdi32,"SelectObject",_
    hDC As Long,_        'handle of memory device context
    hObject As Long,_    'handle of memory bitmap
    SelectObject As Long 'returns previously selected object   
    End Function

'When we no longer need the bitmap in memory, we must delete it with DeleteObject:

Sub DeleteObject hObject
    CallDLL #gdi32,"DeleteObject",_
    hObject As Long,_   'handle of memory bitmap
    r As Boolean
    End Sub

We need to give the memory bitmap a blank, white background each time we want to draw a new icon on it. We do this with a call to PatBlt. It requires the x, y position to begin and the width and height of the rectangle we want to draw on. It also requires a flag telling it what we want to do to that rectangle. Since we want to fill it with white, our flag will be _WHITENESS.

    CallDLL #gdi32, "PatBlt",_
    hDC As Long,_     'handle of device context
    x As Long,_       'upper left corner x coord
    y As Long,_       'upper left corner y coord
    width As Long,_   'rectangle width
    height As Long,_  'rectangle height
    _WHITENESS As Ulong,_ 'fill with white
    r As Boolean

Now that we know all of the API functions, we can write the Liberty BASIC code to extract an icon from a disk file, draw it on a memory bitmap, then save that bitmap to disk with LB's BMPSAVE COMMAND.

We allow the user to choose a file that contains icons:

FileDialog "Open Icon","*.ico;*.exe;*.icl;*.dll",iconfile$
If iconfile$="" Then Wait

If we've extracted an icon previously, we remove it from memory:

If hMemIcon<>0 Then Call DestroyIcon hMemIcon

Now we extract the first icon from the diskfile, getting its handle into hMemIcon:

hMemIcon=ExtractIcon(hWnd(#1),iconfile$)

It is always good to check for errors:

If hMemIcon=0 Then
Notice "Cannot extract icon!"
Wait
End If

We need to clear our memory bitmap to a plain white background:

Call PatBlt hMemDC,0,0,32,32,_WHITENESS

Now we can draw the icon on the memory bitmap:

r=DrawIcon(hMemDC,hMemIcon,0,0)

We can display the memory bitmap in a graphicbox. IMPORTANT: we must first deselect the bitmap from the memory DC before we can load it with LOADBMP. We do this by selecting the original, default bitmap back into the memory DC:

hBitmap=SelectObject(hMemDC,oldBitmap)

Now we can load the bitmap using the LOADBMP command:

LoadBmp "IconBmp", hBitmap

Once we've loaded it, we can draw it with DRAWBMP. We can then UNLOADBMP on our LB bitmap to free the memory.

#1.g "down;cls;drawbmp IconBmp 0 0;flush"
UnloadBmp "IconBmp"

To prepare for the user's next icon choice, we select the memory bitmap back into the memory DC:

oldBitmap=SelectObject(hMemDC,hBitmap)


When the user chooses to save the icon as a bitmap, we'll first check to make sure there is a valid icon to save:

If hMemIcon=0 Then
Notice "No icon to save."
Wait
End If

We'll query the user for a filename for the bitmap:

FileDialog "Save As","*.bmp",savefile$
If savefile$="" Then Wait

Remember that when we wanted to display the bitmap, we deselected it from the memory device context, and we must do that again so that we can load it with LOADBMP, then save it with BMPSAVE:

hBitmap=SelectObject(hMemDC,oldBitmap)
LoadBmp "IconBmp", hBitmap
BmpSave "IconBmp",savefile$
UnloadBmp "IconBmp"

That's everything! Here is the complete demo program:
=====================================

If Val(Version$)<3 Then
    Notice "Requires at least LB3."
    End
End If

NoMainWin
Menu #1, "&File","&Open Icon",[openIcon],_
    "&Save As Bitmap",[saveAsBitmap],_
    |,"E&xit",[quit]
Graphicbox #1.g, 50,50,34,34
Open "Save Icon As Bitmap" For Window_nf As #1
    #1 "trapclose [quit]"
    #1.g "down"
    hWinDC=GetDC(hWnd(#1))
    hMemDC=CreateCompatibleDC(hWinDC)
    hBitmap=CreateCompatibleBitmap(hWinDC,32,32)
    oldBitmap=SelectObject(hMemDC,hBitmap)
    Call PatBlt hMemDC,0,0,32,32,_WHITENESS
    Wait

[quit]
    Call ReleaseDC hWnd(#1),hWinDC
    Call DeleteDC hMemDC
    Call DeleteObject hBitmap
    Call DestroyIcon hMemIcon
    Close #1:End

[openIcon]
    FileDialog "Open Icon","*.ico;*.exe;*.icl;*.dll",iconfile$
    If iconfile$="" Then Wait
    If hMemIcon<>0 Then Call DestroyIcon hMemIcon

    hMemIcon=ExtractIcon(hWnd(#1),iconfile$)
    If hMemIcon=0 Then
        Notice "Cannot extract icon!"
        Wait
    End If

    Call PatBlt hMemDC,0,0,32,32,_WHITENESS
    r=DrawIcon(hMemDC,hMemIcon,0,0)
    'deselect bitmap from memory DC to load
    hBitmap=SelectObject(hMemDC,oldBitmap)
    LoadBmp "IconBmp", hBitmap
    #1.g "down;cls;drawbmp IconBmp 0 0;flush"
    UnloadBmp "IconBmp"
    'select bitmap back into memory DC
    oldBitmap=SelectObject(hMemDC,hBitmap)
    Wait

[saveAsBitmap]
    If hMemIcon=0 Then
        Notice "No icon to save."
        Wait
    End If

    FileDialog "Save As","*.bmp",savefile$
    If savefile$="" Then Wait
    hBitmap=SelectObject(hMemDC,oldBitmap)
    LoadBmp "IconBmp", hBitmap
    BmpSave "IconBmp",savefile$
    UnloadBmp "IconBmp"
    'select bitmap back into memory DC
    oldBitmap=SelectObject(hMemDC,hBitmap)
    Wait

''''Subs and Functions:
Function GetDC(hWnd)
    CallDLL #user32, "GetDC",hWnd As Long,_
    GetDC As Ulong
    End Function

Sub ReleaseDC hWnd, hDC
    CallDLL#user32,"ReleaseDC",hWnd As Long,_
    hDC As Long,result As Long
    End Sub

Function CreateCompatibleDC(hDC)
    CallDLL #gdi32,"CreateCompatibleDC",hDC As Long,_
    CreateCompatibleDC As Ulong
    End Function

Sub DeleteDC hDC
    CallDLL #gdi32, "DeleteDC",hDC As Long,_
    r As Boolean
    End Sub

Function CreateCompatibleBitmap(hDC,w,h )
    CallDLL #gdi32, "CreateCompatibleBitmap",_
    hDC As Long,w As Long,h As Long,_
    CreateCompatibleBitmap As Ulong
    End Function

Function SelectObject(hDC,hObject)
    CallDLL #gdi32,"SelectObject",hDC As Long,_
    hObject As Long,SelectObject As Long
    'returns previously selected object
    End Function

Sub DeleteObject hObject
    CallDLL #gdi32,"DeleteObject",hObject As Long,_
    r As Boolean
    End Sub

Function GetWindowLong(hW, type)
    CallDLL #user32, "GetWindowLongA",_
    hW As Long, type As Long,GetWindowLong As Long
    End Function

Function ExtractIcon(hW, file$)
    hInst=GetWindowLong(hW, _GWL_HINSTANCE)
    CallDLL #shell32, "ExtractIconA",hInst As Long,_
    file$ As Ptr, 0 As Long, ExtractIcon As Long
    End Function

Function DrawIcon(hdc,hIcon,x,y)
    CallDLL #user32, "DrawIcon",hdc As Long, x As Long,_
    y As Long, hIcon As Long, DrawIcon As Long
    End Function

Sub DestroyIcon hIcon
    CallDLL #user32, "DestroyIcon",_
    hIcon As Long,_ 'handle of icon
    ret As Boolean
    End Sub

Sub PatBlt hDC,x,y,w,h,flag
    CallDLL #gdi32, "PatBlt",hDC As Long,_
    x As Long,y As Long,w As Long,h As Long,_
    flag As Ulong,r As Boolean
    End Sub

  

Home

Maintaining checkbox states
Tidbits from the community
Liberty Basic 4
New Alternate Forum
Using the Tsunami Database
Wizard Framework
Links to LB Sites
Update on 10th Anniversary Contest
Extracting Icons And Saving Them As Bitmaps
Applying Symbolic Logic
QuadClicks
Simple Math For Moving Objects
Event Driven Programming - Part 2
The Beginners Series - Part 1