標準MFC WinSock ActiveX控件開發實例(基礎篇)

摘要:本文主要介紹如何開發一個ActiveX控件,提供接口,與相應事件掛鉤。文中涉及到VARIANT,SAFEARRAY,BSTR的詳細使用方法。
另外還提供了WinSock的詳細開發步驟,以及如何響應網絡超時,網絡斷開的事件方法以及在VC,VB調用該控件的方法。
關鍵字:ActiveX,Socket,VARIANT, SAFEARRAY,BSTR。

1、MFC ActiveX控件開發步驟(VC 6.0):
安全

  1. New->Projects->MFC ActiveX ControlWizard,而後輸入MFCWinSock工程名。以下圖:

    網絡

    標準MFC WinSock ActiveX控件開發實例(基礎篇) - 極限ぷ英豪 - 煙雨樓


    圖一 建立工程

    socket

  2. 一路狂按Next,直至Finsh出現,再按下OK,以下圖:
    函數

   
標準MFC WinSock ActiveX控件開發實例(基礎篇) - 極限ぷ英豪 - 煙雨樓
圖二 建立完成

2、架設Socket環境:
ui

  1. 首先在StdAfx.h文件中加入下面這句代碼: #include <afxsock.h>// MFC socket extensions 
    spa

  2. 打開MFCWinSock.cpp文件,添加代碼,看起來以下:code

////////////////////////////////////////////////////////////////////////////// CMFCWinSockApp::InitInstance - DLL initializationBOOL CMFCWinSockApp::InitInstance(){BOOL bInit = COleControlModule::InitInstance();if (bInit){// TODO: Add your own module initialization code here.if (!AfxSocketInit()){AfxMessageBox("沒法初始化Socket,請檢查!");return FALSE;}WSADATA wsaData;WORD wVersion = MAKEWORD(1, 1);//設定爲Winsock 1.1版int errCode;errCode = WSAStartup(wVersion, &wsaData);//啓動Socket服務if (errCode){AfxMessageBox("沒法找到可使用的 WSOCK32.DLL");return FALSE;}}return bInit;}////////////////////////////////////////////////////////////////////////////// CMFCWinSockApp::ExitInstance - DLL terminationint CMFCWinSockApp::ExitInstance(){// TODO: Add your own module termination code here.WSACleanup();//結束網絡服務return COleControlModule::ExitInstance();}      
三,提供控件接口和事件
orm

  1. 在MFCWinSockCtl.cpp加入以下代碼:
    #ifndef WM_MYWINSOCK #define WM_MYWINSOCK WM_USER+1888#endif      
    接口

  2. View->ClassWizard->Automation->Add Method…以下圖:

    事件

    標準MFC WinSock ActiveX控件開發實例(基礎篇) - 極限ぷ英豪 - 煙雨樓


    圖三 建立接口
    這個時候,咱們爲這個控件添加了一個Connect()的接口,出於通用性,安全性和擴展性的考慮,咱們採用了VARIANT類型的參數,
    不少人可能都不太瞭解該類型,又或者有接觸過,但被嚇怕了,那麼咱們來看清它的原本面目:
    struct  tagVARIANT    {    union         {        struct  __tagVARIANT            {            VARTYPE vt;            WORD wReserved1;            WORD wReserved2;            WORD wReserved3;            union                 {                LONG lVal;                BYTE bVal;                SHORT iVal;                FLOAT fltVal;                DOUBLE dblVal;                VARIANT_BOOL boolVal;                _VARIANT_BOOL bool;                SCODE scode;                CY cyVal;                DATE date;                BSTR bstrVal;                IUnknown __RPC_FAR *punkVal;                IDispatch __RPC_FAR *pdispVal;                SAFEARRAY __RPC_FAR *parray;                BYTE __RPC_FAR *pbVal;                SHORT __RPC_FAR *piVal;                LONG __RPC_FAR *plVal;                FLOAT __RPC_FAR *pfltVal;                DOUBLE __RPC_FAR *pdblVal;                VARIANT_BOOL __RPC_FAR *pboolVal;                _VARIANT_BOOL __RPC_FAR *pbool;                SCODE __RPC_FAR *pscode;                CY __RPC_FAR *pcyVal;                DATE __RPC_FAR *pdate;                BSTR __RPC_FAR *pbstrVal;                IUnknown __RPC_FAR *__RPC_FAR *ppunkVal;                IDispatch __RPC_FAR *__RPC_FAR *ppdispVal;                SAFEARRAY __RPC_FAR *__RPC_FAR *pparray;                VARIANT __RPC_FAR *pvarVal;                PVOID byref;                CHAR cVal;                USHORT uiVal;                ULONG ulVal;                INT intVal;                UINT uintVal;                DECIMAL __RPC_FAR *pdecVal;                CHAR __RPC_FAR *pcVal;                USHORT __RPC_FAR *puiVal;                ULONG __RPC_FAR *pulVal;                INT __RPC_FAR *pintVal;                UINT __RPC_FAR *puintVal;                struct  __tagBRECORD                    {                    PVOID pvRecord;                    IRecordInfo __RPC_FAR *pRecInfo;                    }__VARIANT_NAME_4;                }__VARIANT_NAME_3;            }__VARIANT_NAME_2;        DECIMAL decVal;        }__VARIANT_NAME_1;    };      
    它先是一個結構體,裏面有一個重要成員VARTYPE vt;vt便是指明當前的數據類型,好比整型或者字符型,當指明vt後,
    後面看到各類變量類型包括在一個聯合體當中,也就是說指明vt後,你只能使用對應的其中之一變量類型。看着這衆多的各類不一樣
    類型變量集中在一塊兒,確實讓人嚇了一跳,但細細看來,大多數變量跟咱們平時的用法類似。值得一提的是SAFEARRAY __RPC_FAR *parray;
    也許有不少人尚未接觸過SAFEARRAY類型的變量,SAFEARRAY實際上也是一個結構,你們能夠參考MSDN,我也將在後面介紹它的具體使用方法。

  3. 用一樣的方法建立DisConnect()接口

  4. 建立兩個事件,FireCloseWinsock()響應網絡斷開事件,FireRecvSockEvent()響應網絡有數據到達的事件。建立方法以下圖:

    標準MFC WinSock ActiveX控件開發實例(基礎篇) - 極限ぷ英豪 - 煙雨樓


    圖四 建立事件

  5. 重載控件消息處理函數WindowProc(),在View->ClassWizard中打開類嚮導,在消息映射中找到WindowProc,以下圖: 

    標準MFC WinSock ActiveX控件開發實例(基礎篇) - 極限ぷ英豪 - 煙雨樓


    圖五 重載WindowProc()

4、編寫代碼

  1. 編寫VariantToLong()轉換函數,該函數代碼以下: //類型轉換,將VARIANT類型轉換成Long類型long CMFCWinSockCtrl::VariantToLong(const VARIANT &var){long r;switch(var.vt){case VT_UI2://USHORTr = var.uiVal;break;case VT_UI4://ULONGr = var.ulVal;break;case VT_INT://INTr = var.intVal;break;case VT_UINT://UINTr = var.uintVal;break;case VT_I4://LONGr = var.lVal;break;case VT_UI1://BYTEr = var.bVal;break;case VT_I2://SHORTr = var.iVal;break;case VT_R4://FLOATr = (long)var.fltVal;break;case VT_R8://DOUBLEr = (long)var.dblVal;break;default:r = -1;//沒法轉換該值break;}return r;}      
    你們能夠看到,該函數將最基本的若干中數據類型轉換成了long類型,但VARIANT決不是個簡單的譜,我將在後面繼續揭開它的神祕面紗.

  2. 編寫咱們剛纔的接口Connect(),代碼代碼以下: 在MFCWinSockCtrl.h中加入 SOCKET OnlySock;//創建的惟一Socket,不容許重複創建多個bool isOnlyConnect;//是否創建了鏈接      
    而後再編寫Connect(),看起來以下: BOOL CMFCWinSockCtrl::Connect(const VARIANT FAR& RemoteHost, const VARIANT FAR& RemotePort) {// TODO: Add your dispatch handler code hereif(isOnlyConnect)//該鏈接已創建,尚未斷開return FALSE;CString IPAddress;int Port;//轉換成整型的端口switch(RemoteHost.vt){case VT_BSTR://字符串型IPAddress = CString(RemoteHost.bstrVal);break;case VT_BYREF|VT_I1://CHAR *IPAddress.Format("%s",RemoteHost.pcVal);//RemoteHost.pbstrVal);break;default:IPAddress = "";return FALSE;}Port = VariantToLong(RemotePort);//咱們編寫的一個VARIANT轉換成long類型的函數if(Port<=0)return FALSE;_TCHAR *ip = 0;struct hostent *host = 0;struct sockaddr_in addr;ULONG dotIP = inet_addr(IPAddress);OnlySock = socket(AF_INET, SOCK_STREAM, 0);// 判斷是否爲點IP地址格式    if (OnlySock == INVALID_SOCKET){shutdown(OnlySock, 0x02);closesocket(OnlySock);//釋放佔有的SOCK資源return FALSE;}    memset(&addr, 0, sizeof(struct sockaddr_in));    // 設定 SOCKADDR_IN 結構的內容    // 若是通信協議是選擇IP Protocol,那此值固定爲AF_INET    // AF_INET 與 PF_INET 這兩個常量值相同    addr.sin_family = AF_INET;    addr.sin_port = htons(Port);    addr.sin_addr.S_un.S_addr = dotIP;    if (dotIP == INADDR_NONE)    {        host = gethostbyname(IPAddress);        if (!host)        {shutdown(OnlySock, 0x02);closesocket(OnlySock);//釋放佔有的SOCK資源return FALSE;        };        ip = inet_ntoa(*(struct in_addr*)(*host->h_addr_list));        addr.sin_addr.S_un.S_addr = inet_addr(ip);    }    //開始連線    if (connect(OnlySock, (LPSOCKADDR)&addr, sizeof(SOCKADDR)))    {        shutdown(OnlySock, 0x02);closesocket(OnlySock);//釋放佔有的SOCK資源return FALSE;    }int   iError = WSAAsyncSelect(OnlySock, m_hWnd,WM_MYWINSOCK, FD_READ|FD_CLOSE); //只對網絡斷開和數據到達通知感興趣if(iError == SOCKET_ERROR)//沒法綁定Winsock的事件通知{   shutdown(OnlySock, 0x02);closesocket(OnlySock);//釋放佔有的SOCK資源return FALSE;}  isOnlyConnect = true;return TRUE;}      
    有必要提一下WSAAsyncSelect(),這裏接收網絡數據到達和斷開的兩個消息,咱們收到WM_MYWINSOCK消息時將處理該消息並做爲事件傳送給調用者.
    第二個參數,窗口句柄,咱們傳送了m_hWnd,這是由於MFC ActiveX也屬於一個窗口,而且是可見的,所以能夠成功。

  3. 編寫WindowProc(),代碼看起來以下: LRESULT CMFCWinSockCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) {// TODO: Add your specialized code here and/or call the base classswitch(message){case WM_MYWINSOCK://響應自定義的消息switch(WSAGETSELECTEVENT(lParam)){case FD_READ://有新數據到達FireRecvSockEvent();break;case FD_CLOSE://對方已斷掉當前鏈接FireCloseWinsock();break;}break;default:break;}return COleControl::WindowProc(message, wParam, lParam);}      

本部分結束語: 
好了,如今一個能夠運行的控件已經完成,裏面提供有Connect()和DisConnect()接口,和RecvSockEvent()及CloseWinsock()事件。以及WinSock的使用方法。
在下一部分(高級篇)將講解兩個重要接口SendData()和GetData(),下期內容以下: 

  1. long SendData(const VARIANT FAR& Data, const VARIANT FAR& DataType,const VARIANT FAR& DataLength, const VARIANT FAR& TimeOut) 

  2. long GetData(VARIANT FAR* Data, const VARIANT FAR& DataType, const VARIANT FAR& DataMaxLength, const VARIANT FAR& TimeOut) 

  3. VARIANT和SAFEARRAY的複雜用法。

  4. 控件開發出來後在VC和VB環境下的使用方法。

相關文章
相關標籤/搜索