您的位置:首页 > 其它

模拟window桌面实现

2008-03-04 21:31 309 查看
正在开发中的游戏有个全屏功能--可以在window桌面背景上运行,就像一些视频播放器在桌面背景上播放一样的,花了个上午整了个Demo放出来留个纪念。

实现功能:显示图标,双击图标执行相应的程序,右击图标弹出该图标对应得菜单,点击非图标区则弹出桌面菜单。需要完整工程可以点此下载:DesktopWindow.rar。程序效果图如下:



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




struct XShellItem ...{


ITEMIDLIST* itemId;




int x;


int y;


int w;


int h;




int nameX;


int nameY;


int nameW;


int nameH;




BOOL hit;




CStringW name;


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()中对它进行初始化:


// 获取桌面图标的相关数据


BOOL InitShellFolder()




...{


HRESULT hRslt = SHGetDesktopFolder(&folder);




if (FAILED(hRslt)) ...{


return FALSE;


}




CComPtr<IEnumIDList> ids;


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




if (FAILED(hRslt)) ...{


return FALSE;


}




CAtlList<XShellItem> items;




for (;;) ...{


ITEMIDLIST* id = 0;


ULONG cIds = 0;




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




if (hRslt != S_OK) ...{


break;


}




CStringW name;




STRRET str = ...{ 0};


hRslt = folder->GetDisplayNameOf(id, SHGDN_NORMAL | SHGDN_INFOLDER, &str);




if (SUCCEEDED(hRslt)) ...{


LPWSTR pname = 0;


StrRetToStrW(&str, id, &pname);


name = pname;


CoTaskMemFree(pname);


}




XShellItem item;




item.itemId = id;


item.name = name;


items.AddTail(item);


}




SIZE_T iItem = 0;


SIZE_T cItems = items.GetCount();




itemArray.SetCount(cItems);




POSITION pos = items.GetHeadPosition();




while (pos != 0) ...{


XShellItem& si = items.GetNext(pos);


itemArray[iItem] = si;


iItem++;


}




HDC hDC = CreateCompatibleDC(0);




Graphics g(hDC);


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






ICONMETRICS im = ...{ 0};


im.cbSize = sizeof(im);


SystemParametersInfo(SPI_GETICONMETRICS, sizeof(im), &im, 0);




SolidBrush br_t(Color(255, 255, 255));


Font font_i(hDC, &(im.lfFont));


float fcy = font_i.GetHeight(&g) * 2 + 2;


DeleteDC(hDC);




Gdiplus::StringFormat sf(Gdiplus::StringFormat::GenericTypographic());


sf.SetAlignment(Gdiplus::StringAlignmentCenter);


sf.SetTrimming(Gdiplus::StringTrimmingEllipsisWord);




iconSpacingWidth = im.iHorzSpacing + OFFSET_WIDTH;


iconSpacingHeight = im.iVertSpacing + OFFSET_HEIGHT;




int iconWidth = GetSystemMetrics(SM_CXICON);


int iconHeight = GetSystemMetrics(SM_CYICON);






for (SIZE_T i = 0; i < cItems; i++) ...{


XShellItem& item = itemArray[i];




// SHGetFileInfo


HICON hIcon = 0;


HIMAGELIST hImgList;


SHFILEINFO stSHFileInfo;


CImageList cImgList;




// 获取图标


hImgList = (HIMAGELIST)::SHGetFileInfo(


(LPCWSTR) item.itemId,


0,


&stSHFileInfo,


sizeof(SHFILEINFO),


SHGFI_PIDL | SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SYSICONINDEX);




// DIBSection 8bit


BITMAPINFO bmi;


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;




HDC memDC = CreateCompatibleDC(0);


void* pDib = 0;


HBITMAP hBmp = CreateDIBSection(memDC, &bmi, DIB_RGB_COLORS, &pDib, 0, 0);


GdiFlush();




HGDIOBJ old = SelectObject(memDC, hBmp);




// ImageList_Draw WindowsXP


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::FromHICON 0~255


item.icon = Bitmap::FromHICON(hIcon);


item.w = iconWidth;


item.h = iconHeight;




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




Gdiplus::Bitmap * nameIcon = new Bitmap(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;




delete g2;


}




DestroyIcon(hIcon);


DeleteObject(hBmp);


DestroyIcon(stSHFileInfo.hIcon);


}




return TRUE;


}

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


// 根据窗口大小设置图标位置


void SetShellItemPosition()




...{


int iconWidth = GetSystemMetrics(SM_CXICON);


int iconHeight = GetSystemMetrics(SM_CYICON);


static const int OFFSET_Y = 20;


int x = 0;


int y = OFFSET_Y;


SIZE_T cItems = itemArray.GetCount();




for (SIZE_T i = 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::CRect rect;


GetClientRect(&rect);


y += iconSpacingHeight;




if (y + iconSpacingHeight >= rect.bottom) ...{


x += iconSpacingWidth;


y = OFFSET_Y;


}


}


}

描绘图标就很简单了,呵呵,不贴了,下面来说说弹出图标菜单,执行图标对应的程序以及弹出桌面菜单。

执行图标对应的程序,需要以先前保持的图标itemid作为参数,代码如下:


void RunShellItem(ITEMIDLIST* pIID)




...{


SHELLEXECUTEINFO info;


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()




...{


HWND program = FindWindowEx(0, 0, _T("Progman"), _T("Program Manager"));


HWND view = FindWindowEx(program, 0, _T("SHELLDLL_DefView"), 0);




//HWND list = FindWindowEx(view, 0, _T("SysListView32"), 0);


::SetForegroundWindow(view);




POINT pt;


GetCursorPos(&pt);




LPARAM lp = 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)




...{


HWND hwnd = m_hWnd;




LPCONTEXTMENU pContextMenu = NULL;


LPCONTEXTMENU pCtxMenuTemp = NULL;




g_pIContext2 = NULL;


g_pIContext3 = NULL;




int menuType = 0;




HRESULT hRslt = folder->GetUIObjectOf(


hwnd,


1,


(LPCITEMIDLIST*) &(pIID),


IID_IContextMenu,


0,


(void**) &pCtxMenuTemp);




if (FAILED(hRslt)) ...{


return;


}




POINT pt;


GetCursorPos(&pt);






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


menuType = 3;


}




else if (pCtxMenuTemp->QueryInterface(IID_IContextMenu2, (void**) &pContextMenu) == NOERROR) ...{


menuType = 2;


}






if (pContextMenu) ...{


pCtxMenuTemp->Release();


}




else ...{


pContextMenu = pCtxMenuTemp;


menuType = 1;


}






if (menuType == 0) ...{


return;


}




HMENU hMenu = CreatePopupMenu();


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




if (FAILED(hRslt)) ...{


return;


}




#ifndef _WIN64


#pragma warning(disable : 4244 4311)


#endif




// subclass window


WNDPROC oldWndProc = NULL;




if (menuType > 1) ...{




// only subclass if it is IID_IContextMenu2 or IID_IContextMenu3


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




if (menuType == 2) ...{


g_pIContext2 = (LPCONTEXTMENU2) pContextMenu;


}




else ...{


g_pIContext3 = (LPCONTEXTMENU3) pContextMenu;


}


}




else ...{


oldWndProc = NULL;


}




int cmd = ::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


#pragma warning(default : 4244 4311)


#endif




if (cmd != 0) ...{




CMINVOKECOMMANDINFO ci = ...{ 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 LRESULT CALLBACK HookWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)




...{




switch (message) ...{


case WM_MENUCHAR: // only supported by IContextMenu3




if (g_pIContext3) ...{


LRESULT lResult = 0;


g_pIContext3->HandleMenuMsg2(message, wParam, lParam, &lResult);


return(lResult);


}


break;


case WM_DRAWITEM:


case WM_MEASUREITEM:




if (wParam) ...{


break; // if wParam != 0 then the message is not menu-related


}




case WM_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);


}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: