1.3 实现超链接
在网络应用过程中,特别是在Web程序中,超级链接用得非常普遍。其实使用VC技术,也可以实现超级链接功能。在本节的内容中,将介绍使用Visual C++ 6.0开发一个实现超级链接功能的应用程序。在开始之前,首先简单介绍与之相关的基础知识。
1.3.1 数据报套接字编程
流式套接字主要用于TCP协议,接下来将要学的数据报套接字主要用于UDP协议。数据报套接字(Datagram Socket)提供双向的通信,但没有可靠/有序/不重复的保证,所以UDP传送数据可能会收到无次序、重复的信息,甚至信息在传输过程中出现遗漏,但是传输效率较高,在网络上仍然有很多应用。
数据报套接字的编程模型如图1-15所示。
与流式套接字编程的主要区别在于,在数据传输过程中使用的是sendto()及recvfrom()这两个函数。其中sendto()函数的结构如下:
- int sendto(
- SOCKET s,
- const char FAR *buf,
- int len,
- int flags,
- const struct sockaddr FAR *to,
- int tolen
- );
![]() |
(点击查看大图)图1-15 数据报套接字编程模型 |
recvfrom()函数的结构如下:
- int recvfrom(
- SOCKET s,
- char FAR *buf,
- int len,
- int flags,
- struct sockaddr FAR *from,
- int FAR *fromlen
- );
1.3.2 开发准备
在具体实现本实例之前,需要掌握一些与本实例有关的基础知识。
1. 超链接
超链接在本质上属于一个网页的一部分,它是一种允许我们同其他网页或站点之间进行连接的元素。各个网页链接在一起后,才能真正构成一个网站。所谓的超链接是指从一个网页指向一个目标的连接关系,这个目标可以是另一个网页,也可以是相同网页上的不同位置,还可以是一个图片,一个电子邮件地址,一个文件,甚至是一个应用程序。而在一个网页中用来超链接的对象,可以是一段文本或者是一个图片。当浏览者单击已经链接的文字或图片后,链接目标将显示在浏览器上,并且根据目标的类型来打开或运行。
2. CStatic类
CStatic类是一个静态文本框类,此类提供了一个Windows静态控件的性能。一个静态控件用来显示一个文本字符串、框、矩形、图标、光标、位图,或增强的图元文件。它可以被用来作为标签、框,或用来分隔其他的控件。创建一个静态控件分两步。首先,调用构造函数来构造此CStatic对象,然后调用Create成员函数来创建此静态控件并将它与该CStatic对象连接。如果你是在一个对话框中创建了一个静态控件(通过一个对话框资源),则当用户关闭这个对话框时,此CStatic对象被自动销毁。如果你是在一个窗口中创建了一个CStatic对象,则必须由你来销毁它。在一个窗口的堆栈中创建的CStatic对象将自动被销毁。如果你是使用new函数在堆中创建CStatic对象,则当你使用完后,必须调用delete来销毁这个CStatic对象。
在CStatic类中,最常用的成员函数是Create,其定义格式如下:
- BOOL Create(LPCTSTR lpszText, DWORD dwStyle, const RECT &rect,
- CWnd *pParentWnd, UINT nID = 0xffff);
lpszText:指定要放置在控件中的文本。若为NULL,则表示没有文本是可见的。
dwStyle:指定静态控件的窗口风格。任何静态控件风格的组合都可用于该控件。
rect:指定静态控件的位置和大小。可以是一个RECT结构或一个CRect对象。
pParentWnd:指定CStatic父窗口,通常是一个CDialog对象,不能是NULL。
nID:指定静态控件的控件ID。
CStatic类中其他成员函数的具体说明如表1-5所示。
表1-5 CStatic类成员函数
函数名称 |
功能描述 |
SetBitmap |
指定要在此静态控件中显示的位图 |
GetBitmap |
获取先前用SetBitmap设置的位图的句柄 |
SetIcon |
指定一个要在此静态控件中显示的图标 |
GetIcon |
获取先前用SetIcon设置的图标的句柄 |
SetCursor |
指定要显示在此静态控件中的光标图像 |
GetCursor |
获取先前用SetCursor设置的光标图像的句柄 |
SetEnhMetaFile |
指定要显示在此静态控件中的增强的图元文件 |
GetEnhMetaFile |
获取先前用SetEnhMetaFile设置的增强图元文件的句柄 |
1.3.3 小试牛刀--编程实现写邮件超级链接(1)
实例功能 编程实现写邮件超级链接
源码路径 光盘yuanma1Link
本实例的目的是,使用Visual C++ 6.0开发一个实现写邮件超级链接的应用程序。
1. 设计MFC窗体
使用Visual C++ 6.0创建一个MFC项目后,根据本实例的需要设计一个窗体,命名为IDD_ HLSAMPLE_DIALOG,如图1-16所示。
![]() |
图1-16 创建的窗体 |
2. 具体编码
设计好窗体之后,接下来开始讲解具体的编码过程。
(1) 在文件HyperLink.h中定义继承于类CStatic的类CHyperLink,并设置与超链接相关的样式变量,例如鼠标形状、是否访问过等。具体代码如下:
- class CHyperLink : public CStatic
- {
- public:
- CHyperLink();
- virtual ~CHyperLink();
- // 属性
- public:
- public:
- //设定URL
- void SetURL(CString strURL);
- CString GetURL() const;
- //设定颜色
- void SetColours(COLORREF crLinkColour, COLORREF crVisitedColour,
- COLORREF crHoverColour=-1);
- //获得连接颜色
- COLORREF GetLinkColour() const;
- //获得被访问后的颜色
- COLORREF GetVisitedColour() const;
- //获得鼠标移动上以后的颜色
- COLORREF GetHoverColour() const;
- //设定是否被访问过
- void SetVisited(BOOL bVisited=TRUE);
- //获得是否被访问过
- BOOL GetVisited() const;
- //设定鼠标形状
- void SetLinkCursor(HCURSOR hCursor);
- //获得鼠标形状
- HCURSOR GetLinkCursor() const;
- //设定是否有下划线
- void SetUnderline(BOOL bUnderline=TRUE);
- //获得是否有下划线
- BOOL GetUnderline() const;
- //设定是否是自动改变大小
- void SetAutoSize(BOOL bAutoSize=TRUE);
- BOOL GetAutoSize() const;
- public:
- virtual BOOL PreTranslateMessage(MSG *pMsg);
- protected:
- virtual void PreSubclassWindow();
- //}}AFX_VIRTUAL
- protected:
- //连接到URL
- HINSTANCE GotoURL(LPCTSTR url, int showcmd);
- //打印错误
- void ReportError(int nError);
- //获得注册表信息
- LONG GetRegKey(HKEY key, LPCTSTR subkey, LPTSTR retdata);
- //调整位置
- void PositionWindow();
- //设定默认的鼠标形状
- void SetDefaultCursor();
- // 变量
- protected:
- COLORREF m_crLinkColour, m_crVisitedColour; // 超级链接颜色
- COLORREF m_crHoverColour; // 鼠标停留颜色
- BOOL m_bOverControl; // 是否鼠标移到控件上
- BOOL m_bVisited; // 是否被访问
- BOOL m_bUnderline; // 是否有下划线
- BOOL m_bAdjustToFit; // 是否自动调整控件大小
- CString m_strURL; // URL
- CFont m_Font; // 设定字体
- HCURSOR m_hLinkCursor; // 光标
- CToolTipCtrl m_ToolTip; // 提示文字
- protected:
- afx_msg HBRUSH CtlColor(CDC *pDC, UINT nCtlColor);
- afx_msg BOOL OnSetCursor(CWnd *pWnd, UINT nHitTest, UINT message);
- afx_msg void OnMouseMove(UINT nFlags, CPoint point);
- //}}AFX_MSG
- afx_msg void OnClicked();
- DECLARE_MESSAGE_MAP()
- };
1.3.3 小试牛刀--编程实现写邮件超级链接(2)
(2) 在文件HyperLink.cpp中定义类成员函数的具体实现,接下来开始讲解此文件的具体实现过程。
① 定义函数OnMouseMove和OnSetCursor实现鼠标移动事件,具体代码如下:
- //鼠标移动事件
- void CHyperLink::OnMouseMove(UINT nFlags, CPoint point)
- {
- CStatic::OnMouseMove(nFlags, point);
- //判断鼠标是否在控件上方
- if (m_bOverControl)
- {
- CRect rect;
- GetClientRect(rect);
- if (!rect.PtInRect(point))
- {
- m_bOverControl = FALSE;
- ReleaseCapture();
- RedrawWindow();
- return;
- }
- }
- else // 鼠标移过控件
- {
- m_bOverControl = TRUE;
- RedrawWindow();
- SetCapture();
- }
- }
- BOOL CHyperLink::OnSetCursor(
- CWnd* /*pWnd*/,
- UINT /*nHitTest*/,
- UINT /*message*/)
- {
- if (m_hLinkCursor)
- {
- ::SetCursor(m_hLinkCursor);
- return TRUE;
- }
- return FALSE;
- }
② 定义函数PreSubclassWindow()实现鼠标移动事件,具体代码如下:
- void CHyperLink::PreSubclassWindow()
- {
- // 获得鼠标单击事件
- DWORD dwStyle = GetStyle();
- ::SetWindowLong(GetSafeHwnd(), GWL_STYLE, dwStyle | SS_NOTIFY);
- // 如果URL为空,设定为窗体名称
- if (m_strURL.IsEmpty())
- GetWindowText(m_strURL);
- // 同时检查窗体标题是否为空,如果为空则设定为URL
- CString strWndText;
- GetWindowText(strWndText);
- if (strWndText.IsEmpty()) {
- ASSERT(!m_strURL.IsEmpty());
- SetWindowText(m_strURL);
- }
- // 创建字体
- LOGFONT lf;
- GetFont()->GetLogFont(&lf);
- lf.lfUnderline = m_bUnderline;
- m_Font.CreateFontIndirect(&lf);
- SetFont(&m_Font);
- PositionWindow(); // 调整窗体大小
- SetDefaultCursor(); // 设定默认鼠标形状
- //创建提示信息
- CRect rect;
- GetClientRect(rect);
- m_ToolTip.Create(this);
- m_ToolTip.AddTool(this, m_strURL, rect, TOOLTIP_ID);
- CStatic::PreSubclassWindow();
- }
1.3.3 小试牛刀--编程实现写邮件超级链接(3)
③ 定义函数SetURL()和GetURL(),分别设置链接的URL地址并获取URL。具体代码如下:
- //设定URL
- void CHyperLink::SetURL(CString strURL)
- {
- m_strURL = strURL;
- if (::IsWindow(GetSafeHwnd())) {
- PositionWindow();
- m_ToolTip.UpdateTipText(strURL, this, TOOLTIP_ID);
- }
- }
- CString CHyperLink::GetURL() const
- {
- return m_strURL;
- }
④ 定义SetColours()、GetLinkColour()、GetVisitedColour()和GetHoverColour()函数,用于设置链接的不同访问状态下的颜色,具体代码如下:
- //设定颜色
- void CHyperLink::SetColours(COLORREF crLinkColour, COLORREF crVisitedColour,
- COLORREF crHoverColour /* = -1 */)
- {
- m_crLinkColour = crLinkColour;
- m_crVisitedColour = crVisitedColour;
- if (crHoverColour == -1)
- m_crHoverColour = ::GetSysColor(COLOR_HIGHLIGHT);
- else
- m_crHoverColour = crHoverColour;
- if (::IsWindow(m_hWnd))
- Invalidate();
- }
- COLORREF CHyperLink::GetLinkColour() const
- {
- return m_crLinkColour;
- }
- COLORREF CHyperLink::GetVisitedColour() const
- {
- return m_crVisitedColour;
- }
- COLORREF CHyperLink::GetHoverColour() const
- {
- return m_crHoverColour;
- }
⑤ 定义函数SetVisited()和GetVisited(),用于设置是否被访问过,具体代码如下:
- void CHyperLink::SetVisited(BOOL bVisited /* = TRUE */)
- {
- m_bVisited = bVisited;
- if (::IsWindow(GetSafeHwnd()))
- Invalidate();
- }
- BOOL CHyperLink::GetVisited() const
- {
- return m_bVisited;
- }
1.3.3 小试牛刀--编程实现写邮件超级链接(4)
⑥ 定义函数SetLinkCursor()和GetLinkCursor(),用于分别设定鼠标的形状和获取鼠标的形状,具体代码如下:
- void CHyperLink::SetLinkCursor(HCURSOR hCursor)
- {
- m_hLinkCursor = hCursor;
- if (m_hLinkCursor == NULL)
- SetDefaultCursor();
- }
- HCURSOR CHyperLink::GetLinkCursor() const
- {
- return m_hLinkCursor;
- }
⑦ 定义函数SetUnderline()和GetUnderline(),分别用于设置是否有下划线和获取是否具有下划线,具体代码如下:
- //设置下划线
- void CHyperLink::SetUnderline(BOOL bUnderline /* = TRUE */)
- {
- m_bUnderline = bUnderline;
- if (::IsWindow(GetSafeHwnd()))
- {
- LOGFONT lf;
- GetFont()->GetLogFont(&lf);
- lf.lfUnderline = m_bUnderline;
- m_Font.DeleteObject();
- m_Font.CreateFontIndirect(&lf);
- SetFont(&m_Font);
- Invalidate();
- }
- }
- BOOL CHyperLink::GetUnderline() const
- {
- return m_bUnderline;
- }
⑧ 定义函数SetAutoSize()和GetAutoSize(),分别用于设置和获取是否是自动改变大小,具体代码如下:
- void CHyperLink::SetAutoSize(BOOL bAutoSize /* = TRUE */)
- {
- m_bAdjustToFit = bAutoSize;
- if (::IsWindow(GetSafeHwnd()))
- PositionWindow();
- }
- BOOL CHyperLink::GetAutoSize() const
- {
- return m_bAdjustToFit;
- }
⑨ 定义函数PositionWindow(),用于调整窗体的大小,具体代码如下:
- void CHyperLink::PositionWindow()
- {
- if (!::IsWindow(GetSafeHwnd()) || !m_bAdjustToFit)
- return;
- CRect rect;
- GetWindowRect(rect);
- CWnd *pParent = GetParent();
- if (pParent)
- pParent->ScreenToClient(rect);
- CString strWndText;
- GetWindowText(strWndText);
- CDC *pDC = GetDC();
- CFont *pOldFont = pDC->SelectObject(&m_Font);
- CSize Extent = pDC->GetTextExtent(strWndText);
- pDC->SelectObject(pOldFont);
- ReleaseDC(pDC);
- DWORD dwStyle = GetStyle();
- if (dwStyle & SS_CENTERIMAGE)
- rect.DeflateRect(0, (rect.Height() - Extent.cy)/2);
- else
- rectrect.bottom = rect.top + Extent.cy;
- if (dwStyle & SS_CENTER)
- rect.DeflateRect((rect.Width() - Extent.cx)/2, 0);
- else if (dwStyle & SS_RIGHT)
- rectrect.left = rect.right - Extent.cx;
- else
- rectrect.right = rect.left + Extent.cx;
- SetWindowPos(NULL, rect.left, rect.top,
- rect.Width(), rect.Height(), SWP_NOZORDER);
- }
⑩ 定义函数SetDefaultCursor(),用于设定默认的鼠标形状,具体代码如下:
- void CHyperLink::SetDefaultCursor()
- {
- if (m_hLinkCursor == NULL) // No cursor handle - load our own
- {
- // Get the windows directory
- CString strWndDir;
- GetWindowsDirectory(strWndDir.GetBuffer(MAX_PATH), MAX_PATH);
- strWndDir.ReleaseBuffer();
- strWndDir += _T("\winhlp32.exe");
- // This retrieves cursor #106 from winhlp32.exe,
- // which is a hand pointer
- HMODULE hModule = LoadLibrary(strWndDir);
- if (hModule) {
- HCURSOR hHandCursor = ::LoadCursor(hModule, MAKEINTRESOURCE(106));
- if (hHandCursor)
- m_hLinkCursor = CopyCursor(hHandCursor);
- }
- FreeLibrary(hModule);
- }
- }
⑪定义函数GetRegKey(),用于获得注册表信息,具体代码如下:
- LONG CHyperLink::GetRegKey(HKEY key, LPCTSTR subkey, LPTSTR retdata)
- {
- HKEY hkey;
- LONG retval = RegOpenKeyEx(key, subkey, 0, KEY_QUERY_VALUE, &hkey);
- if (retval == ERROR_SUCCESS) {
- long datasize = MAX_PATH;
- TCHAR data[MAX_PATH];
- RegQueryValue(hkey, NULL, data, &datasize);
- lstrcpy(retdata, data);
- RegCloseKey(hkey);
- }
- return retval;
- }
⑫定义函数ReportError(),用于输出打印错误,具体代码如下:
- void CHyperLink::ReportError(int nError)
- {
- CString str;
- switch (nError) {
- case 0:
- str = "The operating system is out of memory or resources.";
- break;
- case SE_ERR_PNF:
- str = "The specified path was not found.";
- break;
- case SE_ERR_FNF:
- str = "The specified file was not found.";
- break;
- case ERROR_BAD_FORMAT:
- str = "The .EXE file is invalid (non-Win32 .EXE "
- + CString("") + "or error in .EXE image).";
- break;
- case SE_ERR_ACCESSDENIED:
- str="The operating system denied access to the specified file.";
- break;
- case SE_ERR_ASSOCINCOMPLETE:
- str = "The filename association is incomplete or invalid.";
- break;
- case SE_ERR_DDEBUSY:
- str = "The DDE transaction could not be completed because "
- + CString("")+"other DDE transactions were being processed.";
- break;
- case SE_ERR_DDEFAIL:
- str = "The DDE transaction failed.";
- break;
- case SE_ERR_DDETIMEOUT:
- str = "The DDE transaction could not be completed because "
- + CString("") + "the request timed out.";
- break;
- case SE_ERR_DLLNOTFOUND:
- str = "The specified dynamic-link library was not found.";
- break;
- case SE_ERR_NOASSOC:
- str = "There is no application associated with the "
- + CString("") + "given filename extension.";
- break;
- case SE_ERR_OOM:
- str = "There was not enough memory to complete the operation.";
- break;
- case SE_ERR_SHARE:
- str = "A sharing violation occurred. ";
- default:
- str.Format("Unknown Error (%d) occurred.", nError);
- break;
- }
- str = "Unable to open hyperlink: " + str;
- AfxMessageBox(str, MB_ICONEXCLAMATION | MB_OK);
- }
定义函数GotoURL(),用于链接到指定的目标地址,具体代码如下:
- //链接到目标地址
- HINSTANCE CHyperLink::GotoURL(LPCTSTR url, int showcmd)
- {
- TCHAR key[MAX_PATH + MAX_PATH];
- // 调用函数ShellExecute()
- HINSTANCE result =
- ShellExecute(NULL, _T("open"), url, NULL, NULL, showcmd);
- // 如果错误,则检查注册表获得.htm文件的注册键值
- if ((UINT)result <= HINSTANCE_ERROR) {
- if (GetRegKey(HKEY_CLASSES_ROOT,_T(".htm"),key) == ERROR_SUCCESS) {
- lstrcat(key, _T("\shell\open\command"));
- if (GetRegKey(HKEY_CLASSES_ROOT,key,key) == ERROR_SUCCESS) {
- TCHAR *pos;
- pos = _tcsstr(key, _T(""%1""));
- if (pos == NULL) { // 没有发现
- pos = strstr(key, _T("%1")); // 检查%1
- if (pos == NULL) // 没有参数
- pos = key+lstrlen(key)-1;
- else
- *pos = ' '; // 删除参数
- }
- else
- *pos = ' '; // 删除参数
- lstrcat(pos, _T(" "));
- lstrcat(pos, url);
- result = (HINSTANCE)WinExec(key, showcmd);
- }
- }
- }
- return result;
- }
至此,整个实例的主要模块介绍完毕。该程序执行后,将在窗体内显示一个超级链接,如图1-17所示。单击"写邮件"后,将激活链接,开始写邮件,如图1-18所示。
![]() |
图1-17 显示一个超级链接 |
![]() |
图1-18 开始写邮件 |