C Language Callbacks

This example demonstrates the general pattern for implementing some standard event callbacks functions for the Chilkat “C” API.  All Chilkat “classes” what have events will use the standard Chilkat events shown in the example below (AbortCheck, PercentDone, and ProgressInfo).

This example demonstrates callbacks for an SFTP download, but the same technique applies to any other Chilkat “class”, such as HTTP, FTP2, MailMan, Rest, etc.

#include <stdio.h>

#include <C_CkSFtp.h>

BOOL myAbortCheck()
    {
    // To abort the current Chilkat method call, return non-zero.
    return 0;
    }

BOOL myPercentDone(int pctDone)
    {
    printf("Percent Done: %d\n",pctDone);

    // To abort the current Chilkat method call, return non-zero.
    return 0;
    }

void myProgressInfo(const char *name, const char *value)
    {
    printf("%s: %s\n",name,value);
    }

void ChilkatSample(void)
    {
    HCkSFtp sftp;
    BOOL success;
    int port;
    const char *hostname;
    const char *handle;

    // Declare event callbacks.
    BOOL (*fnAbortCheck)() = myAbortCheck;
    BOOL (*fnPercentDone)(int pctDone) = myPercentDone;
    void (*fnProgressInfo)(const char *name, const char *value) = myProgressInfo;

    sftp = CkSFtp_Create();

    //  Any string automatically begins a fully-functional 30-day trial.
    success = CkSFtp_UnlockComponent(sftp,"Anything for 30-day trial");
    if (success != TRUE) {
        printf("%s\n",CkSFtp_lastErrorText(sftp));
        CkSFtp_Dispose(sftp);
        return;
    }

    // Setup event callbacks.
    CkSFtp_setAbortCheck(sftp, fnAbortCheck);
    CkSFtp_setPercentDone(sftp, fnPercentDone);
    CkSFtp_setProgressInfo(sftp, fnProgressInfo);

    // Make sure to set a HeartbeatMs for AbortCheck / PercentDone callbacks..
    // Set the HeartbeatMs to 250 milliseconds.
    CkSFtp_putHeartbeatMs(sftp,250);

    //  Set some timeouts, in milliseconds:
    CkSFtp_putConnectTimeoutMs(sftp,5000);
    CkSFtp_putIdleTimeoutMs(sftp,10000);

    //  Connect to the SSH server.
    //  The standard SSH port = 22
    //  The hostname may be a hostname or IP address.

    hostname = "my-Sftp-Server-Domain-Or-IPAddress";
    port = 22;
    success = CkSFtp_Connect(sftp,hostname,port);
    if (success != TRUE) {
        printf("%s\n",CkSFtp_lastErrorText(sftp));
        CkSFtp_Dispose(sftp);
        return;
    }

    //  Authenticate with the SSH server.  Chilkat SFTP supports
    //  both password-based authenication as well as public-key
    //  authentication.  This example uses password authenication.
    success = CkSFtp_AuthenticatePw(sftp,"myLogin","myPassword");
    if (success != TRUE) {
        printf("%s\n",CkSFtp_lastErrorText(sftp));
        CkSFtp_Dispose(sftp);
        return;
    }

    //  After authenticating, the SFTP subsystem must be initialized:
    success = CkSFtp_InitializeSftp(sftp);
    if (success != TRUE) {
        printf("%s\n",CkSFtp_lastErrorText(sftp));
        CkSFtp_Dispose(sftp);
        return;
    }

    //  Open a file on the server:
    handle = CkSFtp_openFile(sftp,"hamlet.xml","readOnly","openExisting");
    if (CkSFtp_getLastMethodSuccess(sftp) != TRUE) {
        printf("%s\n",CkSFtp_lastErrorText(sftp));
        CkSFtp_Dispose(sftp);
        return;
    }

    //  Download the file:
    success = CkSFtp_DownloadFile(sftp,handle,"c:/temp/hamlet.xml");
    if (success != TRUE) {
        printf("%s\n",CkSFtp_lastErrorText(sftp));
        CkSFtp_Dispose(sftp);
        return;
    }

    //  Close the file.
    success = CkSFtp_CloseHandle(sftp,handle);
    if (success != TRUE) {
        printf("%s\n",CkSFtp_lastErrorText(sftp));
        CkSFtp_Dispose(sftp);
        return;
    }

    printf("Success.\n");


    CkSFtp_Dispose(sftp);

    }

int main()
{
  ChilkatSample();
  return 0;
}

Event Callbacks in Java

Event callbacks in Java (including Android) are supported starting in v9.5.0.52. To receive event callbacks, first create a Java class derived from one of the Chilkat event callback classes. The event callback classes are: CkBaseProgress, CkHttpProgress, CkZipProgress, CkFtp2Progress, CkMailManProgress, CkTarProgress, and CkSFtpProgress.

All future Chilkat classes will only use CkBaseProgress. Theses event callback classes will be documented in the “Events” section of each class’s online reference documentation.

Here is an example of an HTTP download using event callbacks in Java. First we have the MyHttpProgress derived from CkHttpProgress. Following that, we have the simple Java program that installs the event handler and does the HTTP download. Event callbacks will also happen with the asynchronous version of the methods, but beware of the fact that when asynchronous, the task is running in a background thread and the callback occurs in the background thread.

import com.chilkatsoft.CkHttpProgress;
import com.chilkatsoft.CkTask;


public class MyHttpProgress extends CkHttpProgress 
{	
  public boolean AbortCheck()
  	{
      System.out.println("AbortCheck");
      // Return true to abort, false to allow the method to continue.
      return false;
  	}
  	
  // pctDone is a value from 0 to 100
  // (it is actually value from 0 to the PercentDoneScale property setting)
  public boolean PercentDone(int pctDone)
  {
    System.out.println(pctDone);
    // Return true to abort, false to allow the method to continue.
    // Note: A PercentDone event is the equivalent of an AbortCheck.  
    // When PercentDone events are frequently firing, AbortCheck events are suppressed.
    // AbortCheck events will fire when the time between PercentDone events is longer 
    // than the HeartbeatMs property setting.
    return false;
  }
  
  public void ProgressInfo(String name, String value)
  {
    System.out.println(name + ": " + value);
  }
  
  public void TaskCompleted(CkTask task)
  {
     System.out.println("task completed!");
  }
  
  }

The synchronous Java program w/ event callbacks… (the asynchronous version follows..)

import com.chilkatsoft.CkHttp;

public class HttpWithEvents {
	
  static {
    try {
    	
    	System.loadLibrary("chilkat");
    	
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load.\n" + e);
      System.exit(1);
    }
  }


	
  public static void main(String argv[])
  	{
    CkHttp http = new CkHttp();
    
    // Install an event callback handler to get progress events.
    MyHttpProgress myProgress = new MyHttpProgress();
    http.put_EventCallbackObject(myProgress);
    
    boolean success;

    //  Any string unlocks the component for the 1st 30-days.
    success = http.UnlockComponent("Anything for 30-day trial");
    if (success != true) {
        System.out.println(http.lastErrorText());
        return;
    }

    //  Download a file at a URL.
    success = http.Download("http://www.chilkatsoft.com/download/9.5.0.51/ChilkatDotNet45-9.5.0-x64.zip","ChilkatDotNet45-9.5.0-x64.zip");
    if (success != true) {
        System.out.println(http.lastErrorText());
        return;
    }
    
    System.out.println("OK");
  	}
  }

The asynchronous Java program w/ event callbacks…

import com.chilkatsoft.CkHttp;
import com.chilkatsoft.CkTask;

public class AsyncHttp {
	
  static {
    try {
    	
    	System.loadLibrary("chilkat");
    	
    } catch (UnsatisfiedLinkError e) {
      System.err.println("Native code library failed to load.\n" + e);
      System.exit(1);
    }
  }


	
  public static void main(String argv[])
  	{
    CkHttp http = new CkHttp();
    
    // Install an event callback handler to get progress events.
    MyHttpProgress myProgress = new MyHttpProgress();
    http.put_EventCallbackObject(myProgress);
    
    boolean success;

    //  Any string unlocks the component for the 1st 30-days.
    success = http.UnlockComponent("Anything for 30-day trial");
    if (success != true) {
        System.out.println(http.lastErrorText());
        return;
    }

    //  Download a file at a URL.
    CkTask task = http.DownloadAsync("http://www.chilkatsoft.com/download/9.5.0.51/ChilkatDotNet45-9.5.0-x64.zip","ChilkatDotNet45-9.5.0-x64.zip");
    if (success != true) {
        System.out.println(http.lastErrorText());
        return;
    }
    
    task.put_UserData("chilkatDotNet45");
    
    if (!task.Run()) {
        System.out.println(task.lastErrorText());
        return;
	}
  
    System.out.println("OK, task is running...");
    
    // Wait a max of 10 seconds for it to finish.
    success = task.Wait(10000);
    
    // What is the task status?
    System.out.println("task status = " + task.status());
  	}
  }

Helpful Hints for Monitoring Progress in iOS Applications

This is some advice from an iOS application developer using Chilkat FTP2.  It is about monitoring the progress of an FTP upload or download, but it’s a technique that can be considered for updating the UI for any long-running operation that provides percent-done event callbacks:
“It took me a while to avoid all the problems with updating the progress window in iOS 6, since all UIKit functions only work in the main task, but now everything is running perfectly (as perfect as software can ;-).
In short my solution:
The CkoFtp2Progress only handles the plain data and got updates from different parts of the program. And a installed NSTimer refreshes the UIWindow from the main task with the actual data in CkoFtp2Progress. Just in case someone asks you. I saw a lot of people having trouble with this point, while working on this solution.”

“SKIP” keyword in FoxPro examples.

A helpful note from a Chilkat customer:

I wanted to update you on the problem for future reference.
SKIP is a reserved word in Visual Foxpro, I should have caught it, but didn’t
until 2 days of screwing with it.  I knew it had to be something simple, because
everything of yours worked perfect, fast, without a hitch except when I compiled
then VFP kicked out a syntax error and nothing else to lead me to a HINT.
I checked and rechecked everything over and over….then I realized it !
I hope this helps you with future VFP questions. Also,
Thanks for responding so quick….Happy 4th of July !!!

Thanks again for great programming tools!   I luv Chilkat Components!

The following callBack procedures used the word SKIP in online examples or blog posts:

**************************** FTP General CallBack Procs **********************
* VerifyDeleteDir
PROCEDURE _IChilkatFtp2Events_VerifyDeleteDir(dirPath AS STRING, SKIP AS NUMBER) AS VOID;
HELPSTRING “method VerifyDeleteDir”
ENDPROC
* VerifyDeleteFile
PROCEDURE _IChilkatFtp2Events_VerifyDeleteFile(FilePath AS STRING, SKIP AS NUMBER) AS VOID;
HELPSTRING “method VerifyDeleteFile”
ENDPROC
**************************** FTP Upload CallBack Procs **********************
* BeginUploadFile
PROCEDURE _IChilkatFtp2Events_BeginUploadFile(FilePath AS STRING, SKIP AS NUMBER) AS VOID;
HELPSTRING “method BeginUploadFile”
pnThermFtpCnt=1
*  ? ‘BeginUploadFile’
ENDPROC
* VerifyUploadDir
PROCEDURE _IChilkatFtp2Events_VerifyUploadDir(dirPath AS STRING, SKIP AS NUMBER) AS VOID;
HELPSTRING “method VerifyUploadDir”
ENDPROC
**************************** FTP Download CallBack Procs **********************
* BeginDownloadFile
PROCEDURE _IChilkatFtp2Events_BeginDownloadFile(FilePath AS STRING, SKIP AS NUMBER) AS VOID;
HELPSTRING “method BeginDownloadFile”
pnThermFtpCnt=1
*  ? ‘BeginDownloadFile’
ENDPROC

* VerifyDownloadDir
PROCEDURE _IChilkatFtp2Events_VerifyDownloadDir(dirPath AS STRING, SKIP AS NUMBER) AS VOID;
HELPSTRING “method VerifyDownloadDir”
ENDPROC

Delphi ActiveX Event Handling

ActiveX event callbacks can be used to get progress information for lengthy operations, especially for API calls involving communications with servers, such as FTP, SFTP (SSH), IMAP, POP3, SMTP, HTTP, etc.

To use an ActiveX in Delphi, the component must be “imported”. This generates wrapper files (TLB.pas) in the “imports” directory, and may also install icons on the component palettte.

The Chilkat components are non-visual. There are two ways of creating an instance of a Chilkat object: (1) drag and drop the icon from the palette onto the form. The non-visual object will be seen as a small box on the form. The component may be selected, and the events can be obtained from the object inspector. (2) The recommended technique for instantiating a Chilkat object is to do so dynamically (do not drag and drop onto the form). In this case, you can go to the generated TLB.pas file and locate the event signatures (i.e. functions). Write your own method that has the same function signature, and point the relevant event property to your new method.

For example, here are the event signatures in the TLB.pas for Chilkat FTP2:

  TChilkatFtp2PutProgress = procedure(ASender: TObject; pctDone: Integer) of object;
  TChilkatFtp2GetProgress = procedure(ASender: TObject; pctDone: Integer) of object;
  TChilkatFtp2AbortCheck = procedure(ASender: TObject; out abort: Integer) of object;
  TChilkatFtp2BeginDownloadFile = procedure(ASender: TObject; const path: WideString; 
                                                              out skip: Integer) of object;
  TChilkatFtp2EndDownloadFile = procedure(ASender: TObject; const path: WideString; 
                                                            numBytes: Integer) of object;
  TChilkatFtp2VerifyDownloadDir = procedure(ASender: TObject; const path: WideString; 
                                                              out skip: Integer) of object;
  TChilkatFtp2BeginUploadFile = procedure(ASender: TObject; const path: WideString; 
                                                            out skip: Integer) of object;
  TChilkatFtp2EndUploadFile = procedure(ASender: TObject; const path: WideString; numBytes: Integer) of object;
  TChilkatFtp2VerifyUploadDir = procedure(ASender: TObject; const path: WideString; 
                                                            out skip: Integer) of object;
  TChilkatFtp2VerifyDeleteDir = procedure(ASender: TObject; const path: WideString; 
                                                            out skip: Integer) of object;
  TChilkatFtp2VerifyDeleteFile = procedure(ASender: TObject; const path: WideString; 
                                                             out skip: Integer) of object;

Cocoa Asynchronous Methods and Event Callbacks

Chilkat v9.3.0 for Cocoa now includes the asynchronous functionality and event callback functionality that has been available in the C++ libs.

Asynchronous methods run in a background thread such that the call immediately returns to the application.  Events are not fired w/ asynchronous method calls — your app would periodically check to see if the background task is finished.

To implement an event callback, your app would define/implement a class that inherits from one of the Cko*Progress base classes.  You provide method implementations to override the methods specified in the Cko*Progress classes (see the header files).  The callback object is specified via the EventCallbackObject property.  For example:

// property setter: EventCallbackObject
– (void)setEventCallbackObject: (CkoFtp2Progress *)eventObj;

FTP2 ActiveX Events

The Chilkat FTP2 ActiveX provides the following event callbacks:

' IMPORTANT: For all callbacks, arguments named "skip" or "abort" are output-only arguments.
' These arguments may be set to 1 within the callback to either skip a
' particular file upload/download,  or abort the entire operation.

Dim WithEvents ftp As ChilkatFtp2

' Called just before a file is to be downloaded.
Private Sub ftp_BeginDownloadFile(ByVal path As String, skipFlag As Long)

End Sub

' Called just before a file is to be uploaded.
Private Sub ftp_BeginUploadFile(ByVal path As String, skipFlag As Long)

End Sub

' Called periodically to indicate the current performance.
Private Sub ftp_DownloadRate(ByVal byteCount As Long, ByVal bytesPerSec As Long)

End Sub

' Called just after a download has completed.
Private Sub ftp_EndDownloadFile(ByVal path As String, ByVal numBytes As Long)

End Sub

' Called just after an upload has completed.
Private Sub ftp_EndUploadFile(ByVal path As String, ByVal numBytes As Long)

End Sub

' Indicate percentage completed for a download.  Note: This is only called
' for cases where it is possible to know the percent complete (0 to 100).
' For directory tree synchronization methods, it is not possible to know ahead of time
' how much work is to be done, and therefore it is not possible to know a percent-compete number.
Private Sub ftp_GetProgress(ByVal pctDone As Long)

End Sub

' Indicate percentage completed for an upload.
Private Sub ftp_PutProgress(ByVal pctDone As Long)
    ProgressBar1.Value = pctDone
End Sub

' Called periodically for upload performance information.
Private Sub ftp_UploadRate(ByVal byteCount As Long, ByVal bytesPerSec As Long)

End Sub

' Called just before a remote directory is to be deleted.
Private Sub ftp_VerifyDeleteDir(ByVal path As String, skipFlag As Long)

End Sub

' Called just before a remote file is to be deleted.
Private Sub ftp_VerifyDeleteFile(ByVal path As String, skipFlag As Long)

End Sub

' Called just before a remote directory is going to be downloaded.
' Setting skip = 1 allows for entire sub-trees to be skipped.
Private Sub ftp_VerifyDownloadDir(ByVal path As String, skipFlag As Long)

End Sub

' Called just before a remote directory is going to be uploaded.
' Setting skip = 1 allows for entire sub-trees to be skipped.
Private Sub ftp_VerifyUploadDir(ByVal path As String, skipFlag As Long)

End Sub

VB6 FTP File Upload Progress Monitoring

Question:

I’ve been trying to get the progress bar in VB6 to work, based on the code on your website, but I’m having no luck.  The event isn’t even being triggered.

Answer:

Here are some things to try:

  1. Test progress monitoring with a large enough file  such that the progress does not go from 0 to 100% instantly.
  2. Use the VB6 IDE to generate the event callback method to make sure you have the naming correct.
  3. Set the SendBufferSize property to a smaller value, to get more frequent callbacks.  See the reference documentation: http://www.chilkatsoft.com/refdoc/xChilkatFtp2Ref.html
  4. Make sure your event callback function updates the UI such that the changes are rendered immediately.  For progress bar controls, setting the Value is all that is needed.  For other controls, you may need to call Refresh.

FTP Progress Monitoring

This post points to the various examples and blog posts scattered among the chilkatsoft.com, example-code.com, and cknotes.com involving progress monitoring of  FTP uploads and downloads using the Chilkat FTP2 ActiveX, .NET component, and C++ libs.

Important for Monitoring Upload Progress: The SendBufferSize property is set to a large value (512K) to maximize performance.  Unfortunately, this usually ruins the frequency of “percent done” callbacks for upload (not downloads, just uploads).  To improve the upload “percent done” callback frequency, set the SendBufferSize property to a smaller value, such as 4K.  It may slow the upload transfer rate a bit, but it improves the progress monitoring capability.  You can experiment with different values for SendBufferSize to determine what best suits your needs.

Monitoring Download Percent-Done: Be sure to read about the AutoGetSizeForProgress and the ProgressMonSize properties in the Chilkat online reference documentation.  Depending on your FTP server, it may be necessary to set the AutoGetSizeForProgress equal to True to properly receive percent-done event callbacks.  In most cases, neither property is needed — it entirely depends on whether the FTP server provides file-size information in the intermediate response to a “RETR” FTP protocol command.

FTP2 Events in C# and VB.NET:

FTP2 Events in VB6

FTP2 Events in C++

FTP2 Events in FoxPro:

More Information: