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

MFC WinSock ActiveX控件開發實戰(一):從專案建立到基礎連線事件實現

     編輯:關於VC++

ActiveX控件曾是Windows元件化開發主流,雖已被.NET/WPF/Web技術取代,但在舊系統維護、VB6/VC6遷移、嵌入式管理介面等場景仍具不可替代性。本系列以 MFC WinSock ActiveX控件 為例,完整演示:專案建立、Socket環境初始化、自動化介面/事件設計、WSAAsyncSelect 非同步通知、連線/斷線/收發核心邏輯、VC/VB雙環境調用。

第一部分:專案建立步驟(VC++ 6.0)

  1. File → New → Projects → MFC ActiveX ControlWizard
  2. 專案名:MFCWinSock,路徑自定
  3. 一路 Next 保持預設,至 Finish → OK

建立ActiveX專案

圖1:MFC ActiveX ControlWizard 專案建立

專案建立完成

圖2:專案結構生成完成

第二部分:Socket 環境初始化

StdAfx.h 加入 MFC Socket 擴展


#include  // MFC Socket 擴展類別

MFCWinSock.cpp:DLL 進入點初始化/清理


BOOL CMFCWinSockApp::InitInstance()
{
    BOOL bInit = COleControlModule::InitInstance();
    if (bInit) {
        // 1. MFC Socket 初始化
        if (!AfxSocketInit()) {
            AfxMessageBox(_T("Socket初始化失敗"));
            return FALSE;
        }
        
        // 2. Winsock 1.1 啟動
        WSADATA wsaData;
        WORD wVer = MAKEWORD(1, 1);
        int nErr = WSAStartup(wVer, &wsaData);
        if (nErr) {
            AfxMessageBox(_T("找不到 WSOCK32.DLL"));
            return FALSE;
        }
    }
    return bInit;
}

int CMFCWinSockApp::ExitInstance()
{
    WSACleanup(); // 關閉 Winsock
    return COleControlModule::ExitInstance();
}

第三部分:自動化介面與事件定義

自訂視窗訊息常數


#ifndef WM_MYWINSOCK
#define WM_MYWINSOCK (WM_USER + 1888)
#endif

類別精靈添加方法

View → ClassWizard → Automation → Add Method

添加方法

圖3:添加 Connect 方法

  • Connect(VARIANT RemoteHost, VARIANT RemotePort) → 返回 BOOL
  • DisConnect() → 返回 BOOL

添加事件

Add Event 添加:

  • FireCloseWinsock() —— 對方斷線通知
  • FireRecvSockEvent() —— 數據到達通知

添加事件

圖4:添加 FireCloseWinsock / FireRecvSockEvent

重載 WindowProc 處理非同步通知

ClassWizard → Message Maps → 選擇 CMFCWinSockCtrl → 添加 WindowProc 處理

重載WindowProc

圖5:重載 WindowProc 處理 WM_MYWINSOCK

第四部分:核心輔助函數與連線實現

VARIANT → Long 通用轉換


long CMFCWinSockCtrl::VariantToLong(const VARIANT& var)
{
    long r = -1;
    switch (var.vt) {
        case VT_UI2: r = var.uiVal; break;     // unsigned short
        case VT_UI4: r = var.ulVal; break;     // unsigned long
        case VT_INT: r = var.intVal; break;    // int
        case VT_UINT: r = var.uintVal; break;  // unsigned int
        case VT_I4:  r = var.lVal; break;      // long
        case VT_UI1: r = var.bVal; break;      // BYTE
        case VT_I2:  r = var.iVal; break;      // short
        case VT_R4:  r = (long)var.fltVal; break; // float
        case VT_R8:  r = (long)var.dblVal; break; // double
        default: r = -1; break;
    }
    return r;
}

Connect 完整實現


BOOL CMFCWinSockCtrl::Connect(const VARIANT FAR& RemoteHost, const VARIANT FAR& RemotePort)
{
    // 1. 重入檢查
    if (m_bConnected) return FALSE;
    
    // 2. 解析 IP 位址(支援 BSTR 與 char*)
    CString strIP;
    switch (RemoteHost.vt) {
        case VT_BSTR:
            strIP = RemoteHost.bstrVal; break;
        case VT_BYREF | VT_I1:
            strIP.Format(_T("%s"), RemoteHost.pcVal); break;
        default:
            return FALSE;
    }
    
    // 3. 解析 Port
    int nPort = VariantToLong(RemotePort);
    if (nPort <= 0) return FALSE;
    
    // 4. 建立 Socket
    m_hSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (m_hSocket == INVALID_SOCKET) return FALSE;
    
    // 5. 解析位址(支援域名)
    ULONG ulAddr = inet_addr(strIP);
    sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(nPort);
    addr.sin_addr.s_addr = ulAddr;
    
    if (ulAddr == INADDR_NONE) { // 域名解析
        hostent* pHost = gethostbyname(strIP);
        if (!pHost) { closesocket(m_hSocket); return FALSE; }
        addr.sin_addr.s_addr = *(ULONG*)pHost->h_addr_list[0];
    }
    
    // 6. 連線
    if (connect(m_hSocket, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
        closesocket(m_hSocket); return FALSE;
    }
    
    // 7. 註冊非同步通知(FD_READ | FD_CLOSE)
    int nErr = WSAAsyncSelect(m_hSocket, m_hWnd, WM_MYWINSOCK, FD_READ | FD_CLOSE);
    if (nErr == SOCKET_ERROR) {
        closesocket(m_hSocket); return FALSE;
    }
    
    m_bConnected = TRUE;
    return TRUE;
}

WindowProc 訊息分派


LRESULT CMFCWinSockCtrl::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
    switch (msg) {
        case WM_MYWINSOCK:
            switch (WSAGETSELECTEVENT(lp)) {
                case FD_READ:
                    FireRecvSockEvent(); // 觸發數據到達事件
                    break;
                case FD_CLOSE:
                    FireCloseWinsock();  // 觸發斷線事件
                    break;
            }
            break;
    }
    return COleControl::WindowProc(msg, wp, lp);
}

VARIANT 結構深度解析

ActiveX 自動化介面統一使用 VARIANT 傳遞參數,本質是 型別標籤 + 聯合體:


struct VARIANT {
    VARTYPE vt;  // 型別標記
    WORD wReserved1, wReserved2, wReserved3;
    union {
        LONG lVal;        // VT_I4
        BYTE bVal;        // VT_UI1
        SHORT iVal;       // VT_I2
        FLOAT fltVal;     // VT_R4
        DOUBLE dblVal;    // VT_R8
        VARIANT_BOOL boolVal; // VT_BOOL
        BSTR bstrVal;     // VT_BSTR (Unicode字串)
        SAFEARRAY* parray; // VT_ARRAY | VT_XXX
        // ... 指標變體 ...
    };
};
  • vt 決定聯合體中哪個欄位有效
  • SAFEARRAY 是 COM 標準陣列,含維度、界限、元素型別、資料指標
  • C++ 端手動操作,VB/Scripting 自動封送

DisConnect 實現


BOOL CMFCWinSockCtrl::DisConnect()
{
    if (!m_bConnected) return FALSE;
    
    // 取消非同步通知
    WSAAsyncSelect(m_hSocket, m_hWnd, WM_MYWINSOCK, 0);
    
    // 關閉 Socket
    shutdown(m_hSocket, SD_BOTH);
    closesocket(m_hSocket);
    m_hSocket = INVALID_SOCKET;
    
    m_bConnected = FALSE;
    return TRUE;
}

階段性總結

目前控件已具備:

  • ✅ Socket 環境初始化/清理(DLL 生命週期)
  • ✅ Connect(IP, Port) / DisConnect() 核心介面
  • ✅ FireRecvSockEvent() / FireCloseWinsock() 非同步事件
  • ✅ WSAAsyncSelect 視窗訊息驅動模式
  • ✅ 域名/IP 雙模式解析、錯誤碼回傳

下一篇預告:高級功能實現

  1. SendData(Data, DataType, Length, Timeout) —— 多型別發送、網路位序、超時控制
  2. GetData(Data, DataType, MaxLength, Timeout) —— 多型別接收、SAFEARRAY 填充
  3. VARIANT/SAFEARRAY 進階操作模式
  4. VC++ 6.0 / VB 6.0 完整客戶端調用範例

完整源碼下載:www.vcfans.cn | 非商業用途請保留版權

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