8. License and demo mode #
← FAQ and troubleshooting · Contents
PBToolboxAI is a commercial library. License checking is entirely offline: no connection, no activation, no server call. A signed key is set at startup and the DLL verifies it locally.
8.1 The three key types #
Keys are cumulative: call PBT_SetLicense as many times as needed, the library keeps one key per type.
| Type | Holder | Unlocks |
|---|---|---|
dev | Named, one developer | Only inside the PowerBuilder IDE |
runtime | Your company, embedded in the application you ship | Only inside a compiled executable |
demo | Shipped with the demo application | The demo application, inside the IDE, until it expires |
A project therefore needs both: one dev seat per developer, and one runtime key for the application you deliver to your customers.
- A
devkey that ends up in an executable unlocks nothing. - A
runtimekey grants no development seat. - Your end customers have nothing to enter and nothing to activate: the
runtimekey is in your code.
The "IDE or executable" detection is performed by the DLL itself (process name, design-time modules loaded), not by PowerBuilder code — it cannot be bypassed from your sources.
8.2 Setting the key #
Only one thing to do, before the first window opens:
// Declaration - application object, "Local External Functions"
Function long PBT_SetLicense (string as_client, string as_key) Library "pbtoolboxai.dll"
// open event of the application object
// Local variables
long ll_rc
string ls_dev_key
// The RUNTIME key belongs in the code : it MUST ship inside the exe.
ll_rc = PBT_SetLicense(gs_licensee, gs_runtime_key)
// The DEV key is read from the workstation : it only unlocks the IDE, and
// embedded in the code it would ship in every copy without serving anything.
ls_dev_key = ProfileString("pbtoolboxai.ini", "license", "dev", "")
if ls_dev_key <> "" then ll_rc = PBT_SetLicense(gs_licensee, ls_dev_key)
Open(w_main)
The key is set through the DLL function, never through a component method: it applies to the whole process and has to be set before any component exists. That is the same reason
PBT_LicenseStatusasks for no handle.
The holder is passed in clear, as the first argument: the name (or e-mail address) the licence was issued to, and it must match the one carried by the signed key. A key therefore cannot travel without the name of whoever bought it.
| Return | Meaning |
|---|---|
0 | Key valid and retained |
-1 | Invalid key format |
-2 | Invalid signature (key tampered with, or from another vendor) |
-3 | Maintenance expired for this version of the library (see 8.5) |
-4 | demo key expired |
-5 | The holder passed does not match the one inside the key |
💡 Which key goes where. The
runtimekey belongs in the code: it must end up inside the executable you ship. Thedevkey does not — it only unlocks the IDE, so inside an exe it is useless while still travelling around, carrying your company name. Its place is apbtoolboxai.inifile next to the IDE, or a registry value.
Setting the key after a component has been created also works: components that are already open unlock immediately. But the application open event remains the simplest place.
💡 Keep the key in a constant or in an application resource. It is signed: it contains no exploitable secret and cannot be modified without invalidating its signature.
8.3 Checking the license state #
// Declaration
Function long PBT_LicenseStatus (ref string as_json, long al_len) Library "pbtoolboxai.dll"
// Local variables
string ls_status
// The licence status, as JSON
ls_status = Space(1024)
PBT_LicenseStatus(ls_status, 1024)
// -> {"licensed":true,"ide":false,"keys":{...},"reason":""}
Useful during integration to confirm that a key is taken into account, or to log a startup in your application journal.
8.4 Demo mode #
Without a valid key — or with a key that does not apply to the context (a dev key inside an exe, for instance) — the library runs in demo mode: everything is usable, everything is visible, but with limits — except the five components of the community edition, described just below, which are never restricted.
Five components stay free #
The community edition takes no setting up: with no key at all, five components run with no badge and no limit, indefinitely.
| Component | What it brings |
|---|---|
| statictext | Marked-up label: bold, colours, links, bullets, foldable blocks |
| messagebox | Themed modal dialog with free-form buttons |
| toaster | Corner-of-screen notifications, with title, image and actions |
| webbrowser | WebView2 browser hosted inside a PowerBuilder window |
| json | Read and write JSON in pure PowerScript (paths, types, building) |
The choice is deliberate: the four visual components are the ones you meet first, they carry no data model of their own, and they are enough to prove on a locked-down machine that the runtime deploys — the real obstacle before any purchase. json joins them because it is written in pure PowerScript, without the DLL: it is a basic tool, not a component.
The list lives in the DLL. PBT_LicenseStatus returns it as it is, under "free": your application can therefore state what it is entitled to without a key, rather than discovering it component by component.
A valid key always wins: on those five components as on any other, it restores your name and your edition.
The badge #
Every component displays a "PBToolboxAI — DEMO" badge, which cannot be hidden.
Functional limits #
| Component | Limit in demo mode |
|---|---|
| tab | 3 visible tabs drawn at most (the component keeps all your pages: the key shows them) |
| ribbon | 2 tabs maximum |
| toolbar | 6 items displayed, across all bars; 6 entries per drop-down menu level; 6 choices per drop-down list |
| tilesbox | 8 tiles displayed |
| commandpalette | 5 commands listed |
| speechout | 4 sentences and 400 characters per reading (queue included), then the demo notice is spoken; past the first 4 sentences, every reading ends with it |
| crypto | texts of 2048 characters, files and blobs of 2048 bytes at most, going in as well as coming out (a decryption or a decoding too) |
| restclient | response bodies cut at 4096 characters, no download, no upload and no range request (Range) |
| soundplayer | only the first 10 seconds of a sound are played (whatever the speed), and two falling notes say so |
| video | a film stops after 10 seconds; no seek and no picture capture beyond them |
| xml | documents of 65,536 characters at most, changes included; a longer built document is neither validated (of_is_valid false) nor indented (of_pretty empty) |
| listbar | 6 entries displayed, across all sections — a search or an of_from_datastore shows only those six |
| statusbar | 4 panels displayed; 6 entries per drop-down list of a panel (separators do not count), a last greyed line says how many stay hidden |
| contextmenu | 6 entries per list, at every cascade level (separators and headers do not count); the others stay in memory without being shown, and a last greyed line says so |
| menubar | 3 menus on the bar; 6 entries per list and 12 per menu, all its cascades included (separators do not count); a list cut short ends with a greyed line that says so |
| radialmenu | 4 branches per ring at most (fewer if ii_max_sectors is smaller), no “More” branch, and one level of sub-wheels (a deeper branch is greyed); a ring cut short ends with a greyed “+N” sector, and “PBToolboxAI — DEMO” is written under the hub |
| shellexplorer | 8 nodes per tree level |
| stepbar | 4 steps displayed |
| buttonbar | 4 buttons displayed; the following ones are neither drawn nor fired from the keyboard (their Alt+letter stays with the window) |
| crosstab | 500 source rows used, export and copy disabled; of_get_cell_value and of_get_cell_rows work on those 500 rows and give the same figures as on screen |
| breadcrumb | 4 segments displayed, the last ones of the trail; 6 branches per branch menu (a demo note says the rest) and 6 typing suggestions |
| jsontree | The first 200 lines of the document displayed, 20,000 characters at most (a longer line is cut); search and selection stay within them |
| xmltree | The first 200 lines of the document displayed, 20,000 characters at most (a longer line is cut); search and selection stay within them |
| datagrid | 100 rows displayed, CSV and Excel exports disabled, copy (Ctrl+C) too |
| markdownview | The beginning of the document: 40 blocks (a heading, a paragraph, a list item, a table row each count as one; a code block is cut at 40 lines) or 4,000 characters of the document, whichever comes first — a longer block is cut —, followed by a note; the search and the table of contents keep to them |
| form | 6 fields, the first ones shown in screen order — a field of a folded section counts too: unfolding redraws nothing; the others stay in memory and in of_values without being shown nor judged |
| propertygrid | 8 rows of properties, counted in the order of the screen (categorized view): a closed group counts as one row, its sub-properties count only once the group is open, category headers do not count; the rest stays in memory and in of_values without being shown, and neither a summary nor the search reads its value |
| kanban | the first 4 visible columns, 5 cards per column counted in the order of the column among the visible cards, each followed by a note; the count and the sum of a header say only the cards drawn; the others stay in memory and move without being shown |
| gantt | 12 tasks drawn, the first rows of the grid in its order (what a folded summary holds does not count), followed by a note; the others stay in memory, in the critical path and in the dates and progress of the summary that holds them, without being shown nor selectable |
| scheduler | The first 12 appointments of the range on screen, the others stay in memory without being shown |
| angulargauge | The first 3 visible ranges drawn (a hidden range does not count); the others stay in memory without being shown, and setting the key shows them without sending anything again |
| chartcartesian | The first 3 series drawn, legend included; the others stay in memory without being shown, and setting the key shows them without sending anything again |
| chartpie | The first 5 slices drawn, legend included, and the percentages worked out on them; the others stay in memory without being shown, and setting the key shows them without sending anything again |
| gauge | The first 3 visible ranges drawn (a hidden range does not count); the others stay in memory without being shown, and setting the key shows them without sending anything again |
| carousel | The first 6 visible items drawn, in the order of the carousel (a hidden item does not count); the others stay in memory without being shown nor selectable, and setting the key shows them without sending anything again |
| treeview | The first 8 nodes of each level drawn, under each parent, in the order of the model, and 200 nodes in the whole tree, the first ones added — before the sort, the search and the hiding (a hidden node counts, a search or a sort never shows the ninth, lots and groups included; a total never counts what the cap hides); the others stay in memory without being shown nor selectable, and setting the key shows them without sending anything again; of_export_csv and of_export_xlsx are refused |
| dockcontainer | 3 panels: the fourth of_add_panel returns -6, its control stays where it was, visible |
| codeeditor | The first 200 lines shown and editable, followed by a note; the others stay in memory and in is_text without being shown: the search, the markers and il_doc_line keep to the lines shown, and pasted text that goes past them joins the lines in memory |
| All the others | Badge only — no functional limit |
These limits apply to the rendering: nothing fails, the display is truncated. So you can genuinely evaluate every component, on your own data, before buying.
Setting a valid key instantly lifts all these limits and removes the badge, without restarting the application.
8.5 Perpetual + maintenance #
The license is perpetual: the versions covered by your maintenance period keep working forever, including after that period expires.
In practice, the key carries a maintenance end date, compared against the build date of the DLL:
- a library version released during your maintenance is unlocked permanently;
- a version released after it expires returns
-3: renew your maintenance to get access; - applications you have already shipped are never affected: they embed the version they have always used.
8.6 Frequently asked questions #
Is my key checked online? No. No network, ever. The check is an RSA signature validated locally by Windows.
Do my customers need a key? No. Your runtime key is inside your application; they never see anything about the license.
What happens if I forget PBT_SetLicense in production? The application works, but every component displays the demo badge and the limits in the table above apply. That is the first thing to check if a badge shows up at a customer site.
Can I set a dev key and a runtime key in the same code? Yes — that is even the usual case: the dev key applies when you run from the IDE, the runtime key when you run the exe. Each one is ignored in the other context.
The badge appears even though the key is set. Three causes, in order of frequency: (1) a dev key while you are running a compiled exe, or the other way round; (2) the return value of PBT_SetLicense was never tested — check that it is 0; (3) maintenance expired for the DLL version installed (return -3). PBT_LicenseStatus settles it in one line.
A key is nominative. A dev key is tied to a developer, a runtime key to your company. Do not share them outside your team.