-
Notifications
You must be signed in to change notification settings - Fork 4
platform sdk getting started
This guide shows how to connect a Platform SDK application to Security Center using the Engine class. The Engine manages the connection lifecycle and provides access to all SDK services.
Before connecting to Security Center, verify:
- Security Center SDK is installed on the machine
- SDK certificate is configured (see Certificate placement)
- Security Center license includes your certificate's part number
- User account has "Log on using the SDK" privilege
- Network access to the Security Center Directory server
The SDK certificate file must be in a folder named Certificates at the same directory level as your application executable. The certificate file name must match your executable name with .cert appended.
Example folder structure:
MyApplication/
├── MyApp.exe
└── Certificates/
└── MyApp.exe.cert
The SDK automatically searches for the certificate in this location when connecting to Security Center. If the certificate is missing, the connection fails with CertificateRegistrationError.
Alternatively, set the ClientCertificate property before connecting:
engine.ClientCertificate = "KxsD11z743Hf5Gq9mv3+5ekxzemlCiUXkTFY5ba1NOGcLCmGstt2n0zYE9NsNimv";For certificate details and licensing requirements, see SDK Certificates.
The Engine class is the main entry point for all SDK operations. Create an instance using the using statement to ensure proper disposal:
using Genetec.Sdk;
using var engine = new Engine();The Engine implements IDisposable. When disposed, it automatically logs off from Security Center (if connected) and releases all resources.
The Dispose method automatically calls LogOff when the Engine is disposed. This ensures:
- Active connections are properly terminated
- Server resources are released
- Certificates are unregistered from the connection
You do not need to explicitly call LogOff before disposing the Engine. The using statement handles both operations:
using var engine = new Engine();
await engine.LogOnAsync(server, username, password);
// Work with engine...
// Dispose automatically logs off and cleans upTo manually log off while keeping the Engine instance alive, call LogOff explicitly:
var engine = new Engine();
try
{
await engine.LogOnAsync(server, username, password);
// Work with engine...
engine.LogOff(); // Explicit logoff
// Engine instance still usable for reconnection
}
finally
{
engine.Dispose(); // Final cleanup
}The Engine class exposes logon methods directly for convenience, but these methods delegate to the LoginManager property internally. Both approaches are functionally equivalent:
// These are equivalent:
await engine.LogOnAsync(server, username, password);
await engine.LoginManager.LogOnAsync(server, username, password);When to use Engine methods:
Use engine.LogOnAsync() directly for cleaner code. This is the standard pattern for SDK applications.
When to use LoginManager methods:
Access engine.LoginManager when you need to:
- Subscribe to connection events (
LogonStatusChanged,LoggedOn,LogonFailed) - Access connection properties (
IsConnected,ActualDirectoryEndPoint) - Configure connection behavior (
ConnectionRetry,PreventDirectoryRedirection)
The Engine class exposes commonly used LoginManager properties (like IsConnected) directly, but all properties and events are available through engine.LoginManager.
The SDK provides multiple logon methods organized into three categories, all accessible from both Engine and LoginManager:
LogOnAsync method family returns Task<ConnectionStateCode> for use with async/await:
ConnectionStateCode result = await engine.LogOnAsync(server, username, password);These methods:
- Return the connection result directly as a
ConnectionStateCode - Support
CancellationTokenfor cooperative cancellation - Follow modern .NET async patterns
- Are non-blocking and efficient
Use these methods for all new SDK applications.
BeginLogOn and EndLogOn methods implement the Asynchronous Programming Model (APM) from .NET Framework 2.0:
IAsyncResult asyncResult = engine.BeginLogOn(server, username, password);
// ... wait for completion
engine.EndLogOn(asyncResult);These methods:
- Return
IAsyncResult(specificallyLogOnAsyncResult) for tracking operation status - Use callback patterns instead of async/await
- Require subscribing to events to determine success or failure
- Do not return
ConnectionStateCodevalues - Exist for backward compatibility only
The IAsyncResult returned by BeginLogOn provides:
-
IsCompletedproperty indicating whether the logon operation finished -
AsyncWaitHandleproperty (ManualResetEvent) for blocking until completion
The SDK sets IsCompleted to true when:
- The logon succeeds (
LoggedOnevent fires) - The logon is cancelled (
LoggedOffevent fires)
The SDK does not set IsCompleted to true when logon fails (LogonFailed event fires). This is a known limitation in the APM implementation.
Important
The EndLogOn method does not wait for the operation to complete. Instead, it cancels the logon operation by calling LogOff internally.
Do not use AsyncWaitHandle.WaitOne() to wait for completion. The wait handle never signals on logon failures, causing indefinite blocking. Always subscribe to the LoggedOn, LoggedOff, and LogonFailed events to handle all outcomes:
engine.LoginManager.LoggedOn += (s, e) => { /* Success */ };
engine.LoginManager.LoggedOff += (s, e) => { /* Cancelled */ };
engine.LoginManager.LogonFailed += (s, e) => { /* Failed */ };
IAsyncResult result = engine.BeginLogOn(server, username, password);
// Do NOT use result.AsyncWaitHandle.WaitOne() - it doesn't signal on failureDo not use these methods in new code. They are less efficient and harder to maintain than async/await.
LogOn and LogOnUsingWindowsCredential methods block the calling thread until connection completes:
engine.LogOn(server, username, password);
// Thread blocks here until connection succeeds or failsThese methods:
- Block the calling thread for the entire connection duration (potentially several seconds)
- Do not return
ConnectionStateCodevalues - Require subscribing to events to determine success or failure
- Provide explicit
portparameter overloads
Use synchronous methods only when maintaining legacy code that cannot be refactored to async/await.
All three method families support the same authentication modes:
| Feature | LogOnAsync | BeginLogOn | LogOn |
|---|---|---|---|
| Username and password | ✓ | ✓ | ✓ |
| Windows credential | ✓ | ✓ | ✓ |
| Security token | ✓ | ✓ | ✓ |
| Supervisor credentials | ✓ | ✓ | ✓ |
| Custom port | ✓ (via server string) | ✓ (port parameter) | ✓ (port parameter) |
| Certificate parameter | ✗ | ✗ | ✓ |
Custom port syntax:
All methods support custom ports. Specify the port in the server string for all methods:
await engine.LogOnAsync("server:5501", username, password);The BeginLogOn and LogOn methods also provide explicit port parameter overloads:
engine.LogOn("server", username, password, port: 5501);Avoid legacy patterns:
Do not write polling loops. This is a common antipattern with APM methods:
// ❌ WRONG - Do not do this
engine.BeginLogOn(server, username, password);
while (!engine.IsConnected)
{
Thread.Sleep(100); // Busy waiting wastes resources
}This pattern is inefficient, blocks threads unnecessarily, and ignores connection failures.
Use async/await with LogOnAsync:
// ✅ CORRECT
ConnectionStateCode result = await engine.LogOnAsync(server, username, password);
if (result == ConnectionStateCode.Success)
{
// Connected successfully
}
else
{
// Handle specific failure
Console.WriteLine($"Connection failed: {result}");
}The async/await pattern is non-blocking, returns the result directly, and integrates with modern C# cancellation and error handling.
Use LogOnAsync to connect to the Directory:
ConnectionStateCode result = await engine.LogOnAsync(
"directory-server",
"username",
"password");
if (result == ConnectionStateCode.Success)
{
// Connection succeeded
}Calling LogOn while already connected throws InvalidOperationException.
Calling LogOnAsync while already connected throws SdkException (SdkError.CouldNotLogon) with an inner InvalidOperationException. The SDK checks the connection state and prevents duplicate login attempts.
To reconnect with different credentials or to a different server, call LogOff first:
if (engine.IsConnected)
{
engine.LogOff();
}
await engine.LogOnAsync(server, username, password);Server address
The first parameter specifies the Directory server. Pass:
- A hostname:
"sc-server" - An IP address:
"192.168.1.100" - An empty string to connect to
localhost
Credentials
Standard authentication uses a Security Center username and password. The user must have the "Log on using the SDK" privilege.
To authenticate as a domain user from an Active Directory integration, specify the domain in the username parameter using either:
- User Principal Name format:
johnDoe@domain.com - Down-Level Logon Name format:
domain\johnDoe
// Active Directory user authentication
ConnectionStateCode result = await engine.LogOnAsync(
"directory-server",
"johnDoe@domain.com",
"password");Domain credentials with LogOn
When you pass domain\username or user@domain to LogOn/LogOnAsync, the SDK uses password-based domain credentials (not Windows integrated auth).
For Windows integrated authentication, use LogOnUsingWindowsCredential*.
Security Center requires an Active Directory role configured for domain authentication.
For Windows authentication, use LogOnUsingWindowsCredentialAsync to authenticate with the current Windows identity:
ConnectionStateCode result = await engine.LogOnUsingWindowsCredentialAsync(
"directory-server");This method:
- Uses the Windows identity of the process running the SDK
- Does not require passing credentials as parameters
- Enhances security by avoiding credential handling in code
Security Center requires an Active Directory role configured for Windows authentication.
The LogOnAsync method returns a ConnectionStateCode enum value. Check the result before proceeding:
ConnectionStateCode result = await engine.LogOnAsync(server, username, password);
switch (result)
{
case ConnectionStateCode.Success:
// Proceed with SDK operations
break;
case ConnectionStateCode.InvalidCredential:
Console.WriteLine("Invalid username or password");
break;
case ConnectionStateCode.InsufficientPrivileges:
Console.WriteLine("User lacks 'Log on using the SDK' privilege");
break;
case ConnectionStateCode.CertificateRegistrationError:
Console.WriteLine("SDK certificate registration failed. Check the certificate file, license inclusion, certificate count, and application ID.");
break;
case ConnectionStateCode.LicenseError:
Console.WriteLine("License does not include the SDK certificate part number");
break;
default:
Console.WriteLine($"Connection failed: {result}");
break;
}Common failure codes:
| Code | Meaning |
|---|---|
InvalidCredential |
Username or password is incorrect |
InsufficientPrivileges |
User account lacks "Log on using the SDK" privilege |
CertificateRegistrationError |
SDK certificate registration failed (missing or invalid file, certificate not included in the license, certificate count exceeded, or invalid application ID) |
LicenseError |
License does not include the certificate's part number or connection limit exceeded |
InvalidVersion |
SDK version is incompatible with the Security Center server |
Timeout |
Connection attempt timed out |
Subscribe to connection events to track progress and handle failures:
engine.LoginManager.LogonStatusChanged += (sender, e) =>
{
Console.WriteLine($"Connection status: {e.Status}");
};
engine.LoginManager.LoggedOn += (sender, e) =>
{
Console.WriteLine($"Logged on to '{e.ServerName}' as '{e.UserName}'");
};
engine.LoginManager.LogonFailed += (sender, e) =>
{
Console.WriteLine($"Logon failed: {e.FormattedErrorMessage}");
Console.WriteLine($"Error code: {e.FailureCode}");
};Register event handlers before calling LogOnAsync to capture all status changes.
Security Center uses TLS/SSL to encrypt communication between the SDK and the Directory. When connecting for the first time, or when the Directory's certificate has not been accepted yet, the RequestDirectoryCertificateValidation event fires:
engine.LoginManager.RequestDirectoryCertificateValidation += (sender, e) =>
{
Console.WriteLine($"Directory: {e.DirectoryHostName}");
Console.WriteLine($"Endpoint: {e.DirectoryEndPoint}");
Console.WriteLine($"Certificate Subject: {e.DirectoryCertificate.Subject}");
Console.WriteLine($"Certificate Issuer: {e.DirectoryCertificate.Issuer}");
Console.WriteLine($"Valid From: {e.DirectoryCertificate.NotBefore}");
Console.WriteLine($"Valid Until: {e.DirectoryCertificate.NotAfter}");
Console.WriteLine($"Thumbprint: {e.DirectoryCertificate.Thumbprint}");
if (e.CertificateRevoked)
Console.WriteLine("Warning: Certificate is revoked");
if (e.CertificateExpired)
Console.WriteLine("Warning: Certificate is expired");
if (e.CertificateIsUntrustedRoot)
Console.WriteLine("Warning: Certificate has an untrusted root");
if (e.CertificateDnsMismatch)
Console.WriteLine("Warning: DNS name mismatch");
// Set to true to accept the certificate
e.AcceptDirectory = true;
};The event fires once per Windows user per computer per certificate. After accepting a certificate, the system stores the acceptance decision and does not prompt again.
Note
This event validates the Secure Communication Certificate used for TLS/SSL encryption. This is separate from the SDK certificate that controls which applications can connect.
Register this event handler before calling LogOnAsync.
The LogonStatusChanged event fires multiple times during connection. Use it to display progress:
engine.LoginManager.LogonStatusChanged += (sender, e) =>
{
switch (e.Status)
{
case ConnectionStateCode.Connecting:
Console.WriteLine("Connecting to Directory...");
break;
case ConnectionStateCode.ConnectionEstablished:
Console.WriteLine("Connection established, authenticating...");
break;
case ConnectionStateCode.Success:
Console.WriteLine("Authentication successful");
break;
}
};Pass a CancellationToken to allow cancellation:
using var cancellationTokenSource = new CancellationTokenSource();
// Cancel after 30 seconds
cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(30));
ConnectionStateCode result = await engine.LogOnAsync(
server,
username,
password,
cancellationTokenSource.Token);
if (result == ConnectionStateCode.LogonAborted)
{
Console.WriteLine("Connection attempt was cancelled");
}Use IsConnected to check if the Engine is currently connected:
if (engine.IsConnected)
{
// Safe to perform SDK operations
}Security Center load balancing may redirect connections across Directory servers. Two properties track endpoint information:
OriginalDirectoryEndpoint - The server address you provided to the logon method:
Console.WriteLine($"Requested server: {engine.OriginalDirectoryEndpoint}");ActualDirectoryEndPoint - The server where you are actually connected:
Console.WriteLine($"Connected to: {engine.ActualDirectoryEndPoint}");These values differ when load balancing redirects the connection to a different Directory server.
After successful connection, the LoggedUser property provides access to the authenticated user entity:
User currentUser = engine.LoggedUser;
Console.WriteLine($"User: {currentUser.Name}");
Console.WriteLine($"Email: {currentUser.EmailAddress}");
Console.WriteLine($"User GUID: {currentUser.Guid}");
// Check user privileges
foreach (var privilege in currentUser.Privileges)
{
Console.WriteLine($"Privilege: {privilege.PrivilegeGuid}, State: {privilege.State}");
}This property returns null when not connected.
When a client signs in to the Directory, Security Center creates an Application entity (EntityType.Application) that represents the connection. Every client that connects to the Directory has its own Application entity. This includes operator workstations running Security Desk or Config Tool, server roles such as Access Manager, Federation, and Mobile Server, and external integrations built with the Platform SDK.
The ApplicationType property identifies the type of client:
| Value | Description |
|---|---|
Sdk |
An application built with the Platform SDK. |
SecurityDesk |
A Security Desk operator workstation. |
ConfigTool |
A Config Tool administration application. |
AccessManager |
An Access Manager server role. |
Federation |
A Security Center Federation role. |
MobileServer |
A Security Center Mobile Server role. |
WebClient |
A Web Client application. |
IntegrationService |
An Integration Service role. |
The enumeration includes additional values for other client types such as MobileClient, RoamingFederation, SaasOperation, and others.
The Application entity exposes:
| Property | Type | Description |
|---|---|---|
ApplicationType |
ApplicationType |
The type of client. |
LoggedUser |
User |
The user entity signed in to this application. |
LoggedUserGuid |
Guid |
The GUID of the signed-in user. |
Monitors |
ReadOnlyCollection<Guid> |
The GUIDs of Monitor entities registered by this application. Security Desk registers one monitor per display. |
ApplicationVersion |
string |
The version of the client application. |
Name |
string |
The workstation and application name (for example, "WS01 - SecurityDesk"). Inherited from Entity. |
The entity Name follows the format "WORKSTATION - APPLICATION", where WORKSTATION is the machine name and APPLICATION is the client type (for example, "SecurityDesk").
The Engine provides two properties to access your own Application entity:
Application myApp = engine.Client;
Guid myAppGuid = engine.ClientGuid;To discover other applications connected to the system, query for them using EntityConfigurationQuery. The GetEntities method returns cached entities only. For details on how the entity cache works, see About the Engine entity cache.
var query = (EntityConfigurationQuery)engine.ReportManager.CreateReportQuery(ReportType.EntityConfiguration);
query.EntityTypeFilter.Add(EntityType.Application);
query.DownloadAllRelatedData = true;
await Task.Factory.FromAsync(query.BeginQuery, query.EndQuery, null);
var applications = engine.GetEntities(EntityType.Application).OfType<Application>();
foreach (Application app in applications)
{
Console.WriteLine($"{app.Name} ({app.ApplicationType}) - Online: {app.IsOnline}");
}When a client disconnects, its Application entity is not deleted. The entity's RunningState transitions to State.NotRunning and its IsOnline property returns false. These properties are inherited from the Entity base class and are available on all entity types.
| Property | Type | Description |
|---|---|---|
IsOnline |
bool |
true when RunningState is Running or Warning, false when NotRunning. |
RunningState |
State |
The running state of the entity: Running, NotRunning, or Warning. |
Call LogOff to disconnect from the Directory:
engine.LogOff();For async operations, use BeginLogOff:
engine.LoginManager.BeginLogOff();The LoggingOff event fires when disconnection starts. The LoggedOff event fires when complete:
engine.LoginManager.LoggingOff += (sender, e) =>
{
Console.WriteLine("Disconnecting...");
};
engine.LoginManager.LoggedOff += (sender, e) =>
{
Console.WriteLine("Disconnected from Security Center");
Console.WriteLine($"Auto-reconnect: {e.AutoReconnect}");
};Calling LogOff when already disconnected is safe. The method:
- Checks the connection state internally
- Only waits for the
LoggedOffevent if previously connected - Completes immediately if no connection exists
- Does not throw exceptions for redundant calls
This allows calling LogOff defensively without checking IsConnected first:
// Safe to call even when not connected
engine.LogOff();Disposing the Engine automatically calls LogOff if still connected. The using statement handles both operations:
using var engine = new Engine();
await engine.LogOnAsync(server, username, password);
// Dispose automatically logs off and cleans upusing Genetec.Sdk;
using var engine = new Engine();
// Subscribe to connection events
engine.LoginManager.LogonStatusChanged += (sender, e) =>
Console.WriteLine($"Status: {e.Status}");
engine.LoginManager.LoggedOn += (sender, e) =>
Console.WriteLine($"Connected to '{e.ServerName}' as '{e.UserName}'");
engine.LoginManager.LogonFailed += (sender, e) =>
Console.WriteLine($"Connection failed: {e.FormattedErrorMessage}");
// Connect to Security Center
ConnectionStateCode result = await engine.LogOnAsync(
"directory-server",
"admin",
"password");
if (result == ConnectionStateCode.Success)
{
// Connection succeeded - perform SDK operations
Console.WriteLine($"Connection state: {engine.IsConnected}");
Console.WriteLine($"Server: {engine.ActualDirectoryEndPoint}");
// Example: Access an entity
var camera = engine.GetEntity(cameraGuid);
// Disconnect when finished
engine.LoginManager.BeginLogOff();
}
else
{
Console.WriteLine($"Connection failed with result: {result}");
}Security Center supports supervised logon where a supervisor user authorizes another user's actions. Pass supervisor credentials to LogOnAsync:
ConnectionStateCode result = await engine.LogOnAsync(
"directory-server",
"operator-username",
"operator-password",
"supervisor-username",
"supervisor-password");Both users must exist in Security Center. The operator's actions execute with combined privileges from both accounts.
When Security Center is configured to trust external identity providers such as SAML 2.0 or OpenID Connect, applications can authenticate users using security tokens issued by those providers. This enables Single Sign-On (SSO) scenarios where users authenticate once with their corporate credentials.
Before using token-based authentication:
- Configure an Authentication Service role in Security Center (SAML 2.0 or OpenID Connect type)
- Configure the role with your identity provider's settings
- Obtain a security token from your identity provider after the user authenticates
The SDK does not provide methods to obtain tokens from identity providers. Token acquisition is handled by your identity provider's SDK or library.
Pass the security token obtained from your identity provider to LogOnUsingSecurityTokenAsync:
// After obtaining a SecurityToken from your identity provider
ConnectionStateCode result = await engine.LogOnUsingSecurityTokenAsync(
"directory-server",
securityToken);Or use the serialized token string directly:
// Using the serialized token string
ConnectionStateCode result = await engine.LogOnUsingSecurityTokenAsync(
"directory-server",
serializedTokenString);Note
Serialized tokens must be Bearer tokens that do not require proof of possession.
Security Center validates the token against the configured Authentication Service role. When validation succeeds, the SDK connects with the user's identity and privileges from Security Center.
Use LogOnUsingSecurityTokenAsync for:
- Single Sign-On (SSO) scenarios where users authenticate with corporate credentials
- Federated authentication where Security Center trusts an external identity provider
- Web applications that authenticate users via OAuth/OpenID Connect and connect to Security Center on behalf of the authenticated user
For standard username and password authentication, use LogOnAsync instead.
The SDK includes built-in mechanisms to handle connection failures and network interruptions. Understanding these features prevents writing unnecessary reconnection logic.
When connecting, the SDK automatically retries failed attempts. The default is 10 retries. Configure this before calling LogOnAsync:
// Retry indefinitely until connection succeeds
engine.ConnectionRetry = -1;
// Try only once (no retries)
engine.ConnectionRetry = 1;
// Try 5 times before giving up
engine.ConnectionRetry = 5;During these retry attempts, the SDK handles all connection logic internally. The LogOnAsync method returns only after all retry attempts are exhausted. The LogonStatusChanged event fires during retries to report connection progress. The LogonFailed event fires only when all retry attempts fail.
The retry mechanism applies to initial connection only. After a successful connection, the SDK handles network interruptions differently.
When a connection to Security Center is lost after being established, the SDK automatically attempts to reconnect. Check the AutoReconnect property in the LoggedOff event to determine if reconnection will occur:
engine.LoginManager.LoggedOff += (sender, e) =>
{
if (e.AutoReconnect)
{
Console.WriteLine("Connection lost. SDK will automatically reconnect.");
}
else
{
Console.WriteLine("Disconnected. No automatic reconnection.");
}
};When AutoReconnect is true, the SDK reconnects in the background. The LoggedOn event fires again when reconnection succeeds. The LogonFailed event fires if reconnection fails.
Do not write custom reconnection logic. The SDK manages reconnection automatically. Simply handle the LoggedOn event to resume operations after reconnection.
The SDK distinguishes between transient errors that may resolve with retries and permanent errors that require fixing the underlying issue. This distinction determines which errors trigger automatic reconnection and which do not.
Transient errors (SDK retries automatically):
These errors indicate temporary problems that may resolve on subsequent attempts:
-
ConnectionLost- Network connection interrupted -
TimeoutorOperationTimeout- Connection attempt timed out -
Failed- General connection failure -
NoAuthenticationAgent- Active Directory role temporarily unavailable -
AuthenticationAgentResponseTimeout- Active Directory did not respond in time -
AuthenticationAgentImportFailure- Failed to import user from Active Directory -
DeniedByFirewall- Firewall blocked the connection -
DirectoryRedirect- Load balancer redirected to another Directory -
NewConnectionorNewBackupConnection- Connection status notifications
Permanent errors (SDK does not retry automatically):
These errors indicate configuration problems that will not resolve without changes:
-
InvalidCredential- Username or password is incorrect -
InsufficientPrivileges- User lacks "Log on using the SDK" privilege -
InsufficientSecurityLevel- User security level is too low -
CertificateRegistrationError- SDK certificate is missing or invalid -
LicenseError- License does not include certificate or connection limit exceeded -
InvalidVersion- SDK version incompatible with Security Center -
UserAccountDisabledOrLocked- User account is disabled or locked -
PasswordExpiredorExternalPasswordExpired- User password requires reset -
DisallowedBySchedule- User attempted logon outside allowed schedule -
ExceededNumberOfWorkstations- User exceeded workstation limit -
SpecifyDomain- Multiple Active Directory domains require explicit domain -
DirectoryCertificateNotTrusted- Directory SSL certificate not accepted -
InvalidSupervisor- Supervisor credentials are invalid -
InteractiveLogonRequired- User must log on interactively -
LogonAborted- Logon was cancelled -
AlreadyConnected- Application is already connected -
BruteForceThrottling- Account temporarily locked due to failed attempts
Note
The LicenseError behavior varies by application type. Config Tool and Security Desk do not auto-reconnect on license errors, but other applications may retry since licenses can change dynamically.
When building applications, check the ConnectionStateCode from LogOnAsync to determine if the error requires user action:
ConnectionStateCode result = await engine.LogOnAsync(server, username, password);
if (result == ConnectionStateCode.InvalidCredential ||
result == ConnectionStateCode.InsufficientPrivileges ||
result == ConnectionStateCode.UserAccountDisabledOrLocked)
{
// Permanent errors - prompt user to fix credentials or contact administrator
Console.WriteLine("Authentication failed. Please check your credentials.");
}
else if (result != ConnectionStateCode.Success)
{
// Transient errors - may work if tried again later
Console.WriteLine("Connection failed. Please try again.");
}This distinction helps applications provide appropriate error messages and avoid wasteful retry attempts for permanent failures.
Security Center supports directory redundancy through failover directories and load balancing. The SDK handles these mechanisms automatically, requiring no manual intervention from client applications.
Directory load balancing:
When multiple Directory servers are configured in a Security Center system, the Directory Manager distributes connection requests across all available directories in a round-robin fashion. When a client connects, the Directory Manager may redirect the connection to a different Directory server.
During connection, the SDK handles redirection automatically:
- Client calls
LogOnAsyncwith a Directory address - Directory Manager evaluates available directories and may redirect to another server
- SDK receives
DirectoryRedirectresponse and automatically connects to the redirected server -
LogonStatusChangedevent fires withDirectoryRedirectstatus - SDK completes connection to the redirected Directory
-
LoggedOnevent fires with the actual server name
The redirection is transparent. Applications do not need to handle DirectoryRedirect differently from other connection progress events.
Directory failover:
Security Center systems can include failover Directory servers configured in the Directory Manager role. When connection to the primary Directory is lost, the SDK automatically attempts to connect to a failover directory.
Critical: Failover requires successful initial connection
The failover directory list is obtained from the Directory Manager DURING the initial successful connection. The SDK stores this list internally for use if connection is lost later.
Connection scenarios:
-
First connection succeeds:
- SDK connects to primary Directory
- Directory Manager sends list of configured failover directories
- SDK stores the failover list in memory
- If connection is lost later, SDK automatically tries failover directories from the stored list
-
First connection fails (primary Directory unavailable):
- SDK has no failover directory list yet
-
LogOnAsyncreturns connection failure (e.g.,Timeout,Failed) - No automatic failover occurs
- Application must handle initial connection failure
- SDK retries the same primary directory address up to
ConnectionRetrytimes
Where the failover list is stored:
The failover directory list is stored in two locations:
- In memory: The SDK caches the list internally within the Engine instance for immediate use during automatic reconnection
- On disk: The list is persisted to a configuration file in the local user's application data folder
When an SDK application connects successfully to Security Center, the Directory Manager provides the failover directory list. The SDK stores this list in a configuration file for future use. On subsequent application launches, if the primary Directory is unavailable, the SDK can attempt connection to failover directories using the previously cached list.
The configuration persists:
- Between application restarts
- Per Windows user account
- In the local application data folder (typically
%LOCALAPPDATA%\Genetec Security Center 5.X\)
Each Engine instance reads the cached failover list on startup. If the primary Directory is unavailable during initial connection, the SDK tries cached failover directories if available. After a successful connection to any Directory, the SDK updates the cached failover list with current configuration from the Directory Manager.
The failover process (after initial connection):
- Connection to primary Directory is lost (
LoggedOffevent fires withAutoReconnect=true) - SDK automatically attempts to connect to each configured failover directory from the stored list
-
LogonStatusChangedevents fire during failover attempts - When connection to failover directory succeeds,
LoggedOnevent fires - Application resumes normal operation on the failover Directory
Initial connection reliability:
Since failover only works after the first successful connection, consider these approaches for initial connection reliability:
-
Use ConnectionRetry property:
engine.ConnectionRetry = -1; // Retry indefinitely until primary Directory responds
-
Handle initial connection failure:
ConnectionStateCode result = await engine.LogOnAsync(server, username, password); if (result != ConnectionStateCode.Success) { // Initial connection failed - no failover list available // Application must handle this (retry later, alert user, etc.) Console.WriteLine($"Cannot connect to Directory: {result}"); }
-
Use transient error detection:
After all retry attempts are exhausted, check the
ConnectionStateCodeto distinguish transient errors from permanent ones:ConnectionStateCode result = await engine.LogOnAsync(server, username, password); if (result == ConnectionStateCode.Timeout || result == ConnectionStateCode.Failed) { // Transient - may succeed later when server comes back Console.WriteLine("Unable to reach server. Check network connectivity."); } else if (result == ConnectionStateCode.InvalidCredential) { // Permanent - requires fixing credentials Console.WriteLine("Invalid username or password."); }
See Antipatterns to avoid for common mistakes when handling connection failures.
No manual failover required:
Applications should not implement custom failover or retry logic. The SDK automatically manages failover after obtaining the directory list. Simply handle the LoggedOn event to resume operations after automatic failover completes.
Detecting which Directory you are connected to:
Use ActualDirectoryEndPoint to determine the current Directory server:
engine.LoginManager.LoggedOn += (sender, e) =>
{
Console.WriteLine($"Connected to Directory: {e.ServerName}");
Console.WriteLine($"Actual endpoint: {engine.ActualDirectoryEndPoint}");
Console.WriteLine($"Original endpoint: {engine.OriginalDirectoryEndpoint}");
};When load balancing redirects the connection or failover switches to a backup directory, ActualDirectoryEndPoint differs from OriginalDirectoryEndpoint.
Connection state during failover:
During failover, IsConnected returns false until connection to a failover directory succeeds. The LogonStatusChanged event reports progress during failover attempts.
Preventing load balancing and failover:
In rare cases where an application must connect to a specific Directory server, disable redirection and failover before logging on:
engine.PreventDirectoryRedirection = true;
ConnectionStateCode result = await engine.LogOnAsync(server, username, password);This bypasses load balancing and prevents automatic failover. The SDK connects only to the specified Directory server. Use this setting only when specifically required, such as administrative tools that must interact with a particular Directory instance.
Note
Load balancing and failover are Security Center infrastructure features. Directory failover servers are configured in Config Tool under the Directory Manager role settings. The SDK automatically uses the failover configuration without requiring any setup in client applications. However, automatic failover only functions after a successful initial connection that retrieves the failover directory list.
❌ WRONG - Wrapping LogOnAsync in a retry loop:
// DO NOT DO THIS
int maxAttempts = 10;
for (int attempt = 0; attempt < maxAttempts; attempt++)
{
var result = await engine.LogOnAsync(server, username, password);
if (result == ConnectionStateCode.Success)
break;
await Task.Delay(1000); // Wait before retrying
}This creates a double-retry situation. The SDK already retries internally based on ConnectionRetry. Wrapping LogOnAsync in your own loop means if ConnectionRetry is 10, and your loop attempts 10 times, the application makes 100 connection attempts (10 × 10).
✅ CORRECT - Use the built-in ConnectionRetry property:
// Set the retry count once
engine.ConnectionRetry = 10;
// LogOnAsync handles all retries internally
var result = await engine.LogOnAsync(server, username, password);The SDK manages timing, exponential backoff, and connection state internally. Custom retry logic interferes with this mechanism and wastes resources.
❌ WRONG - Polling connection state after BeginLogOn:
// DO NOT DO THIS
engine.LoginManager.BeginLogOn(server, username, password);
while (!engine.IsConnected)
{
Thread.Sleep(100);
}This blocking pattern wastes CPU cycles and prevents the application from responding to user input. It also ignores connection failures, causing the loop to run indefinitely if logon fails.
✅ CORRECT - Use LogOnAsync with await:
var result = await engine.LogOnAsync(server, username, password);
if (result == ConnectionStateCode.Success)
{
// Connection established
}Or subscribe to events if using the synchronous LogOn method:
engine.LoginManager.LoggedOn += (sender, e) =>
{
// Connection established
};
engine.LoginManager.LogonFailed += (sender, e) =>
{
// Connection failed
};
engine.LogOn(server, username, password);❌ WRONG - Manually calling LogOnAsync after connection loss:
// DO NOT DO THIS
engine.LoginManager.LoggedOff += async (sender, e) =>
{
// Try to reconnect manually
await engine.LogOnAsync(server, username, password);
};The SDK automatically reconnects when e.AutoReconnect is true. Manual reconnection attempts interfere with the SDK's internal connection management and may cause multiple simultaneous connection attempts.
✅ CORRECT - Let the SDK handle reconnection:
engine.LoginManager.LoggedOff += (sender, e) =>
{
if (e.AutoReconnect)
{
Console.WriteLine("SDK is reconnecting automatically");
}
};
engine.LoginManager.LoggedOn += (sender, e) =>
{
Console.WriteLine("Reconnection successful");
// Resume normal operations
};The SDK reconnects automatically and fires LoggedOn when reconnection succeeds.
After connecting successfully:
- Access entities through the entity cache - see Entities
- Subscribe to real-time events - see Events
- Execute queries - see Report Manager
- Perform system actions - see Actions
-
Security Center SDK Developer Guide Overview of the SDK framework and how to build integrations with Security Center.
-
Platform SDK
- Overview Introduction to the Platform SDK and core concepts.
- Connecting to Security Center Step-by-step guide for connecting and authenticating with the SDK.
- SDK Certificates Details certificates, licensing, and connection validation.
- Referencing SDK Assemblies Best practices for referencing assemblies and resolving them at runtime.
- SDK Compatibility Guide Understanding backward compatibility and versioning in the SDK.
- Entity Guide Explains the core entity model, inheritance, and how to work with entities.
- Entity Cache Guide Describes the engine's local entity cache and synchronization.
- Transactions Covers batching operations for performance and consistency.
- Events Subscribing to real-time system events.
- Actions Sending actions to Security Center.
- Security Desk Displaying content on monitors, reading tiles, sending tasks, and messaging operators.
- Custom Events Defining, raising, and subscribing to custom events.
- ReportManager Querying entities and activity data from Security Center.
- ReportManager Query Reference Complete reference of query types, parameters, and response formats.
- Privileges Checking, querying, and setting user privileges.
- Partitions Entity organization and access control through partitions.
- Logging How to configure logging, diagnostics, and debug methods.
-
Plugin SDK
- Overview Introduction to plugin architecture and capabilities.
- Certificates SDK certificate requirements for plugin roles.
- Lifecycle Initialization and disposal patterns.
- Threading Threading model, QueueUpdate, and async patterns.
- State Management Reporting plugin health and diagnostics.
- Configuration Configuration storage and monitoring.
- Restricted Configuration Secure credential storage and admin-only configuration.
- Events Event subscription and handling.
- Queries Query processing and response handling.
- Request Manager Request/response communication with clients.
- Database Database integration and schema management.
- Entity Ownership Understanding plugin-owned entities, running state management, and ownership release.
- Entity Mappings Using EntityMappings for plugin-specific configuration and external system integration.
- Server Management High availability and server failover.
- Custom Privileges Defining and enforcing custom privileges.
- Custom Entity Types Defining and managing plugin-specific entity types.
- Resolving Non-SDK Assemblies Handling third-party dependencies in plugins and workspace modules.
- Deploying Plugins Registering and deploying plugins and workspace modules.
- .NET 8 Support Building plugins with .NET 8 and .NET Standard compatibility.
-
Workspace SDK
- Overview Introduction to client-side UI extensions for Security Desk and Config Tool.
- Certificates SDK certificate requirements for workspace modules.
- Creating Modules Module lifecycle, registration patterns, and assembly resolution.
- Tasks Executable actions, home page entries, and programmatic invocation.
- Pages Page content, lifecycle, descriptors, and navigation.
- Components Dashboard widgets, tiles, maps, credentials, and content builders.
- Tile Extensions Custom tile widgets, views, and properties panels.
- Services Built-in services for dialogs, maps, alarms, badges, and more.
- Contextual Actions Right-click context menu extensions.
- Options Extensions Custom settings pages in application preferences.
- Configuration Pages Entity configuration pages for Config Tool.
- Monitors Multi-monitor support and shared components.
- Shared Components Using monitor and workspace shared UI components.
- Commands Command execution, evaluation, and interception.
- Extending Events Adding custom fields to Security Center events.
- Map Extensions Custom map objects, layers, and providers.
- Timeline Providers Custom timeline event sources for video playback.
- Image Extractors Custom image sources for cardholder photos and custom fields.
- Credential Encoders Encoding credentials with custom encoder components.
- Cardholder Fields Extractors Importing cardholder data from external sources.
- Content Builders Building and customizing tile content in Security Desk.
-
Macro SDK
- Overview How macros work, creating and configuring macro entities, automation, and monitoring.
- Developer Guide Developing macro code with the UserMacro class and Security Center SDK.
-
- 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.
- About access control in the Web SDK Concepts, relationships, and common access-control operations.
- About video in the Web SDK Concepts, relationships, configuration, and common video operations.
- Users and user groups Creating users, managing group membership, and assigning privileges.
- 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.
- Tasks Listing and executing saved report tasks.
- Macros Monitoring currently running macros.
- Custom Entity Types Listing, retrieving, and deleting custom entity type descriptors.
- System Endpoints License usage, web tokens, and exception handling.
- 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 Guide Setup and configuration of the Media Gateway role for video streaming.
- Developer Guide Complete guide to integrating GWP for live and playback video streaming.
- API Reference Full API documentation with interfaces, methods, properties, and events.
- Sample Application Comprehensive demo showcasing all GWP features with timeline and PTZ controls.
- Multiplexing Sample Multi-camera grid demo using a shared WebSocket connection.