Dragging a shell object, part 4: Adding a prettier drag icon

Date:December 9, 2004 / year-entry #416
Tags:code
Orig Link:https://blogs.msdn.microsoft.com/oldnewthing/20041209-00/?p=37083
Comments:    0
Summary:You may have noticed that the drag feedback is rather sad-looking. Just a box, maybe with a plus sign or an arrow; you don't even know what it is you're dragging. Let's fix that. We'll drag the icon of the file around. We'll need to add the drag image to the data object. void OnLButtonDown(HWND...

You may have noticed that the drag feedback is rather sad-looking. Just a box, maybe with a plus sign or an arrow; you don't even know what it is you're dragging.

Let's fix that. We'll drag the icon of the file around. We'll need to add the drag image to the data object.

void OnLButtonDown(HWND hwnd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
{
  IDataObject *pdto;
  if (SUCCEEDED(GetDataObjectOfFileWithCuteIcon(
                hwnd, g_pszTarget, &pdto))) {
     IDropSource *pds = new CDropSource();
     ...

This new function GetDataObjectOfFileWithCuteIcon creates the data object and then attaches the cute icon to it.

HRESULT GetDataObjectOfFileWithCuteIcon(HWND hwnd,
 LPCWSTR pszPath, IDataObject **ppdto)
{
  HRESULT hr = GetUIObjectOfFile(hwnd, pszPath,
                    IID_IDataObject, (void**)ppdto);
  if (SUCCEEDED(hr)) {
    IDragSourceHelper *pdsh;
    if (SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, NULL, CLSCTX_ALL,
                                   IID_IDragSourceHelper, (void**)&pdsh))) {
      SHDRAGIMAGE sdi;
      if (CreateDragImage(pszPath, &sdi)) {
        pdsh->InitializeFromBitmap(&sdi, *ppdto);
        DeleteObject(sdi.hbmpDragImage);
      }
      pdsh->Release();
    }
  }
  return hr;
}

We use the shell drag/drop helper object to attach the bitmap to the data object. The shell drag/drop helper object requires that the data object be able to accept arbitrary blobs, but fortunately, the standard shell data object does this.

The nasty part is generating the drag image. This is not the fun part, and you're not going to learn anything from this function. It just has to be written.

BOOL CreateDragImage(LPCWSTR pszPath, SHDRAGIMAGE *psdi)
{
  psdi->hbmpDragImage = NULL;
  SHFILEINFOW sfi;
  HIMAGELIST himl = (HIMAGELIST)
    SHGetFileInfoW(pszPath, 0, &sfi, sizeof(sfi), SHGFI_SYSICONINDEX);
  if (himl) {
    int cx, cy;
    ImageList_GetIconSize(himl, &cx, &cy);
    psdi->sizeDragImage.cx = cx;
    psdi->sizeDragImage.cy = cy;
    psdi->ptOffset.x = cx;
    psdi->ptOffset.y = cy;
    psdi->crColorKey = CLR_NONE;
    HDC hdc = CreateCompatibleDC(NULL);
    if (hdc) {
      psdi->hbmpDragImage = CreateBitmap(cx, cy, 1, 32, NULL);
      if (psdi->hbmpDragImage) {
        HBITMAP hbmPrev = SelectBitmap(hdc, psdi->hbmpDragImage);
        ImageList_Draw(himl, sfi.iIcon, hdc, 0, 0, ILD_NORMAL);
        SelectBitmap(hdc, hbmPrev);
      }
      DeleteDC(hdc);
    }
  }
  return psdi->hbmpDragImage != NULL;
}

To create the drag image, we ask the SHGetFileInfo function to give us the imagelist handle and icon index for the icon that represents the file. The icon size in the imagelist goes into the SHDRAGIMAGE structure as the bitmap dimensions and as the cursor point. (We put the cursor at the bottom right corner of the image.) Since we are creating an alpha-blended bitmap, we don't need a color-key. Finally, we create a memory DC to house an ARGB bitmap into which we draw the icon.

If you run this program, you should see the icon for a text file being dragged around as you drag your throwaway file around the screen.

Next time, a way to make somebody else do the heavy lifting for you.



*DISCLAIMER: I DO NOT OWN THIS CONTENT. If you are the owner and would like it removed, please contact me. The content herein is an archived reproduction of entries from Raymond Chen's "Old New Thing" Blog (most recent link is here). It may have slight formatting modifications for consistency and to improve readability.

WHY DID I DUPLICATE THIS CONTENT HERE? Let me first say this site has never had anything to sell and has never shown ads of any kind. I have nothing monetarily to gain by duplicating content here. Because I had made my own local copy of this content throughout the years, for ease of using tools like grep, I decided to put it online after I discovered some of the original content previously and publicly available, had disappeared approximately early to mid 2019. At the same time, I present the content in an easily accessible theme-agnostic way.

The information provided by Raymond's blog is, for all practical purposes, more authoritative on Windows Development than Microsoft's own MSDN documentation and should be considered supplemental reading to that documentation. The wealth of missing details provided by this blog that Microsoft could not or did not document about Windows over the years is vital enough, many would agree an online "backup" of these details is a necessary endeavor. Specifics include:

<-- Back to Old New Thing Archive Index