1. IP 控件的使用
// 将 CString 型 IP 地址在 IPAddressCtrl 中显示
CString strIP="192.168.0.10";
DWORD dwIP;
dwIP = inet_addr(strIP);
unsigned char *pIP = (unsigned char*)&dwIP;
m_ipAddr.SetAddress(*pIP, *(pIP+1), *(pIP+2), *(pIP+3));
// 将 IPAddressCtrl 中的 IP 地址获得并转换成 CString 型
unsigned char *pIP;
CString strIP;
DWORD dwIP;
m_ipAddr.GetAddress(dwIP);
pIP = (unsigned char*)&dwIP;
strIP.Format("%u.%u.%u.%u",*(pIP+3), *(pIP+2), *(pIP+1), *pIP);
DWORD dwIP;
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP);
addrTo.sin_addr.S_un.S_addr = htonl(dwIP);
那么 TCP/IP 协议中的端口指的是什么呢?如果把 IP 地址 比作一间房子 ,端口就是出入这间房子的门。真正的房子只有几个门,但是一个 IP 地址的端口 可以有 65536 个之多!端口是通过端口号来标记的,端口号只有整数,范围是从 0 到 65535 。
端口有什么用呢?我们知道,一台拥有 IP 地址的主机可以提供许多服务,比如 Web 服务、 FTP 服务、 SMTP 服务等,这些服务完全可以通过 1 个 IP 地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠 IP 地址,因为 IP 地址与网络服务的关系是一对多的关系。实际上是通过 “IP 地址 + 端口号 ” 来区 分不同的服务的。
服务器一般都是通过知名端口号来识别的。例如,对于每个 TCP/IP 实现来说, FTP 服务器的 TCP 端口号都是 21 ,每个 Telnet 服务器的 TCP 端口号都是 23 ,每个 TFTP( 简单文件传送协议 ) 服务器的 UDP 端口号都是 69 。任何 TCP/IP 实现所提供的服务都用知名的 1 ~ 1023 之间的端口号。这些知名端口号由 Internet 号分配机构( InternetAssignedNumbersAuthority,IANA )来管理。
3.static member function
The declaration of a static member function is the same as that of a nonstatic member function except that the function declaration in the class body is preceded with the keyword static and the function may not be declared as const or volatile . The function definition that appears outside of the class body must not specify the keyword static .
A static member function does not have a this pointer; therefore, referring either implicitly or explicitly to the this pointer within a static member function results in a compile-time error. Attempting to access a nonstatic class member refers implicitly to the this pointer and thus results in a compile-time error. For example, the member function dailyReturn() presented earlier could not be declared as a static member function because it accesses the nonstatic data member _amount .
A static member function may be invoked for a class object or a pointer to a class object using the member access operators dot and arrow. A static member function can also be accessed or invoked directly using a qualified name even if no class objects are ever declared. Here is a small program to illustrate the use of static class members:
In general, a static data member is initialized outside the class definition. Just as in the case of member functions defined outside the class definition, the name of the static member in such a definition must be qualified by its class name. For example, here is how we might initialize _interestRate :
3 异步I/O
BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut, BOOL bRead)
{
fd_set fdset;
timeval tv;
FD_ZERO(&fdset);
FD_SET(hSocket, &fdset); //FD_SET(s, *set) Adds descriptor s to set.
nTimeOut = nTimeOut > 1000 ? 1000 : nTimeOut;
tv.tv_sec = 0;
tv.tv_usec = nTimeOut;
int iRet = 0;
if ( bRead )
{
iRet = select(0, &fdset, NULL , NULL, &tv);
}
else
{
iRet = select(0, NULL , &fdset, NULL, &tv);
}
if (iRet <= 0)
{
return FALSE;
}
else if (FD_ISSET(hSocket, &fdset))
{
return TRUE;
}
return FALSE;
}
4 关于 ANSI 和 unicode 的宏
一、 在字符串前加一个 L 作用 :
如 L" 我的字符串 " 表示将 ANSI 字符串转换成 unicode 的字符串,就是每个字符占用两个字节。
strlen("asd") = 3 ;
strlen(L "asd") = 6 ;
二、 _T 宏可以把一个引号引起来的字符串,根据你的环境设置,使得编译器会根据编译目标环境选择合适的( Unicode 还是 ANSI )字符处理方式
如果你定义了 UNICODE ,那么 _T 宏会把字符串前面加一个 L 。这时 _T("ABCD") 相当于 L"ABCD" ,这是宽字符串。
如果没有定义,那么 _T 宏不会在字符串前面加那个 L , _T("ABCD") 就等价于 "ABCD"
5 任务栏托盘
1 、封装 “其他功能” 按钮响应函数:
void CChatRoomDlg::OnBnClickedOther()
{
CPoint pt;
CRect mRect;
CMenu mMenu, *pMenu = NULL;
GetDlgItem(IDC_OTHER)->GetWindowRect(&mRect);
pt = mRect.BottomRight();
pt.y = mRect.top+10;
mMenu.LoadMenu(IDR_MENU1);
pMenu = mMenu.GetSubMenu(0);
pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
}
2 、任务栏托盘函数的封装:
BOOL CChatRoomDlg::TrayMyIcon(BOOL bAdd)
{
BOOL bRet = FALSE;
NOTIFYICONDATA tnd;
tnd.cbSize = sizeof(NOTIFYICONDATA);
tnd.hWnd = m_hWnd;
tnd.uID = IDR_MAINFRAME;
if ( bAdd == TRUE ) {
tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
tnd.uCallbackMessage = WM_TRAYICON_MSG;
tnd.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));
_tcscpy_s(tnd.szTip, sizeof(tnd.szTip), _T(" 聊天室v1.0"));
ShowWindow(SW_MINIMIZE);
ShowWindow(SW_HIDE);
bRet = Shell_NotifyIcon(NIM_ADD, &tnd);
}else{
ShowWindow(SW_SHOWNA);
SetForegroundWindow();
bRet = Shell_NotifyIcon(NIM_DELETE, &tnd);
}
return bRet;
}
3 、消息响应函数的添加:
#define WM_TRAYICON_MSG (WM_USER+100)
ON_MESSAGE(WM_TRAYICON_MSG, OnTrayCallBackMsg)
LRESULT CChatRoomDlg::OnTrayCallBackMsg(WPARAM wparam, LPARAM lparam)
{
switch(lparam)
{
case WM_RBUTTONUP:
{
CMenu mMenu, *pMenu = NULL;
CPoint pt;
mMenu.LoadMenu(IDR_MENU2);
pMenu = mMenu.GetSubMenu(0);
GetCursorPos(&pt);
SetForegroundWindow();
pMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
break;
}
case WM_LBUTTONDBLCLK:
ShowWindow(SW_RESTORE);
SetForegroundWindow();
break;
default:break;
}
return NULL;
}
4. 获得CString 指针
如果需要修改CString 中的内容,它有一个特殊的方法可以使用,那就是GetBuffer ,它的作用是返回一个可写的缓冲指针。如果只是打算修改字符或者截短字符串,例如
CString theString( (_T("Char test ")));
LPTSTR lpsz=s.GetBuffer();
/* 添加p 的代码*/
s.ReleaseBuffer(); // 使用完后及时释放
5 CString,string,char* 之间的转换
这几天经常用到的,不如记下吧。
这三种类型各有各的优点,比如CString 比较灵活,是基于MFC 常用的类型,安全性也最高,但可移植性最差。string 是使用STL 时必不可 少的类型,所以是做工程时必须熟练掌握的;char* 是从学习C 语言开始就已经和我们形影不离的了,有许多API 都是以char* 作为参数输入的。所以熟 练掌握三者之间的转换十分必要。
以下我用简单的图示指出三者之间的关系,并以标号对应转换的方法。
1 string to CString
CString.format("%s",string.c_str());
2 CString to string
string str(CString.GetBuffer(str.GetLength()));
3 string to char *
char *p=string.c_str();
4 char * to string
string str(char*);
5 CString to char *
strcpy(char,CString,sizeof(char));
6 char * to CString
CString.format("%s",char*);
CString 的format 方法是非常好用的。string 的c_str() 也是非常常用的,但要注意和char * 转换时,要把char 定义成为const char* ,这样是最安全的。
inline CString toCString(const std::string& str)
{ return CString(str.c_str()); }
//
inline std::string toStdString(const CString& cstr)
{ return std::string((LPCTSTR)cstr); }
//
// :
CString s1 = "Hello, World";
std::string s2 = toStdString(s1);
CString s3 = toCString(s2);
std::string cannot always construct from a LPCTSTR i.e. the code will fail for UNICODE builds.
As std::string can construct only from LPSTR / LPCSTR, a programmer who uses VC++ 7.x or better can utilize conversion classes such as CT2CA as an intermediary.
CString cs ( "Hello" );
// Convert a TCHAR string to a LPCSTR
CT2CA pszConvertedAnsiString ( cs );
// construct a std::string using the LPCSTR input
std :: string strStd ( pszConvertedAnsiString );
'std::string' to 'CString ': (From Visual Studio's CString FAQs... )
std :: string s ( "Hello" );
CString cs ( s . c_str ());
CStringT can construct from both character or wide-character strings. i.e. It can convert from char* (i.e. LPSTR) or from wchar_t* (LPWSTR).
In other words, char-specialization (of CStringT) i.e. CStringA, wchar_t-specilization CStringW, and TCHAR-specialization CString can be constructed from either char or wide-character, null terminated (null-termination is very important here) string sources.
6 解决 error LNK2019
用到了 Psapi.h 中的一些进程函数。我将 Psapi.h 包含到源代码中,但链接时出现了 4 个 LNK2019 错误,都是 Psapi.h 中的函数引起的无法解析的外部符号。
错误 2 error LNK2019: 无法解析的外部符号 _GetModuleFileNameExW@16 ,该符号在函数 "public: class ATL::CStringT<wchar_t,class StrTraitMFC_DLL<wchar_t,class ATL::ChTraitsCRT<wchar_t> > > __thiscall CSystemMangerDlg::GetProcessPath(unsigned long)" ( ?GetProcessPath@CSystemMangerDlg@@QAE?AV?$CStringT@_WV?$StrTraitMFC_DLL@_WV?$ChTraitsCRT@_W@ATL@@@@@ATL@@K@Z ) 中被引用 SystemMangerDlg.obj
因为编译通过了,所以应该不是缺少 Psapi.h 的原因。我就上 msdn2005 查了 LNK2019 ,不过里面列举的情况也没有和我类似。到网上搜,找了很久也没有和我类似的情况。后来看到一文章说是缺少附加依赖项。只要添加上附加依赖项,问题就解决了。添加附加依赖项的方法有:
方法 1 :在包含头文件 Psapi.h 的同时,加上一行代码 #pragma comment(lib,”psapi.lib”)
方法 2 : [ 解决方案资源管理器 ]“ 项目 -> 属性 -> 配置属性 -> 连接器 -> 输入 -> 附加依赖项 ”
虽然问题解决了,不过好像我还是不大理解依赖项这个东东!网上说的也很少。大概就是这个意思
一般大的工程软件都放在一个解决方案里面,分了好多模块(都是以工程方式组织在一起,或 dll, 或 lib )。这样也是为了保证工程的同步开发的方便。
工程之间的链接可以动态链接:使用 LoadLibrary 装入库,也可以静态链接,在工程配置中添加附加依赖库,或者在代码中使用 #pragma comment(lib,"xxx.lib) ; 在 Project->dependencies 中设置,主要是为了保证系统编译时候的顺序,既先编译无依赖项的项目,在编译其父依赖的项目。跟静态链接的效果是一样的。
7 如何获取本窗口句柄
要首先获得这个窗口所对应的对象指针 *p, 然后直接 p->m_hWnd. 如果你本身就在这个窗口类中需要这个句柄 ,this->m_hWnd 就可以了 .
在 VC++ 编程中常需获取控件或窗体句柄,下面总结了几种方法,还希望大家能多多补充。
1 、自身窗口句柄可用 AfxGetMainWnd 获取。
2 、系统中其他 APP 的窗口句柄可用 FindWindow 获取 ( 用 SPY 帮一下忙 ).
HWND hBtnClose;
HWND hWnd=::FindWindow(NULL," 腾讯 QQ 系统广播 ");
if(hWnd)
{
hBtnClose=GetDlgItem(hWnd,2);
if(hBtnClose)
PostMessage(hBtnClose,BM_CLICK,NULL,NULL);
}
3 、通过指针获取窗口句柄
HWND hwnd = pwnd->m_hwnd; // 得到它的 HWND
4 、当我们想得到一个窗口对象( CWnd 的派生对象)指针的句柄( HWND )时,
最安全的方法是使用 GetSafeHwnd() 函数;
5 、 HWND GetDlgltem(HWND hDlg,int nlDDlgltem);
6 、通过控件 ID 号获取。
CListCtrl* pleftList = (CListCtrl* )GetDlgItem(IDC_LIST1);
8 _countof Macro
Compute the number of elements in a statically-allocated array.
_countof( array ); |
Parameters
array
The name of an array.
Return Value
The number of elements in the array.
9 窗口、控件的指针和句柄的相互转化
1 指针转化为句柄
在 MFC 应用程序中首先要获得窗口的指针,然后将其转化为句柄
CWnd* pWnd;
HANDLE hWnd = pWnd->GetSafeHwnd();
2 句柄转化为指针
在 MFC 应用程序中首先获得对话框控件的句柄,然后获得其指针
HANDLE hWnd;
GetDlgItem(IDC_xxx,&hWnd);
CWnd * pWnd = FromHandle(hWnd);
获得程序窗口指针的办法
1 获得主框架窗口指针( 任何时候都可以用,只要是MFC 程 序中)
CWnd* pWnd = AfxGetMainWnd();
2 获得对话框中控件指针
CWnd* pWnd = GetDlgItem(IDC_xxx);
3 获得对话框中某控件的句柄
HANDLE GetDlgItem(m_hDLG,m_nID_DlgItem);
4 获得 GDI 对象的句柄
HANDLE m_hGDIObj = m_pGDIObj->GetSafeHanle();
10 异步套接字基础: select 函数以及 FD_ZERO 、 FD_SET 、 FD_CLR 、 FD_ISSET 使用说明 收藏
select 函数:
系统提供 select 函数来实现多路复用输入 / 输出模型。原型:
#include <sys/time.h>
#include <unistd.h>
select 函数:
系统提供select 函数来实现多路复用输入/ 输出模型。原型:
#include <sys/time.h>
#include <unistd.h>
int select(int maxfd,fd_set *rdset,fd_set *wrset,fd_set *exset,struct timeval *timeout);
参数maxfd 是需要监视的最大的文件描述符值+1 ;rdset,wrset,exset 分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合。struct timeval 结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0 。
FD_ZERO,FD_SET,FD_CLR,FD_ISSET: 参数maxfd 是需要监视的最大的文件描述符值+1 ;rdset,wrset,exset 分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合。struct timeval 结构用于描述一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0 。
FD_ZERO,FD_SET,FD_CLR,FD_ISSET:
FD_ZERO(fd_set *fdset); 将指定的文件描述符集清空,在对文件描述符集合进行设置前,必须对其进行初始化,如果不清空,由于在系统分配内存空间后,通常并不作清空 处理,所以结果是不可知的。
FD_SET(fd_set *fdset); 用于在文件描述符集合中增加一个新的文件描述符。
FD_CLR(fd_set *fdset); 用于在文件描述符集合中删除一个文件描述符。
FD_ISSET(int fd,fd_set *fdset); 用于测试指定的文件描述符是否在该集合中。
struct timeval 结构:
struct timeval{
long tv_sec;//second
long tv_usec;//minisecond
}
timeout 设置情况:
null:select 将一直被阻塞,直到某个文件描述符上发生了事件。
0 :仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。
特定的时间值:如果在指定的时间段里没有事件发生,select 将超时返回。
11. LPCTSTR,LPCSTR 和 Cstring 互换
ansi 情况下,LPCTSTR 就是 const char*, 是常量字符串(不能修改的)。
而LPTSTR 就是 char*, 即普通字符串(非常量,可修改的)。
这两种都是基本类型, 而CString 是 C++ 类, 兼容这两种基本类型是最起码的任务了。
由于const char* 最简单(常量,不涉及内存变更,操作迅速), CString 直接定义了一个类型转换函数
operator LPCTSTR() {......} , 直接返回他所维护的字符串。
当你需要一个const char* 而传入了CString 时, C++ 编译器自动调用 CString 重载的操作符 LPCTSTR() 来进行隐式的类型转换。
当需要CString , 而传入了 const char* 时(其实 char* 也可以),C++ 编译器则自动调用CString 的构造函数来构造临时的 CString 对象。
因此CString 和 LPCTSTR 基本可以通用。
但是 LPTSTR 又不同了,他是 char* , 意味着你随时可能修改里面的数据,这就需要内存管理了( 如字符串变长,原来的存贮空间就不够了,则需要重新调整分配内存) 。
所以 不能随便的将 const char* 强制转换成 char* 使用。
楼主举的例子
LPSTR lpstr = (LPSTR)(LPCTSTR)string;
就是这种不安全的使用方法。
这个地方使用的是强制类型转换,你都强制转换了,C++ 编译器当然不会拒绝你,但同时他也认为你确实知道自己要做的是什么。因此是不会给出警告的。
强制的任意类型转换是C(++) 的一项强大之处,但也是一大弊端。这一问题在 vc6 以后的版本( 仅针对vc 而言) 中得到逐步的改进( 你需要更明确的类型转换声明) 。
其实在很多地方都可以看到类似
LPSTR lpstr = (LPSTR)(LPCTSTR)string;
地用法,这种情况一般是函数的约束定义不够完善的原因 , 比如一个函数接受一个字符串参数的输入,里面对该字符串又没有任何的修改,那么该参数就应该定义成 const char* , 但是很多初学者弄不清const 地用法,或者是懒, 总之就是随意写成了 char* 。 这样子传入CString 时就需要强制的转换一下。
这种做法是不安全的,也是不被建议的用法,你必须完全明白、确认该字符串没有被修改 。
CString 转换到 LPTSTR (char*), 预定的做法是调用CString 的GetBuffer 函数,使用完毕之后一般都要再调用ReleaseBuffer 函数来确认修改 ( 某些情况下也有不调用ReleaseBuffer 的,同样你需要非常明确为什么这么做时才能这样子处理,一般应用环境可以不考虑这种情况) 。
同时需要注意的是, 在GetBuffer 和 ReleaseBuffer 之间,CString 分配了内存交由你来处理,因此不能再调用其他的CString 函数。
CString 转LPCTSTR:
CString cStr;
const char *lpctStr=(LPCTSTR)cStr;
LPCTSTR 转CString:
LPCTSTR lpctStr;
CString cStr=lpctStr;