Aborting in C++

The technique for aborting any time-consuming Chilkat C++ method call follows this recipe:

  1. Declare your own class that derives from the appropriate Chilkat progress monitoring class.  For CkMailMan it is CkMailManProgress (as shown below).  For other Chilkat classes it is CkHttpProgress, CkFtpProgress, CkImapProgress, CkZipProgress, etc.
  2. Create an implementation for the AbortCheck method.  This will override the default implementation from the base class.  (The default implementation simply does nothing.)
  3. The AbortCheck method is called periodically according to the value of the HeartbeatMs property.  The default value of HeartbeatMs is 0, which indicate that no AbortCheck events should be called.  Set HeartbeatMs to a non-zero value (in milliseconds) to enable the AbortCheck callbacks.  Do this by calling put_HeartbeatMs(someValue)  as shown below.
  4. Whenever your application wishes to abort an operation in progress (SMTP, POP3, IMAP, Zip, FTP, etc.), set the abort boolean argument of AbortCheck to true.
  5. Don’t forget to hookup your event monitoring class by calling the SetProgressCallback method.  (See the code example below.)  In some classes, such as for CkImap, the way to hookup the event class is via the put_EventCallbackObject function.  Please review the .h file for details.
#include <CkMailManProgress.h>

class MyMailProgress : public CkMailManProgress
    {
    public:
	MyMailProgress(void) { }
	virtual ~MyMailProgress(void) { }

	// Override the AbortCheck method (which is a virtual method in CkMailProgress)
	// Called periodically according to the value of the HeartbeatMs property.
	void AbortCheck(bool *abort)
	    {
	    printf("AbortCheck!\n");

	    // If your application wishes to abort the email sending/receiving operation,
	    // set the abort boolean like this:
	    *abort = true;
	    }

        // Percentage completion events may also be overridden to abort time-consuming operations.
	void SendPercentDone(long pctDone, bool *abort) 
	    { 
	    printf("SendPercentDone: %d percent\n",pctDone);
	    }
	void ReadPercentDone(long pctDone, bool *abort) 
	    { 
	    printf("ReadPercentDone: %d percent\n",pctDone);
	    }

    };

void AbortCheckDemo(void)
    {
    CkMailMan mailman;
    bool success;
    success = mailman.UnlockComponent("30-day trial");
    if (success != true)
	{
        printf("%s\n",mailman.lastErrorText());
        return;
	}

    // Set the HeartbeatMs property so that AbortCheck is called once every 80 milliseconds.
    mailman.put_HeartbeatMs(80);

    MyMailProgress myProgress;
    mailman.SetProgressCallback(&myProgress);

    CkEmail email;
    // ...
    // ...

    // The AbortCheck event is called periodically while SendEmail is in progress..
    // (Note: In some cases, including here, a percent-done event is also called, and it will have an "abort"
    // boolean argument that may be set to "true" to force an abort.
    // If the percent-done callbacks happen more frequently than the HeartbeatMs setting, then 
    // the AbortCheck events may not be fired at all.)
    success = mailman.SendEmail(email);
    if (success != true)
	{
        printf("%s\n",mailman.lastErrorText());
        return;
	}

    }

SendBufferSize/MaxPacketSize can make progress events more frequent in FTP and SSH/SFTP

Question:

I’ve downloaded the trial version of your FTP2 ActiveX control. I’m
using it in Visual FoxPro 9, but need to have a progress bar to track
an upload. When using your asynchronous example code, the progress
bar jumps from 0 to 524288 and so on. I need much more precision than
that so the progress bar will update smoothly. How do I accomplish
this with your tools?

Solution:

Set the SendBufferSize property to a smaller value, such as 8192. The FTP2 component has the SendBufferSize property. The equivalent property in SSH/SFTP is MaxPacketSize.

HTTP Progress Monitoring in C++

This blog post shows how to monitor the progress of HTTP uploads and downloads in C++.  The first step is to create a C++ callback class that derives from the CkHttpProgress base class.  You’ll be overriding one or more of the callback methods.  For example:

class MyHttpProgress : public CkHttpProgress
    {
    public:
	MyHttpProgress(void) { }
	virtual ~MyHttpProgress(void) { }

	void PercentDone(int pctDone, bool *abort) 
	    { 
	    printf("PercentDone: %d percent\n",pctDone);

	    // To abort the HTTP operation, set the abort flag equal to true.
	    //if (pctDone > 10)
		//{
		//*abort = true;
		//}
	    }
	void AbortCheck(bool *abort) 
	    { 
	    // To abort the HTTP operation, set the abort flag equal to true.
            // Like this:
            // *abort = true;

            // Note: The AbortCheck event callback is called periodically according to the HeartbeatMs 
            // property setting.

	    printf("AbortCheck!\n");
	    }
	void HttpBeginReceive(void) { printf("HttpBeginReceive!\n"); }
	void HttpEndReceive(bool success) { printf("HttpEndReceive!\n"); }
	void HttpBeginSend(void) { printf("HttpBeginSend!\n"); }
	void HttpEndSend(bool success) { printf("HttpEndSend!\n"); }

    };

In your C++ application, create an instance of your MyHttpProgress class and tell the CkHttp object to use it by calling put_EventCallbackObject. Here’s an example:

void TestDownloadWithEventCallbacks(void)
    {
    MyHttpProgress eventObj;

    CkHttp http;
    bool success = http.UnlockComponent("Anything for 30-day trial");
    
    http.put_EventCallbackObject(&eventObj);

    success = http.Download("http://www.chilkatsoft.com/something/something.zip","something.zip");
    if (!success) 
        {
        printf("%s\n",http.lastErrorText());
        }
    else
        {
        printf("Finished.\n");
        }

    }

SFTP Progress Monitoring and Abort (C#)

Here is an example for monitoring the progress of an SFTP file transfer:

        void sftp_OnPercentDone(object sender, Chilkat.PercentDoneEventArgs args)
        {
            progressBar1.Value = args.PercentDone;

            // To abort at any point, you may set args.Abort = true  
            // args.Abort = true;
        }

        void sftp_OnAbortCheck(object sender, Chilkat.AbortCheckEventArgs args)
        {
            // See http://cknotes.com/?p=149  for more information about 
            // using the AbortCheck event...
           
        }

private void button2_Click(object sender, EventArgs e)
{
	// SFTP upload with progress monitoring.
	Chilkat.SFtp sftp = new Chilkat.SFtp();

	// IMPORTANT: Enable event callbacks:
	sftp.EnableEvents = true;

	// Unlock the component.
	bool success = sftp.UnlockComponent("Anything for 30-day trial");
	if (!success)
	{
	textBox1.Text = sftp.LastErrorText;
	return;
	}

	// Connect to the SSH/SFTP server. (use a domain name or IP address)
	success = sftp.Connect("192.168.1.117", 22);
	if (!success)
	{
	textBox1.Text = sftp.LastErrorText;
	return;
	}

	// Authenticate..
	success = sftp.AuthenticatePw("chilkat", "myPassword");
	if (!success)
	{
	textBox1.Text = sftp.LastErrorText;
	return;
	}

	success = sftp.InitializeSftp();
	if (!success)
	{
	textBox1.Text = sftp.LastErrorText;
	return;
	}

	// Establish some event handlers:
	sftp.OnAbortCheck += new Chilkat.SFtp.AbortCheckEventHandler(sftp_OnAbortCheck);
	sftp.OnPercentDone += new Chilkat.SFtp.PercentDoneEventHandler(sftp_OnPercentDone);

	// See http://cknotes.com/?p=149  for more information about 
	// using the AbortCheck event...

	// Do an upload.
	// Note: If the file to be uploaded or downloaded is too small, 
	// the PercentDone callback will not be called for each 1%.
	// It's possible if the file is so small relative to the speed
	// of the connection that you may get a single 100% callback if
	// the data transfer happens almost instantaneously.
	string localFilepath = "c:/temp/hamlet.xml";
	string remoteFilepath = "hamlet.xml";
	progressBar1.Value = 0;
	success = sftp.UploadFileByName(remoteFilepath, localFilepath);
	if (!success)
	{
	textBox1.Text = sftp.LastErrorText;
	return;
	}
	MessageBox.Show("OK!");
}

SFTP Upload in VB6 with Progress Monitoring

The PercentDone event is called when the percentage completion increases by one or more points.  To use events in VB6, Dim the variable WithEvents.  Then name the event callback using the variable name.  (You should already understand how to use VB6 events in general prior to using the Chilkat objects.  A good VB6 book is “Programming Visual Basic 6.0” by Francesco Balena.

Here’s the example:

' Assumes a Form with a Button named "Command1" and a progress bar named "ProgressBar1".

Dim WithEvents sftp As ChilkatSFtp

Private Sub sftp_PercentDone(ByVal pctDone As Long)
    ProgressBar1.Value = pctDone
End Sub

Private Sub Command1_Click()

...


    ProgressBar1.Value = 0
    
    success = sftp.UploadFileByName(remoteFilePath, localFilePath)
    If (success <> 1) Then
        MsgBox sftp.LastErrorText
        Exit Sub
    End If
    
    MsgBox "Success."

End Sub

VB.NET HTTP Download with percent-done progress monitoring

Here is an example:

    Dim WithEvents http As Chilkat.Http

    Private Sub http_OnPercentDone(ByVal sender As Object, ByVal args As Chilkat.PercentDoneEventArgs) Handles http.OnPercentDone
        ProgressBar1.Value = args.PercentDone
    End Sub

    Private Sub HttpDownloadTest()

        http = New Chilkat.Http()

        Dim success As Boolean

        '  Any string unlocks the component for the 1st 30-days.
        success = http.UnlockComponent("Anything for 30-day trial")
        If (success <> True) Then
            MsgBox(http.LastErrorText)
            Exit Sub
        End If

        ' Enable event callbacks...
        http.EnableEvents = True

        '  Download the Python language install.
        '  Note: This URL may have changed since this example was created.
        success = http.Download("http://www.python.org/ftp/python/2.5/python-2.5.msi", "python-2.5.msi")
        If (success <> True) Then
            MsgBox(http.LastErrorText)
        Else
            MsgBox("Python Download Complete!")
        End If

        http = Nothing

    End Sub

AbortCheck Event in Chilkat .NET Components

The AbortCheck event is standard in Chilkat .NET classes that involve communications over sockets (FTP, POP3, SMTP, IMAP, HTTP, etc.) or time-consuming operations such as zipping/unzipping large files. There are three steps to using AbortCheck:

1. Enable event callbacks by setting the EnableEvents property = true.

Chilkat.MailMan mailman = new Chilkat.MailMan();
mailman.EnableEvents = true;

2. Set the HeartbeatMs property. The HeartbeatMs property is the number of milliseconds between each AbortCheck callback. The default value is 0 to indicate that no AbortCheck events will be fired. You must set the HeartbeatMs property to a non-zero value to receive AbortCheck events. For example, a value of 100 sends 10 events per second.

mailman.HeartbeatMs = 100;

3. Add a handler for OnAbortCheck. To do this, (in C#) begin typing “mailman.OnAbortCheck +=” and a tooltip will appear, as shown below:

OnAbortCheck

Press the TAB character to complete the statement. Press the TAB character again to auto-generate the event handler code, which now looks like this:

        private void AbortTest()
        {
            Chilkat.MailMan mailman = new Chilkat.MailMan();
            mailman.EnableEvents = true;

            mailman.HeartbeatMs = 100;
            mailman.OnAbortCheck += new Chilkat.MailMan.AbortCheckEventHandler(mailman_OnAbortCheck);
        }

        void mailman_OnAbortCheck(object sender, Chilkat.AbortCheckEventArgs args)
        {
            throw new Exception("The method or operation is not implemented.");
        }

The mailman_OnAbortCheck event handler is now called once every 100 milliseconds during any POP3 or SMTP operation that involves communication with the server. You may abort by setting args.Abort = true. For example:

        void mailman_OnAbortCheck(object sender, Chilkat.AbortCheckEventArgs args)
        {
            bool bAbort = false;
            // Add code here to determine whether the in-progress operation should be aborted.
            // If so, set args.Abort = true
            if (bAbort) args.Abort = true;
        }

Important: Many Chilkat methods allow for percent-done progress monitoring. Percent-done event callbacks also provide the ability to abort an operation. Therefore, AbortCheck events are suppressed when the frequency of percent-done callbacks is greater than the HeartbeatMs. The reason for this is to prevent too many callbacks from disrupting performance. If you used both percent-done and AbortCheck events, you should also code for aborting from the percent-done event.

Creating a VB.NET Event Handler for AbortCheck

In VB.NET, the object must not be declared as a local variable. It must be declared “WithEvents”, typically as a member of the Form. Then select the data member (as shown below) to get a list of events (in the “Declarations” combo-box to the right).

VB.NET AbortCheck

Finally, select the event from the combo box (you should see the various events listed) and let Visual Studio generate your event handler code…

ActiveX Events IDL for (some but not all) Chilkat Components

SFtp:

	dispinterface _IChilkatSFtpEvents
	{
		properties:
		methods:
		[id(1), helpstring("method PercentDone")] HRESULT PercentDone([in] long pctDone);
		[id(2), helpstring("method AbortCheck")] HRESULT AbortCheck([out] long *abort);
		[id(3), helpstring("method UploadRate")] HRESULT UploadRate([in] long byteCount, [in] long bytesPerSec);
		[id(4), helpstring("method DownloadRate")] HRESULT DownloadRate([in] long byteCount, [in] long bytesPerSec);
	};

Zip:

dispinterface _IChilkatZip2Events
{
	properties:
	methods:
	 HRESULT UnzipPercentDone([in] long percentDone, 
		[out] long *abort);
	 HRESULT WriteZipPercentDone([in] long percentDone, 
		[out] long *abort);
	 HRESULT AddFilesEnd();
	 HRESULT FileUnzipped([in] BSTR filename, 
		[in] long compressedSize, [in] long uncompressedSize, [out] long *abort);
	 HRESULT ToBeAdded([in] BSTR filename, [in] long fileSize, 
		[out] long *exclude);
	 HRESULT FileAdded([in] BSTR filename, [in] long fileSize, [out] long *abort);
	 HRESULT UnzipBegin();
	 HRESULT UnzipEnd();
	 HRESULT WriteZipBegin();
	 HRESULT WriteZipEnd();
	 HRESULT ToBeZipped([in] BSTR filename, [in] long fileSize, 
		[out] long *exclude);
	 HRESULT FileZipped([in] BSTR filename, [in] long fileSize, 
		[in] long compressedSize, [out] long *abort);
	 HRESULT ToBeUnzipped([in] BSTR filename, [in] long compressedSize, 
		[in] long uncompressedSize, [out] long *exclude);
	 HRESULT AddFilesBegin();
};

MailMan:

dispinterface _IChilkatMailEvents
{
	properties:
	methods:
	 HRESULT SendPercentDone([in] long percentDone, [out] long *abort);
	 HRESULT ReadPercentDone([in] long percentDone, [out] long *abort);
	 HRESULT AbortCheck([out] long *abort);
	 HRESULT EmailReceived([in] BSTR subject, [in] BSTR fromAddr, 
		[in] BSTR fromName, [in] BSTR returnPath, [in] BSTR date, 
		[in] BSTR uidl, [in] long sizeInBytes);
};

Ftp2:

dispinterface _IChilkatFtp2Events
{
	properties:
	methods:
	 HRESULT PutProgress([in] long pctDone);
	 HRESULT GetProgress([in] long pctDone);
	 HRESULT AbortCheck([out] long *abort);
	 HRESULT BeginDownloadFile([in] BSTR path, [out] long *skip);
	 HRESULT EndDownloadFile([in] BSTR path, [in] long numBytes);
	 HRESULT VerifyDownloadDir([in] BSTR path, [out] long *skip);
	 HRESULT BeginUploadFile([in] BSTR path, [out] long *skip);
	 HRESULT EndUploadFile([in] BSTR path, [in] long numBytes);
	 HRESULT VerifyUploadDir([in] BSTR path, [out] long *skip);
	 HRESULT VerifyDeleteDir([in] BSTR path, [out] long *skip);
	 HRESULT VerifyDeleteFile([in] BSTR path, [out] long *skip);

};

Imap:

dispinterface _IChilkatImapEvents
{
	properties:
	methods:
	 HRESULT PercentDone([in] long pctDone);
	 HRESULT AbortCheck([out] long *abort);
};

Http:

dispinterface _IChilkatHttpEvents
{
	properties:
	methods:
	 HRESULT PercentDone([in] long pctDone);
	 HRESULT AbortCheck([out] long *abort);
	 HRESULT BeginSend();
	 HRESULT BeginReceive();
	 HRESULT EndSend([in] long success);
	 HRESULT EndReceive([in] long success);
	 HRESULT HttpChunked();
	 HRESULT HttpRedirect([in] BSTR originalUrl, [in] BSTR newUrl, [out] long *abort);
};

ActiveX Events in FoxPro

ActiveX components and controls are used in many programming languages, each of which has it’s own way of handling event callbacks.  This blog post provides helpful hints about how to receive event callbacks from an ActiveX component.

To receive the event, you must bind the ActiveX (also referred to as the COM server) event to the implemented interface methods on a Visual FoxPro object.   This is done using FoxPro’s EVENTHANDLER function.  For example:

* oCtrl will be the object in Visual FoxPro to receive the event notifications.
* It will implement one or more of the ActiveX's event callback methods
oCtrl = CREATEOBJECT("Ctrl")

* Create an instance of an ActiveX component:
loMailman = CreateObject('Chilkat.MailMan2')
...

*The EVENTHANDLER tells the COM object to send events to the VFP object:
EVENTHANDLER(loMailman,oCtrl)

Let’s look at the VFP object’s class (i.e. oCtrl):

*Definition of control class:
DEFINE CLASS Ctrl as Session OLEPUBLIC

*The IMPLEMENTS... line below is what makes Foxpro take control over the SendPercentDone,
ReadPercentDone and AbortCheck
*NOTE - This path part of the IMPLEMENTS must point to the dll, so locate and correct it
before you are using the program.
IMPLEMENTS _IChilkatMailEvents IN "c:\mydlls\chilkatmail_v7_9.dll"

PROCEDURE Init
ENDPROC

PROCEDURE _IChilkatMailEvents_SendPercentDone(percentDone AS Number, abort AS Number) AS VOID;
    HELPSTRING "method SendPercentDone"
    * add user code here

ENDPROC

PROCEDURE _IChilkatMailEvents_ReadPercentDone(percentDone AS Number, abort AS Number) AS VOID;
    HELPSTRING "method ReadPercentDone"
    * add user code here

ENDPROC

PROCEDURE _IChilkatMailEvents_AbortCheck(abort AS Number) AS VOID;
    HELPSTRING "method AbortCheck"
    * add user code here

ENDPROC

PROCEDURE _IChilkatMailEvents_EmailReceived(subject AS String, fromAddr AS String
    fromName AS String, returnPath AS String, date AS String, uidl AS String, 
    sizeInBytes AS Number) AS VOID;
    HELPSTRING "method EmailReceived"
    * add user code here

ENDPROC

ENDDEFINE

This is not enough for events to fire.  You’ll need to take one additional step:  Set the AutoYield property = .T. to allow ActiveX events to fire.

Finally, how do you know what events are available for a given ActiveX?   Unfortunately, Chilkat does not have programming-language specific help for the events.  We can look at the IDL for the events and then understand how to write the FoxPro event interfaces.  For example, here’s the IDL for Chilkat MailMan:

dispinterface _IChilkatMailEvents
{
properties:
methods:
[id(1), helpstring("method SendPercentDone")] HRESULT SendPercentDone([in] long percentDone, [out] long *abort);
[id(2), helpstring("method ReadPercentDone")] HRESULT ReadPercentDone([in] long percentDone, [out] long *abort);
[id(3), helpstring("method AbortCheck")] HRESULT AbortCheck([out] long *abort);
[id(4), helpstring("method EmailReceived")] HRESULT EmailReceived([in] BSTR subject, [in] BSTR fromAddr,
    [in] BSTR fromName, [in] BSTR returnPath, [in] BSTR date, [in] BSTR uidl, [in] long sizeInBytes);
};

A few notes:

  • Notice the “_IChilkatMailEvents” corresponds to the “_IChilkatMailEvents” in the FoxPro code.
  • All event methods will be declared as a PROCEDURE returning VOID (i.e. “AS VOID”)
  • Ignore the HRESULT in the IDL.  It doesn’t apply in FoxPro.
  • In the IDL, BSTR means “string”.
  • In the IDL, input arguments are specified by “[in]”, output arguments are specified by “[out]”.  A common output argument is “abort”.  Your event handler may set it equal to 1 to abort the current Chilkat method that is running, or leave it at 0 to allow it to continue.

That’s about it.   You should be able to look at the IDL and know how to create your event handling code in FoxPro.  Here’s a link to the IDL for SFtp, Zip, MailMan, Ftp2, HTTP, and IMAP: http://cknotes.com/?p=119

FTP2 Events for FoxPro

The Chilkat FTP2 ActiveX has events for progress monitoring. The events may also be used to abort an FTP upload/download, or to skip files and/or directories.

These are the events in Visual FoxPro terms:

PROCEDURE _IChilkatFtp2Events_PutProgress(percentDone AS Number) AS VOID;
PROCEDURE _IChilkatFtp2Events_GetProgress(percentDone AS Number) AS VOID;
PROCEDURE _IChilkatFtp2Events_AbortCheck(abortFlag AS Number) AS VOID;
PROCEDURE _IChilkatFtp2Events_BeginDownloadFile(filePath AS String, skipFlag AS Number) AS VOID;
PROCEDURE _IChilkatFtp2Events_EndDownloadFile(filePath AS String, numBytes AS Number) AS VOID;
PROCEDURE _IChilkatFtp2Events_VerifyDownloadDir(dirPath AS String, skipFlag AS Number) AS VOID;
PROCEDURE _IChilkatFtp2Events_BeginUploadFile(filePath AS String, skipFlag AS Number) AS VOID;
PROCEDURE _IChilkatFtp2Events_EndUploadFile(filePath AS String, numBytes AS Number) AS VOID;
PROCEDURE _IChilkatFtp2Events_VerifyUploadDir(dirPath AS String, skipFlag AS Number) AS VOID;
PROCEDURE _IChilkatFtp2Events_VerifyDeleteDir(dirPath AS String, skipFlag AS Number) AS VOID;
PROCEDURE _IChilkatFtp2Events_VerifyDeleteFile(filePath AS String, skipFlag AS Number) AS VOID;

The “skipFlag” and “abortFlag” args are output-only.

An example of using ActiveX events in FoxPro may be found here:
Email Events in FoxPro
Although the example is for sending email, the same programming techniques apply with the FTP2 component.