建立控件全過程

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

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

  圖片看不清楚?請點擊這裏查看原圖(大圖)。socket

  圖一 建立工程函數

  一路狂按Next,直至Finsh出現,再按下OK,以下圖:ui

  圖二 建立完成spa

  2、架設Socket環境:code

  首先在StdAfx.h文件中加入下面這句代碼:orm

#include <afxsock.h>接口

// MFC socket extensions 事件

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

////////////////////////////////////////////////////////////////////////////

// CMFCWinSockApp::InitInstance - DLL initialization

 

BOOL 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 termination

 

int CMFCWinSockApp::ExitInstance()

{

 

// TODO: Add your own module termination code here.

 

WSACleanup();//結束網絡服務

 

return COleControlModule::ExitInstance();

}

   

  三,提供控件接口和事件

  在MFCWinSockCtl.cpp加入以下代碼:

#ifndef WM_MYWINSOCK 

#define WM_MYWINSOCK WM_USER+1888

#endif

   

  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,我也將在後面介紹它的具體使用方法。

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

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

  圖片看不清楚?請點擊這裏查看原圖(大圖)。

  圖四 建立事件

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

  圖片看不清楚?請點擊這裏查看原圖(大圖)。

  圖五 重載WindowProc()

  4、編寫代碼

  編寫VariantToLong()轉換函數,該函數代碼以下:

//類型轉換,將VARIANT類型轉換成Long類型

long CMFCWinSockCtrl::VariantToLong(const VARIANT &var)

{

 

long r;

 

switch(var.vt)

 

{

 

case VT_UI2://USHORT

 

 

r = var.uiVal;

 

 

break;

 

case VT_UI4://ULONG

 

 

r = var.ulVal;

 

 

break;

 

case VT_INT://INT

 

 

r = var.intVal;

 

 

break;

 

case VT_UINT://UINT

 

 

r = var.uintVal;

 

 

break;

 

case VT_I4://LONG

 

 

r = var.lVal;

 

 

break;

 

case VT_UI1://BYTE

 

 

r = var.bVal;

 

 

break;

 

case VT_I2://SHORT

 

 

r = var.iVal;

 

 

break;

 

case VT_R4://FLOAT

 

 

r = (long)var.fltVal;

 

 

break;

 

case VT_R8://DOUBLE

 

 

r = (long)var.dblVal;

 

 

break;

 

default:

 

 

r = -1;//沒法轉換該值

 

 

break;

 

}

 

return r;

}

 

   

你們能夠看到,該函數將最基本的若干中數據類型轉換成了long類型,但VARIANT決不是個簡單的譜,我將在後面繼續揭開它的神祕面紗.

  編寫咱們剛纔的接口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 here

 

if(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也屬於一個窗口,而且是可見的,所以能夠成功。

  編寫WindowProc(),代碼看起來以下:

雙擊代碼全選

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

LRESULT CMFCWinSockCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) 

{

 

// TODO: Add your specialized code here and/or call the base class

 

switch(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(),下期內容以下:

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

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

  VARIANT和SAFEARRAY的複雜用法。

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

  聲明:

  部分資料來源於網絡,本文所用的全部源代碼僅供非商業用途,並請保留原版權,不然後果自負!

  歡迎你們拍磚,或指正不足的地方,一塊兒探導更好的方法。

相關文章
相關標籤/搜索