標準MFC WinSock ActiveX控制項開發執行個體

來源:互聯網
上載者:User

 

標準MFC WinSock ActiveX控制項開發執行個體

作者:小輝

下載原始碼

摘要:本文主要介紹如何開發一個ActiveX控制項,提供介面,與相應事件掛鈎。文中涉及到VARIANT,SAFEARRAY,BSTR的詳細使用方法。
另外還提供了WinSock的詳細開發步驟,以及如何響應網路逾時,網路斷開的事件方法以及在VC,VB調用該控制項的方法。

關鍵字:ActiveX,Socket,VARIANT, SAFEARRAY,BSTR。

一、MFC ActiveX控制項開發步驟(VC 6.0):

  1. New->Projects->MFC ActiveX ControlWizard,然後輸入MFCWinSock工程名。如:


    圖一 建立工程

  2. 一路狂按Next,直至Finsh出現,再按下OK,如:

   

圖二 建立完成

二、架設Socket環境:

  1. 首先在StdAfx.h檔案中加入下面這句代碼:

    #include // MFC socket extensions
  2. 開啟MFCWinSock.cpp檔案,添加代碼,看起來如下:
////////////////////////////////////////////////////////////////////////////// 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();}      

三,提供控制項介面和事件

  1. 在MFCWinSockCtl.cpp加入如下代碼:
    #ifndef WM_MYWINSOCK #define WM_MYWINSOCK WM_USER+1888#endif      
  2. View->ClassWizard->Automation->Add Method…如:


    圖三 建立介面
    這個時候,我們為這個控制項添加了一個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()響應網路有資料到達的事件。建立方法如:


    圖四 建立事件

  5. 重載控制項訊息處理函數WindowProc(),在View->ClassWizard中開啟類嚮導,在訊息映射中找到WindowProc,如:

    圖五 重載WindowProc()

四、編寫代碼

  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環境下的使用方法。

聲明:

  1. 部分資料來源於網路,本文所用的所有原始碼僅供非商業用途,並請保留原著作權,否則後果自負!
  2. 歡迎大家拍磚,或指正不足的地方,一起探導更好的方法。
  3. 歡迎訪問www.vcfans.cn,感謝您的支援!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.