标准MFC WinSock ActiveX控件开发实例Word格式.docx
《标准MFC WinSock ActiveX控件开发实例Word格式.docx》由会员分享,可在线阅读,更多相关《标准MFC WinSock ActiveX控件开发实例Word格式.docx(13页珍藏版)》请在冰豆网上搜索。
if(bInit)
{
//TODO:
Addyourownmoduleinitializationcodehere.
if(!
AfxSocketInit())
{
AfxMessageBox("
无法初始化Socket,请检查!
"
);
returnFALSE;
}
WSADATAwsaData;
WORDwVersion=MAKEWORD(1,1);
//设定为Winsock1.1版
interrCode;
errCode=WSAStartup(wVersion,&
wsaData);
//启动Socket服务
if(errCode)
无法找到可以使用的WSOCK32.DLL"
}
returnbInit;
}
ExitInstance-DLLtermination
intCMFCWinSockApp:
ExitInstance()
//TODO:
Addyourownmoduleterminationcodehere.
WSACleanup();
//结束网络服务
returnCOleControlModule:
ExitInstance();
三,提供控件接口和事件
1.在MFCWinSockCtl.cpp加入如下代码:
2.#ifndefWM_MYWINSOCK
3.#defineWM_MYWINSOCKWM_USER+1888
4.#endif
5.View->
ClassWizard->
Automation->
AddMethod…如下图:
图三创建接口
这个时候,我们为这个控件添加了一个Connect()的接口,出于通用性,安全性和扩展性的考虑,我们采用了VARIANT类型的参数,
很多人可能都不太了解该类型,又或者有接触过,但被吓怕了,那么我们来看清它的本来面目:
6.structtagVARIANT
7.{
8.union
9.{
10.struct__tagVARIANT
11.{
12.VARTYPEvt;
13.WORDwReserved1;
14.WORDwReserved2;
15.WORDwReserved3;
16.union
17.{
18.LONGlVal;
19.BYTEbVal;
20.SHORTiVal;
21.FLOATfltVal;
22.DOUBLEdblVal;
23.VARIANT_BOOLboolVal;
24._VARIANT_BOOLbool;
25.SCODEscode;
26.CYcyVal;
27.DATEdate;
28.BSTRbstrVal;
29.IUnknown__RPC_FAR*punkVal;
30.IDispatch__RPC_FAR*pdispVal;
31.SAFEARRAY__RPC_FAR*parray;
32.BYTE__RPC_FAR*pbVal;
33.SHORT__RPC_FAR*piVal;
34.LONG__RPC_FAR*plVal;
35.FLOAT__RPC_FAR*pfltVal;
36.DOUBLE__RPC_FAR*pdblVal;
37.VARIANT_BOOL__RPC_FAR*pboolVal;
38._VARIANT_BOOL__RPC_FAR*pbool;
39.SCODE__RPC_FAR*pscode;
40.CY__RPC_FAR*pcyVal;
41.DATE__RPC_FAR*pdate;
42.BSTR__RPC_FAR*pbstrVal;
43.IUnknown__RPC_FAR*__RPC_FAR*ppunkVal;
44.IDispatch__RPC_FAR*__RPC_FAR*ppdispVal;
45.SAFEARRAY__RPC_FAR*__RPC_FAR*pparray;
46.VARIANT__RPC_FAR*pvarVal;
47.PVOIDbyref;
48.CHARcVal;
49.USHORTuiVal;
50.ULONGulVal;
51.INTintVal;
52.UINTuintVal;
53.DECIMAL__RPC_FAR*pdecVal;
54.CHAR__RPC_FAR*pcVal;
55.USHORT__RPC_FAR*puiVal;
56.ULONG__RPC_FAR*pulVal;
57.INT__RPC_FAR*pintVal;
58.UINT__RPC_FAR*puintVal;
59.struct__tagBRECORD
60.{
61.PVOIDpvRecord;
62.IRecordInfo__RPC_FAR*pRecInfo;
63.}__VARIANT_NAME_4;
64.}__VARIANT_NAME_3;
65.}__VARIANT_NAME_2;
66.DECIMALdecVal;
67.}__VARIANT_NAME_1;
68.};
它先是一个结构体,里面有一个重要成员VARTYPEvt;
vt即是指明当前的数据类型,比如整型或者字符型,当指明vt后,
后面看到各种变量类型包括在一个联合体当中,也就是说指明vt后,你只能使用对应的其中之一变量类型。
看着这众多的各种不同
类型变量集中在一起,确实让人吓了一跳,但细细看来,大多数变量跟我们平时的用法相似。
值得一提的是SAFEARRAY__RPC_FAR*parray;
也许有很多人还没有接触过SAFEARRAY类型的变量,SAFEARRAY实际上也是一个结构,大家可以参考MSDN,我也将在后面介绍它的具体使用方法。
69.用同样的方法创建DisConnect()接口
70.创建两个事件,FireCloseWinsock()响应网络断开事件,FireRecvSockEvent()响应网络有数据到达的事件。
创建方法如下图:
图四创建事件
71.重载控件消息处理函数WindowProc(),在View->
ClassWizard中打开类向导,在消息映射中找到WindowProc,如下图:
图五重载WindowProc()
四、编写代码
1.编写VariantToLong()转换函数,该函数代码如下:
2.//类型转换,将VARIANT类型转换成Long类型
3.longCMFCWinSockCtrl:
VariantToLong(constVARIANT&
var)
4.{
5.longr;
6.switch(var.vt)
8.caseVT_UI2:
//USHORT
9.r=var.uiVal;
10.break;
11.caseVT_UI4:
//ULONG
12.r=var.ulVal;
13.break;
14.caseVT_INT:
//INT
15.r=var.intVal;
16.break;
17.caseVT_UINT:
//UINT
18.r=var.uintVal;
19.break;
20.caseVT_I4:
//LONG
21.r=var.lVal;
22.break;
23.caseVT_UI1:
//BYTE
24.r=var.bVal;
25.break;
26.caseVT_I2:
//SHORT
27.r=var.iVal;
28.break;
29.caseVT_R4:
//FLOAT
30.r=(long)var.fltVal;
31.break;
32.caseVT_R8:
//DOUBLE
33.r=(long)var.dblVal;
34.break;
35.default:
36.r=-1;
//无法转换该值
37.break;
38.}
39.returnr;
40.}
41.
大家可以看到,该函数将最基本的若干中数据类型转换成了long类型,但VARIANT决不是个简单的谱,我将在后面继续揭开它的神秘面纱.
42.编写我们刚才的接口Connect(),代码代码如下:
在MFCWinSockCtrl.h中加入
43.SOCKETOnlySock;
//建立的唯一Socket,不允许重复建立多个
44.boolisOnlyConnect;
//是否建立了连接
然后再编写Connect(),看起来如下:
BOOLCMFCWinSockCtrl:
Connect(constVARIANTFAR&
RemoteHost,constVARIANTFAR&
RemotePort)
Addyourdispatchhandlercodehere
if(isOnlyConnect)//该连接已建立,还没有断开
returnFALSE;
CStringIPAddress;
intPort;
//转换成整型的端口
switch(RemoteHost.vt)
caseVT_BSTR:
//字符串型
IPAddress=CString(RemoteHost.bstrVal);
break;
caseVT_BYREF|VT_I1:
//CHAR*
IPAddress.Format("
%s"
RemoteHost.pcVal);
//RemoteHost.pbstrVal);
default:
IPAddress="
;
Port=VariantToLong(RemotePort);
//我们编写的一个VARIANT转换成long类型的函数
if(Port<
=0)
_TCHAR*ip=0;
structhostent*host=0;
structsockaddr_inaddr;
ULONGdotIP=inet_addr(IPAddress);
OnlySock=socket(AF_INET,SOCK_STREAM,0);
//判断是否为点IP地址格式
if(OnlySock==INVALID_SOCKET)
shutdown(OnlySock,0x02);
closesocket(OnlySock);
//释放占有的SOCK资源
memset(&
addr,0,sizeof(structsockaddr_in));
//设定SOCKADDR_IN结构的内容
//如果通讯协议是选择IPProtocol,那此值固定为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)
};
ip=inet_ntoa(*(structin_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);
returnFALSE;
intiError=WSAAsyncSelect(OnlySock,m_hWnd,WM_MYWINSOCK,FD_READ|FD_CLOSE);
//只对网络断开和数据到达通知感兴趣
if(iError==SOCKET_ERROR)//无法绑定Winsock的事件通知
{
}
isOnlyConnect=true;
returnTRUE;
有必要提一下WSAAsyncSelect(),这里接收网络数据到达和断开的两个消息,我们收到WM_MYWINSOCK消息时将处理该消息并作为事件传送给调用者.
第二个参数,窗口句柄,我们传送了m_hWnd,这是因为MFCActiveX也属于一个窗口,并且是可见的,因此可以成功。
45.编写WindowProc(),代码看起来如下:
46.LRESULTCMFCWinSockCtrl:
WindowProc(UINTmessage,WPARAMwParam,LPARAMlParam)
47.{
48.//TODO:
Addyourspecializedcodehereand/orcallthebaseclass
49.switch(message)
50.{
51.caseWM_MYWINSOCK:
//响应自定义的消息
52.switch(WSAGETSELECTEVENT(lParam))
53.{
54.caseFD_READ:
//有新数据到达
55.FireRecvSockEvent();
56.break;
57.caseFD_CLOSE:
//对方已断掉当前连接
58.FireCloseWinsock();
59.break;
60.}
61.break;
62.default:
63.break;
64.}
65.returnCOleControl:
WindowProc(message,wParam,lParam);
66.}
本部分结束语:
好了,现在一个可以运行的控件已经完成,里面提供有Connect()和DisConnect()接口,和RecvSockEvent()及CloseWinsock()事件。
以及WinSock的使用方法。
在下一部分(高级篇)将讲解两个重要接口SendData()和GetData(),下期内容如下:
1.longSendData(constVARIANTFAR&
Data,constVARIANTFAR&
DataType,constVARIANTFAR&
DataLength,constVARIANTFAR&
TimeOut)
2.longGetData(VARIANTFAR*Data,constVARIANTFAR&
DataType,constVARIANTFAR&
DataMaxLength,constVARIANTFAR&
3.VARIANT和SAFEARRAY的复杂用法。
4.控件开发出来后在VC和VB环境下的使用方法。