现在换肤已经告一段落, 总结下中间碰到的问题和解决方法。
1. 关于动态添加菜单
动态添加菜单的方法有很多, 我使用MENUITEMINFO结构实现。代码如下:
CMenu* menuappend = new CMenu;
menuappend->CreateMenu();
menuappend->AppendMenu(MF_ENABLED | MF_STRING, ID_SKIN_FIRST, ResStr(IDS_MENU_ITEM_SKIN_DEFAULT));
MENUITEMINFO mii;
mii.fMask = MIIM_STATE|MIIM_SUBMENU;
mii.fType = MF_POPUP;
mii.hSubMenu = menuappend;
mii.fState = MF_ENABLED;
pPopupMenu->SetMenuItemInfo(i, &mii, TRUE); // pPopupMenu为主菜单
2. 关于载入位图
winapi有两个简单函数可以实现。 LoadBitmap 和 LoadImage, LoadBitmap只能从资源文件读取,LoadImage更可以实现硬盘读取。
HANDLE LoadImage(HINSTANCE hinst,LPCTSTR lpszName,UINT uType,int cxDesired,int CyDesired,UINT fuLoad);
参数设置如下:
hinst 0为当前模块, 也可用 GetModuleHandle(LPCTSTR lpModuleName) 获得特定模块句柄。
lpszName 位图路径,这里要注意的是,这个路径是对应模块路径的相对路径。
uType 位图设置为 IMAGE_BITMAP。
cxDesired, cyDesired 设为0即可。
fuLoad 设置为 LR_DEFAULTCOLOR | LR_LOADFROMFILE, 使函数从硬盘文件读取。
WTL中加入一个名为CImage的类,可直接使用该类成员函数Load(LPCTSTR pszFileName) 直接实现, pszFileName为绝对路径。
位图使用完后,不要忘记调用DeleteObject()删除。
3. 关于贴图
贴图采用的是双缓冲技巧。 代码如下:
CDC dcBmp;
dcbmp.CreateCompatibleDC(&hdc); //hdc为目标设备环境
HBITMAP bmold = (HBITMAP)dcBmp.SelectObject(bmnew); //bmnew为CBitap类对象,SelectObject()返回类型为HWND,需转换为HBITMAP。
BITMAP bm;
bmnew.GetBitmap(&bm);
hdc.SetStretchMode(HALFTONE);
hdc.SetBrushOrg(0, 0); //设置模式为HALFTONE后,必须调用SetBrushOrg();
hdc.StretchBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcBmp, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY);
dcBmp.SelectObject(bmold);
bmnew.DeleteObject();
出于用户可能会有误删文件的情况, 建议将bmnew保存为全局变量,退出程序前删除。
4. 关于对话框
WTL和MFC都可以在类内部直接实例化资源文件中的对话框资源。不过有少许区别,如下:
使用WTL的话,可以直接继承父类,并在emun中初始化。代码为:
class SkinPreviewDlg : public CDialogImpl<SkinPreviewDlg>,
public WTL::CWinDataExchange<SkinPreviewDlg>
{
public:
SkinPreviewDlg(void);
~SkinPreviewDlg(void);
enum { IDD = IDD_SKIN_PREVIEW };
}
这样,SkinPreviewDlg类对象就可以直接关联到资源文件。
如果使用MFC,则要多家一步。
class SkinPreviewDlg : public CDialog
{
SkinPreviewDlg(void);
~SkinPreviewDlg(void);
enum { IDD = IDD_SKIN_PREVIEW };
}
SkinPreviewDlg::SkinPreviewDlg():CDialog(IDD_SKIN_PREVIEW)
{
}
或者调用 CDialog::Create(IDD_SKIN_PREVIEW)。
如果要创建一个不包含内容的对话框,如CDHtmlDialog, 对话框为一个网页。可以调用CreateIndirect()函数创建。代码如下:
DLGTEMPLATE* dialogTemplate;
dialogTemplate = malloc(1, sizeof(DLGTEMPLATE) + sizeof(DLGTEMPLATE));
if (dialogTemplate)
{
dialogTemplate->style = DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_DISABLED | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU;
dialogTemplate->dwExtendedStyle = WS_EX_NOACTIVE;
dialogTemplate->cdit = 0;
dialogTemplate->cx = 237;
dialogTemplate->cy = 178;
if (0 == skindown.CreateIndirect(dialogTemplate, NULL))
{
free(dialogTemplate);
dialogTemplate = NULL;
}
skindown.Navigate("http://www.google.com");
}
5. 关于syslink控件
syslink控件不同于statice控件,有两个地方需要注意。
1. syslink的字符串格式为"<A HREF=/"http://www.google.com//">谷歌</A>", “http://www.google.com/”为超连接的实际地址,“谷歌”是控件上写的字符串。
2. 当点击syslink控件时会发送WM_NOTIFY消息。在WTL中处理如下:
BEGIN_MSG_MAP_EX(SkinPreview)
NOTIFY_HANDLE(IDD_SKIN_SYSLINK, NM_CLICK, OnClickSyslink)
END_MSG_MAP()
LRESULT OnClickSyslink(int wParam, LPNMHDR lParam, BOOL& bHandled)
{
PNMLINK pnmLink = (PNMLINK)lParam;
::ShellExecute(NULL, _T("OPEN"), pnmLink->item.szUrl, NULL, NULL, SW_SHOWNORMAL);
return 0;
}
6. 关于CDHtmlDialog
CDHtmlDialog可以实现对话框与网页的交互,让我们可以像截获WINDOWS消息一样,截获网页消息。头文件为"afxdhtml.h"。
可以通过BEGIN_EVENTSINK_MAP 和 END_EVENTSINK_MAP 宏截获网页消息。代码如下:
BEGIN_EVENTSINK_MAP(SkinDownload, CDHtmlDialog)
ONEVENT(SkinDownload, AFX_IDC_BROWSER, DISPID_BEFORNAVIGATE2, OnBeforeNavigate2, //DISPID_BEFORNAVIGATE2在<exdispid.h>中定义
VTS_DISPATCH VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PBOOL)
END_EVENTSINK_MAP()
void BeforeNavigate2(LPDISPATCH pDisp, VARIANT FAR* URL, VARIANT FAR* Flags, VARIANT FAR* TargetFrameName,
VARIANT FAR* PostData, VARIANT FAR* Headers, BOOL FAR* Cancel)
{
std::wstring url = VBSTR(URL);
if (url.find(L"zip") != std::wstring::npos)
{
DownLoadSkin(url);
*Cancel = TRUE;
}
}
正常情况下,当点击一个链接以后,会跳转到该页面,将*Cancel设置为TURE,可以拦截跳转。
7. 关于下载文件
首先要包含头文件“afxinet.h”。代码如下:
HINTERNET hi = InternetOpen(L"http_down_dll", INTERNET_OPEN_TYPE_PRECONFIG, NULL, INTERNET_INVALID_PORT_NUMBER, 0);
if (hi == NULL)
return;
HINTERNET hUrl = InternetOpenUrl(hi, url, NULL, 0, INTERNET_FLAG_RELOAD, 0);
if (hUrl == NULL)
{
InternetCloseHandle(hi);
return;
}
DWORD optbuf[256];
DWORD optlen = 256;
if (HttpQueryInfo(hUrl, HTTP_QUERY_CONTENT_LENGTH, optbuf, &optlen, NULL) == FALSE)
{
InternetCloseHandle(hUrl);
InternetCloseHandle(hi);
return;
}
char tmpbuff[256];
sprintf(tmpbuff, "%s", optbuf);
long flen = atol(tmpbuff);
if (flen <= 0)
{
InternetCloseHandle(hUrl);
InternetCloseHandle(hi);
return;
}
HANDLE hfile = CReateFile(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hfile == INVALID_HANDLE_VALUE)
{
InternetCloseHandle(hUrl);
InternetCloseHandle(hi);
return;
}
BYTE bufferstr[1024];
DWORD len = 0; wlen = 0;
DWORD wsum = 0;
long sendlen = flen;
while (InternetReadFile(hUrl, (LPVOID)bufferstr, 1024, &len)&&len != 0)
WriteFile(hFile, bufferstr, len, &wlen, NULL);
CloseHandle(hFile);
InternetCloseHandle(hUrl);
InternetCloseHandle(hi);