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