|

MARCH, 1996

![[Help Magician]](/vb-mag/images/banhelma.gif)

NETWORK.BAS:
This file contains all of the WINSOCK call definitions and various supprting routines
needed to utilize them from within VB. The biggest hurdle to using TCP/IP is resolving hostnames. The
GETHOSTBYNAME function performs name resolution but does not pass back a simple value. Instead,
it returns the address of a structure that it has built in a scratch area of memory. You must copy the data
you need from this structure into your own variables before making another winsock call or you risk it
being lost. The structure that the return value points to has the format:
[address of primary name] 4-byte pointer to the full, primary hostname [address of aliases] 4-byte pointer [??] [address of resolutions array] 4-byte pointerThe last field is the one that is most often needed. It is a pointer to an array in memory which has the following format:
[address of resolved IP] 4-byte pointer [address of resolved IP] 4-byte pointerFor most hosts the list will only have a single pointer followed by the null entry which signals the end of the list. Systems that have multiple interfaces may have multiple IP addresses assigned and in this case the name resolution will return all of them. In general, few routines process anything but the first returned IP and the code I use does not try any except the first. What we have, therefore, is a pointer from GETHOSTBYNAME which gets us to a pointer to an array of pointers to the IP address that we really want. Since VB does not offer any way to easily handle memory pointers, we need to slip in a couple of API calls to extract the info we need. This can be done, carefully, with LSTRCPY which normally copies strings in memory but has a secondary function of returning the memory address for a variable if the same item is passed for both the source and the destination, coupled with HMEMCPY which copies data from one memory address to another. The RESOLVE_NAME routine given here traverses the various pointers to return the first IP address to a 4-byte string.[0] 4-bytes, value zero
All TCP/IP I/O is done over ‘sockets’ which are similar to channel numbers for normal disk I/O in many ways. In both cases you must allocate one (FREEFILE for disk I/O, SOCKET for TCP/IP), open the channel (OPEN vs CONNECT/ACCEPT), perform I/O (READ/WRITE/GET/PUT vs SEND/RECV) and close the channel when done (CLOSE vs CLOSESOCKET). In order to accept a connection from a remote host you need to first define a socket and specify the TCP/IP port number that you want to monitor. Port numbers run from 1 to 4095 (zero is used to indicate ‘any available’ sometimes). The lower numbers are reserved for specific uses. Some of the most common ones are:
21 FTP server 70 Gopher server 23 TELNET server 80 WWW serverTo avoid potential conflicts your should design your own servers to use ports in the upper numbers but you can use any port that is not currently in use on the local host. Conversely, to connect to a remote system you need both the IP address and port number for the remote task. The IP address can be found by using a name resolution lookup but the port number must generally be configured. In most cases you are connecting to (1) a standard service like FTP which has a pre-defined port number or (2) your own server process for which you configured the port or have provided an alternate way to find it. In some cases being unable to identify the correct port is an added security measure as in a company with a public FTP server on port 21 and a private one on another port.
NETWORK.BAS
contents:
Option Explicit Type sockaddr_vb ‘ Standard TCP/IP uses sockaddr_in sin_family As Integer ‘ address family: only AF_INET (TCP/IP INTERNET) is valid sin_port As Integer ‘ TCP/IP port number sin_addr As String * 4 ‘ IP address: sockaddr_in uses a 4-byte numeric field sin_zero As String * 8 ‘ unused - null fill: sockaddr_in uses an array of 8 1-byte fields End Type Type wsadata_type ‘ WINSOCK implementation parameters wVersion As Integer ‘ this data is all returned by the WSAstartup call wHighVersion As Integer szDescription As String * 257 szSystemStatus As String * 129 iMaxSockets As Integer iMaxUdpDg As Integer lpVendorInfo As String * 200 End Type Global wsadata As wsadata_type ‘ reserve memory to hold data Rem Socket function prototypesNote:
Declare Function socket Lib "winsock.dll" (ByVal afinet As Integer, ByVal socktype As Integer, ByVal protocol As Integer) As Integer Declare Function bindvb Lib "winsock.dll" Alias "bind" (ByVal s As Integer, addr As sockaddr_vb, ByVal namelen As Integer) As Integer Declare Function connectvb Lib "winsock.dll" Alias "connect" (ByVal sock As Integer, sockstruct As sockaddr_vb, ByVal structlen As Integer) As Integer Declare Function send Lib "winsock.dll" (ByVal sock As Integer, ByVal msg As String, ByVal msglen As Integer, ByVal flag As Integer) As Integer Declare Function recv Lib "winsock.dll" (ByVal sock As Integer, ByVal msg As String, ByVal msglen As Integer, ByVal flag As Integer) As Integer Declare Function htonl Lib "winsock.dll" (ByVal a As Long) As Long Declare Function ntohl Lib "winsock.dll" (ByVal a As Long) As Long Declare Function htons Lib "winsock.dll" (ByVal a As Integer) As Integer Declare Function ntohs Lib "winsock.dll" (ByVal a As Integer) As Integer Declare Function gethostbyname Lib "winsock.dll" (ByVal hn As String) As Long Declare Function WSAgetlasterror Lib "winsock.dll" () As Long Declare Function gethostname Lib "winsock.dll" (ByVal hn As String, ByVal nbytes As Integer) As Long Declare Function closesocket Lib "winsock.dll" (ByVal sn As Integer) As Integer Declare Function getsocknamevb Lib "winsock.dll" Alias "getsockname" (ByVal sn As Integer, saddr As sockaddr_vb, salen As Integer) As Integer Declare Function listen Lib "winsock.dll" (ByVal sn As Integer, ByVal blog As Integer) As Integer Declare Function acceptvb Lib "winsock.dll" Alias "accept" (ByVal sn As Integer, saddr As sockaddr_vb, namelen As Integer) As Integer Rem Microsoft Windows Extension function prototypes Declare Function WSAstartup Lib "winsock.dll" (ByVal a As Integer, b As wsadata_type) As Integer Declare Function WSAcleanup Lib "winsock.dll" () As Integer ‘ Functions needed to manipulate memory directly when resolving hostnames Declare Function lstrcpy Lib "Kernel" (x1 As Any, x2 As Any) As Long ' used to get addresses Declare Sub hmemcpy Lib "Kernel" (dst As Long, src As Long, ByVal nbytes As Long) Rem WINSOCK constants Global Const SOCK_STREAM = 1 Global Const AF_INET = 2 Global Const MSG_PEEK = 2 ' Windows Sockets definitions of regular Microsoft C error constants Global Const WSAEINTR = 10004 Global Const WSAEBADF = 10009 Global Const WSAEACCES = 10013 Global Const WSAEFAULT = 10014 Global Const WSAEINVAL = 10022 Global Const WSAEMFILE = 10024 ' Windows Sockets definitions of regular Berkeley error constants Global Const WSAEWOULDBLOCK = 10035 Global Const WSAEINPROGRESS = 10036 Global Const WSAEALREADY = 10037 Global Const WSAENOTSOCK = 10038 Global Const WSAEDESTADDRREQ = 10039 Global Const WSAEMSGSIZE = 10040 Global Const WSAEPROTOTYPE = 10041 Global Const WSAENOPROTOOPT = 10042 Global Const WSAEPROTONOSUPPORT = 10043 Global Const WSAESOCKTNOSUPPORT = 10044 Global Const WSAEOPNOTSUPP = 10045 Global Const WSAEPFNOSUPPORT = 10046 Global Const WSAEAFNOSUPPORT = 10047 Global Const WSAEADDRINUSE = 10048 Global Const WSAEADDRNOTAVAIL = 10049 Global Const WSAENETDOWN = 10050 Global Const WSAENETUNREACH = 10051 Global Const WSAENETRESET = 10052 Global Const WSAECONNABORTED = 10053 Global Const WSAECONNRESET = 10054 Global Const WSAENOBUFS = 10055 Global Const WSAEISCONN = 10056 Global Const WSAENOTCONN = 10057 Global Const WSAESHUTDOWN = 10058 Global Const WSAETOOMANYREFS = 10059 Global Const WSAETIMEDOUT = 10060 Global Const WSAECONNREFUSED = 10061 Global Const WSAELOOP = 10062 Global Const WSAENAMETOOLONG = 10063 Global Const WSAEHOSTDOWN = 10064 Global Const WSAEHOSTUNREACH = 10065 Global Const WSAENOTEMPTY = 10066 Global Const WSAEPROCLIM = 10067 Global Const WSAEUSERS = 10068 Global Const WSAEDQUOT = 10069 Global Const WSAESTALE = 10070 Global Const WSAEREMOTE = 10071 ' Extended Windows Sockets error constant definitions Global Const WSASYSNOTREADY = 10091 Global Const WSAVERNOTSUPPORTED = 10092 Global Const WSANOTINITIALISED = 10093 Global Const WSAHOST_NOT_FOUND = 11001 Global Const WSATRY_AGAIN = 11002 Global Const WSANO_RECOVERY = 11003 Global Const WSANO_DATA = 11004 Global Const WSANO_ADDRESS = 11004 Function initnet () As Long initnet = WSAstartup(257, wsadata) ‘ initialize network - zero is successful return End FunctionNote:
Function addressofinteger (x As Integer) As Long addressofinteger = lstrcpy(x, x) ‘ return memory address of integer variable End Function Function addressoflong (x As Long) As Long addressoflong = lstrcpy(x, x)’ return memory address of long variable End Function Function addressofstring (x As String) As Long addressofstring = lstrcpy(ByVal x, ByVal x)’ return memory address of string End Function Function resolve_name (inname As String, ipaddr As String) As Long ‘ input hostname and return 4-byte string with IP address in ipaddr variable ‘ return value zero is success, otherwise error code Dim lngRval ‘ return value from gethostbyname call Dim hn As String * 128 ' temporary hostname area Dim hostent As String ' work area for hostent structure Dim haddrlist As Long ' pointer to list of addresses Dim lngipptr As Long ‘ address of ip address Dim lngAddrHostent As Long ‘ address of hostent structure Dim lngAddrPointers As Long ‘ address of pointers Dim lngAddrhaddrlist As Long ‘ address of address list Dim lngAddripaddr As Long ‘ address of return string Dim lngAddripptr As Long ‘ address of ipptr hn = inname + Chr$(0) ' null-delimit hostname hostent = String$(16, 0) ' null-fill hostent work area (makes VB allocate space) ipaddr = String$(4, 0) ' null fill ip addr lngRval = gethostbyname(hn) ' call standard winsock routine lngAddrHostent = addressofstring(hostent) ‘ get memory address of workspace lngAddrPointers = lngAddrHostent + 12 ‘ calculate address of list of pointers lngAddrhaddrlist = addressoflong(haddrlist)’ get memory address haddrlist lngAddripaddr = addressofstring(ipaddr) ‘ get memory address of return string lngAddripptr = addressoflong(lngipptr) ‘ get memory address of ipptr If lngRval Then ‘ gethostbyname returned address of a hostent structure ' copy structure from winsock memory to local string Call hmemcpy(ByVal lngAddrHostent, ByVal lngRval, 16) ' copy address of list of pointers into haddrlist variable Call hmemcpy(ByVal lngAddrhaddrlist, ByVal lngAddrPointers, 4) ' copy value of first pointer into ipptr variable Call hmemcpy(ByVal lngAddripptr, ByVal haddrlist, 4) ' copy first IP address into return string Call hmemcpy(ByVal lngAddripaddr, ByVal lngipptr, 4) resolve_name = 0 ' success! Else ipaddr = String$(4, 0) ' failed, return null IP and error resolve_name = wsagetlasterror() End If End FunctionNow let’s look at the various functions:
INITNET
Function: Initialize network - must be done once by every task doing network functions Input: none Output: zero if successful, error otherwise Example: If initnet() Then MsgBox “Unable to initialize network” End End IfNote:
WSACLEANUP
Function: close down network access for task - should be called before termination Input: none Output: zero if successful, generally ignored Example: Sub Form_Unload() Dim X as Integer X=WSAcleanup() End Sub
SOCKET
Declare Function socket Lib "winsock.dll" (ByVal afinet As Integer, ByVal socktype As Integer, ByVal protocol As Integer) As Integer Function: allocates a TCP/IP socket Input: Protocol Family (must be AF_INET) Socket type: SOCK_STREAM reliable TCP/IP service SOCK_DGRAM unreliable datagram service SOCK_RAW TCP/IP control packets Protocol: use zero to default to standard protocol based on socket type. Output: socket number, zero or negative is error Example: Dim intSocket As Integer intSocket=Socket(AF_INET,SOCK_STREAM,0) If intSocket=0 then MsgBox “Unable to allocate socket”
BINDVB (BIND)
Declare Function bindvb Lib "winsock.dll" Alias "bind" (ByVal s As Integer, addr As sockaddr_vb, ByVal namelen As Integer) As Integer Function: bind socket to local port as setup for LISTEN call Input: Socket Socket number as assigned by SOCKET call Socket structure Specify IP address to accept connections from and/or TCP/IP port to accept connections on. Use zero for the IP to allow connections from any host and zero for the port to get the next available local port number. Structure length Bytecount for structure argument Output: zero if successful, otherwise error Example: Dim intSocket As Integer Dim saiSocket As Integer intSocket=Socket(AF_INET,SOCK_STREAM,0) saiSocket.sin_family=AF_INET saiSocket.sin_addr=String$(4,0) saiSocket.sin_port=0 saiSocket.sin_zero=String$(8,0) If BindVB(intSocket,saiSocket,Len(saiSocket)) Then MsgBox “Bind failed!”
GETHOSTNAME
Declare Function gethostname Lib "winsock.dll" (ByVal hn As String, ByVal nbytes As Integer) As Long Function: retrieves the local hostname Input: Hostname String to receive name (pre-allocate room with spaces) Buffer size Maximum bytes to return to Hostname Output: Null-delimited hostname Example: Dim strMyName As String Dim lngTrash As Long strMyName=Space$(32) lngTrash=GetHostName(strMyName,Len(strMyName)) HtoNs, HtoNl, NtoHs, NtoHl Declare Function htonl Lib "winsock.dll" (ByVal a As Long) As Long Declare Function ntohl Lib "winsock.dll" (ByVal a As Long) As Long Declare Function htons Lib "winsock.dll" (ByVal a As Integer) As Integer Declare Function ntohs Lib "winsock.dll" (ByVal a As Integer) As Integer Function: convert integer or long data types from host-format to network-format or from network-format to host-format. Input: Value Numeric value to be converted Output: Value Converted value Examples: saiSocket.sin_port=HtoNs(21) intPort=NtoHs(saiSocket.sin_port)TCP/IP structires and packets store all numeric data with the most significant byte first while many hosts, including MSDOS PCs, store values in memory with the least significant byte first. Using the Host-To-Network (Short or Long) and Network-To-Host (Short or Long) functions ensures that your code will work across platforms. In the first line of the example I assign 21 to the TCP/IP port. If I had done the assignement directly with a statement like “saiSocket.sin_port=21” it would have assigned the two bytes of the integer 21 in PC format (<21><0>) instead of TCP/IP format (<0><21>). The resulting port would have been 5376 (21 *256 + 0) instead of 21 and I’d start getting some strange behavior.
WSAGETLASTERROR
Declare Function WSAgetlasterror Lib "winsock.dll" () As Long Function: return the error code from the last WinSock call Input: none Output: Error code, or zero if no error occurred Example: If WSAGetLastError()=WSAECONNRESET Then MsgBox “Connection Reset by Remote System” End If
CONNECTVB (CONNECT)
Declare Function connectvb Lib "winsock.dll" Alias "connect" (ByVal sock As Integer, sockstruct As sockaddr_vb, ByVal structlen As Integer) As Integer Function: opens TCP/IP connection to remote process Input: Socket Socket number as returned by SOCKET call Socket structure Structure specifying remote IP and port Structure Size Byte size of structure Output: zero if successful, otherwise error Example: Function ConnectMeTo(intSocket As Integer,strHostName As String) As Long Dim saiSocket As Sockaddr_vb Dim strRemoteIP As String If Resolve_Name(“www.microsoft.com”,strRemoteIP) =0 Then saiSocket.sin_family=AF_INET saiSocket.sin_port=HtoNs(80) saiSocket.sin_zero=String$(8,0) saiSocket.sin_addr=strRemoteIP ConnectMeTo=ConnectVB(intSocket,saiSocket,Len(saiSocket)) Else ConnectMeTo=WSAGetLastError() End If Exit Function
GETSOCKNAMEVB (GETSOCKNAME)
Declare Function getsocknamevb Lib "winsock.dll" Alias "getsockname" (ByVal sn As Integer, saddr As sockaddr_vb, salen As Integer) As Integer Function: fill in socket structure with all current information for a bound or connected socket Input: Socket Socket number from SOCKET call Structure Socket data structure Length Size of structure Output: Socket data structure filled in, return value of zero if successful, otherwise error Example: Function GetIPFromSocket(intSocket As Integer) As String Dim saiSocket as Sockaddr_VB Dim intSize As Integer intSize=len(saiSocket) If GeSockNameVB(intSocket,saiSocket,intSize) Then GetIPFromSocket=“” Else GetIPFromSocket=saiSocket.sin_addr End If
SEND
Declare Function send Lib "winsock.dll" (ByVal sock As Integer, ByVal msg As String, ByVal msglen As Integer, ByVal flag As Integer) As Integer Function: transmit data to remote host Input: Socket Socket number as returned by SOCKET function Buffer Data to send Bytes Number of bytes to send Flag Special handling flag Output: Number of bytes sent Example: Function SendIt(intSocket As Integer, strBuff As String) As Long SendIt=Send(intSocket,strBuff,Len(strBuff),0) Exit Function Note: the special handling flag is normally zero for no special handling. There is a MSG_OOB flag which can be used to send ‘Out-Of-Band’ data for priority handling but this is rarely, if ever, needed.RECV
Declare Function recv Lib "winsock.dll" (ByVal sock As Integer, ByVal msg As String, ByVal msglen As Integer, ByVal flag As Integer) As Integer Function: receive data from socket Input: Socket Socket number from SOCKET call Buffer Receive buffer Bytes Maximum number of bytes to read Flag Special handling flag Output: number of bytes received Example: Function GetData(intSocket as Integer) As String Dim strBuff As String * 2048 Dim intBytes As Integer intBytes=Recv(intSocket,strBuff,Len(strBuff),0) if intBytes<1 then GetData=“” Else GetData=Left$(strBuff,intBytes) End If Note: The MSG_PEEK option can be used in the special handling flag to return a copy of the data waiting to be received without removing it from the TCP/IP buffer. Another call to RECV will get the same information.LISTEN
Declare Function listen Lib "winsock.dll" (ByVal sn As Integer, ByVal blog As Integer) As Integer Function: prepare socket to accept connections from remote hosts Input: Socket Socket number from SOCKET call Backlog Number of connection requests to queue before refusing Output: zero if successful, otherwise error Example: If Listen(intSocket,2) Then MsgBox “Unable to listen for connections”ACCEPTVB (ACCEPT)
Declare Function acceptvb Lib "winsock.dll" Alias "accept" (ByVal sn As Integer, saddr As sockaddr_vb, namelen As Integer) As Integer Function: accept connection from remote system Input: Socket Socket number from SOCKET call Structure Socket data structure Length Size of socket data structure Output: Socket number for new connection Example: Function AcceptRequest(intSocket As Integer) As Integer Dim intSize As Integer Dim saiSocket As Sockaddr_VB intSize=Len(saiSocket) AcceptRequest=AcceptVB(intSocket,saiSocket,intSize) Exit Function Note: the socket that is input to the Accept function must have been bound using the BIND call and been the target of a LISTEN call. The output socket is a completely new socket that has been connected to the remote system. The original socket is still usable for additional ACCEPT calls.CLOSESOCKET
Declare Function closesocket Lib "winsock.dll" (ByVal sn As Integer) As Integer Function: close a socket Input: Socket number Output: zero if successful, otherwise error Example: If CloseSocket(intSocket) Then MsgBox “Error closing socket”
Now we can do a simple application to get a copy of Microsoft’s WWW Home Page:
Option Explicit Dim intSocket As Integer Sub Form_Load() Dim saiSocket As sockaddr_vb Dim intSize As Integer Dim strBuff As String Dim intChannel As Integer Dim strRemoteIP As String If initnet() Then MsgBox “Unable to initialize”:End intSocket=Socket(AF_INET,SOCK_STREAM,0) If intSocket<1 Then MsgBox “Socket call failed”:End If resolve_name(“www.microsoft.com”,strRemoteIP) Then MsgBox “Name Lookup error”:End saiSocket.sin_family=AF_INET saiSocket.sin_addr=strRemoteIP saiSocket.sin_port=htons(80) saiSocket.sin_zero=String$(8,0) If connectvb(intSocket,saiSocket,Len(saiSocket)) Then MsgBox “Connect failure”:End strBuff=“GET /” & Chr$(13) & Chr$(10) ‘ WWW syntax for getting top-level If send(intSocket,strBuff,Len(strBuff),0)<>Len(strBuff) Then MsgBox “Send failure”:End intChannel=FreeFile Open “MSHOME.HTM” For Output As #intChannel strBuff=String$(2048,0) Do While True intSize=recv(intSocket,strBuff,Len(strBuff),0) If IntSize<1 Then Exit Do Print #intChannel,Left$(strBuff,intSize); Loop intSize=LOF(intChannel) Close #intChannel If CloseSocket(intSocket) Then MsgBox “Socket close error” MsgBox “Received “ & Format$(intSize) & “ bytes” End End Sub Sub Form_Unload(Cancel As Integer) Dim x As Integer If intSocket>0 then x=CloseSocket(intSocket) x=WSACleanUp() End SubThe following “server” accepts connections and returns the current date and time (warning: it has no built-in way out!):
Option Explicit Dim intSocket As Integer Sub Form_Load() Dim saiSocket As sockaddr_vb Dim intSize As Integer Dim strBuff As String Dim intChannel As Integer Dim intNewSocket As Integer If initnet() Then MsgBox “Unable to initialize”:End intSocket=Socket(AF_INET,SOCK_STREAM,0) If intSocket<1 Then MsgBox “Socket call failed”:End saiSocket.sin_family=AF_INET saiSocket.sin_addr=String$(4,0) ‘ accept from any host saiSocket.sin_port=htons(2121) ‘ wait for connections to port 2121 saiSocket.sin_zero=String$(8,0) If bindvb(intSocket,saiSocket,Len(saiSocket)) Then MsgBox “Bind failure”:End if listen(intSocket,5) Then MsgBox “Listen failure”:End Do While True intSize=Len(saiSocket) intNewSocket=acceptvb(intSocket,saiSocket,intSize) if intNewSocket<1 Then MsgBox “Accept failed”:End strBuff=format$(now,”yymmddhhmmss”) If send(intNewSocket,strBuff,Len(strBuff),0)<>Len(strBuff) Then MsgBox “Send failure”:End If CloseSocket(intNewSocket) Then MsgBox “Error closing new socket” Loop If CloseSocket(intSocket) Then MsgBox “Socket close error” End End Sub Sub Form_Unload(Cancel As Integer) Dim x As Integer If intSocket>0 then x=CloseSocket(intSocket) x=WSACleanUp() End SubThe last topic that needs to be mentioned is the use of async socket calls. All of the preceeding information has dealt strictly with synchronous operation -- when you issue an ACCEPT call your task will hang until a connection request is received. When you use the RECV call your task will hang until data is received or the remote task closes the socket. For most client applications these are not significant drawbacks. I have written FTP, gopher and WWW clients which perform fine using only the synchronous versions of the calls. For server applications, however, it is hardly practical since you can only service one client at a time.
Here we run into another limitation of Visual Basic. In order to be able to process accept and recv calls asynchronously we need to be able to either (1) check first to see if a connection request or data is waiting or (2) be notified when one of these events occurs. Both of these methods are theoretically possible using standard WinSock calls and both are difficult from Visual Basic.
Method 1:
Checking to see if any requests or data are ready
One of the standard calls is IOCTLSOCKET (I/O Control for Socket) which has an FIONBIO (File I/O:
Non-blocking I/O) parameter. By using this call the selected socket is set for non-blocking mode so that
an ACCEPT or RECV request will fail with an error if it can not return immediately with valid data. I’ve
used this call quite successfully in C under other operating systems but have yet to find a way to get it to
work on a PC with WinSock. The definitions all seem to be there, but every variation I try returns an
error on the IOCTLSocket call.
Method 2:
Receiving notifications when a socket is active
For this we need to add another function definition:
WSAASYNCSELECT Declare Function WSAAsyncSelect Lib "winsock.dll" (ByVal s As Integer, ByVal hwnd As Integer, ByVal wmsg As Integer, ByVal flgs As Long) As Integer Function: puts a socket in asynch mode Input: Socket Socket number from SOCKET call Hwnd HWND property for form or control to receive message Message Type of message sent Flags Conditions on which to send message Output: zero if successful, otherwise error Example: If WSAAsyncSelect(intSocket,txtSocket.hwnd,WM_LBUTTONDOWN,63) Then MsgBox “Unable to set Asynch mode”In the above example “txtSocket” is the name of a text box on the current form. The Visible property for this box is set to False so that I know the user can not see it or affect it in any way. The message value is also defined in my full NETWORK.BAS file as:
Global Const WM_LBUTTONDOWN = &H201The Flags value is a summation of values for various conditions that can trigger activity for a socket including data received, connection request received, socket closed, etc. I generally lump them all together because only one or two conditions are possible for a given socket at any given time so when I get the message it is usually easy to handle whatever caused it. For example, if I expect to receive data but the sender closes the socket instead I will get an error return from the RECV call that I try and that is less work than setting up different messages for different activities. When I have two or more sockets that need to be handled asynchronously I define a text box for each, always invisible, and use the same message code. To save on controls you could also use the same control but a unique message for each socket (e.g. WM_RBUTTONDOWN or WM_LBUTTONUP, etc). When socket activity is triggered the WinSock routines send the given message to the specified control. In the example given, the txtSocket_MouseDown event is triggered with BUTTON set to 1 (Left). The MouseDown event effectively becomes a SocketActive event and my code handles the activity as it happens. By the way, I use text boxes only because they were the first control I tried that had a HWND property. It is possible to obtain the HWND for a label, but that adds extra API calls and obfuscates things even more.
Click here to go back to the March '96 Article Index
|