Table of contents

Visual Basic 6

Visual Basic 6 is the last native version of Visual Basic developed by Microsoft released on 1998 (now known simply as Visual Basic), On April 8, 2008 Microsoft stopped supporting Visual Basic 6.0 IDE. The Microsoft Visual Basic team still maintains compatibility for Visual Basic 6.0 applications on Windows Vista, Windows Server 2008 including R2, Windows 7, Windows 8, Windows 8.1, Windows Server 2012 and Windows 10 through its "It Just Works" program. In 2014 there were tens of thousands of developers who still prefer Visual Basic 6.0 over Visual Basic .NET

SDK Files for Visual Basic 6

  1. Initialize API: GSIntf.bas, GS.cls

Provides initialize api interface to native gsCore.dll.

  1. OOP Class API:

these files provides Object-Oriented programming api to different SoftwareShield objects:

  1. License Model Inspector API: GS5_Ext.pas

These files provides apis to inspect license model parameters:

  1. Helper: GSHelper.bas

    Provides date time helper utility functions.

The source list here is just for quick online reference, please refer to "How to get the latest SDK?" to get the original SDK files. The SDK/VB6 files are in "Lang\VB6" folder.

Preparation

  1. Prepare gsCore lib, license file and parameters;
  2. Copy SDK/VB6 (*.bas, *.cls) files to your app source directory, add them as part of your app project;

We first make a copy of the folder "SDK_Install_Dir\Lang\VB6" to a new folder called "VB6SDK" side by side with your project folder (for example, VBdemo3):

Now we need to add our SDK files as part of the app project, you can press "Ctrl + D" to add all VB6SDK files one by one in VB6 IDE, which is too boring and error-prone, or you can quit VB6 IDE first, open the *.VBP project file in a text editor and paste the following lines (the content of file "SDK/VB6/AddVB6SDKFiles.txt"):

Module=TGSIntf; ..\VB6SDKSoftwareShieldIntf.bas
Module=TGSHelper; ..\VB6SDKSoftwareShieldHelper.bas

Class=TGSApp; ..\VB6SDKSoftwareShieldApp.cls
Class=TGSCore; ..\VB6SDKSoftwareShieldCore.cls
Class=TGS; ..\VB6SDKSoftwareShield.cls
Class=TGSEvent; ..\VB6SDKSoftwareShieldEvent.cls
Class=TGSAction; ..\VB6SDKSoftwareShieldAction.cls
Class=TGSEntity; ..\VB6SDKSoftwareShieldEntity.cls
Class=TGSLicense; ..\VB6SDKSoftwareShieldLicense.cls
Class=TGSRequest; ..\VB6SDKSoftwareShieldRequest.cls
Class=TGSVariable; ..\VB6SDKSoftwareShieldVariable.cls
Class=TMovePackage; ..\VB6SDKSoftwareShieldMovePackage.cls

Class=TGSInspector; ..\VB6SDKSoftwareShieldInspector.cls
Class=TGSInspector_Period; ..\VB6SDKSoftwareShieldInspector_Period.cls
Class=TGSInspector_Duration; ..\VB6SDKSoftwareShieldInspector_Duration.cls
Class=TGSInspector_HardDate; ..\VB6SDKSoftwareShieldInspector_HardDate.cls
Class=TGSInspector_AccessTime; ..\VB6SDKSoftwareShieldInspector_AccessTime.cls
Class=TGSInspector_SessionTime; ..\VB6SDKSoftwareShieldInspector_SessionTime.cls

Save the *.VBP file and re-open it in VB6 IDE, now all SDK files are included in the test app:

If your project is not side by side with the VB6SDK folder, please correct the path before copy & paste.

  1. Copy gsCore.dll to a folder in PATH environment variable, or to the folder where the VB6 is installed.

By default, the VB6 is installed in "C:\Program Files\Microsoft Visual Studio\VB98" (32bit Windows) or "C:\Program Files (x86)\Microsoft Visual Studio\VB98" (64bit Windows). Once the gsCore.dll is accessible to the VB6 IDE, you can debug the project in source code level.

Initialize SDK

Before you can call most SDK apis, you must initialize the SDK first with the project-specific parameters:

Private Sub Form_Load()
    REM These information is a copy from SoftwareShield IDE
    const productId = '8d11ec62-bfa0-4794-9688-41f8aa04630d';
    const password = 'walml_9282&APNQ&18163';
    const license_filename = 'Advanced Tutorial of Notepad2.lic';
    

    On Error GoTo ErrorHandler
 
    REM We put the license file side by side with the exe, and pass the full path to it
    SdkInitialize productId, App.Path & "\" & license_filename, password
    
ErrorHandler:
    REM Update error message
    MsgBox "Error Code:" & gs.Core.LastErrorCode & " Error Message: " & gs.Core.LastErrorMessage
        
End Sub

In this demo we put the license file side by side with your exe, and resolve the exe path at run-time via App.Path, anyway, you must pass in the full path of the license file, otherwise the SDK will blinkdly assume the license file is in current directory, which will fail if the app is launched from another folder.

Query License Information

Get Entity

To query license information, you first need to find the entity objects:

  • Enumerate All Entities
Dim i as Integer
Dim core as TGSCore
Dim entity As TGSEntity

Set core = gs.Core

For i = 0 To core.EntityCount-1
  Set entity = core.Entities(i)
  
  REM dump entity information...
Next

You can also get the specific entity by its entityId:

REM EntityId is copied from IDE
const entityId = "b15c8ac2-d87c-4483-be77-5a8cbc89a62b"

Set entity = core.getEntityById( entityId )

If your app has only one entity, let's get it by a single line:

Set entity = core.Entities(0)

This is because the entity is the only single entity with index 0.

Query Entity License Status

Given an entity object, we can easily retrieve its properties:

  • Get entity Properties
Dim  id, name, description As String
  
id = entity.Id
name = entity.Name
description = entity.Description
  • Test if entity is fully licensed, in trial mode or already expired

If your entity is associated with a trial license model (Expire by Access Times, Expire By Hard Date, Expire By Period, Expire by Duration, Expire by Session Time), you can test the entity's license status as following:

If entity.Unlocked Then
  ' this entity is already activated / fully purchased.
  ' app should run in full featured mode for this entity (game level, app module, etc.)...
  Call run_in_full_featured_mode()
ElseIf entity.Locked Then
  'this entity is locked or trial already expired.
  'we may pop up info to prompt for purchase.
  Call cannot_run()
Else
  'this entity still in trial period or duration
  Call run_in_trial_mode()
End If

If your entity is using License Model Always Lock, then the logic can be simplied as:

If entity.Unlocked then
  'this entity is already activated / fully purchased.
  'app should run in full featured mode for this entity (game level, app module, etc.)...
  Call run_in_full_featured_mode()
Else 
  'this entity is still locked, we need a license to continue.
  'we may pop up info to prompt for purchase.
  Call cannot_run()
End If

Query License Model Parameters

In the above section, we have detected that an entity is still in trial mode, so in order to display proper license information to end user on app UI, such as tell the game player how many trial time left before the game will stop running, we must be able to get more details of the license model.

  • Get License Object Given an entity, it is quite simple to retrive the license object attached to this entity:
Dim lic As TGSLicense
  
Set lic = entity.License

Now that the license object is ready, we can inspect it in various perspectives:

  • Get License Model Type

As a developer we have designed the license project in SoftwareShield IDE before, so we should have known which kind of license model being used for an entity. However, if you are developing a universal logic to parse any entity, it is helpful to find out the license model type being bundled with the input entity:

SoftwareShield SDK defines the following enumeration type for each built-in license model:

Public Enum GSLicenseModelKind
    LM_UNKNOWN = 0
    
    LM_EXPIRE_PERIOD = 1
    LM_EXPIRE_DURATION = 2
    LM_EXPIRE_HARDDATE = 3
    LM_EXPIRE_ACCESSTIME = 4
    LM_EXPIRE_SESSIONTIME = 5
    LM_ALWAYS_LOCK = 6
    LM_ALWAYS_RUN = 7
End Enum

To get the license's type, you can use the license's property Kind:

Dim  model_kind As GSLicenseModelKind
Dim  model_description As String
  
model_kind = lic.Kind
model_description = lic.Description

For example, for Expire By Period, its kind is LM_EXPIRE_PERIOD, so by comparing the license kind you can figure out the correct license model type to inspect further.

  • Get License Model Parameters

Now we have known which license model to inspect, let's retrieve the license parameters according to its model type.

Expire by Access Times

This license model (Expire by Access Times) has two parameters of int type: "usedTimes" and "maxAccessTimes"

Dim usedTimes, maxAccessTimes, how_many_access_left As Integer
Dim inspector As TGSInspector_AccessTime
  
Set inspector = lic.Inspector

usedTimes = inspector.TimeUsed
maxAccessTimes = inspector.MaxAccesssTime

'How many times left the app can launch in trial mode? 
how_many_access_left = inspector.TimeLeft

Expire By Hard Date

This model (Expire By Hard Date) is a bit complex since it have three different use cases, in this tutorial we only demonstrate the most commonly used one: "Expire After", in this case, the license has a predefined expiry date "timeEnd", the app trial mode expires after this date.

Dim expiryDateLocal As Date
Dim inspector As TGSInspector_HardDate 

Set inspector = lic.Inspector
expiryDateLocal = inspector.ExpireDate

The interest part is that SoftwareShield SDK api always returns TDateTime in UTC time zone, but \SDK for VB6 inspectors have convert it to local time to display the expiry date in local time zone. SoftwareShield SDK/VB6 has several time convert functions in TGSHelper.bas:

'Helpers
'UTC <=> Local
Function LocalTimeToUTC(ByVal the_date As Date) As Date
Function UTCToLocalTime(ByVal the_date As Date) As Date

'Date <=> SYSTEMTIME
Sub DateToSystemTime(ByVal the_date As Date, ByRef system_time As SYSTEMTIME)
Sub SystemTimeToDate(system_time As SYSTEMTIME, ByRef the_date As Date)

'Format seconds to a string in format "XX day(s) YY hour(s) ZZ minute(s) DD second(s)"
Function ConvertSecondsToString(ByVal totalSeconds As Long) As String

Expire By Period

This license model (Expire By Period) has a UTC datetime parameter "timeFirstAccess", specify the timestamp the entity is first accessed, and an integer parameter "periodInSeconds", specify the total trial period allowed. The following code tries to figure out when the license will expire and how many trial time left before expire.

Dim  periodInSeconds, timeLeftInSeconds As Integer;
Dim  timeFirstAccess, expiryDate As Date;
Dim inspector As TGSInspector_Period

Set inspector = lic.Inspector

periodInSeconds = inspector.Period
timeLeftInSeconds = inspector.TimeLeft

'Figure out when the entity is first accessed (aka. entity.beginAccess() is first called)

If inspector.IsEntityEverAccessed Then
   timeFirstAccess = inspector.TimeFirstAccess
   expiryDate = inspector.ExpireDate
Else
   ' Never Accessed!
End If

The interesting part of the above code is that: if the "timeFirstAccess" does not hold a valid timestamp (the default value is undefined), which occurs when the entity is never accessed before, we cannot deduce the exact expiry date, however we can deduce how many time left before expire.

Expire by Duration

This license model (Expire by Duration) is simple, it just adds up all elapsed trial time cumulatively (in "usedDurationInSeconds") until the total trial time exceeds the pre-defined maximum value ("maxDurationInSeconds"). We cannot figure out the expiry date.

'Total trial period in seconds
Dim maxDurationInSeconds, usedDurationInSeconds, timeLeftInSeconds As Integer
Dim inspector As TGSInspector_Duration

Set inspector = lic.Inspector

maxDurationInSeconds = inspector.Duration
usedDurationInSeconds = inspector.TimeUsed

'How many trial time left?
timeLeftInSeconds = inspector.TimeLeft

Expire by Session Time

This license model (Expire by Session Time) allows an entity can always be accessed (never expire) but in a limited time, we can get the session time left but cannot get the expiry date.

Dim maxSessionTime, sessionTimeUsed, timeLeftInSeconds As Integer
Dim inspector As TGSInspector_SessionTime

Set inspector = lic.Inspector

maxSessionTime = inspector.MaxSessionTime
sessionTimeUsed = inspector.TimeUsed
timeLeftInSeconds = inspector.TimeLeft

Activation

There are two methods to activate your app, online and offline.

  • Online Activation

After user've made purchase of your app, you send a serial number to the customer as a proof of valid customer and the key to activate your app.

Dim rc As Long
Dim success As Boolean
  
If core.isServerAlive Then
  success = core.applySN(serial, rc)
Else
  ' license server is not available!
  ' optionally fall back to offline activation...
End If
  • Offline Activation

In order to do offline activation, we need to go through three steps:

  1. Generate Request Code
Dim requeseCode As String
  
requestCode = core.DummyRequestCode

We are using dummy request code because the license server can deduce all valid actions from the customer's serial number.

  1. Get License Code from License Server

You display the request code on UI, asking the customer to contact your support team for a valid license code, or if possible, let the customer uses another machine (or a mobile device) to generate the license code by himself/herself via the app's public App Web Activator.

  1. Apply License Code

Once the user input a license code, we can apply it to the app:

'user input the license code.
Dim licenseCode As String 
Dim success As Boolean
  
success = core.applyLicenseCode(licenseCode, serial);

Revoke License

SoftwareShield SDK allows you to revoke serial numbers to license server so that the revoked serial numbers can be reused in another machine or the same machine (after hardware upgrade, system re-install, etc.)

  1. Revoke Single Serial Number
Dim serialToRevoke As String
  
success = core.revokeSN(serialToRevoke)

After successful revoke, all those entities previously unlocked by this serial are locked.

  1. Revoke All Serial Numbers
success = core.revokeApp

After successful revoke, all previously unlocked entities are locked.

Transfer License

Revoke serial number is good for most cases, but there are other scenerios that the serial-based revoke won't work. For example, your license model is based on Pay-as-you-go concept, your app is not fully activated, the serial is used to extend the app's life span, so the unlock-lock based serial-number revoke does not make sense at all.

  • To online transfer the current license information from machine A to machine B

On machine A (sender):

Dim receiptSN As String
  
receiptSN = core.uploadApp

On machine B (receiver):

success = core.applySN(receiptSN)
  • To offline transfer the current license information from machine A to machine B:

On machine A (sender):

Dim lic_backup As string
  
lic_backup = core.exportApp

On machine B (receiver):

Dim  mv As TMovePackage
Dim success As Boolean
Dim moveRequestCode As String
  
'the user reads the request code from app UI and gets license code from support team (or via App Web Activator)
Dim licenseCode As string
   
  mv = core.createMovePackage( lic_backup )
  
  If Core.IsServerAlive Then
    'online import
    success = mp.importOnline(serial)
  Else
    'offline import
    moveRequestCode = mp.getImportOfflineRequestCode
    
    'user get the licenseCode...
    
    success = mp.importOffline(licenseCode)
  End If

  Set mv = Nothing

The workflow seems a little messy, but you do not have to fully support all online/offline cases in your app. The SoftwareShield SDK provides complete apis to support all possible use cases.

How to lock app when the serial number becomes invalid

Sometimes you want to implement a logic to test if a serial number is still valid before app can run at client side. You can disable or delete a serial number at CheckPoint2 license management web portal, as a result you can remote control if the app should run by manipulating the serial number on the server side.

'the serial issued from app vendor
Dim serial As String 
  
If Not core.isValidSN( serial ) Then
  core.lockAllEntities;
  'quit app...
End If

Listen to Events

To handle SoftwareShield license events in VB6, you can add event handlers to interested events:

In GSApp.cls, there are all pre-defined events:

'Generic event handler
Public Event OnEvent(ByVal eventId As Long)

Public Event OnAppBegin()
Public Event OnAppRun()
Public Event OnAppExit()
Public Event OnAppFirstRun()

Public Event OnLicenseLoading()
Public Event OnLicenseLoaded()


Public Event OnAccessStarting(entity As TGSEntity)
Public Event OnAccessStarted(entity As TGSEntity)

Public Event OnAccessEnding(entity As TGSEntity)
Public Event OnAccessEnded(entity As TGSEntity)

Public Event OnAccessInvalid(entity As TGSEntity)
Public Event OnAccessHeartBeat(entity As TGSEntity)

Public Event OnUserEvent(ByVal eventId As Long, ByVal usrDataPtr As Long, ByVal usrDataSize As Long)

'Error handler
Public Event OnError(ByVal err As GSErrorType, ByVal errCode As Long, ByVal errMsg As String)

To handle these events, you can add the following lines to the Form code file as following:

'Declare the instance of gs.App so that all events become visible in VB6 IDE
Private WithEvents GSApp As TGSApp

Then in the VB6 IDE, you can choose the GSApp object and add event handler as usual:

In FormMain.frm we catch several events in the demo app:

Private WithEvents GSApp As TGSApp
Private core As TGSCore

'Initialize SDK on form loading
Private Sub Form_Load()
  'We put the license file side by side with the exe, and pass the full path to it
  SdkInitialize ProductId, App.Path & "\" & LicenseFile, Password
 
  'Assign GSApp so we can receive events
  Set GSApp = gs.App

  'Assign the single gs.Core instance for later reference
  Set Core = gs.Core
 
  ...
  
End Sub

Private Sub GSApp_OnAppBegin()
 'App launches, say hello...
End Sub

Private Sub GSApp_OnAppExit()
  Dim entity As TGSEntity
  
  ' Render Exit-UI
  Set entity = Core.Entities(0)

  If entity.Unlocked Then
    ' activated, do nothing
  ElseIf entity.Locked Then
    ' trial already expired, need license to run
    MsgBox ("your trial is expired, please activate now")
  Else
    ' trial mode...
    MsgBox ("your trial will expire in XXX")
  End If
End Sub

'EVENT_ENTITY_TRY_ACCESS
Private Sub GSApp_OnAccessStarting(entity As TGSEntity)
 If entity.Unlocked Then
   ' activated, do nothing...
 ElseIf entity.Locked Then
  ' trial already expired, need license to run
  ' render_activate_UI(entity)
 Else
   ' trial mode...
 End If
End Sub

'EVENT_ENTITY_ACCESS_INVALID
Private Sub GSApp_OnAccessInvalid(entity As TGSEntity)
  ' trial expired!
  ' render_activate_UI(entity)
End Sub

'EVENT_ENTITY_ACCESS_HEARTBEAT
Private Sub GSApp_OnAccessHeartBeat(entity As TGSEntity)
  ' check when the entity will expire, inspect its license parameter to give user feedback
  ' when 5 minutes left before expire...
End Sub
  

So you can see that the SoftwareShield event handling in Visual Basic is pretty easy in IDE.