SIMPL+ Expressions & Type conversions

Dealing with type conversions without the ability to explicitly cast to another type isn’t always straightforward. Especially in the context of primitive integer types built into the SIMPL+ language, narrowing conversions can cause problems if you aren’t aware of them.

SIMPL+ is a strange, minimalist, C-like language that has more than a handful of problems; including silencing implicit narrowing type conversions.

 

 

 

 

 

#ENABLE_TRACE
#DEFAULT_VOLATILE
#ENABLE_STACK_CHECKING

// Note: This function is simply here to see if any of the calls to this function result in a compiler warning
// about loss of data from the narrowing conversion.
FUNCTION TestForDataLoss(INTEGER value) { }

FUNCTION Main()
{
    SIGNED_LONG_INTEGER n;
    n = 500096; // Binary: 00000000_00000111_10100001_10000000
    // LSB = 128 (10000000)

    TestForDataLoss(n & 0xFF);   // Expression by itself is recognized as a type of SIGNED_LONG_INTEGER
    TestForDataLoss((n & 0xFF)); // Parentheses promotes the expression to a type of INTEGER
    TestForDataLoss(Low(n));     // Something weird is happening here with an implicit narrowing conversion... Works (?)

    // ... But what if we want the least significant 7 bytes?
    TestForDataLoss(Low(n & 0x7F));     // Works
    TestForDataLoss(LowWord(n & 0x7F)); // Works
    TestForDataLoss(n & 0x7F);          // Does NOT work
    TestForDataLoss((n & 0x7F));        // Works!
    TestForDataLoss((n));               // Should NOT work (but it does)
}

Note: In the code comments, “Works” indicates that the line of code compiles without any compiler warnings.

Without running the code, you can see the results from the comments beside each function call in the source code above. What is interesting, is the effect additional parentheses have on an expression. We can deduce that the expression with additional parentheses is being promoted from a type of SIGNED_LONG_INTEGER to INTEGER, and an expression without the additional parentheses is still recognized as a SIGNED_LONG_INTEGER since a compiler warning complains about that line.

The obvious solution to this problem is to use the built-in functions like Low() and LowWord(). LowWord() converts the type from the SIGNED_LONG_INTEGER passed into the function and returns the low 16 bits as an INTEGER. Low() does not convert the type at all; it takes an INTEGER as a function parameter and returns the least significant 8 bits as an INTEGER. This is important to understand, since passing a SIGNED_LONG_INTEGER to this function which takes in an INTEGER type, does not produce a compiler warning! This is potentially a very bad thing, since the compiler in all other circumstances would at least warn us about a potential loss of data from a narrowing conversion, but it lets you pass in a 32-bit SIGNED_LONG_INTEGER to a function which expects a 16-bit INTEGER no problem. A silent implicit narrowing type conversion like this can lead to inadvertent results that are more difficult to troubleshoot and resolve.

One of the main reasons I wanted to write this article was to show a shorthand for promoting the resultant type of an expression to an INTEGER to avoid the compiler warnings. I now see how this can also lead to unexpected results however, similar to how the compiler will not warn you about passing a SIGNED_LONG_INTEGER into a function which accepts a type of INTEGER. The implicit conversion seems to be based on parentheses and not the expression itself, which is even more concerning since an expression of ‘n’ in the code above–should remain a SIGNED_LONG_INTEGER, but it gets promoted to an INTEGER if you write ‘(n)’.

Note: The implicit type conversion only occurs if the type expected is of a different type than the declared type.

At least with languages like C and C++, you would normally have to explicitly cast for a narrowing conversion. This makes the intent a lot more readable, and the results more in line with what was expected.

Overview
In the following sections, we will explore different techniques to expand and enhance the functionality of your SIMPL Windows programs by harnessing the capabilities of SIMPL#. SIMPL# Pro is not covered in this edition, although a lot of the core principles and code examples can still be translated over to SIMPL# Pro.

Prerequisites
It is assumed that you have the following (as a minimum set of requirements):

❖ Microsoft Visual Studio 2008 Pro (SP1)
❖ Crestron SIMPL# Plugin
❖ SIMPL Windows and SIMPL+ language proficiency

Note: A basic understanding of the C# language (syntax) and .NET compact framework is recommended, although not required. It should be noted that this paper is not an alternative to learning how to program in C#.

Type Alias Helpers

You can use the using directive outside (or within) namespace scope to make the type aliases in your
SIMPL# library project source files. A set of those using directives for SIMPL+ type aliasing has been defined below:

using STRING = System.String; // string = STRING
using SSTRING = Crestron.SimplSharp.SimplSharpString; // SimplSharpString = STRING
using INTEGER = System.UInt16; // ushort = INTEGER (unsigned)
using SIGNED_INTEGER = System.Int16; // short = SIGNED_INTEGER
using SIGNED_LONG_INTEGER = System.Int32; // int = SIGNED_LONG_INTEGER
using LONG_INTEGER = System.UInt32; // uint = LONG_INTEGER (unsigned)

Now you can use these as normal identifiers in your code (which are synonymous with their underlying type):

private INTEGER Initialize()
{
    // TODO: ...
}

(Note: In the example above, the INTEGER return value is interchangeable with ushort. The compiler does not identify INTEGER as a separate type, and both INTEGER and ushort can be used in the same manner for this reason.)

I typically avoid using these for any of my classes that don’t need to interface with SIMPL+. The purpose of these aliases is to make SIMPL+ API’s create more similarities in comparing your C# source code.

Compiler Directives & Build Configurations

Using the build configurations provided by default can be beneficial even if you don’t create your own. You’ll notice two that are available by default; Debug and Release. Both of these configurations also have a predefined set of defined compiler constants that you can use to include or omit sections of code in the compiled output. There are other benefits to understanding default build configurations. The Release build for example, enables compiler optimization whereas the Debug configuration does not.

Let’s talk about the compiler for a bit though. Have you ever considered the fact that there is a slight performance impact in having diagnostic code in your compiled dll’s (i.e. console output messages, logging mechanisms, etc)? Even if you enable/disable your debug messages and logging functionality at runtime, there is conditional logic which determines whether to call your debug functions that still need to be evaluated by the CPU. This can be mitigated by telling your compiler to omit this code entirely for a certain build configuration like the Release configuration using conditional directives.

public INTEGER Load(STRING configFilepath)
{
#if DEBUG
    CrestronConsole.PrintLine("Loading configuration file");
#endif
    try
    {
        if (!File.Exists(configFilepath))
        {
#if DEBUG
            CrestronConsole.PrintLine("File does not exist: {0}", configFilepath);
#endif
            return 0;
        }
        return 1;
    }
    catch (ConfigurationLoadException ex)
    {
#if DEBUG
        CrestronConsole.PrintLine("Failed to load configuration: {0}\n\tReason: {1}", ex.Filepath, ex.Reason);
#endif
    }
    return 0;
}

This may be undesirable if you are writing a module that you need to support though, since you might
still want to allow the end-user to turn on debug messages at runtime so that you can help them troubleshoot. This feature may prove useful to selectively omit more verbose debug calls however that aren’t meant to be shown in your production build, and to limit your debug output and/or logging to the end-user level debug output.

The above code looks somewhat ugly too, and if we ended up writing our entire library like this, readability would probably suffer. You could create a function that internally calls CrestronConsole.PrintLine() and have a single pair of #if DEBUG / #endif around that call within the function, although the function call which does nothing is still compiled into the assembly; there is still a better alternative. There is the ConditionalAttribute class, which is already provided by the .NET framework. Using custom attributes can be very useful in C#, and this one allows us to have regular function calls in code, but omit the call itself if the specified directive is not defined.

public const string DEBUG_FLAG_DIRECTIVE = "DEBUG";
[Conditional(DEBUG_FLAG_DIRECTIVE)]
public static void WriteLine(string message, params object[] args)
{
    CrestronConsole.PrintLine(message, args);
}

If we put this into a class named Debug, an example call would look something like this:

Debug.WriteLine("Configuration load exception: {0}", ex.Message);

No need for any of that conditional directive bloat either which increases the line count and negatively impacts readability! In this case this function call is only compiled if we’re running the assembly which was compiled under the Debug configuration, and not the Release configuration. More information can be found here: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.conditionalattribute

Note: This is modern documentation, but the concept is still the same for us.

Cleaning Up the Public API

If you’re writing a library with more than a few classes, and you want to hide certain classes from the public API, you can mark things as internal. This accessibility modifier ensures that usage is still permitted from within the same assembly (or those external assemblies defined in the InternalsVisibleToAttribute definitions in the main assembly). Classes marked as internal hides them from the generated SIMPL+ API.

Passing Arrays to SIMPL+
You cannot return arrays directly back to SIMPL+ because there are no return types in SIMPL+ such as INTEGER_ARRAY_FUNCTION. You can return them via callbacks though. One somewhat “universal” solution involves utilizing inheritance and generics. You can even extend such functionality by expanding upon and deriving from the following class:

/// <summary>
/// Generic array wrapper class for SIMPL+ compatibility.
/// </summary>
/// <remarks>Inheritors of this type for typed array wrappers can be
/// passed back to SIMPL+ using callbacks but cannot be returned from
/// functions back to SIMPL+ for some reason.</remarks>
/// <typeparam name="T">Array element type</typeparam>
public abstract class CrestronArray<T>
{
    protected readonly T[] ObjArray;
    public T[] Elements { get { return ObjArray; } }
    public int Length { get { return ObjArray.Length; } }
    protected CrestronArray() { }
    protected CrestronArray(T[] array) { ObjArray = array; }
    public virtual int IndexOf(T item)
    {
        for (int i = 0; i < ObjArray.Length; i++)
        {
            if (ObjArray[i].Equals(item))
                return i;
        }
        return -1;
    }
}

This class is marked as abstract since it is not meant to be instantiated directly. It exposes an accessor for the Elements, as well as the Length of the encapsulated array. There is also a virtual function, IndexOf(), which is a useful function to have for all derived classes as a searching operation, but you might choose to add others depending on your requirements. Now for a derived class example:

/// <inheritdoc />
/// <summary>
/// String array for SIMPL+ compatibility.
/// </summary>
public sealed class StringArray : CrestronArray<string>
{
    [Obsolete("Provided only for S+ compatibility", false)]
    public StringArray() { }
    public StringArray(string[] array) : base(array) { }
}

Here we are implementing a string array wrapper class, and inheriting from the abstract generic class from above. For the parameterless constructor, we’ve marked it with the ObsoleteAttribute to help prevent unwanted usage from SIMPL# since we want to always have the object’s internal array properly initialized by only calling the constructor that passes an array of the specified type. The parameterless constructor is only necessary to keep the type compatible with SIMPL+. Generics are not directly supported in SIMPL+, which is the reason why we can’t just pass CrestronArray<string> directly back into the SIMPL+ layer. This solution works because SIMPL+ doesn’t care if the object inherits from a generic type. Usage in SIMPL+ is relatively straightforward:

a) Retrieve an element at index i

element = obj.Elements[i];

b) Get the array length

SIGNED_LONG_INTEGER n;
n = obj.Length;

c) Get the index of a particular element

SIGNED_LONG_INTEGER n;
n = obj.IndexOf("Name");

User-Interactive Callbacks
Say we had a module that needed to respond to a callback fired from SIMPL#. A typical example would be to acknowledge or decline a host key received during an SSH connection to a server. We could start the connection process from SIMPL+, but there’s no way to tell when we receive the host key other than an event in the SIMPL# SSH library that we can use to create a callback. To our benefit, SIMPL+ delegate callbacks can also be bi-directional. This would allow us to accept or decline the host key in response to the callback fired from SIMPL# (to proceed with, or abort the connection).

private bool OnValidateNewHostKey(byte[] key)
{
    var target = ValidateNewHostKey;
    if (target != null)
    {
        bool autoAcceptKey = target.Invoke(key.ToHexString()) != 0;
        if (!autoAcceptKey)
        {
            _hostKeyAcknowledgedEvent.Reset();
            if (!_hostKeyAcknowledgedEvent.Wait(10000))
                return false;
            return AcceptNewKey != 0;
        }
        return true;
    }
    return false;
}

In the code above, we’re using the callback to determine whether to auto-accept the key or wait for a manual trigger to check a flag that gets set to determine whether to accept or decline the new key. ValidateNewHostKey is the target callback that invokes the SIMPL+ callback, and this method is assigned to the event handler of the wrapper class I made for my SSH client which gets invoked by the event invocator in the code below (OnAcceptNewHostKey()).

– If the SIMPL+ callback returns TRUE (as a way to indicate that autoAcceptKey has been enabled on the SIMPL+ module, then we return true to accept the new key immediately.
– If the SIMPL+ callback returns FALSE (indicating that we aren’t automatically accepting new keys), then we wait to be signaled on the calling thread and check the AcceptNewKey property to see whether this new key should be accepted or not. The timeout on the Wait() method is there to allow the key to automatically be declined after a certain amount of time if the user hasn’t accepted or declined the key manually so we can free this thread from just sitting there doing nothing. (In this case we assume that the new host key cannot be trusted. If there is no user interaction, we default to declining the received host key.)

The signal to the waiting thread comes from the _hostKeyAcknowledgedEvent being Set() within the mutator on a property which is accessible from SIMPL+:

private INTEGER _acceptNewKey;
public INTEGER AcceptNewKey
{
    get { return _acceptNewKey; }
    set
    {
        _acceptNewKey = value;
        _hostKeyAcknowledgedEvent.Set();
    }
}

The following code is invoked from the SSH.NET library when we receive the host key from the server:

if (OnAcceptNewHostKey(e.FingerPrint))
{
    Debug.WriteInfo("Writing SSH key to file: '{0}'", filepath);
    using(var fs = new FileStream(filepath, FileMode.Create, FileAccess.Write))
    {
        fs.Write(string.Format("Fingerprint: {0}\nKey: {1}", fingerprint, key),
                 Encoding.UTF8);
    }
    e.CanTrust = true;
}
else
{
    e.CanTrust = false;
}

The corresponding SIMPL+ for the unique callback looks like this:

CALLBACK INTEGER_FUNCTION ValidateNewHostKeyHandler(STRING key)
{
    manual_host_key_validate_fb = FALSE;
    if (!auto_accept_new_keys)
    {
        // user must take action to validate received host key otherwise
        // the key will be automatically rejected after a timeout period.
        fingerprint$ = key;
        manual_host_key_validate_fb = TRUE;
        return (FALSE);
    }
    return (TRUE); // auto accept new host key
}

And the two SIMPL+ events that invoke to signal the thread are as follows:

PUSH accept_new_key
{
    ssh.AcceptNewKey = TRUE;
    fingerprint$ = "";
    manual_host_key_validate_fb = FALSE;
}
PUSH decline_new_key
{
    ssh.AcceptNewKey = FALSE;
    fingerprint$ = "";
    manual_host_key_validate_fb = FALSE;
}

manual_host_key_validate_fb is there as feedback to show a popup or something on the UI which allows the user to accept_new_key or decline_new_key respectively. In essence, it acts like our callback to SIMPL Windows, so the output signal can trigger conditional logic that tells the program that a new key needs to be either accepted or declined.

Inter-Symbol Communication (not Inter-Process Communication)
The most basic way is to use a static instance for talking between modules in SIMPL, without having to actually pass data back through SIMPL+. You can store all your information directly in the static instance, or implement a more advanced model like a publisher/subscriber implementation. In my case, I have a static publisher object that I can call upon to send notifications out to a defined set of subscribers, or globally, and this makes for a very dynamic but powerful solution over just a simple static class that stores all the information directly (although it definitely is a lot more work to set up so I won’t cover that here).

public class MyModule
{
    private static readonly Configuration _configuration = new Configuration("...");
    // ...
}

In the code above, we have a static readonly field that is implied to hold some kind of configuration. For every SIMPL+ module using this class, the important thing to remember here is that there is only a single instance, of which is shared across all other SIMPL+ modules. What you do with this is now up to your imagination. You can also create static events that will fire for every instance of your SIMPL+ module, it doesn’t necessarily need to be a static object to share information.

Note: SIMPL+ module events run from a threadpool, the other thing you have to keep in mind here is thread synchronization (which I’ll get to in the next example). Because this instance is shared, a lot more threads will be requesting access to this Configuration class, but threading is a problem we have to deal with regardless because of the way SIMPL+ was designed.

Thread Synchronization
For those unfamiliar with this concept, it is any mechanism that limits the number of threads that can access a shared resource at any given time. Unlike in SIMPL+ where you used a variable to restrict execution, SIMPL# has a lot more to offer.

Variable assignments and conditional checks are not really an acceptable way to deal with thread-safety in most contexts (including C#). In addition to having constructs that can block a thread for a timeout period, or indefinitely, there are also classes that allow you to implement thread synchronization in the following ways:

  1. Across program slots, or only within the same program.
  2. Events that get manually reset or reset automatically, which allow you to implement threads that must wait to be signaled. (This is very useful for initialization processes.)
  3. Semaphore’s which grant a specific number of threads access to a shared resource or pool of resources. (Note: This must be implemented or ported manually, or you can use one of the ports that others have provided to the programming community.)

The flexibility here is vastly greater than what is provided in SIMPL+, however, that doesn’t come without its set of responsibilities! You still must properly implement thread-safety, or you might find yourself spinning up threads in your modules that just sit there because you either forgot to release a lock. (You may have even created a deadlock!)

Note: It is important to avoid the lock statement as a means of thread synchronization. There is a known bug on the WinCE platform for 3-series regarding thread contention which may lead to a deadlock scenario.

Locks can also be expensive in some cases. If you are writing code that is performance critical, atomic instructions may be available to help you maintain the necessary performance, but keep your code thread-safe. One class that you can use to achieve this is the Interlocked class, which provides static methods to perform such atomic instructions to increment/decrement, exchange (conditionally and unconditionally), and more. (Some of the .NET BCL classes provide thread-safety entirely by using the Interlocked class for performance reasons, but this doesn’t mean get carried away with micro-optimization; only use it where it is necessary.) Some wrapper classes are also by default thread-safe for certain things, or fully thread-safe, so thread concurrency is already dealt with for you. In scope-based scenarios with things like CCricitalSection for example, you would call to Enter() to acquire the critical section lock, and Leave() to relinquish that lock which would allow another thread to obtain it. You should get into the habit of calling these functions within the try and finally branches of a try statement respectively. The reason for this is simply because even in the event of an exception that happens after the lock has been acquired, the finally block will ensure that the lock is released by executing Leave().

try
{
    criticalSection.Enter();
    // do work here
}
finally
{
    // this is called even if the work being done
    // after the Enter() call throws an exception.
    criticalSection.Leave();
}

IDisposable and the GC
You may have heard people talk about disposing objects via Dispose() explicitly or implicitly the using statement. The IDisposable interface is one that should be properly implemented to avoid problems down the road. For a managed code like .NET, we have what is called the garbage collector. The rules governing garbage collection can be a fairly lengthy topic so I will omit that discussion, perhaps until a very rainy day.

For simplicity, any object that implement the IDisposable interface will need to be disposed of accordingly in SIMPL#. If you create a wrapper that contains IDisposable members in your class, then you should also make your class implement IDisposable and create the proper dispose pattern to free your managed resource.

(There are other rules when you’re creating something designed to be inherited from, but again, I want to keep things simple for now.)

If P/Invoke was not hardcoded to be sandboxed for us, you would also have to do this for any unmanaged resources, but since it doesn’t apply to us (for 3-series) we’ll only be talking about managed resources. The idea is to ensure that you call Dispose() on any object that implements the IDisposable interface when you no longer need it so that unused resources can be freed up cleanly. Just like with thread synchronization objects, there is also a statement that is used to ensure Dispose() is called within a scope-based scenario, and that is the using statement.

using (var fs = File.OpenRead("file.txt"))
{
    // TODO: ...
}

There’s nothing special about this statement, it’s what we call syntactic sugar; specifically for a try-finally setup. The following is equivalent to what was written above:

{
    var fs = File.OpenRead("file.txt");
    try
    {
        // TODO: ...
    }
    finally
    {
        if (fs != null)
        ((IDisposable)fs).Dispose();
    }
}

The surrounding braces are there to define additional scope because just like in the using statement example above. The FileStream object is not accessible from outside of the statement’s scope. Failure to properly dispose of an object, especially if you’re creating many objects on the heap, will force the GC to do a clean sweep at some point to clean up all the garbage that you didn’t clean up properly. This is an expensive task, and negatively impacts performance across the entire program. This is also the reason why some developers implement object pools, especially in game development; sacrificing framerate is a sin.

Extension Methods
Extension methods can be useful. The syntax for calling an extension method is slightly different to a standard method call where you would just call the method and enclose all of the parameters within the parenthesis of the call. Extension methods are called as an extension of the first (this) parameter, hence the name “extension method.” Let’s show an example to demonstrate what I mean by that.

The following is a method I’ve used in some of my own libraries to ensure that I don’t pass a null string to a callback method for SIMPL+:

public static class Extensions
{
    public static SimplSharpString ToSimplSharpString(this string input)
    {
        return input ?? string.Empty;
    }
}

This method returns string.Empty if input is null, otherwise it returns the value of input if input is not null. Why do this? Because the regular implicit conversion operator for string to SimplSharpString will throw an exception if the string is null since it tries to calculate the length of the string. You can’t call upon the Length property if the string is null. The way you invoke an extension method is via the dot notation on the first parameter, negating the need to pass that variable in as a “normal” argument. See the following:

string data = "testing";
data.ToSimplSharpString();

Assembly Information & Reflection
I figured these topics kind of go hand in hand, especially with the example that I’ve prepared for this section, so I’ll talk with respect to both in this part. There is one particular file we’ll take a look at in this section as well, and that is the AssemblyInfo.cs file. If you take a look at your solution explorer, expand the project you’re working on, then open the “Properties” folder, the AssemblyInfo.cs file should be in there.

By default, that file should contain something like the following:

using System.Reflection;
[assembly: AssemblyTitle("MyProject")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MyProject")]
[assembly: AssemblyCopyright("Copyright © 2019")]
[assembly: AssemblyVersion("1.0.0.*")]

These are the pieces of information that get compiled into your assembly after a compile in the metadata storage streams of the file.

(Note: The wildcard asterisk in the AssemblyVersion line allows for the assembly version to be incrementally changed automatically with every new build output.)

Did you notice the using directive for System.Reflection in the top line? Well we’re going to see how to extract this information at runtime using reflection, because assembly metadata and reflection are closely related. Reflection is feature provided by .NET to find out information about managed items, as well as the ability to dynamically create, and invoke types. It is useful, but it can also be relatively expensive to use in your program, so use it sparingly! The following is an example that will retrieve the assembly version as seen above (with the current numeric value of the wildcard) at runtime:

var asmName = Assembly.GetExecutingAssembly().GetName();
CrestronConsole.PrintLine(asmName.Version.ToString());

There are other properties aside from Version that can be retrieved from the asmName variable as well.

There’s no Company or Product property here though so the way to access those is to get the custom attributes for the assembly, and find the appropriate one:

Assembly asm = Assembly.GetExecutingAssembly();
object[] attributes = asm.GetCustomAttributes(typeof(AssemblyCompanyAttribute), false);
if (attributes.Length > 0)
    CrestronConsole.PrintLine(((AssemblyCompanyAttribute)attributes[0]).Company);

A few valid reasons why you might want this kind of information, would be licensing, logging, or perhaps even some kind of watermarking. Some obfuscators like Dotfuscator will do this.

Conclusion
The road to becoming proficient in SIMPL# is going to take some time. There are lots of resources available for .NET Compact Framework 3.5 and older C# 3.0 language version. As C# becomes increasingly more popular and the SIMPL# libraries mature, eventually .NET Compact Framework, Visual Studio 2008, older C# language and CLR versions will no longer be a limitation. At that time, the gap between modern C# tutorials and the relevant learning material for SIMPL# will be a lot smaller; making it easier to understand and use. Currently, there is no shortcut to learning how the Crestron libraries work though, and some of the wrapper classes have been modified in a way that MIcrosoft documentation incorrect for the equivalent SIMPL# classes provided by the SIMPL# libraries.

If you are aiming to use modern IDE’s for refactoring SIMPL# projects, or even building them, it is going to take some custom-built solution(s) to do this, and some modification to the modern environment in most cases, but it is possible. I personally use JetBrains Rider, and I know others have been able to use VS 2017. Building the project(s) still requires VS 2008 at this point because of the plugin, however, this can be automated if you know how to deal with COM, and can write your own software. That is a topic not within the scope of this paper however.

Resources

  1. Online SIMPL# Documentation: https://help.crestron.com/simpl_sharp/ (Note: you should also have a help file located within your filesystem.)
  2. Downloads for the SIMPL# plugin: https://crestron.com
  3. Microsoft Visual Studio 2008 Pro (SP1): https://microsoft.com (Note: You will need to obtain a license through a subscription or by some other means.)