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());
  	}
  }

“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;

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.

Adding a Callback Event Handler in VB.NET

  1. Dim “WithEvents”
    Dim WithEvents ftp As Chilkat.Ftp2
  2. Add some code to create a new instance of the object.
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
    
            ftp = New Chilkat.Ftp2()
    
            ftp.EnableEvents = True
            ftp.HeartbeatMs = 100
    
    	...
  3. Select the “ftp” object.

  4. Select the event. This causes Visual Studio to generate the event handler function.

  5. Add your application code to the event handler function.
    Private Sub ftp_OnAbortCheck(ByVal sender As Object, ByVal args As Chilkat.AbortCheckEventArgs) Handles ftp.OnAbortCheck
        ' Add your application code here...
        End Sub
    

PercentDone callback counts as an AbortCheck

Question:

“I’ve got the Chilkat C++ libs linked with my project, and am using the HTTP classes successfully.
One thing is confusing me, though. I’ve derived a class from CkHttpProgress.h and overridden AbortCheck(…) to provide cancellation support. However, AbortCheck(…) never gets called. At first, I thought perhaps the HTTP retrieval was occurring too quickly, so I set the “HeartbeatMs” property to “1”, and it still never gets called.
I know the assignment of the callback class is working, because if I override PercentDone(…), it does indeed get called.
Is it possible that my use of the HTTP QuickGetStr(…) method does not support the AbortCheck(…) callback? I’m (successfully) reading a small XML file from a web server in close proximity to the client.”

Answer:
The PercentDone callback method also has an “abort” argument, so it may also be used to abort a method while in progress.  In cases where the PercentDone callback provides abort capability, it counts as an AbortCheck callback — to prevent too many callbacks from bombarding your application.
Therefore, if HeartbeatMs is set to 100ms, and a PercentDone callback is fired, then the timer for the next AbortCheck callback is reset.  The entire operation could be short enough such that all of the callbacks are PercentDone callbacks, and AbortCheck never gets called.  The AbortCheck callback would only be called when the elapsed time between 1% completion intervals exceeds the HeartbeatMs.

Encryption Progress Monitoring

Question:

Is there a way to encrypt a file with progress monitoring?  Huge files can take a while and it seems like the app is hanging.

Answer:

Yes, here is the sample VB6 code:

Public WithEvents myCrypt As ChilkatCrypt2

' ....

Private Sub myCrypt_PercentDone(ByVal pctDone As Long)

    ProgressBar1.Value = pctDone
    
End Sub

Private Sub Command2_Click()

    Set myCrypt = New ChilkatCrypt2
    
    success = myCrypt.UnlockComponent("test")
    
    ' ...

    success = myCrypt.CkEncryptFile("c:/temp/big.txt", "c:/temp/bigEncrypted.dat")
        
End Sub