Skip to content

plugin sdk lifecycle

Andre Lafleur edited this page May 10, 2026 · 8 revisions

About plugin lifecycle

Understanding the plugin lifecycle is crucial for proper initialization, operation, and cleanup.

Lifecycle overview

flowchart TB
    Start[Plugin starts] --> Constructor
    subgraph Phase1[" "]
        Constructor["1. Constructor called<br/>Engine, Logger, Culture are NULL<br/>PluginGuid is Guid.Empty"]
    end

    Constructor --> Initialize
    subgraph Phase2[" "]
        Initialize["2. Initialize(engine, roleGuid, culture) called<br/>Engine, PluginGuid, Logger, Culture populated<br/>Report filters set and handlers registered"]
    end

    Initialize --> Loaded
    subgraph Phase3[" "]
        Loaded["3. OnPluginLoaded() called<br/>Called by Initialize()"]
    end

    Loaded --> Database
    subgraph Phase4[" "]
        Database["4. Database layer initialized (if IPluginDatabaseSupport)<br/>SetDatabaseInformation and start state machine"]
    end

    Database --> Started
    subgraph Phase5[" "]
        Started["5. OnPluginStart() called<br/>Database state may still be connecting"]
    end

    Started --> Running
    subgraph Phase6[" "]
        Running["6. Plugin runs<br/>Process queries, handle events/actions<br/>Update entity states"]
    end

    Running --> Shutdown[7. Plugin shutdown initiated]
    Shutdown --> Dispose

    subgraph Phase7[" "]
        Dispose["8. Dispose() called<br/>Unsubscribe events, stop workers<br/>Dispose resources"]
    end

    Dispose --> Destroyed[9. Plugin instance destroyed]
Loading

Lifecycle methods

Method When called Purpose
Constructor Plugin instantiation Initialize member variables only
Initialize() Called by the plugin host Set base properties and run OnPluginLoaded()
OnPluginLoaded() End of Initialize() Register handlers, filters, and services
OnPluginStart() After database layer starts (if supported) Start background work and runtime processing
Dispose() Plugin shutdown Clean up resources and unsubscribe events

Constructor

The constructor runs early in the lifecycle, so keep the work here minimal and deterministic.

What's available

  • Engine - null
  • Logger - null
  • PluginGuid - Guid.Empty
  • Culture - null

Initialize member variables only.

public MyPlugin()
{
    m_httpClient = new HttpClient();
    m_cancellation = new CancellationTokenSource();
}

Do not access Engine, Logger, or PluginGuid in the constructor.

Initialize()

Called by the plugin host.

What happens

  • Engine, PluginGuid, Logger, and Culture are set.
  • Report filters are applied from SupportedQueries and SupportedCustomReports.
  • Query and entity event handlers are registered.
  • OnPluginLoaded() is called.

This work is handled by the base Plugin class.

OnPluginLoaded()

First lifecycle method where you can use the Engine.

What's available

  • Engine - fully initialized
  • Logger - ready to use
  • PluginGuid - your role GUID
  • Culture - current culture
  • Database - may not be ready (if using database support)

What to do

protected override void OnPluginLoaded()
{
    // Set up event filters
    Engine.SetEventFilter(new List<EventType> 
    { 
        EventType.AccessGranted,
        EventType.DoorOpenedTooLong 
    });

    // Subscribe to events
    Engine.EventReceived += OnEventReceived;
    Engine.ActionReceived += OnActionReceived;
    Engine.EntitiesInvalidated += OnEntitiesInvalidated;

    // Register request handlers using Plugin's protected helper methods
    AddRequestHandler<MyRequest, MyResponse>(HandleRequest);

    // Initialize services (but don't start long-running work yet)
    InitializeExternalConnection();

    // Report initial state
    ModifyPluginState(new PluginStateEntry("Startup", "Plugin loaded"));
}

What not to do

  • Do not start background workers yet.
  • Do not access the database yet (if using database support).
  • Do not perform long-running initialization.

OnPluginStart()

Called after the database layer starts (if supported). The database state can still be connecting.

What's available

  • Engine - fully initialized
  • Database state - may still be connecting (if using database support)

What to do

protected override void OnPluginStart()
{
    // Start background workers
    m_backgroundWorker = Task.Run(() => BackgroundWork(m_cancellation.Token));

    // Begin data processing
    StartDataProcessing();

    // Report ready state
    ModifyPluginState(new PluginStateEntry("Running", "Plugin started"));
}

This is the right place to

  • Start background workers
  • Begin periodic tasks
  • Start data processing
  • Perform initialization that does not require a connected database

For database-dependent work, wait for DatabaseState.Connected in OnDatabaseStateChanged().

Dispose()

Called when the plugin is shutting down.

What to do

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        // Unsubscribe from all events
        Engine.EventReceived -= OnEventReceived;
        Engine.ActionReceived -= OnActionReceived;
        Engine.EntitiesInvalidated -= OnEntitiesInvalidated;

        // Stop background workers
        m_cancellation?.Cancel();

        // Remove request handlers
        RemoveRequestHandler<MyRequest, MyResponse>(HandleRequest);

        // Dispose managed resources
        m_httpClient?.Dispose();
        m_databaseConnection?.Dispose();
    }
}

Critical rules

  • Unsubscribe from events
  • Stop background workers
  • Dispose managed resources
  • Handle partially initialized state

Database lifecycle integration

When a plugin implements IPluginDatabaseSupport, initialization happens in this order:

  1. Constructor
  2. Initialize() - base initialization and OnPluginLoaded() runs
  3. DatabaseManager.SetDatabaseInformation() - connection info provided
  4. Database layer created - reads GetDatabaseUpgradeItems()
  5. Database state machine starts
  6. Creating state calls GetSpecificCreationScript() when database is missing
  7. Upgrading state runs upgrade items when version is behind
  8. OnDatabaseStateChanged() notifications for database state changes
  9. OnPluginStart() is called

This means

  • In OnPluginLoaded(), database may not be ready.
  • OnPluginStart() does not guarantee DatabaseState.Connected.
  • Use OnDatabaseStateChanged() to wait for DatabaseState.Connected.

For details, see Plugin SDK database.

Threading considerations

Lifecycle methods are called by the plugin host. Callbacks from timers and task continuations run on background threads.

Key points

  • Use Engine.QueueUpdate() or Engine.QueueUpdateAndWait() to update entities from background work.

Example: queue an entity update from a timer callback.

private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
    Engine.QueueUpdate(() =>
    {
        var entity = Engine.GetEntity<CustomEntity>(m_entityGuid);
        entity.RunningState = State.Running;
    });
}

For detailed threading patterns, QueueUpdate usage, async/await considerations, and background worker examples, see Plugin SDK threading.

See also

Platform SDK

Plugin SDK

Workspace SDK

Media SDK

Macro SDK

Web SDK

Media Gateway

Genetec Web Player

Clone this wiki locally