首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

MFC DDX 機制擴展:IP 地址驗證與交換自定義 DDX_IPAddress 完整實作

     編輯:關於C語言

DDX (Dialog Data Exchange) 機制回顧

MFC 對話框資料交換 (DDX) 在 CDialog::DoDataExchange 中進行,標準流程:

  1. UpdateData(TRUE):控制項 → 變數 (驗證 + 交換)。
  2. UpdateData(FALSE):變數 → 控制項 (顯示)。

標準 DDX_Text、DDX_Check 等宏對應 AFX_DDX 函數。自定義 DDX 需:

  • 宣告函數原型 (通常放在對話框 .h)。
  • 在 DoDataExchange 中呼叫。
  • 實作函數處理 CDataExchange 方向與驗證。

DDX_IPAddress 完整實作

標頭檔宣告


// MyDialog.h
#include  // inet_addr, INADDR_NONE
#pragma comment(lib, "ws2_32.lib")

// 自定義 DDX 函數宣告
void AFXAPI DDX_IPAddress(CDataExchange* pDX, int nIDC, UINT& value);

對話框中呼叫


void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_IPAddress(pDX, IDC_EDIT_IP, m_uServerIP); // m_uServerIP 為 UINT 成員
}

核心實作 (放在 .cpp)


void AFXAPI DDX_IPAddress(CDataExchange* pDX, int nIDC, UINT& value)
{
    HWND hWndCtrl = pDX->PrepareEditCtrl(nIDC); // 取得編輯控制項 HWND,處理 Focus
    CString strIP;
    ::GetWindowText(hWndCtrl, strIP.GetBufferSetLength(255), 255);
    strIP.ReleaseBuffer();

    if (pDX->m_bSaveAndValidate) { // 控制項 → 變數 (驗證階段)
        // 1. 字元合法性檢查:只允許數字與點
        for (int i = 0; i < strIP.GetLength(); ++i) {
            TCHAR ch = strIP[i];
            if (ch != '.' && !_istdigit(ch)) {
                AfxMessageBox(_T("IP 位址只能包含數字與點號"));
                pDX->Fail(); // 設定焦點回控制項
                return;
            }
        }

        // 2. 結構驗證:四段、各段 0-255、無前導零 (可選)
        // 簡化版:直接用 inet_addr 驗證
        value = inet_addr(CT2A(strIP)); // 轉為網路位序 (大端序)
        if (value == INADDR_NONE) { // 255.255.255.255 也會返回此值,需額外判斷
            AfxMessageBox(_T("無效的 IP 位址格式"));
            pDX->Fail();
            return;
        }
        // 額外檢查 "255.255.255.255" (廣播地址通常不作為伺服器 IP)
        if (value == 0xFFFFFFFF && strIP != _T("255.255.255.255")) {
            AfxMessageBox(_T("無效的 IP 位址"));
            pDX->Fail();
        }
    }
    else { // 變數 → 控制項 (顯示階段)
        // 將網路位序 UINT 轉為點分十進位字串
        in_addr addr; addr.s_addr = value;
        const char* psz = inet_ntoa(addr); // 返回靜態緩衝區
        if (psz) {
            CString strDisp(CA2T(psz));
            ::SetWindowText(hWndCtrl, strDisp);
        }
    }
}

進階版:嚴格 RFC 790 格式驗證


bool ValidateIPStrict(const CString& strIP)
{
    // 分割四段
    int nStart = 0, nCount = 0;
    CString token;
    while (AfxExtractSubString(token, strIP, nCount++, '.')) {
        if (token.IsEmpty() || token.GetLength() > 3) return false;
        // 無前導零 (除非單獨 "0")
        if (token.GetLength() > 1 && token[0] == '0') return false;
        // 全數字
        for (int i = 0; i < token.GetLength(); ++i)
            if (!_istdigit(token[i])) return false;
        // 數值範圍
        int val = _ttoi(token);
        if (val < 0 || val > 255) return false;
    }
    return nCount == 4; // 必須剛好四段
}

IP Address Control (SysIPAddress32) 原生支援

Windows 內建 IP 位址控制項 (Common Control 6.0,SysIPAddress32),四欄位分別編輯,自動驗證:


// 資源編輯器加入 "IP Address Control" (IDC_IPADDRESS)
// 對話框成員變數
CIPAddressCtrl m_ipCtrl;

// DoDataExchange
void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_IPADDRESS, m_ipCtrl); // 關聯控制項

    if (pDX->m_bSaveAndValidate) {
        DWORD dwIP = 0;
        m_ipCtrl.GetAddress(dwIP); // 取得網路位序 DWORD
        if (dwIP == 0) { AfxMessageBox(_T("請輸入 IP")); pDX->Fail(); }
        m_uServerIP = dwIP;
    }
    else {
        m_ipCtrl.SetAddress(m_uServerIP); // 顯示
    }
}

優點:原生 UI 體驗、自動限制輸入、無需自寫驗證。缺點:需 Common Control 6.0 (Manifest)、樣式較固定。

常見坑與解決

問題原因解決
inet_addr 返回 INADDR_NONE 但字串合法"255.255.255.255" 合法但值為 -1額外字串比對排除
DDX 失敗後焦點不回控制項未呼叫 pDX->Fail()驗證失敗分支必加 pDX->Fail()
Unicode 專案 inet_addr 編譯錯誤需轉 ANSI: CT2A(str) 或 inet_addr(CA2T(...))使用 atlconv.h 轉換宏
IPv6 支援inet_addr 僅 IPv4改用 InetPton(AF_INET6, ...) (Vista+),或第三方庫

總結

自定義 DDX_IPAddress 展示 MFC DDX 機制的擴展性:PrepareEditCtrl → 取得文字 → 方向判斷 → 驗證/轉換 → Fail/設定文字。結合 inet_addr/ntoa 實現 IPv4 點分十進位與網路位序雙向轉換。現代專案優先考慮原生 CIPAddressCtrl,自定義 DDX 適用於舊版相容、特殊格式需求或輕量化場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved