Table of contents

C++

SDK Files for C++

  • Flat API: GS5_Intf.h, GS5_Intf.cpp

    Provides low-level api interface to native gsCore.dll.

  • OOP API: GS5.h, GS5.cpp

    Based on flat api, these two files provides Object-Oriented programming api.

  • Extension: GS5_Ext.h, GS5_Ext.cpp

    Based on OOP API, these two files are needed to develop customized license model, easy license event handling, etc.

    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/C++ files are in "Lang\C++" folder.

Preparation

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

Initialize SDK

Before you can call most SDK apis, you must initialize the SDK first with the project-specific parameters. Depending on how you pass in the license file, there are two methods to initialize the SDK:

  • Initialize SDK with license file
#include "GS5.h"
using namespace gs;

//These information are copied from IDE:
const char* productId = "8d11ec62-bfa0-4794-9688-41f8aa04630d";
const char* password = "walml_9282&APNQ&18163";
const char* license_filename = "Advanced Tutorial of Notepad2.lic";

//Resolve full path to license file
//Depending where you put the license file, its path can be resolved in different way.
std::string getFullPathToLicenseFile()
{
  char* dir = getenv("MY_DEV_PATH");
  assert(dir != NULL); //
  return std::string(dir) + "/" + license_filename; 
}


TGSCore * core = TGSCore::getInstance();
bool ok = core->init(productId, getFullPathToLicenseFile().c_str(),  password);
if(!ok)
{
  std::cerr << "Error initializing SDK, error code: " << core->lastErrorCode() 
    << ", error message: " << core->lastErrorMessage() << std::endl; 
}

In this demo, we copy our license file to a folder and setup an environment variable MY_DEV_PATH to point to it, so that the app can locate the license file easily.

You can also put the license file side by side with your exe, and resolve the exe path at run-time on Windows via Win32 API GetModuleFileName, 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.

  • Initialize SDK with raw license data

Instead of dealing with the license file path, you can also pass the license information as raw data array:

//The license info must be set in other function before calling init()
const unsigned char* pLicData;
int licSize;

TGSCore * core = TGSCore::getInstance();
bool ok = core->init(productId, pLicData, licSize,  password);

So given a license file, how do you transfer it to a block of memory that can be accessed by C++ code? there are several ways:

  1. Convert raw license file to byte array in C++ soruce code

bin2c

bin2c: Convert binary file to C/C++ source

  
  Usage: bin2c  inputfile [ outfile ]
  
  Parameters:

    input: binary data file to convert
    
    outfile: [Optional] the output pascal source file
            if not specified, outfile = input + ".cpp" 

The output looks like:

/* f:SoftwareShield53_Ne2_test1.lic */
const unsigned char[645] BinaryData = {
        0x47,0x53,0x03,0x00,0x01,0x01,0x0C,0x03,0x00,0x00,0x6B,0x02,0x00,0x00,0x95,0x88,
        0x47,0xB8,0x1E,0x57,0xE0,0x82,0xC1,0x87,0x8D,0x6A,0xDF,0x6F,0xA6,0x14,0x88,0x23,
        0x4B,0x27,0x14,0x60,0xE4,0x3C,0x65,0x17,0x09,0xB9,0x43,0x38,0x45,0x23,0x36,0x67,
    ... };
  1. Embed the license file as exe resource.

Windows provides win32 api to allow you retrieve resource data in memory.

Query License Information

Get Entity

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

-Enumerate All Entities

#include <memory> //for std::auto_ptr

 int N = core->getTotalEntities();
 for(int i = 0; i < N; i++){
   std::auto_ptr<TGSEntity> entity(core->getEntityByIndex(i));
   //dump entity information...
   std::cout << "entity name: " << entity->name() << " id: " << entity->id();
 }

You can also get the specific entity by its entityId:

//EntityId is copied from IDE
const char* entityId = "b15c8ac2-d87c-4483-be77-5a8cbc89a62b";

std::auto_ptr<TGSEntity> entity( core->getEntityById( entityId ));

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

std::auto_ptr<TGSEntity> entity( core->getEntityByIndex( 0 ));

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

All SDK/C++ objects (Entity, License, Action, etc.) are derived from TGSObject, which encapsulates an internal handle created from gsCore, when the object is destroyed, the handle is released and its associated resources are freed in gsCore. So it is very important to delete any SDK/C++ objects when it is not used, the std::auto_ptr<> is perfect for this purpose. If you have C++11 compiler, std::unique_ptr<> or std::shared_ptr<> is also good.

Query Entity License Status

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

  • Get entity Properties
const char* id = entity->id();
const char* name = entity->name();
const char* 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->isUnlocked()){
  //this entity is already activated / fully purchased.
  //app should run in full featured mode for this entity (game level, app module, etc.)...
  run_in_full_featured_mode();
} else if (entity->isLocked()){
  //this entity is locked or trial already expired.
  //we may pop up info to prompt for purchase.
  cannot_run();
} else {
  //this entity still in trial period or duration
  run_in_trial_mode();
}

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

if(entity->isUnlocked()){
  //this entity is already activated / fully purchased.
  //app should run in full featured mode for this entity (game level, app module, etc.)...
  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.
  cannot_run();
}

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:
std::auto_ptr<TGSLicense> lic( entity->getLicense() );

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:

const char* model_id = lic->id();
const char* model_name = lic->name();
const char* model_description = lic->description();

For example, for Expire By Period, its LicenseID is "gs.lm.expire.period.1", so by comparing the license id 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"

int usedTimes = lic->getParamInt("usedTimes");
int maxAccessTimes = lic->getParamInt("maxAccessTimes");

//How many times left the app can launch in trial mode? 
int how_many_access_left = maxAccessTimes - usedTimes;

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.

#include <time.h>

time_t expiryDate = lic->getParamUTCTime("timeEnd");

//Convert UTC time_t to local time
struct tm * timeinfo;
timeinfo = localtime (&expiryDate);
std::cout << "Current local time and date: " << asctime(timeinfo);

The interest part is that SoftwareShield SDK api always returns time_t value, which is an integral value representing the number of seconds elapsed since 00:00 hours, Jan 1, 1970 UTC (i.e., a unix timestamp), you might have to convert it to local time to display the expiry date in local time zone.

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.

#include <time.h>

//Total trial period in seconds
int periodInSeconds = lic->getParamInt("periodInSeconds");

//Figure out when the entity is first accessed (aka. entity->beginAccess() is first called)
//if never accessed before, the _timeFirstAccess_ parameter won't hold a valid datetime and the api can throw exception.

try {
  time_t timeFirstAccess = lic->getParamUTCTime("timeFirstAccess");
  
  //accessed before, so we can calculate the expiry date
  time_t expiryDate = timeFirstAccess + periodInSeconds;
  
  //How many trial time left?
  time_t now;
  time(&now); 
  int timeLeftInSeconds = expiryDate - now;
}catch(...){
  //Never accessed, the expiry date is still unknown!

  //How many trial time left = full trial period.
  int timeLeftInSeconds = periodInSeconds;
}

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
int usedDurationInSeconds = lic->getParamInt("usedDurationInSeconds");
int maxDurationInSeconds = lic->getParamInt("maxDurationInSeconds");

//How many trial time left?
int timeLeftInSeconds = maxDurationInSeconds - usedDurationInSeconds;

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.

int sessionTimeUsed = lic->getParamInt("sessionTimeUsed");
int maxSessionTime = lic->getParamInt("maxSessionTime");

//How many trial time left?
int timeLeftInSeconds = maxSessionTime - sessionTimeUsed;

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.

if(core->isServerAlive()){
  int rc; //returned error code
  bool success = core->applySN(serial, &rc);
}else{
  std::cerr << "license server is not available!";
  //optionally fall back to offline activation...
}
  • Offline Activation

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

  1. Generate Request Code
std::string requestCode = core->getDummyRequestCode();

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:

const char* licenseCode = readLicenseCodeFromUI(); //user input the license code.
bool 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
const char* serialToRevoke;
bool success = core->revokeSN(serialToRevoke);

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

  1. Revoke All Serial Numbers
bool 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):

std::string receiptSN = core->uploadApp();

On machine B (receiver):

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

On machine A (sender):

std::string lic_backup = core->exportApp();

On machine B (receiver):

std::auto_ptr<TMovePackage> mv (core->createMovePackage( lic_backup ));

if(core->isServerAlive()){
  //online import
  bool success = mv->importOnline( serial );
}else{
  //offline import
  std::string moveRequestCode = mv->getImportOfflineRequestCode();
  
  // the user reads the request code from app UI and gets license code from support team (or via App Web Activator)
  const char* licenseCode;
  bool success = mv->importOffline(licenseCode);
}

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.

const char* serial; //the serial issued from app vendor
if(!core->isValidSN( serial )){
  core->lockAllEntities();
  exit(0); //quit app
}

Listen to Events

To handle SoftwareShield license events, you can sub-class the TGSApp, overriding any event handler as needed:

In MyApp.h:

#include "GS5_Ext.h"

 void initSDK();

 class TMyApp : public TGSApp {
   DECLARE_APP(TMyApp);
   
   private:
     virtual void OnAppBegin(){
       //App launches, say hello...
     }
     
     virtual void OnAppExit(){
       //Render Exit-UI
       std::auto_ptr<TGSEntity> entity( core()->getEntityByIndex(0) );

       if(entity->isUnlocked()){
         //activated, do nothing
       }else if(entity->isLocked()){
         //trial already expired, need license to run
         messagebox("your trial is expired, please activate now");
       }else {
         //trial mode...
         messagebox("your trial will expire in XXX");
       }       
     }
     
     virtual void OnEntityAccessStarting(TGSEntity* entity){//EVENT_ENTITY_TRY_ACCESS
       if(entity->isUnlocked()){
         //activated, do nothing
       }else if(entity->isLocked()){
         //trial already expired, need license to run
         render_activate_UI( entity );
       }else {
         //trial mode...
       }
     }
     
     virtual void OnEntityAccessInvalid(TGSEntity* entity, bool inGame){//EVENT_ENTITY_ACCESS_INVALID
       //trial expired!
        render_activate_UI( entity );
     }
     
     virtual void OnEntityHeartBeat(TGSEntity* entity){ //EVENT_ENTITY_ACCESS_HEARTBEAT
       //check when the entity will expire, inspect its license parameter to give user feedback  
       //when 5 minutes left before expire...
     }
 };

In MyApp.cpp:

#include "MyApp.h"

IMPLEMENT_APP(TMyApp);


void initSDK(){
  GS_INIT_APP;
  
  TGSCore* core = TGSCore::getInstance();
  bool ok = core->init(productId, full_path_to_license_file,  password);
   ...
}

Upon app launching, you should call initSDK() to initialize SoftwareShield SDK first.