# Cross-Platform Abstraction Layer
**Referenced Files in This Document**
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
- [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs)
- [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs)
- [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs)
- [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs)
- [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs)
- [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs)
- [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs)
## Table of Contents
1. [Introduction](#introduction)
2. [Project Structure](#project-structure)
3. [Core Components](#core-components)
4. [Architecture Overview](#architecture-overview)
5. [Detailed Component Analysis](#detailed-component-analysis)
6. [Dependency Analysis](#dependency-analysis)
7. [Performance Considerations](#performance-considerations)
8. [Troubleshooting Guide](#troubleshooting-guide)
9. [Conclusion](#conclusion)
10. [Appendices](#appendices)
## Introduction
This document explains the cross-platform abstraction layer that enables ClassicAssist to run on Windows, Linux, and macOS. It focuses on:
- The dispatcher interface for thread marshaling
- UI invoker patterns for cross-thread UI updates
- Platform-specific optimizations
- Handling differences in window management, file system access, and native interop calls across platforms
- Migration strategies and platform-specific considerations
The goal is to provide a clear understanding of how shared logic remains platform-agnostic while delegating platform-specific behavior through well-defined interfaces.
## Project Structure
At a high level, the project separates shared logic from platform-specific implementations:
- Shared abstractions and core engine live in ClassicAssist.Shared
- WPF-based implementation lives in ClassicAssist
- Avalonia-based implementation lives in ClassicAssist.Avalonia
- Launcher and updater are separate applications with their own UI frameworks
```mermaid
graph TB
subgraph "Shared"
S_ID["IDispatcher.cs"]
S_UI["IUIInvoker.cs"]
S_Eng["Engine.cs (Shared)"]
S_MsgProv["MessageBoxProvider.cs"]
S_Native["NativeMethods.cs"]
S_Platform["PlatformDetector.cs"]
end
subgraph "WPF"
W_Dispatch["WPFDispatcher.cs"]
W_UIInv["WPFUIInvoker.cs"]
W_Eng["Engine.cs (WPF)"]
W_MsgProv["WPFMessageBoxProvider.cs"]
end
subgraph "Avalonia"
A_Dispatch["AvaloniaDispatcher.cs"]
A_UIInv["AvaloniaUIInvoker.cs"]
A_Eng["Engine.cs (Avalonia)"]
A_Program["Program.cs (Avalonia)"]
A_App["App.axaml.cs (Avalonia)"]
A_MsgProv["AvaloniaMessageBoxProvider.cs"]
end
S_ID --> W_Dispatch
S_ID --> A_Dispatch
S_UI --> W_UIInv
S_UI --> A_UIInv
S_Eng --> W_Eng
S_Eng --> A_Eng
S_MsgProv --> W_MsgProv
S_MsgProv --> A_MsgProv
S_Native --> W_Eng
S_Native --> A_Eng
S_Platform --> W_Eng
S_Platform --> A_Eng
A_Program --> A_Eng
A_App --> A_Eng
```
**Diagram sources**
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
- [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs)
- [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs)
- [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs)
- [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs)
- [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs)
- [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs)
- [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs)
**Section sources**
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
- [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs)
- [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs)
- [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs)
- [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs)
- [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs)
- [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs)
- [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs)
## Core Components
The cross-platform abstraction centers around two primary interfaces:
- Dispatcher interface for thread marshaling
- UI Invoker interface for cross-thread UI updates
These interfaces are implemented per platform (WPF and Avalonia), allowing shared code to remain framework-agnostic.
Key responsibilities:
- Marshal work onto the UI thread safely
- Provide a consistent API for invoking UI operations from background threads
- Abstract away platform-specific threading models and message pumps
**Section sources**
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
## Architecture Overview
The architecture uses dependency injection or static initialization to select the correct platform implementation at runtime. Shared components depend only on interfaces, not concrete classes.
```mermaid
classDiagram
class IDispatcher {
+ "Dispatch(action)"
+ "BeginInvoke(action)"
+ "Invoke(action)"
}
class IUIInvoker {
+ "InvokeOnUiThread(action)"
+ "IsOnUIThread()"
}
class WPFDispatcher
class WPFUIInvoker
class AvaloniaDispatcher
class AvaloniaUIInvoker
IDispatcher <|.. WPFDispatcher : "implements"
IDispatcher <|.. AvaloniaDispatcher : "implements"
IUIInvoker <|.. WPFUIInvoker : "implements"
IUIInvoker <|.. AvaloniaUIInvoker : "implements"
```
**Diagram sources**
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
## Detailed Component Analysis
### Dispatcher Interface and Implementations
Purpose:
- Provide a unified way to schedule work on the UI thread regardless of the underlying UI framework
- Support both synchronous and asynchronous dispatching patterns
Implementation highlights:
- WPFDispatcher bridges to WPF’s dispatcher model
- AvaloniaDispatcher bridges to Avalonia’s dispatcher model
- Both expose methods to enqueue actions and ensure they execute on the UI thread
```mermaid
sequenceDiagram
participant BG as "Background Thread"
participant Shared as "Shared Code"
participant Disp as "IDispatcher"
participant Impl as "WPFDispatcher / AvaloniaDispatcher"
participant UIThread as "UI Thread"
BG->>Shared : "Perform long-running task"
Shared->>Disp : "BeginInvoke(action)"
Disp->>Impl : "Forward call"
Impl->>UIThread : "Marshal action"
UIThread-->>Impl : "Execute action"
Impl-->>Shared : "Return"
```
**Diagram sources**
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
**Section sources**
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
### UI Invoker Interface and Implementations
Purpose:
- Encapsulate UI framework-specific invocation semantics
- Offer IsOnUIThread checks and safe InvokeOnUiThread helpers
Implementation highlights:
- WPFUIInvoker uses WPF’s thread affinity APIs
- AvaloniaUIInvoker uses Avalonia’s thread context APIs
- Shared code calls IUIInvoker without knowing the underlying framework
```mermaid
flowchart TD
Start(["Start"]) --> Check["Check if currently on UI thread"]
Check --> |Yes| ExecuteDirect["Execute UI update directly"]
Check --> |No| Enqueue["Enqueue action via dispatcher"]
Enqueue --> Wait["Wait for completion if needed"]
ExecuteDirect --> End(["End"])
Wait --> End
```
**Diagram sources**
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
**Section sources**
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
### Engine Initialization and Platform Selection
Purpose:
- Initialize the correct platform-specific dispatcher and UI invoker
- Configure application lifecycle and entry points per platform
Implementation highlights:
- Shared Engine defines common startup logic and configuration
- WPF Engine initializes WPF-specific resources and sets up WPFDispatcher/WPFUIInvoker
- Avalonia Engine initializes Avalonia-specific resources and sets up AvaloniaDispatcher/AvaloniaUIInvoker
- Avalonia Program and App coordinate bootstrapping and DI setup
```mermaid
sequenceDiagram
participant OS as "Operating System"
participant Entry as "Program.cs (Avalonia)"
participant App as "App.axaml.cs (Avalonia)"
participant Eng as "Engine.cs (Avalonia)"
participant Disp as "AvaloniaDispatcher"
participant UIInv as "AvaloniaUIInvoker"
OS->>Entry : "Launch process"
Entry->>App : "Initialize Avalonia"
App->>Eng : "Create and configure Engine"
Eng->>Disp : "Register dispatcher"
Eng->>UIInv : "Register UI invoker"
Eng-->>App : "Ready"
```
**Diagram sources**
- [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs)
- [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs)
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
**Section sources**
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
- [Program.cs (Avalonia)](file://ClassicAssist.Avalonia/Program.cs)
- [App.axaml.cs (Avalonia)](file://ClassicAssist.Avalonia/App.axaml.cs)
### Window Management Abstraction
Purpose:
- Provide a consistent API for creating, showing, and managing windows across platforms
- Hide differences between WPF Window and Avalonia Window lifecycles
Approach:
- Use platform-specific providers registered by each Engine implementation
- Expose simple Create/Show/Close operations from shared code
Considerations:
- Ensure UI thread affinity when manipulating window state
- Handle platform differences in modal dialogs and owner relationships
[No sources needed since this section doesn't analyze specific files]
### File System Access Abstraction
Purpose:
- Normalize path separators, case sensitivity, and special directories across platforms
- Provide helper utilities for reading/writing configuration and data files
Approach:
- Use shared utility functions that adapt to platform specifics
- Centralize path resolution and normalization
Considerations:
- Respect user profile locations and sandboxing constraints
- Handle permission differences on Linux/macOS vs Windows
[No sources needed since this section doesn't analyze specific files]
### Native Interop Calls
Purpose:
- Provide a single point for platform-specific native calls
- Abstract away differences in P/Invoke signatures and availability
Approach:
- Define conditional compilation or runtime detection for available APIs
- Wrap native calls behind shared interfaces where possible
Considerations:
- Guard against missing symbols on non-Windows platforms
- Prefer managed alternatives when available
**Section sources**
- [NativeMethods.cs](file://ClassicAssist.Shared/Misc/NativeMethods.cs)
- [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs)
### Message Box Provider Abstraction
Purpose:
- Standardize user-facing dialog interactions across platforms
- Allow shared code to show messages without depending on UI framework details
Implementation highlights:
- MessageBoxProvider defines the shared contract
- WPFMessageBoxProvider implements it using WPF dialogs
- AvaloniaMessageBoxProvider implements it using Avalonia dialogs
```mermaid
classDiagram
class MessageBoxProvider {
<>
+ "Show(message)"
}
class WPFMessageBoxProvider
class AvaloniaMessageBoxProvider
MessageBoxProvider <|.. WPFMessageBoxProvider : "implements"
MessageBoxProvider <|.. AvaloniaMessageBoxProvider : "implements"
```
**Diagram sources**
- [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs)
- [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs)
- [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs)
**Section sources**
- [MessageBoxProvider.cs](file://ClassicAssist.Shared/Misc/MessageBoxProvider.cs)
- [WPFMessageBoxProvider.cs](file://ClassicAssist/UI/Misc/WPFMessageBoxProvider.cs)
- [AvaloniaMessageBoxProvider.cs](file://ClassicAssist.Avalonia/Misc/AvaloniaMessageBoxProvider.cs)
## Dependency Analysis
The following diagram shows how shared interfaces are implemented by platform-specific classes and how engines wire them together.
```mermaid
graph LR
Shared["Shared Interfaces
IDispatcher, IUIInvoker"] --> WPFImpl["WPF Implementations
WPFDispatcher, WPFUIInvoker"]
Shared --> AvaloniaImpl["Avalonia Implementations
AvaloniaDispatcher, AvaloniaUIInvoker"]
SharedEng["Shared Engine"] --> WPFEng["WPF Engine"]
SharedEng --> AvaloniaEng["Avalonia Engine"]
WPFEng --> WPFImpl
AvaloniaEng --> AvaloniaImpl
```
**Diagram sources**
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
**Section sources**
- [IDispatcher.cs](file://ClassicAssist.Shared/IDispatcher.cs)
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
- [WPFDispatcher.cs](file://ClassicAssist/WPFDispatcher.cs)
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
- [AvaloniaDispatcher.cs](file://ClassicAssist.Avalonia/AvaloniaDispatcher.cs)
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
- [Engine.cs (Shared)](file://ClassicAssist.Shared/Engine.cs)
- [Engine.cs (WPF)](file://ClassicAssist/Engine.cs)
- [Engine.cs (Avalonia)](file://ClassicAssist.Avalonia/Engine.cs)
## Performance Considerations
- Minimize cross-thread marshals: batch UI updates when possible to reduce dispatcher overhead
- Avoid blocking the UI thread: use BeginInvoke-style patterns for long-running tasks
- Prefer lightweight invocations: keep actions small and focused to reduce contention
- Cache frequently accessed platform info: avoid repeated platform detection calls
- Be mindful of memory pressure: avoid capturing large objects in dispatched closures
[No sources needed since this section provides general guidance]
## Troubleshooting Guide
Common issues and resolutions:
- Cross-thread operation exceptions: ensure all UI updates go through IUIInvoker.InvokeOnUiThread
- Deadlocks on UI thread: avoid calling synchronous Invoke from the UI thread; prefer asynchronous patterns
- Missing platform features: guard native calls with platform detection and fallbacks
- Dialog visibility problems: verify that dialogs are shown on the correct UI thread and have proper owners
**Section sources**
- [IUIInvoker.cs](file://ClassicAssist.Shared/IUIInvoker.cs)
- [WPFUIInvoker.cs](file://ClassicAssist/WPFUIInvoker.cs)
- [AvaloniaUIInvoker.cs](file://ClassicAssist.Avalonia/AvaloniaUIInvoker.cs)
- [PlatformDetector.cs](file://ClassicAssist.Shared/Engine/PlatformDetector.cs)
## Conclusion
The cross-platform abstraction layer cleanly separates shared logic from platform-specific concerns using well-defined interfaces for dispatching and UI invocation. By centralizing platform selection in the Engine implementations and providing consistent APIs for window management, file system access, and native interop, ClassicAssist achieves robust support across Windows, Linux, and macOS. Following the migration strategies and performance guidelines outlined here will help maintain stability and portability as the codebase evolves.
[No sources needed since this section summarizes without analyzing specific files]
## Appendices
### Migration Strategies
- Introduce new platform-specific feature:
- Add an interface method to the relevant shared interface if necessary
- Implement the method in WPF and Avalonia providers
- Update Engines to register the new provider
- Replace direct UI framework calls:
- Refactor to use IUIInvoker and IDispatcher
- Remove direct references to WPF/Avalonia types from shared code
- Handle native interop:
- Wrap calls in shared utilities with platform guards
- Provide no-op or alternative implementations for unsupported platforms
[No sources needed since this section doesn't analyze specific files]