ktsu.SingleAppInstance 1.2.7-pre.1

ktsu.SingleAppInstance

A .NET library that ensures only one instance of your application is running at a time.

License NuGet NuGet Downloads Build Status GitHub Stars

Introduction

ktsu.SingleAppInstance is a lightweight .NET library that provides a robust mechanism to ensure only one instance of an application is running at a time. This is essential for desktop applications, services, or any software that requires instance exclusivity to prevent resource conflicts, maintain data integrity, or provide a consistent user experience.

Features

  • Single Instance Enforcement: Ensures only one instance of the application is running
  • Enhanced Process Identification: Uses multiple attributes (PID, name, start time, executable path) for accurate identification
  • Race Condition Handling: Manages potential conflicts when instances start simultaneously
  • PID File Management: Stores process information securely in the application data directory
  • Cross-Platform Support: Works on Windows, macOS, and Linux
  • Backward Compatibility: Maintains compatibility with older versions that only stored the PID
  • Simple API: Clean, easy-to-use interface with just two primary methods

Installation

Package Manager Console

Install-Package ktsu.SingleAppInstance

.NET CLI

dotnet add package ktsu.SingleAppInstance

Package Reference

<PackageReference Include="ktsu.SingleAppInstance" Version="x.y.z" />

Usage Examples

Basic Example

The simplest way to use SingleAppInstance is to call the ExitIfAlreadyRunning method at the start of your application:

using ktsu.SingleAppInstance;

class Program
{
    static void Main(string[] args)
    {
        SingleAppInstance.ExitIfAlreadyRunning();
        
        // Your application code here
        Console.WriteLine("Application is running.");
    }
}

Custom Launch Logic

If you prefer to explicitly handle the case where another instance is already running:

using ktsu.SingleAppInstance;

class Program
{
    static void Main(string[] args)
    {
        if (SingleAppInstance.ShouldLaunch())
        {
            // Your application code here
            Console.WriteLine("Application is running.");
        }
        else
        {
            // Handle the case where another instance is already running
            Console.WriteLine("Another instance is already running.");
            
            // Optional: Activate the existing window, pass command-line arguments, etc.
        }
    }
}

Advanced Usage

WPF Application Integration

using System.Windows;
using ktsu.SingleAppInstance;

namespace MyWpfApp
{
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            
            if (!SingleAppInstance.ShouldLaunch())
            {
                // Another instance is already running
                MessageBox.Show("Application is already running.");
                Shutdown();
                return;
            }
            
            // Continue with normal startup
            MainWindow = new MainWindow();
            MainWindow.Show();
        }
    }
}

Console Application with Command Passing

For a more complex scenario where you want to pass commands to an existing instance:

using System;
using ktsu.SingleAppInstance;

class Program
{
    static void Main(string[] args)
    {
        // Check if we can launch
        if (!SingleAppInstance.ShouldLaunch())
        {
            // Another instance is already running
            // Here you could implement an IPC mechanism to pass arguments to the running instance
            Console.WriteLine("Another instance is already running. Sending arguments...");
            
            // Example: Using named pipes, TCP, or memory-mapped files to communicate
            // SendArgsToRunningInstance(args);
            
            return;
        }
        
        // We're the primary instance
        Console.WriteLine("Application is running.");
        
        // Optional: Setup your IPC listening mechanism
        // SetupIpcListener();
        
        // Application main loop
        Console.ReadLine();
    }
}

API Reference

SingleAppInstance Class

The static class that provides instance management functionality.

Methods

Name Return Type Description
ExitIfAlreadyRunning() void Exits the application with a status code of 0 if another instance is already running
ShouldLaunch() bool Determines whether the application should launch based on whether another instance is running

Technical Implementation

Under the hood, SingleAppInstance:

  1. Stores a JSON-serialized ProcessInfo object containing:

    • Process ID
    • Process name
    • Start time
    • Main module filename
  2. Uses the application data directory to store the PID file. The file is named .SingleAppInstance.pid.

  3. When checking for running instances, it:

    • Reads the PID file
    • Verifies if a process with the stored ID exists
    • Confirms it's the same application by comparing process name and executable path
    • Handles access restrictions and various edge cases
  4. Implements a 1-second timeout in ShouldLaunch() to detect race conditions when multiple instances start simultaneously.

Contributing

Contributions are welcome! Here's how you can help:

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Please make sure to update tests as appropriate and adhere to the existing coding style.

License

This project is licensed under the MIT License - see the LICENSE.md file for details.

No packages depend on ktsu.SingleAppInstance.

## v1.2.7-pre.1 (prerelease) Changes since v1.2.6: - Sync .gitattributes ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .runsettings ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .github\workflows\dotnet.yml ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync scripts\update-winget-manifests.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync scripts\PSBuild.psm1 ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.2.6 (patch) Changes since v1.2.5: - Add Directory.Packages.props and global.json for centralized package version management; update GitHub Actions workflows for improved CI/CD processes and SDK updates; introduce CompatibilitySuppressions.xml for compatibility diagnostics; refactor SingleAppInstance project files for .NET 9 compatibility; enhance PSBuild.psm1 with optional parameters for NuGet and Ktsu package keys. ([@matt-edmondson](https://github.com/matt-edmondson)) - Update .editorconfig, .gitignore, .runsettings, and PSBuild.psm1 for improved configurations and coverage settings ([@matt-edmondson](https://github.com/matt-edmondson)) ## v1.2.6-pre.18 (prerelease) Changes since v1.2.6-pre.17: - Sync scripts\PSBuild.psm1 ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .mailmap ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .gitattributes ([@ktsu[bot]](https://github.com/ktsu[bot])) - Merge remote-tracking branch 'refs/remotes/origin/main' ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .editorconfig ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .gitignore ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .runsettings ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.2.6-pre.17 (prerelease) Changes since v1.2.6-pre.16: - Update ktsu.AppDataStorage to 1.15.4 ([@dependabot[bot]](https://github.com/dependabot[bot])) ## v1.2.6-pre.16 (prerelease) Changes since v1.2.6-pre.15: - Sync .mailmap ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .gitignore ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .runsettings ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .gitattributes ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync scripts\PSBuild.psm1 ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .editorconfig ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.2.6-pre.15 (prerelease) Changes since v1.2.6-pre.14: ## v1.2.6-pre.14 (prerelease) Changes since v1.2.6-pre.13: ## v1.2.6-pre.13 (prerelease) Changes since v1.2.6-pre.12: ## v1.2.6-pre.12 (prerelease) Changes since v1.2.6-pre.11: ## v1.2.6-pre.11 (prerelease) Changes since v1.2.6-pre.10: ## v1.2.6-pre.10 (prerelease) Changes since v1.2.6-pre.9: ## v1.2.6-pre.9 (prerelease) Changes since v1.2.6-pre.8: ## v1.2.6-pre.8 (prerelease) Changes since v1.2.6-pre.7: ## v1.2.6-pre.7 (prerelease) Changes since v1.2.6-pre.6: ## v1.2.6-pre.6 (prerelease) Changes since v1.2.6-pre.5: ## v1.2.6-pre.5 (prerelease) Changes since v1.2.6-pre.4: ## v1.2.6-pre.4 (prerelease) Changes since v1.2.6-pre.3: ## v1.2.6-pre.3 (prerelease) Changes since v1.2.6-pre.2: ## v1.2.6-pre.2 (prerelease) Changes since v1.2.6-pre.1: ## v1.2.6-pre.1 (prerelease) Incremental prerelease update. ## v1.2.5 (patch) Changes since v1.2.4: - Remove .markdownlint.json configuration file, update DESCRIPTION.md for clarity, and change project SDK references in .csproj files to ktsu.Sdk.Lib and ktsu.Sdk.Test version 1.8.0. ([@matt-edmondson](https://github.com/matt-edmondson)) - Remove Directory.Build.props, Directory.Build.targets, and several PowerShell scripts for metadata and version management. Update SingleAppInstance and its tests to use 'var' for variable declarations and add copyright information. ([@matt-edmondson](https://github.com/matt-edmondson)) ## v1.2.5-pre.3 (prerelease) Changes since v1.2.5-pre.2: - Bump ktsu.AppDataStorage from 1.11.0 to 1.15.0 in the ktsu group ([@dependabot[bot]](https://github.com/dependabot[bot])) ## v1.2.5-pre.2 (prerelease) Changes since v1.2.5-pre.1: - Bump ktsu.AppDataStorage from 1.7.2 to 1.11.0 in the ktsu group ([@dependabot[bot]](https://github.com/dependabot[bot])) ## v1.2.5-pre.1 (prerelease) Incremental prerelease update. ## v1.2.4 (patch) Changes since v1.2.3: - Update README to match standard template format ([@matt-edmondson](https://github.com/matt-edmondson)) ## v1.2.3 (patch) Changes since v1.2.2: - Add tests for SingleAppInstance behavior with no other instance running and custom launch logic ([@matt-edmondson](https://github.com/matt-edmondson)) - Enhance SingleAppInstance to store and verify process information in JSON format ([@matt-edmondson](https://github.com/matt-edmondson)) - Enhance memory.jsonl to include advanced process detection and compatibility details for SingleAppInstance class ([@matt-edmondson](https://github.com/matt-edmondson)) - Refine README.md for clarity and usage instructions of SingleAppInstance ([@matt-edmondson](https://github.com/matt-edmondson)) - Update language-specific guidelines to clarify test execution commands and improve resource management ([@matt-edmondson](https://github.com/matt-edmondson)) - Refine README.md to enhance feature descriptions and clarify technical implementation details for SingleAppInstance ([@matt-edmondson](https://github.com/matt-edmondson)) - Set max cpus for tests to 1 ([@matt-edmondson](https://github.com/matt-edmondson)) - Add memory.jsonl file with project and class details ([@matt-edmondson](https://github.com/matt-edmondson)) - Update test command to run in a single process ([@matt-edmondson](https://github.com/matt-edmondson)) - Enhance memory.jsonl to include additional details for SingleAppInstance tests and project structure ([@matt-edmondson](https://github.com/matt-edmondson)) - Refactor SingleAppInstanceTests to simplify file deletion logic and remove unnecessary try-finally blocks, ensuring clearer test flow and improved readability. ([@matt-edmondson](https://github.com/matt-edmondson)) - Update Copilot instructions for .NET project build and testing guidelines ([@matt-edmondson](https://github.com/matt-edmondson)) - Refactor SingleAppInstanceTests for better isolation ([@matt-edmondson](https://github.com/matt-edmondson)) - Organize and enhance Copilot documentation with detailed guidelines on memory usage, coding standards, and project management practices. ([@matt-edmondson](https://github.com/matt-edmondson)) - Refine workflow guidelines for clarity on specialized tool usage and documentation checks ([@matt-edmondson](https://github.com/matt-edmondson)) - Add SingleAppInstance.Test project to solution and configure build settings ([@matt-edmondson](https://github.com/matt-edmondson)) - Refactor SingleAppInstanceTests to improve clarity and structure of test methods, ensuring accurate PID file handling and process information validation. ([@matt-edmondson](https://github.com/matt-edmondson)) - Update language-specific guidelines to clarify tool usage and fallback options ([@matt-edmondson](https://github.com/matt-edmondson)) - Add workflow and process guidelines to Copilot instructions ([@matt-edmondson](https://github.com/matt-edmondson)) - Comment out MaxCpuCount setting in .runsettings to disable process-level parallelization ([@matt-edmondson](https://github.com/matt-edmondson)) - Clarify command line usage by providing an example for non-interactive mode with `git` commands ([@matt-edmondson](https://github.com/matt-edmondson)) - Enhance SingleAppInstanceTests by adding assertions to verify PID file handling and initial state checks ([@matt-edmondson](https://github.com/matt-edmondson)) - Add guidelines for using command line in non-interactive mode and directory context ([@matt-edmondson](https://github.com/matt-edmondson)) ## v1.2.2 (patch) Changes since v1.2.1: - Add comprehensive Copilot instructions and memory management guidelines ([@matt-edmondson](https://github.com/matt-edmondson)) - Add markdownlint configuration file for linting rules ([@matt-edmondson](https://github.com/matt-edmondson)) ## v1.2.2-pre.3 (prerelease) Changes since v1.2.2-pre.2: - Bump ktsu.AppDataStorage from 1.7.1 to 1.7.2 in the ktsu group ([@dependabot[bot]](https://github.com/dependabot[bot])) ## v1.2.2-pre.2 (prerelease) Changes since v1.2.2-pre.1: - Sync .editorconfig ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.2.2-pre.1 (prerelease) Incremental prerelease update. ## v1.2.1 (patch) Changes since v1.2.0: - Update packages ([@matt-edmondson](https://github.com/matt-edmondson)) ## v1.2.0 (minor) Changes since v1.1.0: - Add LICENSE template ([@matt-edmondson](https://github.com/matt-edmondson)) ## v1.1.1-pre.2 (prerelease) Changes since v1.1.1-pre.1: - Sync scripts\make-changelog.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync scripts\make-version.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.1.1-pre.1 (prerelease) Changes since v1.1.0: - Sync .editorconfig ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync scripts\make-changelog.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync .gitignore ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync Directory.Build.targets ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync scripts\make-version.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.1.0 (minor) Changes since v1.0.0-pre.19: - Apply new editorconfig ([@matt-edmondson](https://github.com/matt-edmondson)) ## v1.0.0-pre.19 (prerelease) Changes since v1.0.0-pre.18: - Bump ktsu.AppDataStorage from 1.4.7 to 1.5.0 in the ktsu group ([@dependabot[bot]](https://github.com/dependabot[bot])) ## v1.0.0-pre.18 (prerelease) Changes since v1.0.0-pre.17: - Sync scripts\make-version.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync scripts\make-changelog.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.0.0-pre.17 (prerelease) Changes since v1.0.0-pre.16: - Sync .github\workflows\dotnet.yml ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.0.0-pre.16 (prerelease) Changes since v1.0.0-pre.15: - Sync scripts\make-changelog.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot])) - Sync scripts\make-version.ps1 ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.0.0-pre.15 (prerelease) Changes since v1.0.0-pre.14: - Sync .github\workflows\dotnet.yml ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.0.0-pre.14 (prerelease) Changes since v1.0.0-pre.13: - Review Feedback ([@Damon3000s](https://github.com/Damon3000s)) - Revert unintended changes ([@Damon3000s](https://github.com/Damon3000s)) - Create the directory the Pid will be stored in ([@Damon3000s](https://github.com/Damon3000s)) ## v1.0.0-pre.13 (prerelease) Changes since v1.0.0-pre.12: - Sync .github\workflows\dotnet.yml ([@ktsu[bot]](https://github.com/ktsu[bot])) ## v1.0.0-pre.12 (prerelease) Changes since v1.0.0-pre.11: ## v1.0.0-pre.11 (prerelease) Changes since v1.0.0-pre.10: ## v1.0.0-pre.10 (prerelease) Changes since v1.0.0-pre.9: - Catch DirectoryNotFoundException ([@Damon3000s](https://github.com/Damon3000s)) ## v1.0.0-pre.9 (prerelease) Changes since v1.0.0-pre.8: - Bump MSTest from 3.7.2 to 3.7.3 ([@dependabot[bot]](https://github.com/dependabot[bot])) ## v1.0.0-pre.8 (prerelease) Changes since v1.0.0-pre.7: ## v1.0.0-pre.7 (prerelease) Changes since v1.0.0-pre.6: ## v1.0.0-pre.6 (prerelease) Changes since v1.0.0-pre.5: - Bump MSTest from 3.7.1 to 3.7.2 ([@dependabot[bot]](https://github.com/dependabot[bot])) ## v1.0.0-pre.5 (prerelease) Changes since v1.0.0-pre.4: - Bump coverlet.collector from 6.0.3 to 6.0.4 ([@dependabot[bot]](https://github.com/dependabot[bot])) ## v1.0.0-pre.4 (prerelease) Changes since v1.0.0-pre.3: ## v1.0.0-pre.3 (prerelease) Changes since v1.0.0-pre.2: ## v1.0.0-pre.2 (prerelease) Changes since v1.0.0-pre.1: - Remove ktsu.ScopedAction package from project ([@matt-edmondson](https://github.com/matt-edmondson)) ## v1.0.0-pre.1 (prerelease) Incremental prerelease update. ## v0.0.1-pre.1 (prerelease) Changes since 0.0.1.0: - Initial commit ([@matt-edmondson](https://github.com/matt-edmondson)) - Remove ktsu.ScopedAction package from project ([@matt-edmondson](https://github.com/matt-edmondson))

.NET 5.0

.NET Standard 2.1

.NET Standard 2.0

.NET 9.0

.NET 8.0

.NET 7.0

.NET 6.0

Version Downloads Last updated
1.2.7-pre.4 18 11/24/2025
1.2.7-pre.3 14 11/23/2025
1.2.7-pre.2 15 11/23/2025
1.2.7-pre.1 14 11/23/2025
1.2.6 18 08/25/2025