Skip to content

macro sdk developer guide

Andre Lafleur edited this page Jan 8, 2026 · 14 revisions

Macro SDK Developer Guide

This guide covers developing macro code using the Security Center SDK. For information on creating and configuring macro entities, execution contexts, automation, and monitoring, see the Macro SDK Overview.

About macro development

Macros consist of two parts: the macro entity and your C# class.

Macro entity

The macro entity is a Security Center entity stored in the Directory (like cameras, doors, or users). It contains:

  • The C# source code
  • Configuration (name, description, partitions)
  • Default execution context (parameter values)
  • Permissions and access control

Macro entities can be created in:

  • Config Tool: Manually through the graphical interface
  • Platform SDK: Programmatically using Engine.CreateEntity(EntityType.Macro)

UserMacro class

The UserMacro class is the C# code you write that inherits from Genetec.Sdk.Scripting.UserMacro. It contains:

  • The Execute() method with your logic
  • Parameter properties (input values)
  • SDK access for interacting with Security Center

Key characteristics:

  • Written in C#
  • Run server-side on the Security Center server (not on client machines)
  • Have full access to the Platform SDK
  • Can respond to events, process entities, and automate workflows
  • One macro entity can spawn multiple UserMacro instances (unless SingleInstance = true)

Important

Macros execute on the Security Center server, not on the client machine running Config Tool or Security Desk. This means:

  • Macros have access to server resources
  • File paths in macros refer to the server's file system, not the client's
  • The SDK context is pre-authenticated by the macro runtime. All SDK calls run under a Security Center service context rather than the interactive user who triggered the macro.
  • Macros continue running even if the client disconnects

Creating macros with the Platform SDK

You can create macro entities programmatically using the Platform SDK. This is useful for automated macro deployment, testing, or management scenarios.

using Genetec.Sdk;
using Genetec.Sdk.Entities;
using Genetec.Sdk.Scripting.Compiler;
using Genetec.Sdk.Scripting.Compiler.CodeBuilder;

// Create a macro source code builder
IMacroSourceCodeBuilder builder = CodeBuilderFactory.Create();
builder.AddProperty(typeof(Guid), "CameraEntity");
builder.AddProperty(typeof(string), "Message");
builder.SetClassAttributeParameters(singleInstance: true);
builder.SetExecuteMethodContent(@"
    MacroLogger.TraceInformation($""Camera: {CameraEntity}, Message: {Message}"");
");

string macroSourceCode = builder.Build();

// Create the macro entity within a transaction
Macro macro = await engine.TransactionManager.ExecuteTransactionAsync(() =>
{
    var newMacro = (Macro)engine.CreateEntity("Camera Alert Macro", EntityType.Macro);
    newMacro.SetSourceCode(macroSourceCode);
    newMacro.Description = "Logs camera alert messages";
    return newMacro;
});

// Execute the macro
var parameters = macro.DefaultParameters;
parameters["CameraEntity"].Value = someCameraGuid;
parameters["Message"].Value = "Motion detected";
Guid instanceId = macro.Execute(parameters);

For more details on working with macro entities, see MacroSample.cs in the Platform SDK samples.

UserMacro Base Class

All macros inherit from Genetec.Sdk.Scripting.UserMacro, which provides access to Security Center and the macro runtime.

Macro Execution Lifecycle

Understanding the complete lifecycle of a macro execution:

1. User/System triggers macro execution
   ↓
2. Macro Agent creates instance via parameterless constructor
   ⚠️  Sdk, MacroLogger, parameters are NULL here
   ↓
3. Framework initializes base class properties
   • Sets Sdk, MacroLogger, InstanceGuid, MacroGuid, etc.
   ↓
4. Framework sets macro parameter properties
   • Your public properties are populated
   ↓
5. Framework raises Started event
   ↓
6. Framework calls your Execute() method
   • All properties are now initialized and ready
   • Your macro logic runs here
   ↓
7. Execute() completes or throws exception
   ↓
   ┌─────────────────┴─────────────────┐
   │                                   │
   ▼ Normal completion                 ▼ Exception thrown
   │                                   │
   │ If KeepRunningAfterExecute:       │ Macro marked as failed
   │   - false: Go to cleanup          │ Exception logged
   │   - true: Wait for Exit() call    │
   │                                   │
   └─────────────────┬─────────────────┘
                     ↓
8. Framework calls CleanUp() (ALWAYS)
   • Guaranteed execution even on exception
   • Active transactions are auto-rolled back
   • Unsubscribe events and dispose resources
   ↓
9. Framework raises Completed or Aborted event
   ↓
10. Framework disposes macro instance

Key Points:

  • The constructor runs before any initialization
  • Execute() is where your main logic lives
  • CleanUp() always executes, even on exceptions
  • Events let you track lifecycle stages
  • Long-running macros need KeepRunningAfterExecute = true and Exit()

Essential Properties

The UserMacro base class provides these key properties:

Property Type Description
Sdk Engine Access to the Platform SDK Engine for interacting with Security Center
MacroLogger Logger Logger instance for tracing macro execution
MacroAbortToken CancellationToken Token signaled when the macro should stop
InstanceGuid Guid Unique identifier for this execution instance
MacroGuid Guid The macro entity GUID
InstigatorGuid Guid GUID of the user or application that triggered execution
TriggeringEvent Event The event that triggered execution (if applicable)
UtcStartTimestamp DateTime When the macro started (UTC)

Protected Virtual Properties

You can override these properties in your macro class to customize behavior:

Property Type Default Value Description
LoggerName string InstanceGuid.ToString("n") Name used for the logger instance
TracePrefix string "Macro {MacroGuid} - " Prefix added to trace messages
[MacroParameters(SingleInstance = true)]
public class CustomLogMacro : UserMacro
{
    protected override string LoggerName 
    { 
        get { return "MyCustomLogger"; }
    }
    
    protected override string TracePrefix 
    { 
        get { return string.Format("[{0}] ", MacroGuid); }
    }
    
    public override void Execute()
    {
        MacroLogger.TraceInformation("This uses custom logger name and prefix");
    }
    
    protected override void CleanUp()
    {
    }
}

Constructor (Optional)

Your macro class can optionally define a parameterless constructor.

Important

The framework calls the constructor before any macro properties are initialized. Sdk, MacroLogger, InstanceGuid, MacroGuid, and all other base class properties are NULL or empty in the constructor.

  • Macro parameters (your public properties) are not yet set
  • You should not perform any work in the constructor
  • Keep the constructor empty or use it only for initializing your own private fields
[MacroParameters(SingleInstance = true)]
public class MyMacro : UserMacro
{
    public Guid CameraGuid { get; set; }

    private readonly List<string> m_myData = new List<string>(); // OK - initializing your own fields

    public MyMacro()
    {
        // DON'T do this - Sdk is null here!
        // var camera = Sdk.GetEntity(CameraGuid); // WILL FAIL!

        // DON'T do this - MacroLogger is null here!
        // MacroLogger.TraceInformation("Starting"); // WILL FAIL!

        // OK - initializing your own state
        m_myData.Add("initialized");
    }

    public override void Execute()
    {
        // NOW everything is initialized - do your work here
        MacroLogger.TraceInformation("Macro started");
        var camera = Sdk.GetEntity(CameraGuid);
    }

    protected override void CleanUp()
    {
    }
}

Initialization order:

  1. Constructor called (nothing initialized yet)
  2. Macro framework sets Sdk, MacroLogger, InstanceGuid, etc.
  3. Macro framework sets your parameter properties
  4. Framework calls Execute() (everything is now initialized)

Using multiple classes in a macro

A macro file can contain multiple classes to organize complex logic. However, the macro runtime requires exactly one entry point class.

Requirements:

  • Exactly one class must inherit from UserMacro (or UserMacroWithSetup)
  • Additional classes must be regular C# classes that do not inherit from UserMacro
  • Helper classes can be static classes, utility classes, data models, or extension methods

Tip

Use a unique, descriptive class name for your macro entry point class (e.g., SendEmailOnAlarmMacro instead of the default myMacro). While the system supports multiple macros with the same class name (each compiles to a separate assembly), unique names make logs, stack traces, and debugging much clearer.

using Genetec.Sdk.Scripting;

// Helper class - does NOT inherit from UserMacro
public class MessageFormatter
{
    public string Format(string name, int count)
    {
        return string.Format("{0}: {1} items", name, count);
    }
}

// Entry point - the ONLY class that inherits from UserMacro
public class MyMacro : UserMacro
{
    private MessageFormatter _formatter = new MessageFormatter();

    public override void Execute()
    {
        string message = _formatter.Format("Result", 42);
        MacroLogger.TraceInformation(message);
    }

    protected override void CleanUp()
    {
    }
}

Important

If multiple classes inherit from UserMacro, the macro will fail to execute because the runtime cannot determine which class is the entry point.

Required Methods

Execute()

The entry point for your macro logic. You must override this abstract method. The framework calls it when the macro starts.

public override void Execute()
{
    MacroLogger.TraceInformation("Macro started");

    // Your automation logic here
}

CleanUp()

Called when the macro completes or is aborted. This is a virtual method with an empty default implementation, so overriding it is optional. Override this method when you need to release resources, unsubscribe from events, or perform cleanup operations.

protected override void CleanUp()
{
    // Unsubscribe from events
    Sdk.AlarmTriggered -= OnAlarmTriggered;
    
    // Dispose resources
    MacroLogger.TraceInformation("Cleanup completed");
}

Important

Always clean up in CleanUp(), not in Execute(). The runtime guarantees CleanUp() will be called even if Execute() throws an exception.

MacroParameters Attribute

Configure macro behavior with the [MacroParameters] attribute applied to your macro class.

[MacroParameters(
    SingleInstance = true, 
    KeepRunningAfterExecute = true,
    Run64Bit = false,
    RequestAdminRights = false,
    MacroGroup = "MyGroup")]
public class MyMacro : UserMacro
{
    public override void Execute()
    {
        // Your macro logic
    }
    
    protected override void CleanUp()
    {
    }
}

Quick Reference

Property Type Default Description
SingleInstance bool false Only one instance of this macro can run at a time
KeepRunningAfterExecute bool false Macro stays active after Execute() returns
Run64Bit bool false Force macro to run in 64-bit process
RequestAdminRights bool false Request elevated Windows privileges
MacroGroup string null Group macros in same application domain

SingleInstance

Type: bool (default: false)

When true, only one instance of this specific macro can run at a time. If another execution request is made while an instance is already running, the new request will be ignored.

Important

This applies per-macro, not globally. Other macros can still run concurrently.

Use when:

  • The macro modifies shared state or resources specific to its purpose
  • Multiple instances of the same macro would conflict with each other
  • The macro performs resource-intensive operations that shouldn't run concurrently
[MacroParameters(SingleInstance = true)]
public class ConfigUpdateMacro : UserMacro
{
    // Only one instance of ConfigUpdateMacro at a time
    // Other macros can still run
    
    public override void Execute()
    {
        // Your logic
    }
    
    protected override void CleanUp()
    {
    }
}

KeepRunningAfterExecute

Type: bool (default: false)

When true, the macro continues running after the Execute() method completes. This is essential for event-driven macros that need to respond to ongoing system events.

  • The macro stays active until manually aborted by a user or until it calls Exit()
  • Typically used with event subscriptions in the Execute() method
  • The macro waits indefinitely after Execute() returns
  • Call Exit() from within the macro to terminate gracefully

When false (default), the macro terminates automatically when Execute() returns.

[MacroParameters(KeepRunningAfterExecute = true, SingleInstance = true)]
public class AlarmMonitorMacro : UserMacro
{
    public override void Execute()
    {
        // Subscribe to events
        Sdk.AlarmTriggered += OnAlarm;
        
        // Execute() returns but macro keeps running
        // waiting for alarm events
    }
    
    void OnAlarm(object sender, AlarmTriggeredEventArgs e)
    {
        // Handle alarm
        if (MacroAbortToken.IsCancellationRequested)
        {
            Exit(); // Cleanly terminate the macro
        }
    }
    
    protected override void CleanUp()
    {
        Sdk.AlarmTriggered -= OnAlarm;
    }
}

Run64Bit

Type: bool (default: false)

Controls whether the macro runs in a 64-bit or 32-bit process.

  • When true, the macro runs in a 64-bit process
  • When false (default), the macro runs in a 32-bit process

Use Run64Bit = true when:

  • The macro needs more than 2GB of memory
  • The macro uses 64-bit native libraries
  • The macro performs memory-intensive operations (large datasets, image processing, etc.)

Note

Most macros don't need this. Keep the default unless you have specific requirements. The Run64Bit parameter availability depends on your Security Center version.

[MacroParameters(Run64Bit = true)]
public class LargeDataProcessingMacro : UserMacro
{
    // Processes large amounts of data
    
    public override void Execute()
    {
        // Your logic
    }
    
    protected override void CleanUp()
    {
    }
}

RequestAdminRights

Type: bool (default: false)

When true, the macro requests elevated privileges to run with administrative rights.

  • Required when the macro needs to perform privileged operations
  • The macro process will run with elevated permissions

Use when:

  • The macro needs to access protected system resources
  • The macro performs operations that require administrative privileges
  • The macro interacts with Windows services or system-level components

Caution

Only use this when absolutely necessary. Running with admin rights increases security risks.

[MacroParameters(RequestAdminRights = true)]
public class SystemConfigMacro : UserMacro
{
    // Modifies system-level configuration
    
    public override void Execute()
    {
        // Your logic
    }
    
    protected override void CleanUp()
    {
    }
}

MacroGroup

Type: string (default: empty/null)

Defines which application domain (AppDomain) the macro runs in. All macros with the same MacroGroup value share the same AppDomain and SDK Engine instance.

  • By default (null or empty), all macros run in the same AppDomain
  • Each AppDomain has a single shared SDK Engine instance
  • Macros in the same group share resources but are isolated from other groups
  • Different groups run in separate processes, providing isolation

Use when:

  • You need to isolate macros from each other
  • Different macros have conflicting dependencies or assemblies
  • You want to prevent one macro's failures from affecting another
  • You need to control resource allocation per group
[MacroParameters(MacroGroup = "VideoProcessing")]
public class VideoAnalysisMacro : UserMacro
{
    // Runs in "VideoProcessing" AppDomain with other video macros
    
    public override void Execute()
    {
        // Your logic
    }
    
    protected override void CleanUp()
    {
    }
}

[MacroParameters(MacroGroup = "AccessControl")]
public class DoorControlMacro : UserMacro
{
    // Runs in "AccessControl" AppDomain, isolated from video macros

    public override void Execute()
    {
        // Your logic
    }

    protected override void CleanUp()
    {
    }
}

Tip

Creating separate AppDomains has overhead. Group related macros together when possible to minimize resource usage.

Macro Parameters

Macros can accept typed parameters that are set before execution. Parameters are defined as public auto-implemented properties in your macro class.

Defining Parameters

Parameters are automatically discovered by the compiler through reflection. Any public property with a supported type becomes a parameter.

[MacroParameters(SingleInstance = true)]
public class ParameterizedMacro : UserMacro
{
    // These properties automatically become macro parameters
    public Guid CameraGuid { get; set; }
    public bool IsEnabled { get; set; }
    public string Message { get; set; }
    public int RetryCount { get; set; }
    public DateTime StartTime { get; set; }
    public double Threshold { get; set; }

    public override void Execute()
    {
        if (CameraGuid == Guid.Empty)
        {
            MacroLogger.TraceWarning("CameraGuid not set");
            return;
        }
        
        MacroLogger.TraceInformation(string.Format("Processing camera: {0}", CameraGuid));
        MacroLogger.TraceInformation(string.Format("Message: {0}", Message));
        MacroLogger.TraceInformation(string.Format("RetryCount: {0}", RetryCount));
    }

    protected override void CleanUp()
    {
    }
}

Supported Parameter Types

Only these types are supported as macro parameters:

Type Default Value Description
int 0 32-bit signed integer
Guid Guid.Empty Globally unique identifier
bool false Boolean value
string "" (empty) Text string
DateTime DateTime.MinValue Date and time
double 0.0 Double-precision floating point

Important

Using any other type will cause the property to be ignored as a parameter. Notably, uint, long, and float are not supported.

Parameter Requirements

For a property to be recognized as a macro parameter, it must meet ALL of these conditions:

  1. Must be a property (not a field)
  2. Must have a public setter - { get; set; } with public accessibility
  3. Must be writable - Read-only properties are ignored
  4. Must have a supported type - One of the types listed above
  5. Must not conflict with base class property names - Read-only base class properties like Sdk, MacroLogger, InstanceGuid, MacroGuid, etc. will cause compilation errors if redeclared. Additionally, avoid using the property name PermissionSet, which is reserved by the base class (though it won't cause compilation errors, it may be ignored during parameter discovery)
// ✅ VALID parameters
public int Count { get; set; }                    // Public auto-property
public string Name { get; set; }                  // Public auto-property

// ❌ INVALID - will be IGNORED
private int privateCount { get; set; }            // Private - ignored
public int ReadOnlyCount { get; }                 // No setter - ignored
public int ReadOnlyCount { get; private set; }    // Private setter - ignored
public TimeSpan Duration { get; set; }            // Unsupported type - ignored
public int myField;                               // Field, not property - ignored

Logging

Use MacroLogger for all macro logging. Do not use Console.WriteLine().

public override void Execute()
{
    MacroLogger.TraceInformation("Macro started");
    MacroLogger.TraceDebug("Debug information");
    MacroLogger.TraceWarning("Warning message");
    
    try
    {
        // Do work
    }
    catch (Exception ex)
    {
        MacroLogger.TraceError(ex, "Operation failed");
        throw; // Re-throw to mark macro as failed
    }
}

Log Levels:

  • TraceInformation() - General information
  • TraceDebug() - Detailed debugging information
  • TraceWarning() - Warning messages
  • TraceError() - Error messages (with optional exception)

Alternative: Trace() Method

The UserMacro base class provides a simpler Trace(string) protected method that automatically prepends the TracePrefix:

public override void Execute()
{
    Trace("Starting processing"); // Automatically includes TracePrefix
    
    // Equivalent to:
    // MacroLogger.TraceInformation(TracePrefix + "Starting processing");
}

This is convenient for quick logging, but MacroLogger methods offer more control with different log levels and exception handling.

Accessing the Platform SDK

The Sdk property gives you full access to the Platform SDK Engine. The macro framework automatically handles authentication using credentials provided by the Directory server, so you don't need to call engine.LogOn() or manage connections.

Warning

Never instantiate your own Engine class, call LogOn()/LogOff() methods, or dispose the Sdk or MacroLogger instances. The macro runtime manages these shared resources across all macros in the application domain.

public Guid CameraGuid { get; set; }
public Guid AlarmGuid { get; set; }

public override void Execute()
{
    // Get entity by GUID
    var camera = Sdk.GetEntity(CameraGuid);

    // Subscribe to events
    Sdk.AlarmTriggered += OnAlarmTriggered;
    Sdk.EntityAdded += OnEntityAdded;

    // Use managers
    Sdk.AlarmManager.AcknowledgeAlarm(InstanceGuid, AlarmGuid, AcknowledgementType.Ack);

    // Run queries
    var query = (EntityConfigurationQuery)Sdk.ReportManager.CreateReportQuery(ReportType.EntityConfiguration);

    // Transactions (synchronous version)
    Sdk.TransactionManager.ExecuteTransaction(() =>
    {
        // Modify entities atomically
    });

    // Or use async version with helper method (see Async Operations section)
    ExecuteAsync().GetAwaiter().GetResult();
}

private async Task ExecuteAsync()
{
    await Sdk.TransactionManager.ExecuteTransactionAsync(() =>
    {
        // Modify entities atomically
    });
}

For more details on working with the Platform SDK, see the Platform SDK overview.

Async Operations

The Execute() method is synchronous, but you can use async/await internally:

public Guid TargetGuid { get; set; }

public override void Execute()
{
    ExecuteAsync().GetAwaiter().GetResult();
}

private async Task ExecuteAsync()
{
    var entity = Sdk.GetEntity(TargetGuid);
    await ProcessEntityAsync(entity);
}

private async Task ProcessEntityAsync(Entity entity)
{
    // Async processing logic
    await Task.Delay(100); // Example async operation
    MacroLogger.TraceInformation(string.Format("Processed: {0}", entity.Name));
}

Handling Abort Requests

Check MacroAbortToken for long-running operations:

public override void Execute()
{
    for (int i = 0; i < 1000; i++)
    {
        if (MacroAbortToken.IsCancellationRequested)
        {
            MacroLogger.TraceInformation("Abort requested, stopping");
            return;
        }
        
        ProcessItem(i);
    }
}

Gracefully Stopping Long-Running Macros

For macros with KeepRunningAfterExecute = true, use the Exit() method to cleanly terminate the macro from within your code:

[MacroParameters(KeepRunningAfterExecute = true, SingleInstance = true)]
public class MonitoringMacro : UserMacro
{
    private int eventCount = 0;
    
    public override void Execute()
    {
        Sdk.AlarmTriggered += OnAlarm;
        
        // Execute() returns but macro keeps running
    }
    
    void OnAlarm(object sender, AlarmTriggeredEventArgs e)
    {
        eventCount++;
        MacroLogger.TraceInformation(string.Format("Alarm #{0}", eventCount));
        
        // Stop after processing 100 alarms
        if (eventCount >= 100)
        {
            MacroLogger.TraceInformation("Limit reached, exiting");
            Exit(); // Cleanly stops the macro
        }
    }
    
    protected override void CleanUp()
    {
        Sdk.AlarmTriggered -= OnAlarm;
    }
}

Important

  • Exit() can only be called in macros with KeepRunningAfterExecute = true
  • Calling it on other macros throws InvalidOperationException
  • After calling Exit(), CleanUp() will be called automatically
  • This is the proper way to stop a long-running macro from within (rather than external abort)

Macro Instance Events

The UserMacro class provides three instance-level events that you can subscribe to within your macro code:

[MacroParameters(SingleInstance = true)]
public class EventAwareMacro : UserMacro
{
    public EventAwareMacro()
    {
        // Subscribe to macro lifecycle events
        Started += OnMacroStarted;
        Completed += OnMacroCompleted;
        Aborted += OnMacroAborted;
    }
    
    private void OnMacroStarted(object sender, MacroEventArgs e)
    {
        // Called when this macro instance starts
        MacroLogger.TraceInformation(string.Format("Macro {0} started, instance {1}", e.MacroGuid, e.InstanceGuid));
    }
    
    private void OnMacroCompleted(object sender, MacroEventArgs e)
    {
        // Called when this macro instance completes successfully
        MacroLogger.TraceInformation("Macro completed successfully");
    }
    
    private void OnMacroAborted(object sender, MacroEventArgs e)
    {
        // Called when this macro instance is aborted
        if (e.RuntimeException != null)
        {
            MacroLogger.TraceError(e.RuntimeException, "Macro aborted due to exception");
        }
        else
        {
            MacroLogger.TraceWarning("Macro was aborted");
        }
    }
    
    public override void Execute()
    {
        // Your macro logic
    }
    
    protected override void CleanUp()
    {
        // Unsubscribe from events
        Started -= OnMacroStarted;
        Completed -= OnMacroCompleted;
        Aborted -= OnMacroAborted;
    }
}

Events:

  • Started - Raised when the macro execution begins
  • Completed - Raised when the macro completes normally
  • Aborted - Raised when the macro is aborted or fails

MacroEventArgs Properties:

  • InstanceGuid - The unique execution instance ID
  • MacroGuid - The macro entity GUID
  • RuntimeException - The exception that caused failure (if any)

Note

These are instance events for this specific macro execution. They're different from the static events on the Macro entity class used for external monitoring.

Exception Handling and Macro Lifecycle

Understanding how exceptions affect macro execution:

Normal Completion

When Execute() completes without throwing an exception:

  1. If KeepRunningAfterExecute = false: Macro stops and is marked as completed
  2. If KeepRunningAfterExecute = true: Macro continues running until you call Exit() or Security Center aborts it
  3. Framework calls CleanUp()
  4. Framework raises the Completed event

Exception in Execute()

When Execute() throws an exception:

  1. The exception is caught by the macro framework
  2. The macro is marked as failed
  3. The exception is logged via MacroLogger.TraceError()
  4. CleanUp() is still called (guaranteed)
  5. Framework raises the Aborted event with RuntimeException set
public override void Execute()
{
    try
    {
        // Your logic
        if (somethingWentWrong)
        {
            throw new InvalidOperationException("Something went wrong");
        }
    }
    catch (Exception ex)
    {
        MacroLogger.TraceError(ex, "Failed to process");
        throw; // Re-throw to mark macro as failed
    }
    // CleanUp() is guaranteed to be called even if exception is thrown
}

ThreadAbortException

ThreadAbortException is treated specially - it's considered an abort rather than a failure. This happens when:

  • The macro is manually aborted by a user
  • The Security Center service is shutting down

Transaction Rollback

If a transaction is active when the macro ends (normally or abnormally), it's automatically rolled back in CleanUp().

Important

Since Execute() is void (not async), you must use the sync-over-async pattern to call async transaction methods:

public override void Execute()
{
    // Pattern 1: Using GetAwaiter().GetResult() for async methods
    ExecuteAsync().GetAwaiter().GetResult();
}

private async Task ExecuteAsync()
{
    await Sdk.TransactionManager.ExecuteTransactionAsync(() =>
    {
        // Modify entities
        var cardholder = (Cardholder)Sdk.CreateEntity("John Doe", EntityType.Cardholder);
        cardholder.FirstName = "John";
        
        if (someError)
        {
            // Exception here rolls back the transaction automatically
            throw new InvalidOperationException("Cannot proceed");
        }
    });
    
    // Transaction is committed when ExecuteTransactionAsync completes successfully
}

protected override void CleanUp()
{
    // If a transaction is still active here (unusual case), it will be rolled back automatically
    // This is a safety net - you should normally commit or rollback explicitly
}

Alternative Pattern: Use synchronous ExecuteTransaction() directly:

public override void Execute()
{
    Sdk.TransactionManager.ExecuteTransaction(() =>
    {
        // Modify entities
        var cardholder = (Cardholder)Sdk.CreateEntity("John Doe", EntityType.Cardholder);
        cardholder.FirstName = "John";
        
        if (someError)
        {
            throw new InvalidOperationException("Cannot proceed");
        }
    });
    // Transaction is committed when ExecuteTransaction completes successfully
}

Automatic Rollback Scenarios:

  1. Exception during transaction: Transaction is automatically rolled back by ExecuteTransaction() or ExecuteTransactionAsync()
  2. Uncommitted transaction at macro end: If you manually create a transaction but don't commit it before the macro ends, CleanUp() automatically rolls it back
  3. Manual transaction with exception: If using CreateTransaction() / CommitTransaction() and an exception occurs, you must handle rollback explicitly (unless using the rollBackOnFailure: true parameter)

Best Practice: Always complete transactions explicitly using ExecuteTransaction() / ExecuteTransactionAsync() or by calling CommitTransaction() after CreateTransaction(). The automatic rollback in CleanUp() is a safety mechanism, not the primary way to manage transactions.

Design-Time vs Runtime Detection

The static property Macro.InExecutionEnvironment indicates whether Security Center is executing the macro or just inspecting it:

using Genetec.Sdk.Scripting;
using Genetec.Sdk.Scripting.Interfaces;

public class DesignAwareMacro : UserMacro
{
    public DesignAwareMacro()
    {
        // Avoid expensive initialization during design-time
        if (!Genetec.Sdk.Scripting.Interfaces.Macro.InExecutionEnvironment)
        {
            return; // Being instantiated for inspection only
        }
        
        // Safe to perform initialization that has side effects
    }
    
    public override void Execute()
    {
        // This is only called during actual execution
        // InExecutionEnvironment is always true here
    }
    
    protected override void CleanUp()
    {
    }
}

When is InExecutionEnvironment false?

  • When the Macro Compiler inspects your macro to discover parameters
  • During compilation and validation

When is InExecutionEnvironment true?

  • During actual macro execution
  • Inside Execute() and CleanUp() methods

Use this to:

  • Avoid expensive initialization during design-time
  • Prevent side effects when Config Tool inspects the macro
  • Skip resource allocation that's only needed at runtime

Important

Properties like Sdk, MacroLogger, and parameters are NULL when InExecutionEnvironment is false. Only use this flag in the constructor to control initialization logic.

Debugging Macros

Important

Macros run server-side on the Security Center server, not on the client machine. This means:

  • Visual Studio must be running on the server for local debugging
  • Alternatively, use Visual Studio Remote Debugging from your development machine to connect to the server
  • You cannot debug macros by running Visual Studio only on the client without remote debugging configured

Using Debugger.Launch()

The most effective way to debug your macro code is to use System.Diagnostics.Debugger.Launch() to attach a debugger when the macro executes:

using System.Diagnostics;
using Genetec.Sdk.Scripting;
using Genetec.Sdk.Scripting.Interfaces.Attributes;

[MacroParameters(SingleInstance = true)]
public class MyDebuggableMacro : UserMacro
{
    public override void Execute()
    {
        // Launch debugger at the start of execution
        if (!Debugger.IsAttached)
        {
            Debugger.Launch();
        }
        
        // Your macro logic here - you can now set breakpoints
        MacroLogger.TraceInformation("Starting macro execution");
        
        // ... rest of your code
    }
    
    protected override void CleanUp()
    {
    }
}

How it works:

  1. Add Debugger.Launch() at the beginning of your Execute() method
  2. Run the macro from Config Tool (manually or via Event-to-Action)
  3. When the dialog appears, select your running Visual Studio instance
  4. Visual Studio will attach and break at the Debugger.Launch() call
  5. Set breakpoints in your code and step through execution
  6. Debug normally using Visual Studio's debugging tools

Tips:

  • Use if (!Debugger.IsAttached) to avoid re-launching if already debugging
  • Remove or comment out Debugger.Launch() before deploying to production
  • You can also add it conditionally based on a macro parameter:
public bool EnableDebugging { get; set; }

public override void Execute()
{
    if (EnableDebugging && !Debugger.IsAttached)
    {
        Debugger.Launch();
    }
    
    // Your code
}

Alternative: Manual Attach to Process

If you prefer not to modify your macro code, you can manually attach Visual Studio to the macro process:

Steps:

  1. On the Security Center server, open Visual Studio
  2. Go to Debug > Attach to Process... (or press Ctrl+Alt+P)
  3. Look for one of these processes:
    • MacroAgent.exe - The main macro execution service
    • GenetecMacroSubprocess32.exe - 32-bit macro subprocess
    • GenetecMacroSubprocess64.exe - 64-bit macro subprocess (if using Run64Bit = true)
  4. Select the appropriate process and click Attach
  5. Set breakpoints in your macro code
  6. Trigger the macro execution from Config Tool

Tips:

  • If your macro uses Run64Bit = false (default), attach to GenetecMacroSubprocess32.exe
  • If your macro uses Run64Bit = true, attach to GenetecMacroSubprocess64.exe
  • You may need to attach before the macro runs, or ensure it's a long-running macro
  • For first-time execution, the subprocess may not exist until the macro starts
  • Consider using Debugger.Launch() for easier one-time debugging, and manual attach for production troubleshooting

Debugging Managed Code:

When attaching, ensure you select Managed (.NET Framework) as the code type in the "Attach to Process" dialog.

Remote Debugging:

If Visual Studio is running on your development machine (not the server), you can use Visual Studio Remote Debugging:

  1. Install Remote Tools for Visual Studio on the Security Center server
  2. Run Remote Debugger (msvsmon.exe) on the server
  3. In Visual Studio on your dev machine, use Debug > Attach to Process...
  4. Set Connection type to Remote (no authentication) or Remote (Windows Authentication)
  5. Enter the server name or IP address
  6. Select the macro process and attach

This allows you to debug from your development workstation without running Visual Studio on the server.

Alternative: SendMessage for Quick Diagnostics

For quick state inspection without a full debugger, you can send messages to Config Tool:

public override void Execute()
{
    // Send diagnostic messages visible in Config Tool
    Sdk.ActionManager.SendMessage(
        SdkGuids.Administrator, 
        $"Processing {entityCount} entities"
    );
    
    // Continue with your logic
}

This is useful for:

  • Checking if the macro is executing
  • Viewing variable values without stopping execution
  • Confirming logic flow in production environments

Note

Use this sparingly as it can create notification noise in Config Tool.

Best Practices

Use Unique, Descriptive Class Names

Change the default macro class name from myMacro to something descriptive and unique. Log messages show the class name, making it easier to identify which macro is executing. Stack traces and error messages become clearer when class names are unique. While the system supports multiple macros with identical class names (each compiles to a separate assembly), unique names improve maintainability.

Poor example - default name, not descriptive:

public class myMacro : UserMacro
{
    // ...
}

Better example - descriptive and unique:

public class SendEmailOnAlarmMacro : UserMacro
{
    // ...
}

Remember: Macros Run Server-Side

Always keep in mind that your macro executes on the Security Center server. File paths refer to the server's file system, not the client's. Network resources must be accessible from the server.

Incorrect approach - assumes client file system:

public override void Execute()
{
    string data = File.ReadAllText(@"C:\temp\config.txt");
}

Correct approaches:

public override void Execute()
{
    // Use UNC paths if accessing network resources
    string data = File.ReadAllText(@"\\fileserver\share\config.txt");

    // Or use server-local paths that exist on the server
    string serverPath = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
        "Genetec", "MacroData", "config.txt"
    );
}

Warning

Common pitfalls:

  • File paths refer to the server's file system, not the client's
  • Network resources must be accessible from the server (check server account permissions)
  • Environment variables are from the server context
  • Time zones, regional settings, and installed applications are the server's

Always Clean Up Resources

protected override void CleanUp()
{
    // Unsubscribe from ALL events
    Sdk.AlarmTriggered -= OnAlarmTriggered;
    Sdk.EntityAdded -= OnEntityAdded;
    
    // Dispose resources
    someDisposableResource?.Dispose();
    
    MacroLogger.TraceInformation("Cleanup completed");
}

Validate Parameters

public Guid CameraGuid { get; set; }
public int RetryCount { get; set; }

public override void Execute()
{
    if (CameraGuid == Guid.Empty)
    {
        MacroLogger.TraceWarning("CameraGuid parameter not set");
        return;
    }
    
    if (RetryCount <= 0)
    {
        MacroLogger.TraceWarning("RetryCount must be positive");
        return;
    }
    
    // Use parameters...
}

Handle Exceptions Properly

public override void Execute()
{
    try
    {
        // Your logic
    }
    catch (Exception ex)
    {
        MacroLogger.TraceError(ex, "Macro execution failed");
        throw; // Re-throw to mark macro as failed
    }
}

Remember:

  • CleanUp() is always called, even if Execute() throws an exception
  • Re-throwing the exception marks the macro as failed
  • Active transactions are automatically rolled back if the macro fails

Advanced Topics

UserMacroWithSetup Class

For advanced scenarios requiring custom application domain configuration, inherit from UserMacroWithSetup instead of UserMacro.

Purpose: Control which application domain your macro runs in.

using Genetec.Sdk.Scripting;
using Genetec.Sdk.Scripting.Interfaces.Attributes;

[MacroParameters(SingleInstance = true)]
public class MyAdvancedMacro : UserMacroWithSetup
{
    // ApplicationDomain is configured in Config Tool under "Default execution context"
    // All macros with same ApplicationDomain share one app domain and SDK Engine
    
    public override void Execute()
    {
        // Your macro logic
        // Access the configured domain via MacroGroup or ApplicationDomain (both return the same value)
        MacroLogger.TraceInformation(string.Format("Running in domain: {0}", MacroGroup));
    }
    
    protected override void CleanUp()
    {
    }
}

When to use UserMacroWithSetup:

  • You need to isolate macros in separate application domains
  • One macro triggers events that another macro handles (avoid cross-domain issues)
  • Performance optimization by grouping related macros (see Performance Optimization section)

Configuration:

  1. Create your macro inheriting from UserMacroWithSetup
  2. In Config Tool, open the macro's Default execution context tab
  3. Set the ApplicationDomain field (e.g., "MyMacroGroup")
  4. All macros with the same ApplicationDomain value share one app domain and one SDK Engine instance

Note

The ApplicationDomain property in UserMacroWithSetup is read-only and returns the same value as MacroGroup. The actual configuration is done in Config Tool, not by setting this property in code. The setter exists for backwards compatibility but does nothing.

Using External Assemblies

By default, macros have access to a limited set of assemblies (see Available Assemblies section). To use additional .NET assemblies or third-party libraries, you must configure them using the MacroAssembly command in Server Admin.

Important

This procedure is manual and must be performed by a Security Center administrator with access to the Directory server. It cannot be automated from macro code.

Adding .NET Framework Assemblies

To reference additional .NET Framework assemblies (e.g., System.Drawing.dll):

  1. On the Directory server, open Server Admin in a browser: http://localhost/genetec/console
  2. Navigate to Commands
  3. Select All commands (or uncheck User commands only)
  4. Find and select the MacroAssembly command
  5. When prompted, enter the full assembly name: System.Drawing.dll
  6. Restart the Macro Agent:
    • Option A: Restart the Directory service
    • Option B: Kill GenetecMacroAgent32.exe in Task Manager (it will restart automatically)
  7. Your macro can now use the assembly
using System.Drawing;
using Genetec.Sdk.Scripting;

public class ImageProcessingMacro : UserMacro
{
    public override void Execute()
    {
        // Now you can use System.Drawing
        using (var bitmap = new Bitmap(640, 480))
        {
            // Process image
        }
    }
    
    protected override void CleanUp()
    {
    }
}

Adding Third-Party Libraries

To use external DLLs (e.g., custom libraries, NuGet packages):

  1. Copy the DLL to the Directory server:
    • Recommended location: C:\Program Files (x86)\Genetec Security Center\[YourDLL].dll
    • Or any accessible folder on the server
  2. If you have a failover Directory, copy the DLL there too (same path)
  3. Open Server Admin: http://localhost/genetec/console
  4. Go to CommandsMacroAssembly
  5. Enter the DLL path:
    • Relative path (from SC installation folder): .\SharpSnmpLib.dll
    • Absolute path: C:\Program Files (x86)\Genetec Security Center\SharpSnmpLib.dll
  6. Restart the Macro Agent:
    • Option A: Restart the Directory service
    • Option B: Kill GenetecMacroAgent32.exe in Task Manager

After this, you can reference the library in your macro code normally.

Important

Deployment considerations:

  • The DLL must be copied to all Directory servers (primary + failover)
  • Re-run the MacroAssembly command after Security Center upgrades
  • If a new Directory server is added, copy the DLL and re-run the command

Removing Assembly References

To remove an assembly reference:

  1. In Server Admin, run the MacroAssembly command
  2. Prefix the assembly name with a minus sign: -System.Drawing.dll
  3. Restart the Macro Agent

Example: Complete Workflow

Here's a complete example showing how to add and use System.Drawing:

Step 1: Add the assembly via Server Admin

MacroAssembly command: System.Drawing.dll

Step 2: Restart Macro Agent and Config Tool

Step 3: Use in your macro

using System.Drawing;
using System.Drawing.Imaging;
using Genetec.Sdk.Scripting;

public class ImageProcessingMacro : UserMacro
{
    public string OutputPath { get; set; }
    
    public override void Execute()
    {
        // Create a simple image
        using (var bitmap = new Bitmap(800, 600))
        using (var graphics = Graphics.FromImage(bitmap))
        {
            graphics.Clear(Color.White);
            graphics.DrawString("Generated by Macro", 
                new Font("Arial", 24), 
                Brushes.Black, 
                new PointF(50, 50));
            
            bitmap.Save(OutputPath, ImageFormat.Png);
            MacroLogger.TraceInformation(string.Format("Image saved to {0}", OutputPath));
        }
    }
    
    protected override void CleanUp()
    {
    }
}

Note

The OutputPath would need to be a path accessible on the server (e.g., C:\Temp\output.png or a UNC path).

Compilation and Caching

When a macro runs for the first time, the runtime compiles the C# source code into an assembly. This compiled assembly is cached and reused for subsequent executions.

Important

Recompilation adds significant startup delay. It occurs when:

  • The macro source code is modified
  • The referenced assemblies change
  • The Genetec Server service restarts

The application domain also requires initialization on first use. The runtime creates the domain, initializes a shared SDK Engine, and authenticates with the Directory server. Once initialized, all macros in the same domain reuse this Engine.

Scenario Expected Startup Time
First execution after code change Slowest (compilation + domain initialization)
First execution after server restart Slow (domain initialization only)
Subsequent executions Fast (everything cached)

Tip

For time-critical macros, use the StayAlive pattern described below to keep the application domain initialized.

Performance Optimization with ApplicationDomains

You can improve macro startup performance by grouping macros in the same ApplicationDomain and keeping the domain initialized with a persistent macro.

Strategy: Keep one macro running continuously to prevent the application domain from unloading, which keeps compiled binaries cached in memory.

Step 1: Create a StayAlive Macro

using Genetec.Sdk.Scripting;
using Genetec.Sdk.Scripting.Interfaces.Attributes;

[MacroParameters(KeepRunningAfterExecute = true, SingleInstance = true)]
public class StayAliveMacro : UserMacroWithSetup
{
    public override void Execute()
    {
        // Execute returns immediately but macro stays running
        // This keeps the application domain alive
        MacroLogger.TraceInformation("StayAlive macro started - keeping domain warm");
    }
    
    protected override void CleanUp()
    {
        MacroLogger.TraceInformation("StayAlive macro terminated");
    }
}

Step 2: Configure ApplicationDomain

  1. In Config Tool, open the StayAlive macro
  2. Go to Default execution context tab
  3. Set ApplicationDomain: MyMacroGroup (choose any name)
  4. Save the macro

Step 3: Configure Other Macros

For all macros that should benefit from the performance boost:

  1. Inherit from UserMacroWithSetup
  2. Set the same ApplicationDomain value: MyMacroGroup
  3. All these macros will share the same app domain and SDK Engine instance

Step 4: Auto-Start StayAlive Macro

Create a Scheduled Task to start the StayAlive macro automatically:

  1. In Config Tool, go to System task → Scheduled tasks
  2. Create new scheduled task
  3. Set Recurrence: On startup
  4. Set Action: Run a macro
  5. Select Macro: Your StayAlive macro
  6. Save the scheduled task

Result: When the Directory starts, the StayAlive macro launches and keeps the application domain alive. All macros in MyMacroGroup will start faster because the domain is already loaded and warmed up.

Important

The StayAlive pattern is an advanced optimization technique. Only use it when you have a clear performance need and understand how to monitor and stop the macro if necessary. Ensure your StayAlive macro has no resource leaks.

Benefits:

  • Reduced cold-start time for macros in the group
  • Compiled macro binaries remain cached in memory
  • Shared SDK Engine instance reduces memory overhead
  • Ideal for frequently-executed macros

Server Admin Logging for Production Debugging

When debugging macros in production environments without attaching a debugger, use Server Admin to view macro logs in real-time.

Viewing Macro Logs

  1. On the Directory server, open Server Admin
  2. In the Console window, look for log entries from your macro
  3. Macro logs appear with the macro class name as the logger name

Adding a Macro Logger

If you don't see your macro's logs:

  1. In Server Admin, click the Add logger button
  2. Look for your macro by its class name (e.g., myMacro)
  3. Select the logger and add it
  4. Choose the log level (Info, Debug, Warning, Error)

If your macro doesn't appear in the logger list:

  1. Add a MacroLogger.TraceInformation() call in your macro's Execute() method
  2. Run the macro at least once
  3. Reload or restart Server Admin
  4. The logger should now appear in the list

Example: Production Logging

using Genetec.Sdk.Scripting;

public class ProductionMacro : UserMacro
{
    public Guid TargetEntity { get; set; }

    public override void Execute()
    {
        MacroLogger.TraceInformation("Macro started");
        MacroLogger.TraceDebug(string.Format("Processing entity: {0}", TargetEntity));

        try
        {
            // Your logic
            MacroLogger.TraceInformation("Operation completed successfully");
        }
        catch (Exception ex)
        {
            MacroLogger.TraceError(ex, "Operation failed");
            throw;
        }
    }
    
    protected override void CleanUp()
    {
        MacroLogger.TraceInformation("Cleanup completed");
    }
}

Log Levels:

Method Level Use For
TraceInformation() Info General operational messages (start, complete, key milestones)
TraceDebug() Debug Detailed diagnostic information (loop iterations, variable values)
TraceWarning() Warning Recoverable issues or unexpected conditions
TraceError() Error Errors and exceptions that prevent normal operation

Tips:

  • Use meaningful log messages that help diagnose issues
  • Include relevant context (entity GUIDs, parameter values, etc.)
  • Log at appropriate levels (avoid excessive Debug logging in production)
  • Server Admin logs persist and can be reviewed after macro execution

Debugging macros

Debugging macros is more challenging than debugging regular applications because they run inside the Security Center server environment. Use these strategies to diagnose and troubleshoot macro issues.

Logging strategies

Use extensive logging in your macro code to follow execution flow and diagnose issues.

public override void Execute()
{
    MacroLogger.TraceInformation("Execute started");
    MacroLogger.TraceDebug($"Parameter value: {MyParameter}");
    
    try
    {
        // Your logic
        MacroLogger.TraceDebug("Reached checkpoint A");
        
        var entity = Sdk.GetEntity(EntityGuid);
        if (entity == null)
        {
            MacroLogger.TraceWarning($"Entity not found: {EntityGuid}");
            return;
        }
        
        MacroLogger.TraceDebug($"Processing entity: {entity.Name}");
        
        // More logic
        MacroLogger.TraceInformation("Operation completed successfully");
    }
    catch (Exception ex)
    {
        MacroLogger.TraceError(ex, "Execute failed");
        throw; // Re-throw to trigger macro aborted event
    }
}

Best practices:

  • Log at the start of Execute() to confirm the macro was triggered
  • Log major decision points and branches in your logic
  • Log entity GUIDs, parameter values, and other relevant context
  • Log exceptions with full details before re-throwing
  • Remove or reduce verbose logging once the macro is stable

Console output

Use Console.WriteLine() for quick diagnostic output during development:

public override void Execute()
{
    Console.WriteLine("Macro started");
    Console.WriteLine($"Parameter: {MyParameter}");
    
    // Your logic
    
    Console.WriteLine("Macro completed");
}

Console output may appear in the server's console if running interactively, or can be captured in debug logs. This is useful for quick debugging but MacroLogger is preferred for production code.

Compile-time vs runtime errors

Compile-time errors are caught when you save the macro in Config Tool:

  • Click Check syntax to validate code before saving
  • Syntax errors show in a dialog with line numbers and descriptions
  • Fix all compilation errors before attempting to run the macro

Runtime errors occur during execution:

  • Check macro events (started/completed/aborted) in Security Desk
  • If a macro aborts, check the event details for exception information
  • Review Directory logs for full stack traces and error messages

Debug console

Enable the debug console to capture runtime messages and traces:

For Security Desk:

  1. Open Security Desk
  2. Go to About > Debug Console > Enable
  3. Restart Security Desk
  4. Navigate to http://localhost:6020/console/ in a web browser

For server-side macros:

  • Use Genetec Server Admin > Logs to access Directory logs
  • Enable trace levels for more detailed logging
  • Look for macro-related entries around the time of execution

Error handling patterns

Always handle exceptions gracefully to prevent abrupt macro failures:

public override void Execute()
{
    try
    {
        // Risky operation (network call, file access, etc.)
        PerformExternalOperation();
    }
    catch (IOException ioEx)
    {
        MacroLogger.TraceError(ioEx, "File operation failed");
        // Handle file-specific error
    }
    catch (Exception ex)
    {
        MacroLogger.TraceError(ex, "Unexpected error");
        throw; // Re-throw if recovery isn't possible
    }
}

Error handling guidelines:

  • Catch specific exceptions first, then general exceptions
  • Log all exceptions with context before handling or re-throwing
  • Consider whether to absorb the error (return) or propagate it (throw)
  • For critical macros, implement retry logic or fallback behavior
  • Validate inputs and check for null references before use

Testing strategies

Test in isolation:

  • If possible, test macro logic outside Security Center first
  • Use unit tests for complex algorithms or business logic
  • Validate parameter handling with edge cases (null, empty, invalid values)

Test in staging:

  • Always run new macros in a test/staging environment first
  • Test with realistic data and conditions
  • Verify that the macro completes successfully and produces expected results
  • Check for resource leaks (memory, file handles, connections)

Test automation triggers:

  • If using event-to-action, test both manual execution and automatic triggers
  • Verify that context parameters are passed correctly from events
  • Test edge cases (what if the triggering entity doesn't exist?)

Load testing:

  • For macros that run frequently, test under load
  • Verify performance with multiple concurrent instances
  • Monitor server resource usage (CPU, memory) during execution

Activity trails

If your macro makes changes to the system (creates entities, changes door states, triggers alarms), those actions are recorded in Security Center Audit Trails. Run an audit report to confirm the macro's effects and troubleshoot unexpected behavior.

Common issues

Macro doesn't start:

  • Check if macro is in Draft mode (toggle off in Config Tool)
  • Verify user has "Execute macros" privilege
  • Check if macro entity is in an accessible partition
  • Review event logs for "Macro aborted" events with error details

Macro hangs or runs too long:

  • Check for infinite loops without delays
  • Verify that blocking operations (network calls, file I/O) complete
  • Use System Status > Macros to stop the macro manually
  • Add timeouts to long-running operations

SDK operations fail:

  • Verify entities exist before accessing them
  • Check that the macro has appropriate permissions (limited access rights may restrict certain operations)
  • Ensure entity types match expected types (cast safely)

Parameters are null or wrong:

  • Verify default execution context is configured in Config Tool
  • Check if event-to-action or scheduled task overrides context correctly
  • Initialize properties with default values in code as a fallback

Security Constraints

Macros run server-side under the Macro Agent process. Starting in Security Center 5.8, macros can run with limited access rights to prevent security risks. On new installations, this is enabled by default. If you upgraded from a pre-5.8 system, ensure this setting is enabled in Genetec Server Admin under Directory settings ("Run macros with limited access rights").

With limited access rights enabled:

  • Macros run in a restricted context
  • Cannot create Admin user accounts
  • Cannot perform certain high-risk configuration changes
  • Some SDK operations may be denied or require special permissions

When developing macros, be aware that certain operations may not be available if limited access rights are enabled. Test your macro in an environment that matches your production security settings.

Warning

For security, macros must not:

  • Open inbound network listeners (web servers, TCP sockets, etc.)
  • Access Security Center databases directly (use SDK only)
  • Execute arbitrary shell commands from untrusted input
  • Store or transmit sensitive credentials

Always interact with Security Center through the SDK APIs provided.

Available Assemblies

Macros are compiled and executed by the Directory using a limited set of preconfigured assemblies (a subset of the .NET Framework plus key Genetec SDK assemblies such as Genetec.Sdk.dll and Genetec.Sdk.Scripting.Interfaces.dll).

The exact list of loaded assemblies depends on the Security Center version and on any assemblies added by an administrator using the MacroAssembly command. If your macro fails to compile due to a missing assembly reference, you must ask an administrator to add that assembly via MacroAssembly in Server Admin.

Licensing Requirements

To use macros, the Security Center system requires the Macro SDK license feature. Macros do not require SDK development certificates.

What you need:

  • Security Center system with the "Macro SDK" license feature enabled
  • No SDK development certificate required

See Also

Security Center SDK


Web SDK Developer Guide

  • Getting Started Setup, authentication, and basic configuration for the Web SDK.
  • Referencing Entities Entity discovery, search capabilities, and parameter formats.
  • Entity Operations CRUD operations, multi-value fields, and method execution.
  • Partitions Managing partitions, entity membership, and user access control.
  • Custom Fields Creating, reading, writing, and filtering custom entity fields.
  • Custom Card Formats Managing custom credential card format definitions.
  • Actions Control operations for doors, cameras, macros, and notifications.
  • Events and Alarms Real-time event monitoring, alarm monitoring, and custom events.
  • Incidents Incident management, creation, and attachment handling.
  • Reports Activity reports, entity queries, and historical data retrieval.
  • Performance Guide Optimization tips and best practices for efficient API usage.
  • Reference Entity GUIDs, EntityType enumeration, and EventType enumeration.
  • Under the Hood Technical architecture, query reflection, and SDK internals.
  • Troubleshooting Common error resolution and debugging techniques.

Media Gateway Developer Guide


Web Player Developer Guide

Clone this wiki locally