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

圖2:專案結構生成完成
#include // MFC Socket 擴展類別
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) → 返回 BOOLDisConnect() → 返回 BOOLAdd Event 添加:
FireCloseWinsock() —— 對方斷線通知FireRecvSockEvent() —— 數據到達通知
圖4:添加 FireCloseWinsock / FireRecvSockEvent
ClassWizard → Message Maps → 選擇 CMFCWinSockCtrl → 添加 WindowProc 處理

圖5:重載 WindowProc 處理 WM_MYWINSOCK
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;
}
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;
}
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);
}
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 標準陣列,含維度、界限、元素型別、資料指標
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;
}
目前控件已具備:
Connect(IP, Port) / DisConnect() 核心介面FireRecvSockEvent() / FireCloseWinsock() 非同步事件WSAAsyncSelect 視窗訊息驅動模式SendData(Data, DataType, Length, Timeout) —— 多型別發送、網路位序、超時控制GetData(Data, DataType, MaxLength, Timeout) —— 多型別接收、SAFEARRAY 填充VARIANT/SAFEARRAY 進階操作模式完整源碼下載:www.vcfans.cn | 非商業用途請保留版權