Figure 2 MSJSock
MSJSOCK.CPP (excerpts)
// MSJSock.cpp : Implementation of CMSJSockApp and DLL registration.
#include "stdafx.h"
#include "MSJSock.h"
////////////////////////////////////////////////////////////
// CMSJSockApp::InitInstance - DLL initialization
BOOL CMSJSockApp::InitInstance()
{
BOOL bInit = COleControlModule::InitInstance();
// Normally we'd call WSAStartup, but AfxSocketInit
// wraps the call for us and takes care of some of the
// version checking automatically.
if (bInit)
{
if (!AfxSocketInit(&m_wsa))
{
AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
return FALSE;
}
}
return bInit;
}
////////////////////////////////////////////////////////////
// CMSJSockApp::ExitInstance - DLL termination
int CMSJSockApp::ExitInstance()
{
AfxSocketTerm();
return COleControlModule::ExitInstance();
}
MSJSOCK.H (excerpts)
////////////////////////////////////////////////////////////
// CMSJSockApp : See MSJSock.cpp for implementation.
class CMSJSockApp : public COleControlModule
{
public:
BOOL InitInstance();
int ExitInstance();
LPWSADATA Sock() { return &m_wsa; };
protected:
WSADATA m_wsa; // Windows socket info - filled on startup
};
MSJSOCKCtTL.CPP (excerpts)
// MSJSockCtl.cpp : Implementation of the CMSJSockCtrl OLE control class.
.
.
.
extern CMSJSockApp theApp;
LPBYTE pm_szBuf;
/////////////////////////////////////////////////////////////////////////////
// Message map
BEGIN_MESSAGE_MAP(CMSJSockCtrl, COleControl)
//{{AFX_MSG_MAP(CMSJSockCtrl)
// NOTE - ClassWizard will add and remove message map entries
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG_MAP
ON_MESSAGE(WSCB_GETHOST, OnGetHostCB)
ON_MESSAGE(WSCB_ASELECT, OnASelectCB)
ON_OLEVERB(AFX_IDS_VERB_EDIT, OnEdit)
ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// Dispatch map
BEGIN_DISPATCH_MAP(CMSJSockCtrl, COleControl)
//{{AFX_DISPATCH_MAP(CMSJSockCtrl)
DISP_PROPERTY_EX(CMSJSockCtrl, "Version", GetVersion, SetVersion, VT_I2)
DISP_PROPERTY_EX(CMSJSockCtrl,"SystemStatus",GetSystemStatus,SetSystemStatus,
VT_BSTR)
DISP_PROPERTY_EX(CMSJSockCtrl,"MaxSockets",GetMaxSockets,SetMaxSockets,VT_I2)
DISP_PROPERTY_EX(CMSJSockCtrl,"HiVersion",GetHiVersion,SetHiVersion,VT_I2)
DISP_PROPERTY_EX(CMSJSockCtrl,"Description",GetDescription,SetDescription,
VT_BSTR)
DISP_FUNCTION(CMSJSockCtrl, "CreateSocket", CreateSocket, VT_I4, VTS_NONE)
DISP_FUNCTION(CMSJSockCtrl,"gethostbyname",gethostbyname,VT_HANDLE,VTS_BSTR)
DISP_FUNCTION(CMSJSockCtrl, "getservbyname", getservbyname, VT_I2, VTS_BSTR)
DISP_FUNCTION(CMSJSockCtrl,"AsyncSelect",AsyncSelect,VT_I4, VTS_I4 VTS_I4)
DISP_FUNCTION(CMSJSockCtrl,"connect",connect,VT_HANDLE,VTS_I4 VTS_I2
VTS_HANDLE)
//}}AFX_DISPATCH_MAP
DISP_FUNCTION_ID(CMSJSockCtrl, "AboutBox", DISPID_ABOUTBOX, AboutBox,
VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()
/////////////////////////////////////////////////////////////////////////////
// Event map
BEGIN_EVENT_MAP(CMSJSockCtrl, COleControl)
//{{AFX_EVENT_MAP(CMSJSockCtrl)
EVENT_CUSTOM("Connect", FireConnect, VTS_HANDLE)
EVENT_CUSTOM("RecvData", FireRecvData, VTS_HANDLE VTS_BSTR)
EVENT_CUSTOM("SockError", FireSockError, VTS_HANDLE VTS_I2)
EVENT_CUSTOM("GotHost", FireGotHost, VTS_HANDLE)
//}}AFX_EVENT_MAP
END_EVENT_MAP()
.
.
.
/////////////////////////////////////////////////////////////////////////////
// Interface IDs
const IID BASED_CODE IID_DMSJSock =
{ 0x9e638961, 0x2c21, 0x11cf, { 0xa0, 0x18, 0x44, 0x45, 0x53, 0x54, 0, 0 } };
const IID BASED_CODE IID_DMSJSockEvents =
{ 0x9e638962, 0x2c21, 0x11cf, { 0xa0, 0x18, 0x44, 0x45, 0x53, 0x54, 0, 0 } };
/////////////////////////////////////////////////////////////////////////////
// Control type information
// Important: make sure OLEMISC_INVISIBLEATRUNTIME is not included.
// This would cause the control not to have an hWnd at runtime;
// we want one, but we want it hidden.
static const DWORD BASED_CODE _dwMSJSockOleMisc =
OLEMISC_ACTIVATEWHENVISIBLE |
OLEMISC_SETCLIENTSITEFIRST |
OLEMISC_INSIDEOUT |
OLEMISC_CANTLINKINSIDE |
OLEMISC_RECOMPOSEONRESIZE;
IMPLEMENT_OLECTLTYPE(CMSJSockCtrl, IDS_MSJSOCK, _dwMSJSockOleMisc)
.
.
.
/////////////////////////////////////////////////////////////////////////////
// CMSJSockCtrl::OnDraw - Drawing function
void CMSJSockCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
// Hide the control at runtime
if (m_hWnd != NULL)
ShowWindow(SW_HIDE);
else
{
// Draw something at design-time
pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
pdc->Ellipse(rcBounds);
}
}
.
.
.
/////////////////////////////////////////////////////////////////////////////
// CMSJSockCtrl message handlers
// Get the current Windows Sockets version from the info returned at startup
short CMSJSockCtrl::GetVersion()
{
return theApp.Sock()->wVersion;
}
void CMSJSockCtrl::SetVersion(short nNewValue)
{
// Read/only property, so do nothing.
}
// Get the current system status from the info returned at startup
BSTR CMSJSockCtrl::GetSystemStatus()
{
CString s;
s = theApp.Sock()->szSystemStatus;
return s.AllocSysString();
}
void CMSJSockCtrl::SetSystemStatus(LPCTSTR lpszNewValue)
{
// Read/only property, so do nothing.
}
// Get the maximum sockets allowed on this control
short CMSJSockCtrl::GetMaxSockets()
{
return theApp.Sock()->iMaxSockets;
}
void CMSJSockCtrl::SetMaxSockets(short nNewValue)
{
// Read/only property, so do nothing.
}
// Get the highest allowed version from the info returned at startup
short CMSJSockCtrl::GetHiVersion()
{
return theApp.Sock()->wHighVersion;
}
void CMSJSockCtrl::SetHiVersion(short nNewValue)
{
// Read/only property, so do nothing.
}
// Get the Windows Sockets description from the startup info
BSTR CMSJSockCtrl::GetDescription()
{
CString s;
s = theApp.Sock()->szDescription;
return s.AllocSysString();
}
void CMSJSockCtrl::SetDescription(LPCTSTR lpszNewValue)
{
// Read/only property, so do nothing.
}
// Create a socket - wrap the socket() API call
long CMSJSockCtrl::CreateSocket()
{
SOCKET hSock;
hSock = ::socket(AF_INET, SOCK_STREAM, 0);
if (hSock <= 0)
return -1;
else
return hSock;
}
// Convert a host string to its IP address
OLE_HANDLE CMSJSockCtrl::gethostbyname(LPCTSTR host)
{
pm_szBuf = (LPBYTE) GlobalAllocPtr(GHND, 2048);
HANDLE hTask = WSAAsyncGetHostByName(m_hWnd, WSCB_GETHOST, host,
(char FAR *) pm_szBuf, 2048);
return (OLE_HANDLE) hTask;
}
// Wait for the GetHostByName to return info. This maps WSCB_GETHOST
// into the OnGetHostCB message handler.
afx_msg LONG CMSJSockCtrl::OnGetHostCB(UINT wParam, LONG lParam)
{
HANDLE hTask = (HANDLE) wParam;
UINT wserr = HIWORD(lParam);
UINT uCode = LOWORD(lParam);
LPVOID lphe;
u_long addr;
hostent he;
sockaddr_in ca;
// If the call didn't return an error, call GotHost in the
// container program.
if (wserr == WSANOERROR)
{
lphe = (LPVOID) GlobalAllocPtr(GHND, sizeof(hostent));
memcpy(lphe, (LPVOID) pm_szBuf, sizeof(hostent));
FireGotHost((OLE_HANDLE) lphe);
return TRUE;
}
else // call SockError in the container
FireSockError((OLE_HANDLE) hTask, wserr);
return TRUE;
}
// Handle all messages requested by a WSAAsyncSelect call
afx_msg LONG CMSJSockCtrl::OnASelectCB(UINT wParam, LONG lParam)
{
HANDLE hTask = (HANDLE) wParam;
UINT wserr = HIWORD(lParam);
UINT uCode = LOWORD(lParam);
u_long addr;
hostent he;
sockaddr_in ca;
switch (uCode)
{
// If a connect message comes back, fire the Connect event
case FD_CONNECT:
if (wserr != WSANOERROR)
FireSockError((OLE_HANDLE) hTask, wserr);
else
{
FireConnect((OLE_HANDLE) hTask);
}
break;
// Not yet handling a close message
case FD_CLOSE:
break;
// When data is incoming, call the RecvData event
case FD_READ:
{
FireRecvData((OLE_HANDLE) hTask, NULL);
break;
}
// Not yet handling a ready to write message
case FD_WRITE:
break;
// Not yet handling an out of band data message
case FD_OOB:
break;
// Not yet handling an accept message
case FD_ACCEPT:
break;
default:
break;
}
return TRUE;
}
// Wrap the getservbyname function - returning a port ID when
// given a service like "finger." This only handles tcp now, not
// udp.
short CMSJSockCtrl::getservbyname(LPCTSTR service)
{
short port;
LPSERVENT servent;
servent = ::getservbyname(service, "tcp");
port = servent->s_port;
// Convert the port from network byte order to host
// machine byte order and return.
return ntohs(port);
}
// Wrap the WSAAsyncSelect call for the container's sake. This
// automatically routes incoming messages to this control's
// window.
long CMSJSockCtrl::AsyncSelect(long socket, long msg)
{
return WSAAsyncSelect(socket, m_hWnd, WSCB_ASELECT, msg);
}
// Wrap the functions needed to perform a connect to a port/host.
OLE_HANDLE CMSJSockCtrl::connect(long socket, short port, OLE_HANDLE phostent)
{
hostent he;
u_long addr;
sockaddr_in ca;
// Copy the hostent data handle from the container to a real
// hostent structure.
memcpy((LPVOID) &he, (LPVOID) phostent, sizeof(hostent));
// Get the IP address from the hostent.
addr = (*(u_long FAR *) he.h_addr_list[0]);
// Fill in the sockaddr_structure so we can connect to
// the specific port and IP address
ca.sin_family = AF_INET;
ca.sin_port = htons(port); // Convert from our format to net format
ca.sin_addr.s_addr = addr;
// Make the control watch for connections
WSAAsyncSelect(socket, m_hWnd, WSCB_ASELECT, FD_CONNECT);
// Call the API connect() function, returning immediately.
// The container will get a Connect event when the
// connection is complete.
return ::connect(socket, (LPSOCKADDR) &ca, sizeof(sockaddr_in));
}
Figure 3 FLIPPER
FLIPPER.FRM (procedures)
'-----------------------------------------------------
' Flipper.FRM
' This is the form definition for Flipper: The MSJ
' Finger program.
'-----------------------------------------------------
' When the "Finger" button is clicked, Flipper should
' perform three tasks: create a socket, get the finger
' service port, and get the remote host's IP address
' given its name.
Private Sub btnFinger_Click()
Dim rc
sock = MSJSockCtrl.CreateSocket
fingport = MSJSockCtrl.getservbyname("finger")
rc = MSJSockCtrl.gethostbyname(edtSite.Text)
End Sub
'-----------------------------------------------------
' When a user types in the site or userid edit boxes,
' the "Finger" button is enabled or disabled depending
' on whether both boxes have data.
Private Sub edtSite_Change()
If Len(edtUser) = 0 Or Len(edtSite) = 0 Then
btnFinger.Enabled = False
Else
btnFinger.Enabled = True
End If
End Sub
Private Sub edtUser_Change()
If Len(edtUser) = 0 Or Len(edtSite) = 0 Then
btnFinger.Enabled = False
Else
btnFinger.Enabled = True
End If
End Sub
'-----------------------------------------------------
' When the form is loaded, disable the Finger
' button until both userid and site are filled in.
Private Sub Form_Load()
btnFinger.Enabled = False
End Sub
'-----------------------------------------------------
' MSJSockCtrl_connect is an event called by the MSJSock
' OLE control when a successful connection has been made.
' If you want, you can track a particular task by its handle
' (given here as hTask)
Private Sub MSJSockCtrl_connect(ByVal hTask As Long)
Dim rcl As Long, rc As Integer
' TellMSJSockcntrltowatchfor"readytoread,""readyformorewriting,"
' and "connection closed" events.
rcl = MSJSockCtrl.AsyncSelect(sock, FD_READ + FD_WRITE + FD_CLOSE)
' send() is a direct WINSOCK API call. It sends the indicated
' text to the socket specified. In this case, we're just sending
' a username to the finger port...
rc = send(sock, edtUser.Text, Len(edtUser.Text), 0)
' ...and appending a CR-LF pair.
rc = send(sock, Chr$(13) & Chr$(10), 2, 0)
End Sub
'-----------------------------------------------------
' When we called gethostbyname when the Finger button is
' clicked, it had to go off and find the host's IP address. When
' the OLE control gets that data back, it returns it by calling
' the GotHost event.
Private Sub MSJSockCtrl_GotHost(ByVal hostent As Long)
' Now that we have a pointer to the host's address (in hostent),
' we can take the socket we created earlier and connect it to
' the appropriate host and its finger port.
Call MSJSockCtrl.Connect(sock, fingport, hostent)
End Sub
'-----------------------------------------------------
' Whenever our TCP/IP stack receives data from the network,
' it notifies the MSJSock control. In turn, if the control is
' expecting some data, it informs the VB container program
' that data needs to be read by calling the RecvData event.
Private Sub MSJSockCtrl_RecvData(ByVal hTask As Long, ByVal Data As String)
Dim msg As String
Dim rc As String
' Initialize the variable-length string with 1024 blanks - enough room to
' read in data.
msg = Space(1024)
' recv() is part of WINSOCK.DLL. Since we've just been notified that
' there's data to receive, Flipper calls it to receive the data.
rc = recv(sock, msg, 1024, 0)
' Trim the network input and display it in the output text box.
lblOutput = Trim(msg)
End Sub
'-----------------------------------------------------
' If the MSJSock control returns a WinSock error, filter it through our
' WSERROR.BAS and display it for the user.
Private Sub MSJSockCtrl_SockError(ByVal hTask As Long, ByVal ENum As Integer)
MsgBox "WinSock error " & ENum & "(" & WSErrStr(ENum) & ")", vbOKOnly,_
"WinSock Error"
End Function
WINSOCK.BAS
'--------------------------------------------------------
' WINSOCK.BAS
' This file contains Declares for some of the more
' popular Windows Socket functions.
Global sock As Long ' Our own personal socket!
Global fingport As Integer
' Constants for use with the WSAAsyncSelect call
Global Const FD_READ = &H1
Global Const FD_WRITE = &H2
Global Const FD_OOB = &H4
Global Const FD_ACCEPT = &H8
Global Const FD_CONNECT = &H10
Global Const FD_CLOSE = &H20
' These are some of the standard Windows Socket calls we need in
' Flipper, defined for both 16- and 32-bit versions of the DLL.
#If Win16 Then
Declare Function oBind Lib "winsock.dll" Alias "bind" (ByVal s As Integer,_
Addr As sockaddr_in, ByVal namelen As Integer) As Integer
Declare Function htonl Lib "winsock.dll" (ByVal a As Long) As Long
Declare Function inet_addr Lib "winsock.dll" (ByVal s As String) As Long
Declare Function inet_ntoa Lib "winsock.dll" (ByVal inn As Long) As Long
Declare Function ntohl Lib "winsock.dll" (ByVal a As Long) As Long
Declare Function oSocket Lib "winsock.dll" Alias "socket" (ByVal af As_
Integer, ByVal typesock As Integer, ByVal protocol As Integer) As Integer
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 oConnect Lib "winsock.dll" Alias "connect" (ByVal sock As_
Integer, sockstruct As sockaddr_in, 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 getsockname Lib "winsock.dll" (ByVal s As Integer, Addr As_
Any, ByVal namelen As Integer) As Integer
Declare Function getservbyname Lib "winsock.dll" (ByVal nm As String, ByVal_ proto As String) As Long
Declare Function WSAStartup Lib "winsock.dll" (ByVal a As Integer, b As_ WSAdata_type) As Integer
Declare Function WSACleanup Lib "winsock.dll" () As Integer
Declare Function WSAGetLastError Lib "winsock.dll" () As Integer
Declare Function lstrcpyn Lib "kernel" (ByVal lpszString1 As Any, ByVal_
lpszString2 As Long, ByVal cChars As Integer) As String
Declare Sub hmemcpy Lib "kernel" (hpvDest As Any, hpvSource As Any, ByVal_ cbCopy As Long)
#Else
Declare Function htonl Lib "wsock32.dll" (ByVal a As Long) As Long
Declare Function inet_addr Lib "wsock32.dll" (ByVal s As String) As Long
Declare Function inet_ntoa Lib "wsock32.dll" (ByVal inn As Long) As Long
Declare Function ntohl Lib "wsock32.dll" (ByVal a As Long) As Long
Declare Function oSocket Lib "wsock32.dll" Alias "socket" (ByVal af As_
Integer, ByVal typesock As Integer, ByVal protocol As Integer) As Integer
Declare Function htons Lib "wsock32.dll" (ByVal a As Integer) As Integer
Declare Function ntohs Lib "wsock32.dll" (ByVal a As Integer) As Integer
Declare Function send Lib "wsock32.dll" (ByVal sock As Integer, ByVal msg As_
String, ByVal msglen As Integer, ByVal flag As Integer) As Integer
Declare Function recv Lib "wsock32.dll" (ByVal sock As Integer, ByVal msg As_
String, ByVal msglen As Integer, ByVal flag As Integer) As Integer
Declare Function ogetservbyname Lib "wsock32.dll" Alias "getservbyname"_
(ByVal nm As String, ByVal proto As String) As Long
Declare Function gethostname Lib "wsock32.dll" (ByVal s As String, ByVal l As_
Integer) As Integer
Declare Function gethostbyname Lib "wsock32.dll" (ByVal s As String) As Long
Declare Function WSACleanup Lib "wsock32.dll" () As Integer
Declare Function WSAGetLastError Lib "wsock32.dll" () As Integer
Declare Function WSAAsyncGetHostByName Lib "wsock32.dll" (ByVal hWnd As_
Integer, ByVal wMsg As Integer, ByVal name As String, ByVal buf As String,_
ByVal buflen As Integer) As Integer
Declare Function lstrcpyn Lib "kernel32" Alias "lstrcpynA" (ByVal lpString1_
As Any, ByVal lpString2 As Long, ByVal iMaxLength As Long) As Long
Declare Sub hmemcpy Lib "kernel32" (hpvDest As Any, hpvSource As Any, ByVal_ cbCopy As Long)
#End If
Figure 5 Common Internet Services
Internet Service | Info location |
Mail (send) | STD 010, SMTP-Simple Mail Transfer Protocol |
Mail (receive) | RFC1460, POP3-PostOfficeProtocol |
Usenet | RFC 0977, Network News Transmission Protocol |
World Wide Web (HTML) | RFC 1866, "Hypertext Markup Language - 2.0" |
WWW (HTTP) | ftp://info.cern.ch/pub/www/doc/http.txt |
Gopher | RFC 1436, "The Internet Gopher Protocol (a distributed document search and retrieval protocol)" |
Telnet | RFC 0854, "Telnet Protocol specification" |
Chat | RFC 1459, "Internet Relay Chat Protocol" |
Finger | RFC 1288 |
Internet Phone | RFC 1789, "INETPhone: Telephone Services and Servers on Internet" |
FTP | RFC 1350, "The TFTP Protocol (revision 2)" STD 009 |
Figure 7 Simple Sockets.FRM
VERSION 4.00
Begin VB.Form frmSimple
Caption = "MSJ Simple Sockets"
ClientHeight = 6705
ClientLeft = 1215
ClientTop = 1545
ClientWidth = 8445
Height = 7125
Left = 1155
LinkTopic = "Form1"
ScaleHeight = 6705
ScaleWidth = 8445
Top = 1185
Width = 8565
Begin VB.CommandButton btnSend
Caption = "&Send"
Height = 375
Left = 7440
TabIndex = 8
Top = 1080
Width = 855
End
Begin VB.TextBox edtOutput
Height = 285
Left = 120
TabIndex = 7
Top = 1200
Width = 7215
End
Begin VB.TextBox edtPort
Height = 285
Left = 840
TabIndex = 4
Text = "119"
Top = 480
Width = 2895
End
Begin VB.TextBox edtSite
Height = 285
Left = 840
TabIndex = 1
Text = "nntp.ix.netcom.com"
Top = 120
Width = 2895
End
Begin VB.CommandButton btnConnect
Caption = "Connect"
Height = 375
Left = 3840
TabIndex = 0
Top = 120
Width = 1215
End
Begin VB.Label Label5
BackStyle = 0 'Transparent
BorderStyle = 1 'Fixed Single
Caption = "Outbound"
ForeColor = &H00008000&
Height = 255
Left = 5520
TabIndex = 12
Top = 120
Width = 1215
End
Begin VB.Label Label4
BackStyle = 0 'Transparent
BorderStyle = 1 'Fixed Single
Caption = "Inbound"
ForeColor = &H000000FF&
Height = 255
Left = 6840
TabIndex = 11
Top = 480
Width = 1455
End
Begin VB.Label Label3
BackStyle = 0 'Transparent
BorderStyle = 1 'Fixed Single
Caption = "WinSock Errors"
Height = 255
Left = 5520
TabIndex = 10
Top = 480
Width = 1215
End
Begin VB.Label Label2
BackStyle = 0 'Transparent
BorderStyle = 1 'Fixed Single
Caption = "Info"
ForeColor = &H00FF0000&
Height = 255
Left = 6840
TabIndex = 9
Top = 120
Width = 1455
End
Begin RichtextLib.RichTextBox redInOut
Height = 5055
Left = 120
TabIndex = 6
Top = 1560
Width = 8175
_Version = 65536
_ExtentX = 14420
_ExtentY = 8916
_StockProps = 69
BackColor = -2147483643
ScrollBars = 2
TextRTF = $"Simple Sockets.frx":0000
End
Begin VB.Label Label1
Alignment = 1 'Right Justify
Caption = "Port:"
Height = 255
Left = 120
TabIndex = 5
Top = 480
Width = 615
End
Begin VB.Label lblSite
Alignment = 1 'Right Justify
Caption = "Server:"
Height = 255
Left = 120
TabIndex = 3
Top = 120
Width = 615
End
Begin MSJSOCKLib.MSJSock MSJSockCtrl
Height = 375
Left = 120
TabIndex = 2
Top = 120
Width = 375
_Version = 65536
_ExtentX = 661
_ExtentY = 661
_StockProps = 0
End
End
Attribute VB_Name = "frmSimple"
Attribute VB_Creatable = False
Attribute VB_Exposed = False
' SIMPLE SOCKETS.FRM -
' This is the main (and only) form for the MSJ Simple Sockets program.
' It connects to a user-supplied server and port, and allows the user to
' interactively type commands to the server. Results are shown in a jaunty,
' multicolored rich text edit box.
'--------------------------------------------------------------------------
Private Sub btnConnect_Click()
' Using the MSJSock control, create a socket
sock = MSJSockCtrl.CreateSocket
' Get the port the user typed in, as an integer
port = CInt(edtPort.Text)
' Look up the user-supplied host name. This will come back as a
' GotHost event later on, at which point the connection will continue.
host = MSJSockCtrl.gethostbyname(edtSite.Text)
' Add informational text to the output RTE (rich text edit), colored blue
Call DebugToList("Connecting to host " & edtSite.Text & " port " _
& port & Chr$(13) & Chr$(10), RGB(0, 0, 255))
End Sub
'--------------------------------------------------------------------------
Private Sub btnSend_Click()
' When the user clicks the send button, send their current typed-in
' command, with a CR-LF appended
rc = WSSend(sock, edtOutput.Text & Chr$(13) & Chr$(10))
End Sub
'--------------------------------------------------------------------------
Private Sub edtOutput_KeyPress(KeyAscii As Integer)
' If the user hits <enter>, treat it just as if they'd clicked the
' Send button.
If KeyAscii = 13 Or KeyAscii = 10 Then
btnSend_Click
End If
End Sub
'--------------------------------------------------------------------------
Private Sub MSJSockCtrl_connect(ByVal hTask As Long)
Dim rcl As Long
' When the host connects, print an informational message to the RTE
Call DebugToList("Connected to host." & Chr$(13) & Chr$(10), RGB(0, 0, 255))
' Now that you're connected, have the MSJSock control capture read and
' write messages
rcl = MSJSockCtrl.AsyncSelect(sock, FD_READ + FD_WRITE + FD_CLOSE)
End Sub
'--------------------------------------------------------------------------
Private Sub MSJSockCtrl_GotHost(ByVal hostent As Long)
Dim rcl As Long
' Once you've found the host, tell the MSJSock control to form a
' connection to it using the current socket and user-indicated port.
rcl = MSJSockCtrl.Connect(sock, port, hostent)
End Sub
'--------------------------------------------------------------------------
Private Sub MSJSockCtrl_RecvData(ByVal hTask As Long, ByVal Data As String)
Dim msg As String
' When the MSJSock control receives incoming data, call the WinSock
' recv API function to retrieve it 10k at a time.
msg = Space(10240)
rc = recv(sock, msg, 10240, 0)
msg = Trim(msg)
' Put the received information into the RTE box using red text, so you
' can tell it from other messages.
DebugToList msg, RGB(127, 0, 0)
End Sub
'--------------------------------------------------------------------------
Private Sub MSJSockCtrl_SockError(ByVal hTask As Long, ByVal ENum As Integer)
' Print WinSock errors to the RTE using black.
DebugToList "WinSock error " & ENum & ": " & WSErrStr(ENum) & Chr$(13) &
Chr$(10), RGB(0, 0, 0)
End Sub
'--------------------------------------------------------------------------
Sub DebugToList(dbgtxt As String, clr As Long)
Dim prvlen As Long
' DebugToList appends colored text to a rich text edit (RTE) box. Since
' you can't just do Ctrl.Text = Ctrl.Text + newtext (you'd overwrite all
' the old formatting, you have to move the insertion point to the end
' and insert new text.
' Find the location the new text will start (at the end of the old text)
prvlen = Len(redInOut.Text)
' Move the insertion point to the end of the RTE box
redInOut.SelStart = Len(redInOut.Text) + 1
' Replace the selection (which is nothing right now) with the new
' text, appending it without changing the previous formatting.
redInOut.SelText = dbgtxt
' Now select the entire inserted text
redInOut.SelStart = prvlen
redInOut.SelLength = Len(dbgtxt)
' Change the selected (newly inserted) text to the requested color
redInOut.SelColor = clr
' Move the insertion point back to the end of the RTE
redInOut.SelStart = Len(redInOut.Text) + 1
End Sub
'--------------------------------------------------------------------------
Sub HandleWSError(rc As Integer)
' Check for an unhandled WinSock error. If there is one, handle it
' just like an error coming in from the MSJSock control.
If (rc = SOCKET_ERROR) Then
WSErrorN = WSAGetLastError()
Call MSJSockCtrl_SockError(-1, WSErrorN)
End If
End Sub
'--------------------------------------------------------------------------
Function WSSend(wsock As Long, strg As String) As Integer
Dim rc As Integer
' As the user sends data, send it to the RTE in green.
DebugToList strg, RGB(0, 127, 0)
' Call the WinSock API send function to send the data.
rc = send(wsock, strg, Len(strg), 0)
DoEvents
' Check to see if send returned an error. If so, it will be handled
' by HandleWSError
HandleWSError rc
WSSend = rc
End Function