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;