Skip to content

workspace sdk modules

Andre Lafleur edited this page Jul 10, 2026 · 9 revisions

About workspace modules

A workspace module is the entry point for extending Security Desk or Config Tool. Modules are discovered and loaded by the host application, and they register extensions such as tasks, pages, components, and services.

Use a workspace module when you need a client-side entry point that registers UI extensions, shared services, or component builders as the host application starts.

How modules work

Modules progress through four stages:

  1. Constructor: Security Center creates your module instance
  2. Initialize: The framework calls Initialize(Workspace) to provide the workspace instance
  3. Load: The framework calls Load() where you register all extensions
  4. Unload: The framework calls Unload() during application shutdown

The Workspace property is not available in the constructor. Access it only in Load, Unload, or after Initialize completes.

Loading order

Modules are loaded from plugin installation entries. A deployment file can set the Priority item for a plugin entry; lower values load first. Entries without Priority use the default priority and have no guaranteed order relative to each other. If one assembly contains multiple module classes, do not rely on the order of those classes. Avoid module-to-module load-order dependencies.

Exception handling

If your module throws an exception during Load(), Security Center logs the error and continues loading other modules. Your module will not be added to the workspace, but other modules remain unaffected. The same applies to Unload(): exceptions are caught and logged, allowing other modules to unload normally.

Connection state during Load

The Load() method is called before the application connects to the Directory server. Do not assume the client is connected when Load() executes. Tasks and pages can work offline by default through the AllowOfflineExecution property on PageDescriptor.

If your module needs to perform actions after connection is established, subscribe to the LoggedOn event:

public override void Load()
{
    Workspace.Sdk.LoginManager.LoggedOn += OnLoggedOn;
    // Register extensions that work offline
}

private void OnLoggedOn(object sender, LoggedOnEventArgs e)
{
    // Perform actions that require connection
}

Module

Create a workspace module by inheriting from Genetec.Sdk.Workspace.Modules.Module and overriding the Load and Unload methods:

public class SampleModule : Module
{
    public override void Load()
    {
        // Register extensions here
    }

    public override void Unload()
    {
        // Clean up resources here
    }
}

Class requirements

Your module class must meet these requirements:

  • Public class: The module class must be declared public.
  • Default constructor: Security Center requires a public default constructor. If you do not define any constructor, the implicit default constructor satisfies this requirement.
  • Multiple modules per assembly: A single assembly can contain multiple module classes. Security Center discovers and loads all public classes that inherit from Module.

Module members

Member Type Description
Workspace Workspace The workspace instance (available after Initialize)
UniqueId Guid (virtual) Module's unique identifier (defaults to Type.GUID)
Load() abstract void Called when module loads; register extensions here
Unload() abstract void Called when module unloads; clean up here
Initialize(Workspace) void Called by the framework to set the Workspace property. Do not call or override this method.

Accessing the Platform SDK

Access the Platform SDK Engine through Workspace.Sdk:

public override void Load()
{
    var engine = Workspace.Sdk;

    // Subscribe to events (works before connection)
    engine.EventReceived += OnEventReceived;

    // Check connection before accessing connected-only properties
    if (engine.LoginManager.IsConnected)
    {
        var user = engine.LoggedUser;
    }
}

All modules share the same Engine instance with the host application. See Platform SDK Overview for Engine operations.

Module communication

Modules can interact with each other through shared workspace resources:

  • Services: Register a service in one module and retrieve it in another using Workspace.Services.Get<T>(). Services must implement an interface that extends IService.
  • Components: Access components registered by other modules through Workspace.Components.
  • Modules: Enumerate loaded modules through Workspace.Modules, though direct module-to-module dependencies are discouraged.
public interface IMyCustomService : IService
{
    void DoSomething();
}

public class MyCustomService : IMyCustomService
{
    public void Initialize(Workspace workspace) { }
    public void DoSomething() { /* ... */ }
}

public class ProducerModule : Module
{
    private MyCustomService m_service;

    public override void Load()
    {
        m_service = new MyCustomService();
        m_service.Initialize(Workspace);
        Workspace.Services.Register(m_service);
    }

    public override void Unload()
    {
        if (m_service != null)
        {
            Workspace.Services.Unregister(m_service);
            m_service = null;
        }
    }
}

public class ConsumerModule : Module
{
    public override void Load()
    {
        var service = Workspace.Services.Get<IMyCustomService>();
        service?.DoSomething();
    }

    public override void Unload()
    {
    }
}

Since loading order is not guaranteed, check for null when retrieving services from other modules. Consider subscribing to Workspace.Initialized to access services after all modules have loaded.

Registering workspace extensions

Register extensions in the Load method using the appropriate workspace collection:

public override void Load()
{
    // Register a task
    var task = new MyTask();
    task.Initialize(Workspace);
    Workspace.Tasks.Register(task);

    // Register a component
    var widget = new MyWidgetBuilder();
    widget.Initialize(Workspace);
    Workspace.Components.Register(widget);

    // Register an options extension
    var options = new MyOptionsExtension();
    options.Initialize(Workspace);
    Workspace.Options.Register(options);
}

Registration by extension type

Extension type Registration target Method
Task Workspace.Tasks Register(Task)
Component Workspace.Components Register(Component)
OptionsExtension Workspace.Options Register(OptionsExtension)
IService Workspace.Services Register(IService)
ContextualAction Workspace.Services.Get<IContextualActionsService>() Register(ContextualAction)
EventExtender Workspace.Services.Get<IEventService>() RegisterEventExtender(EventExtender)
ConfigPage Workspace.Services.Get<IConfigurationService>() Register(ConfigPage)

Application-specific registration

Check Workspace.ApplicationType to register extensions only for the appropriate application:

public override void Load()
{
    switch (Workspace.ApplicationType)
    {
        case ApplicationType.SecurityDesk:
            RegisterSecurityDeskComponents();
            break;
        case ApplicationType.ConfigTool:
            RegisterConfigToolComponents();
            break;
    }
}

Resolving third-party dependencies

If your module uses third-party libraries beyond the Genetec SDK, you must configure assembly resolution. The SDK assemblies are automatically resolved by Security Center.

AddFoldersToAssemblyProbe

This approach configures Security Center to automatically probe your module's directory for dependencies. Set it in the registration XML:

<PluginInstallation>
  <Version>1</Version>
  <Configuration>
    <Item Key="Enabled" Value="True" />
    <Item Key="ClientModule" Value="C:\MyModule\MyModule.dll" />
    <Item Key="AddFoldersToAssemblyProbe" Value="True" />
  </Configuration>
</PluginInstallation>

Place all third-party DLLs in the same directory as your module DLL.

AssemblyResolver

For custom loading logic, use the AssemblyResolver sample helper from the DAP samples repository. Copy the class into your project and call its Initialize() method from a static constructor:

public class SampleModule : Module
{
    static SampleModule() => AssemblyResolver.Initialize();

    public override void Load() { /* ... */ }
    public override void Unload() { }
}

The static constructor runs before any instance is created, ensuring dependencies resolve correctly.

Choosing an approach

Use AddFoldersToAssemblyProbe for most scenarios. It requires no code and handles simple dependencies automatically. Use AssemblyResolver when you need custom loading logic, dependencies in multiple locations, or version-specific behavior.

For module registration during development and production deployment, see Deploying plugins and workspace modules.

Resource cleanup

Release resources in the Unload method:

public override void Unload()
{
    // Unsubscribe from events
    Workspace.Sdk.EventReceived -= OnEventReceived;

    // Dispose loggers
    m_logger?.Dispose();

    // Release other resources
}

The Unload() method is called when the workspace is disposed during normal application shutdown. Do not rely on modules unloading in a specific order. Unload() may not be called if the application crashes, is forcefully terminated, or exits abnormally, so do not make required cleanup depend only on Unload().

See also

Platform SDK

Plugin SDK

Workspace SDK

Media SDK

Macro SDK

Web SDK

Synergis RIO

Media Gateway

Genetec Web Player

Clone this wiki locally