使用 Windows Imaging Component (WIC) 为MFC菜单项添加图标

说到为MFC菜单添加图标,比较容易想到的是通过CMenu类提供的接口SetMenuItemBitmaps来实现这个需求,该函数的原型如下:

BOOL SetMenuItemBitmaps(
UINT nPosition,
UINT nFlags,
const CBitmap* pBmpUnchecked,
const CBitmap* pBmpChecked);

但是使用这个函数有一个问题,就是它提供的参数是CBitmap类型,也就是说它只允许你设置一个位图作为图标,但是如果我们使用Win32API加载的位图是不透明的,使用此方法为菜单设置的图标就总是会有一个背景。因此我们希望能够将一个HICON资源设置为菜单项的图标,但是很遗憾,CMenu类没有并诸如SetMenuItemIcon这样的接口,也没有其他的API能够实现这个需求。

为了达到这个目的,我之前在网上找了很多帖子,但是都没有找到我想要的。如果你去搜索引擎上搜索“MFC如何为菜单添加图标”之类的关键字,网上的帖子只会告诉你使用SetMenuItemBitmaps。

然而,功夫不负有心人,我终于在微软MSDN网站找到了这样篇文章:Visual Style Menus

文章介绍了使用Windows Imaging Component (WIC) 和 GDI 两种方式为菜单项添加具有透明混合 (alpha-blended) 的图标。本文主要介绍前一种方法。

要为菜单项添加具有透明混合的图标,就需要将图标 (HICON) 转换为具有透明混合的位图。透明混合 (alpha-blended) 的位图是从Windows Vista开始支持的功能。这个转换可以通过 Windows Imaging Component (WIC) 来实现。

要使用 WIC,需要使用CoCreateInstance函数创建一个WIC 图像工厂对象:

HRESULT _hrOleInit{};
IWICImagingFactory *m_pWICFactory{};
_hrOleInit = ::OleInitialize(NULL);
CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pWICFactory));

如果创建成功,IWICImagingFactory 的指针会得到一个WIC 图像工厂对象。

以下代码用于把一个HICON转换成一个具有透明混合的位图(代码来自MSDN网站):

IWICBitmap *pBitmap;
hr = pFactory->CreateBitmapFromHICON(hicon, &pBitmap);
if (SUCCEEDED(hr))
{
	UINT cx, cy;
	hr = pBitmap->GetSize(&cx, &cy);
	if (SUCCEEDED(hr))
	{
		const SIZE sizIcon = { (int)cx, -(int)cy };
		BYTE *pbBuffer;
		hr = Create32BitHBITMAP(NULL, &sizIcon, reinterpret_cast<void**>(&pbBuffer), &hbmp);
		if (SUCCEEDED(hr))
		{
			const UINT cbStride = cx * sizeof(ARGB);
			const UINT cbBuffer = cy * cbStride;
			hr = pBitmap->CopyPixels(NULL, cbStride, cbBuffer, pbBuffer);
		}
	}
	pBitmap->Release();
}

最后附上我封装整理后的代码:

WIC.h:

#pragma once
class CWCIFactory
{
public:
    ~CWCIFactory();
    static IWICImagingFactory* GetWCI() { return m_instance.m_pWICFactory; }

private:
    HRESULT _hrOleInit{};
    IWICImagingFactory *m_pWICFactory{};

    static CWCIFactory m_instance;      //CWCIFactory类唯一的对象

private:
    CWCIFactory();
};

class CMenuIcon
{
public:
    CMenuIcon();
    ~CMenuIcon();

    //向一个菜单项添加图标
    static HRESULT AddIconToMenuItem(HMENU hmenu, int iMenuItem, BOOL fByPosition, HICON hicon);

private:
    static HRESULT AddBitmapToMenuItem(HMENU hmenu, int iItem, BOOL fByPosition, HBITMAP hbmp);
    static void InitBitmapInfo(__out_bcount(cbInfo) BITMAPINFO *pbmi, ULONG cbInfo, LONG cx, LONG cy, WORD bpp);
    static HRESULT Create32BitHBITMAP(HDC hdc, const SIZE *psize, __deref_opt_out void **ppvBits, __out HBITMAP* phBmp);
};

WIC.cpp

#include "stdafx.h"
#include "WIC.h"

CWCIFactory CWCIFactory::m_instance;

CWCIFactory::CWCIFactory()
{
    //初始化m_pWICFactory
    _hrOleInit = ::OleInitialize(NULL);
    CoCreateInstance(CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pWICFactory));
}

CWCIFactory::~CWCIFactory()
{
    if (m_pWICFactory)
    {
        m_pWICFactory->Release();
        m_pWICFactory = NULL;
    }

    if (SUCCEEDED(_hrOleInit))
    {
        OleUninitialize();
    }
}


//

typedef DWORD ARGB;

CMenuIcon::CMenuIcon()
{
}

CMenuIcon::~CMenuIcon()
{
}

HRESULT CMenuIcon::AddIconToMenuItem(HMENU hmenu, int iMenuItem, BOOL fByPosition, HICON hicon)
{
    if (CWCIFactory::GetWCI() == nullptr)
        return 0;
    HBITMAP hbmp = NULL;

    IWICBitmap *pBitmap;
    HRESULT hr = CWCIFactory::GetWCI()->CreateBitmapFromHICON(hicon, &pBitmap);
    if (SUCCEEDED(hr))
    {
        IWICFormatConverter *pConverter;
        hr = CWCIFactory::GetWCI()->CreateFormatConverter(&pConverter);
        if (SUCCEEDED(hr))
        {
            hr = pConverter->Initialize(pBitmap, GUID_WICPixelFormat32bppPBGRA, WICBitmapDitherTypeNone, NULL, 0.0f, WICBitmapPaletteTypeCustom);
            if (SUCCEEDED(hr))
            {
                UINT cx, cy;
                hr = pConverter->GetSize(&cx, &cy);
                if (SUCCEEDED(hr))
                {
                    const SIZE sizIcon = { (int)cx, -(int)cy };
                    BYTE *pbBuffer;
                    hr = Create32BitHBITMAP(NULL, &sizIcon, reinterpret_cast<void **>(&pbBuffer), &hbmp);
                    if (SUCCEEDED(hr))
                    {
                        const UINT cbStride = cx * sizeof(ARGB);
                        const UINT cbBuffer = cy * cbStride;
                        hr = pConverter->CopyPixels(NULL, cbStride, cbBuffer, pbBuffer);
                    }
                }
            }

            pConverter->Release();
        }

        pBitmap->Release();
    }

    if (SUCCEEDED(hr))
    {
        hr = AddBitmapToMenuItem(hmenu, iMenuItem, fByPosition, hbmp);
    }

    if (FAILED(hr))
    {
        DeleteObject(hbmp);
        hbmp = NULL;
    }

    return hr;
}

HRESULT CMenuIcon::AddBitmapToMenuItem(HMENU hmenu, int iItem, BOOL fByPosition, HBITMAP hbmp)
{
    HRESULT hr = E_FAIL;

    MENUITEMINFO mii = { sizeof(mii) };
    mii.fMask = MIIM_BITMAP;
    mii.hbmpItem = hbmp;
    if (SetMenuItemInfo(hmenu, iItem, fByPosition, &mii))
    {
        hr = S_OK;
    }

    return hr;
}

void CMenuIcon::InitBitmapInfo(BITMAPINFO * pbmi, ULONG cbInfo, LONG cx, LONG cy, WORD bpp)
{
    ZeroMemory(pbmi, cbInfo);
    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biPlanes = 1;
    pbmi->bmiHeader.biCompression = BI_RGB;

    pbmi->bmiHeader.biWidth = cx;
    pbmi->bmiHeader.biHeight = cy;
    pbmi->bmiHeader.biBitCount = bpp;
}

HRESULT CMenuIcon::Create32BitHBITMAP(HDC hdc, const SIZE * psize, void ** ppvBits, HBITMAP * phBmp)
{
    *phBmp = NULL;

    BITMAPINFO bmi;
    InitBitmapInfo(&bmi, sizeof(bmi), psize->cx, psize->cy, 32);

    HDC hdcUsed = hdc ? hdc : GetDC(NULL);
    if (hdcUsed)
    {
        *phBmp = CreateDIBSection(hdcUsed, &bmi, DIB_RGB_COLORS, ppvBits, NULL, 0);
        if (hdc != hdcUsed)
        {
            ReleaseDC(NULL, hdcUsed);
        }
    }
    return (NULL == *phBmp) ? E_OUTOFMEMORY : S_OK;
}

在以上代码中,我封装了一个单实例类CWCIFactory来管理 IWICImagingFactory 的指针。该类提供了唯一的公共接口

static IWICImagingFactory* GetWCI()

来获取 IWICImagingFactory 的指针。

CMenuIcon 是一个静态类,使用 CMenuIcon::AddIconToMenuItem 接口即可为菜单项添加一个图标。

static HRESULT AddIconToMenuItem(HMENU hmenu, int iMenuItem, BOOL fByPosition, HICON hicon);

其中 iItem 和 fByPosition 两个参数的用法和CMenu::SetMenuItemBitmaps相同。

下面附上在我开发的音乐播放器MusicPlayer2使用这个方法添加图标后的效果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值