Displaying a downloaded icon

Let's say that you want your Microsoft Foundation Classes (MFC) application to display an icon that it has downloaded, rather in the way that Internet Explorer displays icons on its tabs. 

The first step is to download the remote icon to a local file.  Here is a function for doing that:

void WebResource::Download()

{

       if(!(this->uniqueIdentifier))

       {

              ::AfxThrowNotSupportedException();

       }

      

       CFile fileLocal(this->fileName,CFile::modeWrite|CFile::modeCreate);

       const UINT BufferSize = Constants::BufferSizeInBytesDownload;

       char* buffer = new char[BufferSize];

       if(!(buffer))

       {

              THROW(new CMemoryException());

       }

 

       CInternetSession session;

       CStdioFile* fileRemote = session.OpenURL(this->GetLocator());

       if(!(fileRemote))

       {

              Exception::Throw(IDS_ERROR_DOWNLOAD_TARGET_TEMPLATE,this->GetLocator());

       }

 

       UINT downloaded = 0;

       BOOL result = TRUE;

       try

       {

              do

              {

                     ::ZeroMemory(buffer,BufferSize);

 

                     downloaded = fileRemote->Read(buffer,BufferSize);

                     if(downloaded <= 0)

                     {

                           break;

                     }

 

                     fileLocal.Write(buffer,downloaded);

 

                     if(downloaded < BufferSize)

                     {

                           break;

                     }

              }

              while(TRUE);

       }

       catch(...)

       {

              result = FALSE;

       }  

 

       if(fileRemote)

       {

              delete fileRemote;

       };

             

       if(buffer)

       {

              delete [] buffer;

       }

 

       if(!(result))

       {

              Exception::Throw(IDS_ERROR_DOWNLOAD_TEMPLATE,this->GetLocator());

       }

}

 

The next step is to load the icon from the local file. Here is a function that accomplishes that task: 

HICON WebResource::GetIcon()

{

       if(!(this->icon))

       {

              if(!(::PathFileExistsW(this->fileName)))

              {

                     this->Download();

              } 

              this->icon = (HICON)::LoadImage(

                                                       NULL,

                                                       this->fileName,

                                                       IMAGE_ICON,

                                                       Constants::HeightWidthIcon, //16

                                                       Constants::HeightWidthIcon,

                                                       LR_CREATEDIBSECTION|LR_LOADMAP3DCOLORS|LR_LOADFROMFILE|LR_SHARED|LR_VGACOLOR); 

       }

       return this->icon;

}

 

Note that the desired height and width of the icon are specified. If the .ico file happens to include icons of different dimensions, then specifying the desired height and width will ensure that the preferred one will be loaded. 

 

The last step is to paint the icon. Here is the implementation of OnPaint that accomplishes that:

 

void Tab::OnPaint()

{

       CPaintDC deviceContext(this);

       CRect area;

       this->GetClientRect(&area);

 

       this->displayBehavior->DisplayTab(&deviceContext,&area);

 

       WebResource* iconResource = webResource->GetIconResource();

       if(iconResource)

       {

              this->PaintIcon(&deviceContext,iconResource);

       }

      

       deviceContext.SelectObject(this->font);

       deviceContext.SetBkMode(TRANSPARENT);

       deviceContext.SetTextColor(this->colorText);

       deviceContext.DrawText(this->webResource->GetLocator(),&this->areaLabel,Tab::StyleText);

}

 

void Tab::PaintIcon(CDC* deviceContext, WebResource* iconResource)

{

       if(!(iconResource))

       {

              return;

       }

 

       try

       {

              if(!(this->icon))

              {

                     this->icon = new Icon();

                     if(!(this->icon))

                     {

                           return;

                     }

 

                     if(!(this->icon->Create(deviceContext, this->iconHandle,this->colorBackground)))

                     {

                           this->InvalidateIcon();

                           return;

                     }

              }

 

              if(this->icon)

              {

                     CDC* memoryContext = this->icon->GetDeviceContext();

                     if(memoryContext)

                     {

                           deviceContext->BitBlt(this->areaIcon.left,this->areaIcon.top,Constants::HeightWidthIcon,Constants::HeightWidthIcon,memoryContext,0,0,SRCCOPY);

                     }

              }

       }

       catch(...)

       {

              return;

       }

}

 

Tab::Icon::Icon()

{

       this->InvalidateDeviceContext();

}

 

Tab::Icon::~Icon()

{

       this->DeleteDeviceContext();

}

 

BOOL Tab::Icon::Create(CDC* targetContext, HICON iconHandle, COLORREF backgroundColor)

{

       BOOL result = FALSE;

 

       this->InvalidateDeviceContext();

      

       this->backgroundBrush.CreateSolidBrush(backgroundColor);

 

       while(!(result))

       {

              CBitmap iconBitmap;

              if(!(iconBitmap.CreateCompatibleBitmap(targetContext,Constants::HeightWidthIcon,Constants::HeightWidthIcon)))

              {

                     break;

              }

 

              this->deviceContext = new CDC();

              this->deviceContext->CreateCompatibleDC(targetContext);

 

              CBitmap* originalBitmap = this->deviceContext->SelectObject(&iconBitmap);

                    

              HGDIOBJ originalBrush = this->deviceContext->SelectObject(this->backgroundBrush);

              if(!(this->deviceContext->PatBlt(0, 0, Constants::HeightWidthIcon, Constants::HeightWidthIcon,PATCOPY)))

              {

                     break;

              }

                    

              if(!(this->deviceContext->DrawState(

                     CPoint(0,0),

                     CSize(Constants::HeightWidthIcon,Constants::HeightWidthIcon),

                     iconHandle,

                     DST_ICON|DSS_NORMAL,

                     (HBRUSH)NULL)))

              {

                     break;

              }

 

              result = TRUE;

       }

 

       if(!(result))

       {

              this->InvalidateDeviceContext();

       }

 

       return result;

}

 

void Tab::Icon::DeleteDeviceContext()

{

       if(this->deviceContext)

       {

              if(this->originalBrush)

              {

                     (void)this->deviceContext->SelectObject(this->originalBrush);

              }

              if(this->originalBitmap)

              {

                     (void)this->deviceContext->SelectObject(this->originalBitmap);

              }

              delete this->deviceContext;

       }

}

 

void Tab::Icon::InvalidateDeviceContext()

{

       this->deviceContext = NULL;

       this->originalBitmap = NULL;

}

 

CDC* Tab::Icon::GetDeviceContext()

{

       return this->deviceContext;

}

 

 

Here is what happens. First, a bitmap is created that is compatible with the CPaintDC, the full-color screen device context onto which the icon is to be painted:

CBitmap iconBitmap;

if(!(iconBitmap.CreateCompatibleBitmap(targetContext,Constants::HeightWidthIcon,Constants::HeightWidthIcon)))

Then a device context that will serve as the buffer of the painted icon is created:

this->deviceContext = new CDC();

this->deviceContext->CreateCompatibleDC(targetContext);

Next, within that buffer, the bitmap is painted with the background color, and then with the icon:

  

                                 CBitmap* originalBitmap = this->deviceContext->SelectObject(&iconBitmap);

          HGDIOBJ originalBrush = this->deviceContext->SelectObject(this->backgroundBrush);

              if(!(this->deviceContext->PatBlt(0, 0, Constants::HeightWidthIcon, Constants::HeightWidthIcon,PATCOPY)))

              {

                     break;

              }

                    

              if(!(this->deviceContext->DrawState(

                     CPoint(0,0),

                     CSize(Constants::HeightWidthIcon,Constants::HeightWidthIcon),

                     iconHandle,

                     DST_ICON|DSS_NORMAL,

                     (HBRUSH)NULL)))

              {

                     break;

              }

Finally, the buffer is swapped onto the CPaintDC:

 

deviceContext->BitBlt(this->areaIcon.left,this->areaIcon.top,Constants::HeightWidthIcon,Constants::HeightWidthIcon,memoryContext,0,0,SRCCOPY); //deviceContext is CPaintDC, whereas memoryContext is the CDC* buffer.