正在开发中的游戏有个全屏功能--可以在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);
}
2万+

被折叠的 条评论
为什么被折叠?



