首页 > 代码库 > 模拟window桌面实现

模拟window桌面实现

正在开发中的游戏有个全屏功能--可以在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(
0000));
技术分享
技术分享        ICONMETRICS im 
= 0};
技术分享        im.cbSize 
= sizeof(im);
技术分享        SystemParametersInfo(SPI_GETICONMETRICS, 
sizeof(im), &im, 0);
技术分享
技术分享        SolidBrush br_t(Color(
255255255));
技术分享        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, 00);
技术分享            GdiFlush();
技术分享
技术分享            HGDIOBJ old 
= SelectObject(memDC, hBmp);
技术分享
技术分享            
// ImageList_Draw WindowsXP
技术分享
            ImageList_SetBkColor(hImgList, 0x0);
技术分享            ImageList_Draw(hImgList, stSHFileInfo.iIcon, memDC, 
00, 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(00, _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, 010x7fff, 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);
技术分享    }
jpg改rar 技术分享
 

模拟window桌面实现