Replace socket approach with native Windows mutex.

This commit is contained in:
Dain Nilsson 2022-01-21 11:05:53 +01:00
parent 71b66f53b0
commit af1a2dad05
No known key found for this signature in database
GPG Key ID: F04367096FBA95E8
6 changed files with 22 additions and 103 deletions

View File

@ -1,68 +0,0 @@
import 'dart:io';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as path;
import 'package:path_provider/path_provider.dart';
import 'package:window_manager/window_manager.dart';
const _lockFileName = 'lockfile';
const _pingMessage = 'YA-PING';
const _pongMessage = 'YA-PONG';
final log = Logger('single_instance');
void _startServer(File lockfile) async {
final socket = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
lockfile.writeAsString('${socket.port}');
log.info('Lock file and socket created.');
socket.listen((client) {
client.listen((data) async {
final message = String.fromCharCodes(data);
if (message == _pingMessage) {
log.info('Got incomming connection');
if (!await WindowManager.instance.isMinimized()) {
// Causes the window to be brought to the front.
await WindowManager.instance.setAlwaysOnTop(true);
await WindowManager.instance.setAlwaysOnTop(false);
} else {
await WindowManager.instance.restore();
}
// This doesn't seem to always work
await WindowManager.instance.focus();
client.write(_pongMessage);
}
client.close();
}, cancelOnError: true);
});
}
Future<void> ensureSingleInstance() async {
final appSupport = await getApplicationSupportDirectory();
final lockfile = File(path.join(appSupport.path, _lockFileName));
log.info('Lock file: $lockfile');
if (await lockfile.exists()) {
try {
final port = int.parse(await lockfile.readAsString());
final client = await Socket.connect(InternetAddress.loopbackIPv4, port);
client.write(_pingMessage);
client.listen((data) async {
final message = String.fromCharCodes(data);
await client.close();
if (message == _pongMessage) {
log.info('Other application instance already running, exit.');
exit(0);
}
}, cancelOnError: true);
} on Exception {
// No server listening on the port, or bad data in the file
await lockfile.delete();
_startServer(lockfile);
}
} else {
_startServer(lockfile);
}
}

View File

@ -6,7 +6,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:window_manager/window_manager.dart'; import 'package:window_manager/window_manager.dart';
import 'package:yubico_authenticator/desktop/single_instance.dart';
import 'app/app.dart'; import 'app/app.dart';
import 'app/views/main_page.dart'; import 'app/views/main_page.dart';
@ -20,9 +19,6 @@ final log = Logger('main');
void main() async { void main() async {
_initLogging(Level.INFO); _initLogging(Level.INFO);
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
if (Platform.isWindows) {
await ensureSingleInstance();
}
await windowManager.ensureInitialized(); await windowManager.ensureInitialized();
// Either use the _YKMAN_EXE environment variable, or look relative to executable. // Either use the _YKMAN_EXE environment variable, or look relative to executable.

View File

@ -5,12 +5,10 @@
import FlutterMacOS import FlutterMacOS
import Foundation import Foundation
import path_provider_macos
import shared_preferences_macos import shared_preferences_macos
import window_manager import window_manager
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
} }

View File

@ -352,27 +352,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.0" version: "1.8.0"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.11"
path_provider_ios:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.7"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@ -380,13 +359,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.5" version: "2.1.5"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
path_provider_platform_interface: path_provider_platform_interface:
dependency: transitive dependency: transitive
description: description:

View File

@ -37,7 +37,6 @@ dependencies:
async: ^2.8.2 async: ^2.8.2
logging: ^1.0.2 logging: ^1.0.2
path_provider: ^2.0.8
shared_preferences: ^2.0.12 shared_preferences: ^2.0.12
flutter_riverpod: ^1.0.0 flutter_riverpod: ^1.0.0
json_annotation: ^4.4.0 json_annotation: ^4.4.0

View File

@ -107,6 +107,28 @@ bool Win32Window::CreateAndShow(const std::wstring& title,
const Size& size) { const Size& size) {
Destroy(); Destroy();
// Attempt to create a mutex to enforce single instance.
CreateMutex(NULL, TRUE, L"com.yubico.authenticator.mutex");
if (GetLastError() == ERROR_ALREADY_EXISTS) {
HWND handle=FindWindowA(NULL, "Yubico Authenticator");
WINDOWPLACEMENT place = { sizeof(WINDOWPLACEMENT) };
GetWindowPlacement(handle, &place);
switch(place.showCmd) {
case SW_SHOWMAXIMIZED:
ShowWindow(handle, SW_SHOWMAXIMIZED);
break;
case SW_SHOWMINIMIZED:
ShowWindow(handle, SW_RESTORE);
break;
default:
ShowWindow(handle, SW_NORMAL);
break;
}
SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);
SetForegroundWindow(handle);
return 0;
}
const wchar_t* window_class = const wchar_t* window_class =
WindowClassRegistrar::GetInstance()->GetWindowClass(); WindowClassRegistrar::GetInstance()->GetWindowClass();