Files
nfc-actions/NfcActions/App.xaml.cs
Amal Graafstra a1d03fec2a Release v1.0.3: Enhanced NDEF reading and UX improvements
Major Improvements:
- Implement exclusive card access with retry logic and exponential backoff
- Fix Type 4 NDEF length parsing (now correctly reads NTAG424 and similar chips)
- Add enhanced card type detection (Type 2 vs Type 4)
- Implement chunked reading for large NDEF messages
- Add proper TLV parsing for Type 2 tags

Bug Fixes:
- Fix WPF window lifecycle issue (visual tree error on reopen)
- Fix NDEF length parsing incorrectly detecting extended format
- Correct data offset for Type 4 tag reading

New Features:
- Multi-line log selection and copy to clipboard
- Context menu with Copy Selected Lines, Copy All, Select All
- Runtime version roll-forward support (.NET 8.0.x compatibility)

Technical Details:
- Type 4 tags now use correct 2-byte NLEN field per NFC Forum spec
- Removed incorrect 3-byte extended length detection
- Window now hides instead of closing for proper tray app behavior
- Connection attempts exclusive access first, falls back to shared mode
- Status timeout increased from 0ms to 1000ms for better card detection
2025-11-18 22:20:13 -08:00

178 lines
5.4 KiB
C#

using System;
using System.Drawing;
using System.Threading;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media;
using NfcActions.Services;
using NfcActions.ViewModels;
using Application = System.Windows.Application;
namespace NfcActions;
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
private static Mutex? _instanceMutex;
private NotifyIcon? _notifyIcon;
private Icon? _customIcon;
private MainWindow? _mainWindow;
private CardReaderService? _cardReaderService;
private SettingsService? _settingsService;
private ActionService? _actionService;
private LogService? _logService;
private MainViewModel? _viewModel;
private System.ComponentModel.CancelEventHandler? _windowClosingHandler;
public App()
{
// CRITICAL: Set software rendering mode BEFORE any WPF initialization
// This must be done in the constructor before InitializeComponent()
try
{
// Set environment variable as additional safeguard
Environment.SetEnvironmentVariable("DOTNET_SYSTEM_WINDOWS_DONOTUSEPRESENTATIONDPICAPABILITYTIER2ORGREATER", "1");
// Force software rendering
RenderOptions.ProcessRenderMode = System.Windows.Interop.RenderMode.SoftwareOnly;
}
catch
{
// If this fails, try to continue anyway
}
}
private void Application_Startup(object sender, StartupEventArgs e)
{
// Ensure only one instance runs at a time
bool createdNew;
_instanceMutex = new Mutex(true, "NfcActions_SingleInstance_Mutex", out createdNew);
if (!createdNew)
{
System.Windows.MessageBox.Show("NFC Actions is already running. Check the system tray.",
"NFC Actions",
MessageBoxButton.OK,
MessageBoxImage.Information);
Shutdown();
return;
}
// Initialize services
_logService = new LogService();
_cardReaderService = new CardReaderService(_logService);
_settingsService = new SettingsService();
_actionService = new ActionService();
// Initialize ViewModel
_viewModel = new MainViewModel(_cardReaderService, _settingsService, _actionService, _logService);
// Create main window but don't show it yet
_mainWindow = new MainWindow(_viewModel);
// Load custom icon
try
{
var iconPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Resources", "icon.ico");
if (System.IO.File.Exists(iconPath))
{
_customIcon = new Icon(iconPath);
}
}
catch
{
// Fall back to default if custom icon can't be loaded
}
// Create system tray icon
_notifyIcon = new NotifyIcon
{
Icon = _customIcon ?? SystemIcons.Application,
Visible = true,
Text = "NFC Actions"
};
// Set up context menu
var contextMenu = new ContextMenuStrip();
contextMenu.Items.Add("Open", null, (s, args) => ShowMainWindow());
contextMenu.Items.Add(new ToolStripSeparator());
contextMenu.Items.Add("Exit", null, (s, args) => ExitApplication());
_notifyIcon.ContextMenuStrip = contextMenu;
_notifyIcon.MouseClick += (s, args) =>
{
if (args.Button == MouseButtons.Left)
{
ShowMainWindow();
}
};
// Start the card reader service
_cardReaderService.Start();
// Show a notification that the app is running
_notifyIcon.ShowBalloonTip(
3000,
"NFC Actions",
"NFC Actions is now monitoring for card events. Right-click the tray icon to configure.",
ToolTipIcon.Info);
}
private void ShowMainWindow()
{
// If window was closed, recreate it
if (_mainWindow == null || !_mainWindow.IsLoaded)
{
if (_viewModel == null) return;
_mainWindow = new MainWindow(_viewModel);
// Handle window closing - hide instead of close
_windowClosingHandler = (s, e) =>
{
e.Cancel = true;
if (_mainWindow != null)
{
_mainWindow.Hide();
}
};
_mainWindow.Closing += _windowClosingHandler;
}
_mainWindow.Show();
_mainWindow.WindowState = WindowState.Normal;
_mainWindow.Activate();
}
private void ExitApplication()
{
// Properly close the main window if it exists
if (_mainWindow != null)
{
// Remove the cancel handler so the window can actually close
if (_windowClosingHandler != null)
{
_mainWindow.Closing -= _windowClosingHandler;
}
_mainWindow.Close();
}
_notifyIcon?.Dispose();
_customIcon?.Dispose();
_cardReaderService?.Dispose();
Shutdown();
}
private void Application_Exit(object sender, ExitEventArgs e)
{
_notifyIcon?.Dispose();
_customIcon?.Dispose();
_cardReaderService?.Dispose();
_instanceMutex?.ReleaseMutex();
_instanceMutex?.Dispose();
}
}