PLUSNative Overview

If your application is a native application (written in C/C++, for example), or is written in a language which can make calls to shared/dynamic-link libraries, then PLUSNative is the API designed for your needs (see more about selecting a solution).

The purpose of this section is to guide developers through the process of using the PLUSNative API in applications. Understanding this portion of the manual and using the API effectively requires at least a basic understanding of application development and procedural programming, and also requires a basic understanding of the SoftwareKey Systemtm and licensing.

Important

The steps laid out in the common implementation sub-topics of this manual do not match exactly what is in the Protection PLUS 5 SDK sample projects. The manual topics are more simplified to make it easier to explain, while the sample projects have more complex organization, but the resulting functionality is similar in both cases.

Calling Conventions

Calling conventions define how functions or subroutines receive parameter data from the calling code, as well as how the a result is returned. The default calling convention used by your application can vary on operating system, processor architecture, and the programming language used. If you are using an include file provided with PLUSNative (such as PLUSNative.h for C/C++, PLUSNative.bas for Visual Basic 6, PLUSNative.pas for Delphi), then the calling conventions will already be specified correctly for your application. However, if you find you need to define the function prototypes for calling PLUSNative functions, then using the correct calling convention is imperative. The calling conventions used are as follows:

Include Files

The Protection PLUS 5 SDK installation directory has a sub-directory named Include. Each file in this directory contains definitions your application needs to call PLUSNative, and in many cases, it may also contain helper functions that simplify the use of PLUSNative by addressing language-specific limitations and nuances. (An example if this is how the Visual Basic 6 PLUSNative.bas file has its own implementation for SK_ApiResultCodeToString, so you do not need to worry about how to properly deal with string buffers allocated by our library in your code.)

Library Files

The Protection PLUS 5 SDK installation directory also contains a sub-directory named Library. Here, you will find a variety of builds of the PLUSNative API, which are organized as outlined below.

Dynamic Link Libraries / Shared Objects

Dynamic Link Libraries (DLLs), or shared objects, are organized with the following structure: Operating System/[DLL/shared]/Architecture. So for example, the 64 bit Windows DLL is located in Library\Windows\DLL\x64, and the universal OS X library is located in Library/macOS/shared/universal. When used, you will need to distribute one or more library files with your protected application(s).

Static Libraries

Static libraries allow you use PLUSNative with your application(s) without the need to distribute additional library files (this only applies to languages that can statically link a C library, such as C/C++). These files are organized with the following structure: Operating System/Development Environment/[import/static/static-nodeps]/Architecture. The third-level of this structure describes the nature of the static libraries, which include:

Next, you might also notice the static libraries for Windows include two versions named PLUSNative.lib and PLUSNative_md.lib. This is related to the Runtime Library setting in Visual C++ projects (under Configuration Properties/C++/Code Generation in the project properties). The PLUSNative.lib file may be used with the "Multi-threaded (/MT)" and "Multi-threaded Debug (/MTd)" runtime libraries, while the PLUSNative_md.lib file may be used with the "Multi-threaded DLL (/MD)" and "Multi-threaded Debug DLL (/MDd)" runtime libraries.

Getting Acquainted with the PLUSNative API

There are a few things you need to know when implementing PLUSNative.

Understanding PLUSNative's I/O functions

PLUSNative has a variety of functions available to assist with common file input and output (I/O) tasks that are generally necessary for licensing, but are made available to you for convenience. The relevance of these convenience functions to PLUSNative is that they provide you a way to fully control all steps of reading and saving licensing data, while also helping you avoid potential conflict with PLUSNative in areas like character encoding, memory management, formatting requirements, etc... PLUSNative offers a few different types/levels of convenience functions to consider for reading and saving license data:

Working with Windows Registry Paths

When working in Microsoft Windows, you may opt to store the license file and/or alias files in the registry. To use registry paths in PLUSNative that support them, you will need to specify either the hive or a special prefix to the path string to indicate the hive you wish to access as follows:

Hive Prefix
HKEY_CURRENT_USER :CU:
HKEY_LOCAL_MACHINE :LM:
HKEY_CLASSES_ROOT :CR:

Additionally, the name of the value must be appended to the end of the path, after a colon (:) character. So for example, to access a value named MyValue under a HKEY_LOCAL_MACHINE\Software\MyCompany\MyProgram key, you would provide PLUSNative a path like: ":LM:Software\MyCompany\MyProgram:MyValue" or "HKEY_LOCAL_MACHINE\Software\MyCompany\MyProgram:MyValue".

The following API functions support registry access on Windows:

SK_PLUS_LicenseFileLoad

SK_PLUS_LicenseFileSave

SK_PLUS_LicenseAliasAdd

SK_XmlDocumentLoadFile

Strings, Encoding, and Locale

As a C API, PLUSNative uses null-terminated strings. By default, PLUSNative relies on a neutral locale, and expects all strings to use UTF-8 encoding. Additionally, PLUSNative dynamically allocates strings so they only use the amount of space that is required. The benefit is that there is no need to worry about allocating a buffer with enough space, reallocating buffers when there is not enough space, and it tends to be much easier to work with when using higher-level programming languages and frameworks. However, the downside is that you are responsible for freeing the memory allocated using the provided SK_StringDispose function. In most cases, sample code or language bindings provided will handle this for you reliably. However, this is definitely something to be well aware of when implementing PLUSNative using a language where bindings have not already been provided. See the memory management section below for additional details.

Memory Management

Since PLUSNative provides a C API, it is necessary for memory to be managed correctly. In general, safest approach is to base your code off our sample applications. This is because the sample code (which is generally written in higher-level languages, like C++, Objective C, etc...) already manages memory for you correctly.  However, if you are implementing PLUSNative from scratch, then it's pivotal that you understand how to manage memory correctly with this API. Failing to do so can result in memory leaks, random crashes, and can leave your users with a general experience of instability with your application(s). The important things you must understand to avoid these kinds of issues are outlined at a high level below.

Initialization Functions

In PLUSNative, initialization functions can include any function that allocates memory for an object which is returned. This can include things such as the API Context, a string, an XML document, etc...

Disposal Functions

When your application is done using some object or data that PLUSNative has provided/returned, you must use PLUSNative to de-allocate the memory that was previously allocated from an initialization function.

Understand Best Practices

Proper memory management is a critical area of concern for programmers, as it can affect application performance, stability, and (perhaps most importantly) security. Preventing leaks and memory corruption is essential to providing your users a responsive, reliable, and safe experience with your applications. Some practices are outlined below that can be helpful in general, and are essential when using PLUSNative.

Initialize Variable Values

Any time you declare a variable, especially a pointer or an array, initialize the data.  (Generally, this data should be initially zero.) Some examples include:

Verify Pointers

First, consistently initializing pointers to NULL (as described above) is a critical part of verifying pointers reliably. With that practice firmly in place, you can always check that a pointer is not NULL before attempting to use it. Consider the following example excerpt of bad code:

C/C++
char *myString = NULL;
size_t myStringSize = 10;
myString = (char *)malloc(myStringSize);
memset(myString, 0, myStringSize); /*It is bad to do this without checking the pointer first!*/
/*The program might have additional statements here, which could use myString.*/
free(myString); /*It is bad to do this without checking the pointer first!*/

Should the malloc function fail in the example above, the program would crash with an access violation in memset because myString would contain a NULL value. And because it is relatively unlikely for you to run into this condition while debugging and testing yourself, it is more likely that a customer would eventually report the crash (either on a very old computer, or possibly one affected by malware). This can easily be avoided by consistently using basic control structures as shown in the next example excerpt:

C/C++
char *myString = NULL;
size_t myStringSize = 10;
do
{
myString = (char *)malloc(myStringSize);
if (NULL == myString)
{
/*Your application can handle error reporting here.*/
break;
}
memset(myString, 0, myStringSize);
/*The program might have additional statements here, which could use myString.*/
} while (0);
if (NULL != myString)
{
free(myString);
myString = NULL;
}

Using the do/while control structure allows us to check pointers and conveniently skip any logic that would otherwise attempt to use an uninitialized pointer. Of course, you can use any control structure (such as try/catch) you feel is appropriate; what matters most is that you are consistent with your practices.) Additionally, in the expression that checks the pointer, the constant value is on the left side of the expression so as to avoid accidental assignment. (For example, if (myString = NULL) would assign a NULL value to myString and cause a memory leak with this example; and the expression would always evaluate to FALSE, causing the same exact problem as the first example.)

Free Memory Safely and Consistently

Freeing memory safely is directly related to the previous two points on consistently initializing pointers and verifying pointers before use. Attempting to free memory using an uninitialized pointer is just as problematic as attempting to use uninitialized pointers. Additionally, using control structures as shown above is critical to ensuring there are no memory leaks. Consider the example below, which expands on the prior examples:

C/C++
char *myString = NULL;
size_t myStringSize = 10;
char mySecondString = NULL;
size_t mySecondStringSize = 20;
do
{
myString = (char *)malloc(myStringSize);
if (NULL == myString)
{
/*Your application can handle error reporting here.*/
break;
}
memset(myString, 0, myStringSize);

mySecondString = (char *)malloc(mySecondStringSize);
if (NULL == mySecondString)
{
/*Your application can handle error reporting here.*/
break;
}
memset(mySecondString, 0, mySecondStringSize);

/*The program might have additional statements here, which could use myString and/or mySecondString.*/
} while (0);
if (NULL != myString)
{
free(myString);
myString = NULL;
}
if (NULL != mySecondString)
{
free(mySecondString);
mySecondString = NULL;

}

In the example above, the benefit of this overall approach in organizing code is that it can prevent memory leaks in unusual circumstances such as having a successful allocation for myString, but a failed allocation for mySecondString (because myString is still freed even when we break out of the do-while loop because mySecondString's allocation failed).

Consistency is Key

The examples above are meant to provide high-level suggestions.  As with many things, there are many ways to be right. Regardless of the specific control structures and variations from the suggestions and examples here, it is pivotal that you adhere to your practices strictly, and consistently.

Using Protection PLUS 5 SDK Native Edition in multiple threads

When implementing the PLUSNative library with multiple threads, there are specific steps to properly initialize and shutdown the library. If these steps are not correctly implemented, it could result in unexpected behavior or possibly memory leaks in the application.