Chilkat ActiveX Object Creation in VB6 (Visual Basic 6.0)

Most ActiveX objects, including Chilkat, provide what is called a “dual interface”.   A dual interface allows for programs to bind at compile-time (early binding) or at runtime (late binding).

The type of binding is determined by how the object is created.  For example, compile-time binding in VB6 looks like this:

Dim cert As New ChilkatCert

Runtime binding looks like this:

Set cert = CreateObject(“Chilkat_9_5_0.Cert”)

An ActiveX class (such as ChilkatCert) has a set of properties and methods.  In the underlying COM object, these are contained in a “vTable”, which is an array of function pointers.  Each method or property (i.e. entry) is a function pointer occupying a particular position in the vTable.  For example, the function LoadPem might be the function pointer at index 12.

When compile-time binding is used, the indexes of the entries are hard-coded into  your VB6 executable.  When runtime binding is used, the entry is called by name.  The name is converted to the correct vTable index at runtime.  This means that if the positions of entries change, the application that uses runtime binding will not break.  The application that uses compile time binding must be recompiled to use the updated ActiveX.

For many years, the positions of the entries in the Chilkat ActiveX classes mostly did not change.  This was not by a deliberate action.  Recently, with the introduction of the Async versions of methods, and especially with the latest v9.5.0.64 release, the order of entries in the vTable has unintentionally changed.  This only affects programs that use compile-time (early) binding.  Most languages that provide the ability to use ActiveX classes, such as classic ASP, SQL Server, etc. only provide the ability to create objects via CreateObject (i.e. runtime/late binding), and are thus unaffected.

This leaves VB6 programs using compile-time binding in a difficult position.  If a new version of the ActiveX is registered, then the VB6 application must be recompiled.  It’s not possible to simply install the new ActiveX *if* the vtbl has changed.  Unfortunately, there is no way to fix the versions of Chilkat that have already been released.  In addition, there is no way to make the next version of Chilkat have the same vTable ordering as previous versions.  For example, if v9.5.0.59 has order A, and v9.5.0.64 has order B, then v9.5.0.65 cannot have and order that matches both A and B.

The solution is to have a scheme in place that works going forward.  The proposed scheme is this:

  1. Starting with v9.5.0.65, the vtbl order will become established.  Future versions of Chilkat (where the CLSID is the same) will have vTables with the order of entries matching v9.5.0.65.
  2. When Chilkat releases an entirely new ActiveX, where the CLSID changes and the version, for example, becomes 10.0.0, then the vTable order can change.  This is OK because effectively it’s an entirely new ActiveX.  In other words, the CreateObject statement becomes:  CreateObject(“Chilkat_10_0.Cert”) instead of CreateObject(“Chilkat_9_5_0.Cert”).  When the CLSID changes, and the names of the objects change, then it’s a new ActiveX that can be registered and coexist with the older Chilkat ActiveX.  To use the entirely new ActiveX, a VB6 program would have to remove it’s reference from the old, and add a reference to the new. (When adding a reference in VB6, you would see BOTH “Chilkat_10_0” and “Chilkat_9_5_0” and you could choose either.

 

Chilkat Success/Failure Return Status Values in VB.NET vs. VB6

When programming in VB.NET or VB6, be careful that you are looking at the correct online examples.

VB6 examples are located here.

whereas

VB.NET examples are located here.

In VB6, it is the Chilkat ActiveX that is used, and return values (in general) are 1 for success and 0 for failure. However, in VB.NET, return values are True for success and False for failure. The value True is not equal to 1. Therefore, if programming in VB.NET, do not check for 1/0 return values, make sure to check for True/False.

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.

VB6 Error — “TYPE MISMATCH (ERROR CODE 13)” w/ IMAP Search method

This happens if the Chilkat ActiveX objects are dragged and dropped onto your formvia the “Project–>Components” menu. Instead, add a reference to the objects via the “Project–>References” menu.

Instances of the object should be created dynamically. For example:

Dim imap as ChilkatImap
Set imap = New ChilkatImap

Windows Application User-Interface Freezes?

Question:

I recently purchased the Chilkat HTTP component and am using it with a Visual FoxPro application. However, each time I run it the form freezes and I get the Not Responding message in the title bar. I have an animated GIF that I am using as a quasi-progress meter but that freezes as well. As soon as the HTTP Download finished, the form gets the focus back and the download is successful.

I need the animated GIF to keep working and to avoid the freeze and Not Responding message. Happens every time. Plus the mouse cursor pointer disappears from the VFP form.

Answer:

To answer this question, I must first describe the basic architecture of all interactive Windows-based applications w/ user-interfaces. This applies to all programming languages: FoxPro, VB6, MFC (C++), C#, VB.NET, etc.

Microsoft Windows programs are event-based. They act upon messages that the operating system posts to the main application thread. These messages are received from the message queue by the application by repeatedly calling the GetMessage (or PeekMessage) function in a section of code called the “event loop.” (See this Wikipedia article: Message Loop in Microsoft Windows)

In higher-level programming languages such as FoxPro, VB6, etc., the actual code for the app’s message loop is hidden from you. The application programmer interactively designs a user-interface and provides code (handlers) for user-interface events. For example, when “Button A” is pressed, call this subroutine (or function). Typically, the code in your button handler finishes quickly and returns control back to the caller. It happens so fast that you never notice that technically the user-interface was “frozen” for the short amount of time spent in your button-handler code.

It becomes apparent that user-interface events are not being handled when your application code takes longer to complete before returning control back to the app’s main event loop (i.e. before returning control to the FoxPro, VB6, C#, etc. runtime). It may also help to think of it this way: Since the application is single-threaded, it can only be doing one thing at a time. While the application thread is executing the application code, it can’t possibly be doing something else, such as executing code to update the user-interface.

The solution is that within your application code you must periodically handle any accumulated user-interface events. In Visual Basic 6.0, C#, and VB.NET, this is accomplished by calling the DoEvents function:

(from the Microsoft online documentation for Application.DoEvents)

“When you run a Windows Form, it creates the new form, which then waits for events to handle. Each time the form handles an event, it processes all the code associated with that event. All other events wait in the queue. While your code handles the event, your application does not respond. For example, the window does not repaint if another window is dragged on top.

If you call DoEvents in your code, your application can handle the other events. For example, if you have a form that adds data to a ListBox and add DoEvents to your code, your form repaints when another window is dragged over it. If you remove DoEvents from your code, your form will not repaint until the click event handler of the button is finished executing…”

In Visual FoxPro, it is the DOEVENTS command.

Since the bulk of the time spent within your application code may be within a Chilkat function call (especially if it involves communications over the Internet), then you must tell the Chilkat component to make periodic callbacks to your application code, so that your application can then call DoEvents. The standard way of doing this w/ Chilkat is via the AbortCheck event. The HeartbeatMs property controls the frequency of AbortCheck event callbacks. The default value is 0, which indicates no AbortCheck events. Set it to a value such as 100 for event callbacks every 1/10 of a second.

For more information: ActiveX Events in FoxPro

For more information: AbortCheck in C# and VB.NET

VB6 – “TYPE MISMATCH (ERROR CODE 13)” or “Invalid Use of New Keyword”

This error occurs if you incorrectly add the ActiveX component to your VB6 project.  Follow these rules:

1) If the ActiveX objects are dynamically created (they are not dragged and dropped onto a form from the palette), then add a Reference to the ActiveX.  You should NOT see the icons for the ActiveX in the palette.  Instances of the ActiveX objects are created by New statements in your code.

2) If the Chilkat ActiveX objects are dragged and dropped onto your form, then add the component via the “Project–>Components” menu, NOT the “Project–>References” menu.  In my opinion, it is always better to create instances of the objects dynamically because the Chilkat objects are not visual controls and won’t be visible on your form anyway at runtime.

3) Never do both — you should either add a Reference (Project–>References) or add the component to the palette (Project–>Components) but NEVER BOTH.

How to use an ActiveX in VB6

There are two ways to reference an ActiveX in Visual Basic 6.0.  The first is to select “Project–>References” from the VB6 menu.  The second way is to select “Project–>Components…” from the VB6 menu.

Important:  You should do one or the other, but not both.

Which do you choose?   Use “Project–>References” if you are going to instantiate the ActiveX dynamically.  In other words, you’ll be creating the object dynamically using the New statement.  Such as:

Dim mailman As New ChilkatMailMan2

Dim mm as ChilkatMailMan2
Set mm = New ChilkatMailMan2

Use “Project–>Components…” if you are going to drop an instance of the component onto a Form. When you include an ActiveX using “Project–>Components…”, you’ll see icons added to your toolbox. In this case, you’ll see an icon for the Chilkat MailMan object. You would drag this onto your Form. Adding an ActiveX in this way only makes sense if the component has a visual interface. The Chilkat ActiveX components are non-visual, so there is no point in doing it this way, and I would highly recommend adding a Reference and instantiating the object instances dynamically using the New statement.

Common Errors:

If you add the ActiveX via “Project–>Components…”  and then try to instantiate an object dynamically using the New statement, you’ll get the “Invalid use of New keyword” error.

If you don’t add the ActiveX to your project in any way, (i.e not through “Project–>References” nor through “Project–>Components…”,  you’ll get the “User-defined type not defined” error.

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

VB6 – SHA-1 Hash and Base64 Encode

Private Sub Command1_Click()

    Dim crypt As New ChilkatCrypt2
    
    success = crypt.UnlockComponent("30-day trial")
    If (success = 0) Then
        MsgBox crypt.LastErrorText
        Exit Sub
    End If
    
    Dim plainText As String
    plainText = "To be SHA-1 hashed..."
    
    crypt.EncodingMode = "base64"
    
    ' Hash the string and base64-encode in 1-Step
    Text1.Text = crypt.HashStringENC(plainText)
    
    ' Now do it in 2-steps. Should produce the same results as for HashStringENC...
    ' Hash the string to get the raw hash bytes, then
    ' base64 encode:
    Dim hashBytes As Variant
    hashBytes = crypt.HashString(plainText)
    
    ' Now Base64 encode:
    Text2.Text = crypt.Encode(hashBytes, "base64")
    
End Sub

VB6 Variant vs Byte Array

In Visual Basic 6.0, a Variant containing a byte array is different than a byte array.
For example, examine this code:

    ' Assume mime is a ChilkatMime object...
    Set mimePart = mime.GetPart(1)
    
    thefile = FreeFile()
    'Get the attachment filename
    FileName = mimePart.FileName
    'Get the attachment
    Dim mBody As Variant
    mBody = mimePart.GetBodyBinary
    If Len(mBody) > 0 Then
        Open FileName For Binary As #thefile
            Put #thefile, , mBody
        Close #thefile
    End If

The result is not what you expected. There’s a mysterious extra 12 bytes prepended to the content that is saved. Why? Because you saved the Variant structure as well as the contents of the Variant.
This is what you really wanted:

    ' Assume mime is a ChilkatMime object...
    Set mimePart = mime.GetPart(1)
    
    thefile = FreeFile()
    'Get the attachment filename
    FileName = mimePart.FileName
    'Get the attachment
    Dim mBody() As Byte
    mBody = mimePart.GetBodyBinary
    If UBound(mBody) > 0 Then
        Open FileName For Binary As #thefile
            Put #thefile, , mBody
        Close #thefile
    End If

In the above code fragment, mBody is a byte array. There is no Variant structure encapsulating the data. The content of the MIME body is saved exactly as you expected.

Note: There is an implicit conversion happening in this statement:

mBody = mimePart.GetBodyBinary

GetBodyBinary is a function that returns a Variant. However, mBody is declared as an array of Byte. Therefore, the VB6 runtime is converting the Variant to a byte array. In other words, it’s removing the Variant wrapping so-to-speak…