正在开发中的游戏有个全屏功能--可以在window桌面背景上运行,就像一些视频播放器在桌面背景上播放一样的,花了个上午整了个Demo放出来留个纪念。
实现功能:显示图标,双击图标执行相应的程序,右击图标弹出该图标对应得菜单,点击非图标区则弹出桌面菜单。需要完整工程可以点此下载:DesktopWindow.rar。程序效果图如下:

在这个程序里,定义了一个XShellItem的数据结构,保持桌面图标的iten id(ITEMIDLiST),图标以及文字图标。
struct
XShellItem
...
{
ITEMIDLIST*itemId;

intx;
inty;
intw;
inth;

intnameX;
intnameY;
intnameW;
intnameH;

BOOLhit;

CStringWname;
Bitmap*icon;
Bitmap*nameIcon;

XShellItem()
:
itemId(NULL),
x(0),
y(0),
w(0),
h(0),
nameX(0),
nameY(0),
nameW(0),
nameH(0),
name(L""),
hit(FALSE),
icon(NULL),

nameIcon(NULL)...{
}

~XShellItem()...{
}
}
;
然后定义一个数组CAtlArray<XShellItem> itemArray;用来保存所有桌面图标对象,在InitShellFolder()中对它进行初始化:
//
获取桌面图标的相关数据
BOOLInitShellFolder()

...
{
HRESULThRslt=SHGetDesktopFolder(&folder);

if(FAILED(hRslt))...{
returnFALSE;
}

CComPtr<IEnumIDList>ids;
hRslt=folder->EnumObjects(0,SHCONTF_FOLDERS|SHCONTF_NONFOLDERS,&ids);

if(FAILED(hRslt))...{
returnFALSE;
}

CAtlList<XShellItem>items;

for(;;)...{
ITEMIDLIST*id=0;
ULONGcIds=0;

hRslt=ids->Next(1,&id,&cIds);

if(hRslt!=S_OK)...{
break;
}

CStringWname;

STRRETstr=...{0};
hRslt=folder->GetDisplayNameOf(id,SHGDN_NORMAL|SHGDN_INFOLDER,&str);

if(SUCCEEDED(hRslt))...{
LPWSTRpname=0;
StrRetToStrW(&str,id,&pname);
name=pname;
CoTaskMemFree(pname);
}

XShellItemitem;

item.itemId=id;
item.name=name;
items.AddTail(item);
}

SIZE_TiItem=0;
SIZE_TcItems=items.GetCount();

itemArray.SetCount(cItems);

POSITIONpos=items.GetHeadPosition();

while(pos!=0)...{
XShellItem&si=items.GetNext(pos);
itemArray[iItem]=si;
iItem++;
}

HDChDC=CreateCompatibleDC(0);

Graphicsg(hDC);
g.Clear(Color(0,0,0,0));


ICONMETRICSim=...{0};
im.cbSize=sizeof(im);
SystemParametersInfo(SPI_GETICONMETRICS,sizeof(im),&im,0);

SolidBrushbr_t(Color(255,255,255));
Fontfont_i(hDC,&(im.lfFont));
floatfcy=font_i.GetHeight(&g)*2+2;
DeleteDC(hDC);

Gdiplus::StringFormatsf(Gdiplus::StringFormat::GenericTypographic());
sf.SetAlignment(Gdiplus::StringAlignmentCenter);
sf.SetTrimming(Gdiplus::StringTrimmingEllipsisWord);

iconSpacingWidth=im.iHorzSpacing+OFFSET_WIDTH;
iconSpacingHeight=im.iVertSpacing+OFFSET_HEIGHT;

inticonWidth=GetSystemMetrics(SM_CXICON);
inticonHeight=GetSystemMetrics(SM_CYICON);


for(SIZE_Ti=0;i<cItems;i++)...{
XShellItem&item=itemArray[i];

//SHGetFileInfo
HICONhIcon=0;
HIMAGELISThImgList;
SHFILEINFOstSHFileInfo;
CImageListcImgList;

//获取图标
hImgList=(HIMAGELIST)::SHGetFileInfo(
(LPCWSTR)item.itemId,
0,
&stSHFileInfo,
sizeof(SHFILEINFO),
SHGFI_PIDL|SHGFI_ICON|SHGFI_LARGEICON|SHGFI_SYSICONINDEX);

//DIBSection8bit
BITMAPINFObmi;
BITMAPINFOHEADER&bmih=bmi.bmiHeader;
bmih.biSize=sizeof(bmih);
bmih.biWidth=ICON_WIDTH;
bmih.biHeight=-ICON_HEIGHT;//BMP反转
bmih.biPlanes=1;
bmih.biBitCount=32;
bmih.biCompression=BI_RGB;
bmih.biSizeImage=0;
bmih.biXPelsPerMeter=0;
bmih.biYPelsPerMeter=0;
bmih.biClrUsed=0;
bmih.biClrImportant=0;

HDCmemDC=CreateCompatibleDC(0);
void*pDib=0;
HBITMAPhBmp=CreateDIBSection(memDC,&bmi,DIB_RGB_COLORS,&pDib,0,0);
GdiFlush();

HGDIOBJold=SelectObject(memDC,hBmp);

//ImageList_DrawWindowsXP
ImageList_SetBkColor(hImgList,0x0);
ImageList_Draw(hImgList,stSHFileInfo.iIcon,memDC,0,0,ILD_NORMAL);
SelectObject(memDC,old);
DeleteDC(memDC);

cImgList.Attach(hImgList);
hIcon=cImgList.ExtractIcon(stSHFileInfo.iIcon);
cImgList.Detach();


if(hIcon!=0)...{

//Bitmap::FromHICON0~255
item.icon=Bitmap::FromHICON(hIcon);
item.w=iconWidth;
item.h=iconHeight;

Gdiplus::RectFrc(float(2),float(2),float(iconSpacingWidth-4),fcy);

Gdiplus::Bitmap*nameIcon=newBitmap(NAME_WIDTH,NAME_HEIGHT,&g);
Gdiplus::Graphics*g2=Gdiplus::Graphics::FromImage(nameIcon);
g2->Clear(Gdiplus::Color(Gdiplus::ARGB(0)));

g2->DrawString(item.name,item.name.GetLength(),&font_i,rc,&sf,&br_t);

item.nameIcon=nameIcon;
item.nameW=NAME_WIDTH;
item.nameH=NAME_HEIGHT;

deleteg2;
}

DestroyIcon(hIcon);
DeleteObject(hBmp);
DestroyIcon(stSHFileInfo.hIcon);
}

returnTRUE;
}
注意这里面并没有设置图标对象的位置,因为当窗口改变大小的时候,相应地也要调整图标的描绘位置,所以图标位置是在SetShellItemPosition()中动态调整的.
//
根据窗口大小设置图标位置
void
SetShellItemPosition()

...
{
inticonWidth=GetSystemMetrics(SM_CXICON);
inticonHeight=GetSystemMetrics(SM_CYICON);
staticconstintOFFSET_Y=20;
intx=0;
inty=OFFSET_Y;
SIZE_TcItems=itemArray.GetCount();

for(SIZE_Ti=0;i<cItems;i++)...{
XShellItem&item=itemArray[i];

if(item.icon)...{
item.x=x+(iconSpacingWidth-iconWidth)/2;
item.y=y;
}


if(item.nameIcon)...{
item.nameX=x;
item.nameY=y+iconHeight+2;
}

WTL::CRectrect;
GetClientRect(&rect);
y+=iconSpacingHeight;

if(y+iconSpacingHeight>=rect.bottom)...{
x+=iconSpacingWidth;
y=OFFSET_Y;
}
}
}
描绘图标就很简单了,呵呵,不贴了,下面来说说弹出图标菜单,执行图标对应的程序以及弹出桌面菜单。
执行图标对应的程序,需要以先前保持的图标itemid作为参数,代码如下:
void
RunShellItem(ITEMIDLIST
*
pIID)

...
{
SHELLEXECUTEINFOinfo;
info.cbSize=sizeof(SHELLEXECUTEINFO);
info.fMask=SEE_MASK_INVOKEIDLIST;
info.hwnd=m_hWnd;
info.lpVerb=NULL;
info.lpFile=NULL;
info.lpParameters=NULL;
info.lpDirectory=NULL;
info.nShow=SW_SHOWNORMAL;
info.hInstApp=NULL;
info.lpIDList=pIID;
ShellExecuteEx(&info);
}
弹出桌面菜单的代码如下:
//
桌面菜单
void
DesktopMenu()

...
{
HWNDprogram=FindWindowEx(0,0,_T("Progman"),_T("ProgramManager"));
HWNDview=FindWindowEx(program,0,_T("SHELLDLL_DefView"),0);

//HWNDlist=FindWindowEx(view,0,_T("SysListView32"),0);
::SetForegroundWindow(view);

POINTpt;
GetCursorPos(&pt);

LPARAMlp=pt.y<<16|(pt.x-32);
::PostMessage(view,WM_LBUTTONDOWN,0,lp);
::PostMessage(view,WM_RBUTTONUP,0,lp);
}
弹出图标菜单的代码如下,这里定义了两个全局的IContextMenu对象:
static IContextMenu2* g_pIContext2 = NULL;
static IContextMenu3* g_pIContext3 = NULL;
以便在消息回调函数中使用。具体代码如下:
//
图标菜单
void
RightMenu(ITEMIDLIST
*
pIID)

...
{
HWNDhwnd=m_hWnd;

LPCONTEXTMENUpContextMenu=NULL;
LPCONTEXTMENUpCtxMenuTemp=NULL;

g_pIContext2=NULL;
g_pIContext3=NULL;

intmenuType=0;

HRESULThRslt=folder->GetUIObjectOf(
hwnd,
1,
(LPCITEMIDLIST*)&(pIID),
IID_IContextMenu,
0,
(void**)&pCtxMenuTemp);

if(FAILED(hRslt))...{
return;
}

POINTpt;
GetCursorPos(&pt);


if(pCtxMenuTemp->QueryInterface(IID_IContextMenu3,(void**)&pContextMenu)==NOERROR)...{
menuType=3;
}

elseif(pCtxMenuTemp->QueryInterface(IID_IContextMenu2,(void**)&pContextMenu)==NOERROR)...{
menuType=2;
}


if(pContextMenu)...{
pCtxMenuTemp->Release();
}

else...{
pContextMenu=pCtxMenuTemp;
menuType=1;
}


if(menuType==0)...{
return;
}

HMENUhMenu=CreatePopupMenu();
hRslt=pContextMenu->QueryContextMenu(hMenu,0,1,0x7fff,CMF_NORMAL|CMF_EXPLORE);

if(FAILED(hRslt))...{
return;
}

#ifndef_WIN64
#pragmawarning(disable:42444311)
#endif

//subclasswindow
WNDPROColdWndProc=NULL;

if(menuType>1)...{

//onlysubclassifitisIID_IContextMenu2orIID_IContextMenu3
oldWndProc=(WNDPROC)SetWindowLongPtr(GWL_WNDPROC,(LONG)HookWndProc);

if(menuType==2)...{
g_pIContext2=(LPCONTEXTMENU2)pContextMenu;
}

else...{
g_pIContext3=(LPCONTEXTMENU3)pContextMenu;
}
}

else...{
oldWndProc=NULL;
}

intcmd=::TrackPopupMenu(
hMenu,
TPM_LEFTALIGN|TPM_BOTTOMALIGN|TPM_RETURNCMD|TPM_LEFTBUTTON,
pt.x,
pt.y,
0,
hwnd,
0);

//unsubclass

if(oldWndProc)...{
SetWindowLongPtr(GWL_WNDPROC,(LONG)oldWndProc);
}

#ifndef_WIN64
#pragmawarning(default:42444311)
#endif

if(cmd!=0)...{

CMINVOKECOMMANDINFOci=...{0};
ci.cbSize=sizeof(CMINVOKECOMMANDINFO);
ci.hwnd=hwnd;
ci.lpVerb=(LPCSTR)MAKEINTRESOURCE(cmd-1);
ci.nShow=SW_SHOWNORMAL;

pContextMenu->InvokeCommand(&ci);
}

pContextMenu->Release();
g_pIContext2=NULL;
g_pIContext3=NULL;
::DestroyMenu(hMenu);
}

static
LRESULTCALLBACKHookWndProc(HWNDhWnd,UINTmessage,WPARAMwParam,LPARAMlParam)

...
{

switch(message)...{
caseWM_MENUCHAR://onlysupportedbyIContextMenu3

if(g_pIContext3)...{
LRESULTlResult=0;
g_pIContext3->HandleMenuMsg2(message,wParam,lParam,&lResult);
return(lResult);
}
break;
caseWM_DRAWITEM:
caseWM_MEASUREITEM:

if(wParam)...{
break;//ifwParam!=0thenthemessageisnotmenu-related
}

caseWM_INITMENUPOPUP:

if(g_pIContext2)...{
g_pIContext2->HandleMenuMsg(message,wParam,lParam);
}

else...{
g_pIContext3->HandleMenuMsg(message,wParam,lParam);
}

return(message==WM_INITMENUPOPUP?0:TRUE);
break;
default:
break;
}

return::CallWindowProc((WNDPROC)GetProp(hWnd,TEXT("oldWndProc")),hWnd,message,wParam,lParam);
}