//********************************************************************
#define STRICT_TYPED_ITEMIDS // In case you use IDList, you want this on for better type safety.
#include <windows.h>
#include <windowsx.h> // For WM_COMMAND handling macros
#include <shlobj.h> // For shell
#include <shlwapi.h> // QISearch, easy way to implement QI
#include <commctrl.h>
#include <uxtheme.h>
#include "resource.h"
#pragma comment(lib, "shlwapi.lib") // Default link libs do not include this.
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "uxtheme.lib")
// Set up common controls v6 the easy way.
#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"")
HINSTANCE g_hinst = 0;
// AddIconToMenuItem and supporting functions.
// Note: BufferedPaintInit/BufferedPaintUnInit should be called to
// improve performance.
// In this sample they are called in _OnInitDlg/_OnDestroyDlg.
// In a full application you would call these during WM_NCCREATE/WM_NCDESTROY.
typedef DWORD ARGB;
void InitBitmapInfo(__out_bcount(cbInfo) 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 Create32BitHBITMAP(HDC hdc, const SIZE *psize, __deref_opt_out void **ppvBits, __out 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;
}
HRESULT 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;
}
HRESULT ConvertToPARGB32(HDC hdc, __inout ARGB *pargb, HBITMAP hbmp, SIZE& sizImage, int cxRow)
{
BITMAPINFO bmi;
InitBitmapInfo(&bmi, sizeof(bmi), sizImage.cx, sizImage.cy, 32);
HRESULT hr = E_OUTOFMEMORY;
HANDLE hHeap = GetProcessHeap();
void *pvBits = HeapAlloc(hHeap, 0, bmi.bmiHeader.biWidth * 4 * bmi.bmiHeader.biHeight);
if (pvBits)
{
hr = E_UNEXPECTED;
if (GetDIBits(hdc, hbmp, 0, bmi.bmiHeader.biHeight, pvBits, &bmi, DIB_RGB_COLORS) == bmi.bmiHeader.biHeight)
{
ULONG cxDelta = cxRow - bmi.bmiHeader.biWidth;
ARGB *pargbMask = static_cast<ARGB *>(pvBits);
for (ULONG y = bmi.bmiHeader.biHeight; y; --y)
{
for (ULONG x = bmi.bmiHeader.biWidth; x; --x)
{
if (*pargbMask++)
{
// transparent pixel
*pargb++ = 0;
}
else
{
// opaque pixel
*pargb++ |= 0xFF000000;
}
}
pargb += cxDelta;
}
hr = S_OK;
}
HeapFree(hHeap, 0, pvBits);
}
return hr;
}
bool HasAlpha(__in ARGB *pargb, SIZE& sizImage, int cxRow)
{
ULONG cxDelta = cxRow - sizImage.cx;
for (ULONG y = sizImage.cy; y; --y)
{
for (ULONG x = sizImage.cx; x; --x)
{
if (*pargb++ & 0xFF000000)
{
return true;
}
}
pargb += cxDelta;
}
return false;
}
HRESULT ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer, HDC hdc, HICON hicon, SIZE& sizIcon)
{
RGBQUAD *prgbQuad;
int cxRow;
HRESULT hr = GetBufferedPaintBits(hPaintBuffer, &prgbQuad, &cxRow);
if (SUCCEEDED(hr))
{
ARGB *pargb = reinterpret_cast<ARGB *>(prgbQuad);
if (!HasAlpha(pargb, sizIcon, cxRow))
{
ICONINFO info;
if (GetIconInfo(hicon, &info))
{
if (info.hbmMask)
{
hr = ConvertToPARGB32(hdc, pargb, info.hbmMask, sizIcon, cxRow);
}
DeleteObject(info.hbmColor);
DeleteObject(info.hbmMask);
}
}
}
return hr;
}
HRESULT AddIconToMenuItem(HMENU hmenu, int iMenuItem, BOOL fByPosition, HICON hicon, BOOL fAutoDestroy, __out_opt HBITMAP *phbmp)
{
HRESULT hr = E_OUTOFMEMORY;
HBITMAP hbmp = NULL;
SIZE sizIcon;
sizIcon.cx = GetSystemMetrics(SM_CXSMICON);
sizIcon.cy = GetSystemMetrics(SM_CYSMICON);
RECT rcIcon;
SetRect(&rcIcon, 0, 0, sizIcon.cx, sizIcon.cy);
HDC hdcDest = CreateCompatibleDC(NULL);
if (hdcDest)
{
hr = Create32BitHBITMAP(hdcDest, &sizIcon, NULL, &hbmp);
if (SUCCEEDED(hr))
{
hr = E_FAIL;
HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcDest, hbmp);
if (hbmpOld)
{
BLENDFUNCTION bfAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
BP_PAINTPARAMS paintParams = {0};
paintParams.cbSize = sizeof(paintParams);
paintParams.dwFlags = BPPF_ERASE;
paintParams.pBlendFunction = &bfAlpha;
HDC hdcBuffer;
HPAINTBUFFER hPaintBuffer = BeginBufferedPaint(hdcDest, &rcIcon, BPBF_DIB, &paintParams, &hdcBuffer);
if (hPaintBuffer)
{
if (DrawIconEx(hdcBuffer, 0, 0, hicon, sizIcon.cx, sizIcon.cy, 0, NULL, DI_NORMAL))
{
// If icon did not have an alpha channel, we need to convert buffer to PARGB.
hr = ConvertBufferToPARGB32(hPaintBuffer, hdcDest, hicon, sizIcon);
}
// This will write the buffer contents to the
// destination bitmap.
EndBufferedPaint(hPaintBuffer, TRUE);
}
SelectObject(hdcDest, hbmpOld);
}
}
DeleteDC(hdcDest);
}
if (SUCCEEDED(hr))
{
hr = AddBitmapToMenuItem(hmenu, iMenuItem, fByPosition, hbmp);
}
if (FAILED(hr))
{
DeleteObject(hbmp);
hbmp = NULL;
}
if (fAutoDestroy)
{
DestroyIcon(hicon);
}
if (phbmp)
{
*phbmp = hbmp;
}
return hr;
}
//-----------------------------------------------------------------
//-- TrackPopupIconMenuEx -----------------------------------------
// Wrapper to TrackPopupMenuEx that shows how to use AddIconToMenuItem and how to manage the lifetime of the created bitmaps
struct ICONMENUENTRY
{
UINT idMenuItem;
HINSTANCE hinst;
LPCWSTR pIconId;
};
BOOL TrackPopupIconMenuEx(HMENU hmenu, UINT fuFlags, int x, int y, HWND hwnd, __in_opt LPTPMPARAMS lptpm,
UINT nIcons, __in_ecount_opt(nIcons) const ICONMENUENTRY *pIcons)
{
HRESULT hr = S_OK;
BOOL fRet;
MENUINFO menuInfo = { sizeof(menuInfo) };
menuInfo.fMask = MIM_STYLE;
if (nIcons)
{
for (UINT n = 0; SUCCEEDED(hr) && n < nIcons; ++n)
{
HICON hicon;
hr = LoadIconMetric(pIcons[n].hinst, pIcons[n].pIconId, LIM_SMALL, &hicon);
if (SUCCEEDED(hr))
{
hr = AddIconToMenuItem(hmenu, pIcons[n].idMenuItem, FALSE, hicon, TRUE, NULL);
}
}
GetMenuInfo(hmenu, &menuInfo);
MENUINFO menuInfoNew = menuInfo;
menuInfoNew.dwStyle = (menuInfo.dwStyle & ~MNS_NOCHECK) | MNS_CHECKORBMP;
SetMenuInfo(hmenu, &menuInfoNew);
}
if (SUCCEEDED(hr))
{
fRet = TrackPopupMenuEx(hmenu, fuFlags, x, y, hwnd, lptpm) ? S_OK : E_FAIL;
hr = fRet ? S_OK : E_FAIL;
}
if (nIcons)
{
for (UINT n = 0; n < nIcons; ++n)
{
MENUITEMINFO mii = { sizeof(mii) };
mii.fMask = MIIM_BITMAP;
if (GetMenuItemInfo(hmenu, pIcons[n].idMenuItem, FALSE, &mii))
{
DeleteObject(mii.hbmpItem);
mii.hbmpItem = NULL;
SetMenuItemInfo(hmenu, pIcons[n].idMenuItem, FALSE, &mii);
}
}
SetMenuInfo(hmenu, &menuInfo);
}
return SUCCEEDED(hr) ? fRet : FALSE;
}
//---------------------------------------------------------------------
class CShellSimpleApp : public IUnknown
{
public:
CShellSimpleApp() : _cRef(1), _hdlg(NULL), _hrOleInit(E_FAIL)
{
}
HRESULT DoModal(HWND hwnd)
{
INT_PTR iRet = DialogBoxParam(g_hinst, MAKEINTRESOURCE(IDD_DIALOG1), hwnd, s_DlgProc, (LPARAM)this);
return (iRet == IDOK) ? S_OK : S_FALSE;
}
// IUnknown
STDMETHODIMP QueryInterface(__in REFIID riid, __deref_out void **ppv)
{
static const QITAB qit[] =
{
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) Release()
{
LONG cRef = InterlockedDecrement(&_cRef);
if (!cRef)
delete this;
return cRef;
}
private:
~CShellSimpleApp()
{
}
static BOOL CALLBACK s_DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
#pragma warning(push)
#pragma warning(disable:4312) // GetWindowLongPtr is not w4 clean.
CShellSimpleApp *pssa = reinterpret_cast<CShellSimpleApp *>(GetWindowLongPtr(hdlg, DWLP_USER));
#pragma warning(pop)
if (uMsg == WM_INITDIALOG)
{
pssa = reinterpret_cast<CShellSimpleApp *>(lParam);
pssa->_hdlg = hdlg;
#pragma warning(push)
#pragma warning(disable:4244) // SetWindowLongPtr is not w4 clean.
SetWindowLongPtr(hdlg, DWLP_USER, reinterpret_cast<LONG_PTR>(pssa));
#pragma warning(pop)
}
return pssa ? pssa->_DlgProc(uMsg, wParam, lParam) : FALSE;
}
BOOL _DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
void _OnInitDlg();
void _OnDestroyDlg();
void _OnRightClick(WPARAM wParam, int x, int y);
long _cRef;
HWND _hdlg;
HRESULT _hrOleInit;
};
void _SetDialogIcon(HWND hdlg, SHSTOCKICONID siid)
{
SHSTOCKICONINFO sii = {sizeof(sii)};
if (SUCCEEDED(SHGetStockIconInfo(siid, SHGFI_ICON | SHGFI_SMALLICON, &sii)))
{
SendMessage(hdlg, WM_SETICON, ICON_SMALL, (LPARAM) sii.hIcon);
}
if (SUCCEEDED(SHGetStockIconInfo(siid, SHGFI_ICON | SHGFI_LARGEICON, &sii)))
{
SendMessage(hdlg, WM_SETICON, ICON_BIG, (LPARAM) sii.hIcon);
}
}
void _ClearDialogIcon(HWND hdlg)
{
DestroyIcon((HICON)SendMessage(hdlg, WM_GETICON, ICON_SMALL, 0));
DestroyIcon((HICON)SendMessage(hdlg, WM_GETICON, ICON_BIG, 0));
}
void CShellSimpleApp::_OnInitDlg()
{
_SetDialogIcon(_hdlg, SIID_APPLICATION);
BufferedPaintInit();
_hrOleInit = OleInitialize(0); // Needed for drag drop
}
void CShellSimpleApp::_OnDestroyDlg()
{
_ClearDialogIcon(_hdlg);
BufferedPaintUnInit();
if (SUCCEEDED(_hrOleInit))
{
OleUninitialize();
}
}
void CShellSimpleApp::_OnRightClick(WPARAM wParam, int x, int y)
{
POINT ptClient = { x, y };
ClientToScreen(_hdlg, &ptClient);
HMENU hmenu = LoadMenu(g_hinst, MAKEINTRESOURCE(IDM_CONTEXTMENU));
if (hmenu)
{
ICONMENUENTRY aIcons[] = {
{ IDM_INFORMATION, NULL, IDI_INFORMATION },
{ IDM_ELEVATE, NULL, IDI_SHIELD }
};
TrackPopupIconMenuEx(GetSubMenu(hmenu, 0), 0, ptClient.x, ptClient.y, _hdlg, NULL, ARRAYSIZE(aIcons), aIcons);
DestroyMenu(hmenu);
}
}
BOOL CShellSimpleApp::_DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BOOL bRet = TRUE; // Default for all handled cases in switchbelow
switch (uMsg)
{
case WM_INITDIALOG:
_OnInitDlg();
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDOK:
case IDCANCEL:
return EndDialog(_hdlg, IDOK == GET_WM_COMMAND_ID(wParam, lParam));
}
break;
case WM_RBUTTONUP:
_OnRightClick(wParam, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
break;
case WM_DESTROY:
_OnDestroyDlg();
break;
default:
bRet = FALSE;
}
return bRet;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
g_hinst = hInstance;
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
CShellSimpleApp *pdlg = new CShellSimpleApp();
if (pdlg)
{
pdlg->DoModal(NULL);
pdlg->Release();
}
CoUninitialize();
}
return 0;
}