以前總結的關於MFC的一些知識

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);

 

2. 端口號

  那麼 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*);

 CStringformat 方法是非常好用的。stringc_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 是需要監視的最大的文件描述符值+1rdset,wrset,exset 分別對應於需要檢測的可讀文件描述符的集合,可寫文件描述符的集 合及異常文件描述符的集合。struct   timeval 結構用於描述一段時間長度,如果在這個時間內,需要監視的描述符沒有事件發生則函數返回,返回值爲0    
  FD_ZERO,FD_SET,FD_CLR,FD_ISSET:          
參數maxfd 是需要監視的最大的文件描述符值+1rdset,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*), 預定的做法是調用CStringGetBuffer 函數,使用完畢之後一般都要再調用ReleaseBuffer 函數來確認修改 ( 某些情況下也有不調用ReleaseBuffer 的,同樣你需要非常明確爲什麼這麼做時才能這樣子處理,一般應用環境可以不考慮這種情況)

同時需要注意的是, 在GetBuffer ReleaseBuffer 之間,CString 分配了內存交由你來處理,因此不能再調用其他的CString 函數。

CString LPCTSTR:
CString cStr;
const char *lpctStr=(LPCTSTR)cStr;

LPCTSTR
CString:
LPCTSTR lpctStr;
CString cStr=lpctStr;

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章