Microsoft Foundation Classes  
 

The SocketTools controls can be used with MFC based applications by including the control in the project that is being developed. This is done through the Visual C++ IDE by selecting the menu option Project | Add to Project | Components and Controls. This will display a dialog which is used to select the component to add. First select the Registered ActiveX Controls folder, scroll over to the control that you're interested in using, and press the Insert button. A dialog is then displayed which determines the class name and files which will be generated to "wrap" the ActiveX control. A new source file will be added to the project which contains the methods for the wrapper class that was created. A header file will also be created and included in the header file for the dialog class.

To create an instance of the control, the simplest approach is to create a dialog-based application, in which case the control can be selected from the dialog component palette, similar to how controls are placed on forms in Visual Basic. The control is included as a resource and assigned a resource ID.

Then, using the MFC Class Wizard, a member variable for the dialog class is assigned to that instance of the control. This means that a declaration similar to this will be added to the dialog class:

CFtpClient m_ctlFtpClient;

In the DoDataExchange method, a line will be added which initializes the control when the dialog is created:

DDX_Control(pDX, IDC_FTPCLIENT1, m_ctlFtpClient);

Now, any of the control's properties or methods may be accessed through the member variable for the CFtpClient class. For example:

COleVariant varServerName(m_strServerName);
COleVariant varServerPort(m_nServerPort);
COleVariant varUserName(m_strUserName);
COleVariant varPassword(m_strPassword);
COleVariant varAccount(m_strAccount);
COleVariant varTimeout(m_nTimeout);
COleVariant varLocalFile(m_strLocalFile);
COleVariant varRemoteFile(m_strRemoteFile);
COleVariant varOptions;
COleVariant varError;
 
varError = m_ctlFtpClient.Connect(varServerName,
                                  varServerPort,
                                  varUserName,
                                  varPassword,
                                  varAccount,
                                  varTimeout,
                                  varOptions);

varError.ChangeType(VT_I4);

if (V_I4(&varError) != 0)
{
    CString strError;
    strError.Format(_T("Unable to connect to %s\n%s"),
        m_strServerName,
        m_ctlFtpClient.GetLastErrorString());

    AfxMessageBox(strError, MB_ICONEXCLAMATION, 0);
}
else
{
    CString strMessage;
    LONG nBytes ;
    COleVariant varRestartOffset;
    varError = m_ctlFtpClient.GetFile(varLocalFile, 
                                           varRemoteFile, 
                                           varRestartOffset);
        
    varError.ChangeType(VT_I4);

    if (V_I4(&varError) != 0)
    {
        CString strError;
        strError.Format(_T("Unable to download %s\n%s"),
                m_strRemoteFile,
                m_ctlFtpClient.GetLastErrorString());
        AfxMessageBox(strError, MB_ICONEXCLAMATION, 0);
    }
    else
    {
        nBytes = m_ctlFtpClient.GetTransferBytes();    
        strMessage.Format(_T("Transferred %ld bytes of %s"), 
                                       nBytes, m_strRemoteFile);
        AfxMessageBox(strMessage, MB_ICONINFORMATION, 0);
    }
    m_ctlFtpClient.Disconnect();
}

In this example, the arguments are converted to variants by initializing COleVariant variables which are then passed to the Connect method. There are two important things to note here. First, even though the documentation lists some of the arguments as optional, when using the control this way in C++, you must specify all of them. This is because optional parameters really aren't omitted from the method; they are still passed as variants, but instead of having a value, they are initialized to tell the control that they were not specified. This is accomplished here by passing an empty (uninitialized) COleVariant, as with the varOptions variable. The second important point is that although the method is documented as returning a long integer, the actual return type is a variant that contains a long integer value. Similar considerations apply to the GetFile method. By contrast, GetTransferBytes corresponds to the long-valued property TransferBytes, and not to a method of the control, so it really does return a long integer.

You'll notice that in this code, there are also some macros being used with the variant types. The first one is used when checking the return value from the method:

varError.ChangeType(VT_I4);

if (V_I4(&varError) != 0)
{
    .
    .
    .
}

The ChangeType method for the COleVariant class changes the type of variant, in this case to a long integer, specified by the value VT_I4. What this does is coerce the variant data into a long integer if it already isn't one. If the variant already represents a long integer, then the call to ChangeType doesn't have any effect. Next, the V_I4 macro is used to obtain the actual value from the long integer. Note that it expects a pointer to a variant, not the variant itself.