模拟window桌面实现

正在开发中的游戏有个全屏功能--可以在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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值