From 8c77be6397e675c960f501fe7fa5a8e49d373535 Mon Sep 17 00:00:00 2001 From: teamclouday Date: Tue, 6 Sep 2022 12:49:28 -0400 Subject: [PATCH] release v2.2.0 * change build version * change threads to tasks in Windows app --- Android/app/build.gradle | 4 +- Windows/SteeringWheel/Connection.cs | 196 +++++++++++++-------- Windows/SteeringWheel/Controller.cs | 72 ++++---- Windows/SteeringWheel/MainWindow.xaml.cs | 61 ++++--- Windows/SteeringWheel/SteeringWheel.csproj | 3 +- 5 files changed, 204 insertions(+), 132 deletions(-) diff --git a/Android/app/build.gradle b/Android/app/build.gradle index 3e296f4..30a4f56 100644 --- a/Android/app/build.gradle +++ b/Android/app/build.gradle @@ -6,8 +6,8 @@ android { applicationId "com.example.androidsteering" minSdkVersion 19 targetSdkVersion 32 - versionCode 5 - versionName "2.1.0" + versionCode 6 + versionName "2.2.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/Windows/SteeringWheel/Connection.cs b/Windows/SteeringWheel/Connection.cs index 84c48c8..f6283c2 100644 --- a/Windows/SteeringWheel/Connection.cs +++ b/Windows/SteeringWheel/Connection.cs @@ -4,6 +4,7 @@ using System.Net.Sockets; using System.Windows; using System.Threading; +using System.Threading.Tasks; using System.Diagnostics; using System.Collections.Generic; using InTheHand.Net; @@ -128,8 +129,8 @@ public class Connection { private readonly MainWindow mainWindow; private readonly SharedBuffer sharedBuffer; - public ConnectionMode mode { get; set; } - public ConnectionStatus status { get; private set; } + public ConnectionMode Mode { get; set; } + public ConnectionStatus Status { get; private set; } private readonly int MAX_WAIT_TIME = 1500; private readonly int DATA_SEPARATOR = 10086; @@ -138,7 +139,7 @@ public class Connection private readonly int DEVICE_CHECK_EXPECTED = 123456; private readonly int DEVICE_CHECK_DATA = 654321; private bool isConnectionAllowed = false; - private byte[] lastPack = new byte[13]; + private readonly byte[] lastPack = new byte[13]; private int lastPackLength = 0; // bluetooth components @@ -147,7 +148,8 @@ public class Connection private BluetoothListener bthListener = null; private BluetoothClient bthClient = null; private BluetoothEndPoint bthTargetDeviceID = null; - private Thread bthThread = null; + private Task bthTask = null; + private CancellationTokenSource bthTaskToken = new CancellationTokenSource(); // wifi components private readonly int wifiPort = 55555; private string wifiAddress; @@ -155,24 +157,25 @@ public class Connection private IPEndPoint wifiTargetDeviceID = null; private Socket wifiServer = null; private Socket wifiClient = null; - private Thread wifiThread = null; + private Task wifiTask = null; + private CancellationTokenSource wifiTaskToken = new CancellationTokenSource(); public Connection(MainWindow window, SharedBuffer buffer) { mainWindow = window; sharedBuffer = buffer; - mode = ConnectionMode.Bluetooth; - status = ConnectionStatus.Default; + Mode = ConnectionMode.Bluetooth; + Status = ConnectionStatus.Default; } /// /// general connect function /// - public void Connect() + public async Task Connect() { - if (mode == ConnectionMode.Bluetooth) ConnectBluetooth(); - else ConnectWifi(); - if (status == ConnectionStatus.Default) + if (Mode == ConnectionMode.Bluetooth) await ConnectBluetooth(); + else await ConnectWifi(); + if (Status == ConnectionStatus.Default) { Application.Current.Dispatcher.Invoke(new Action(() => { @@ -184,7 +187,7 @@ public void Connect() /// /// connect with bluetooth server /// - private void ConnectBluetooth() + private async Task ConnectBluetooth() { // first check if bluetooth is enabled if (!BluetoothRadio.IsSupported) @@ -193,7 +196,7 @@ private void ConnectBluetooth() return; } // initialize bluetooth server - if (bthListener != null) Disconnect(); + if (bthListener != null) await Disconnect(); bthListener = new BluetoothListener(bthServerUUID) { ServiceName = bthServerName @@ -239,15 +242,28 @@ private void ConnectBluetooth() } if (bthTargetDeviceID == null) { - Disconnect(); + await Disconnect(); return; } Debug.WriteLine("[Connection] ConnectBluetooth found valid client"); - // prepare thread - if (bthThread != null && bthThread.IsAlive) + // prepare task + if (bthTask?.IsCompleted == false) { isConnectionAllowed = false; - if (!bthThread.Join(MAX_WAIT_TIME)) bthThread.Abort(); + bthTaskToken.Cancel(); + try + { + await bthTask; + } + catch (OperationCanceledException e) + { + Debug.WriteLine("[Connection] ConnectBluetooth -> " + e.Message); + } + finally + { + bthTask.Dispose(); + } + bthTaskToken = new CancellationTokenSource(); } // try to accept client with same ID try @@ -265,22 +281,22 @@ private void ConnectBluetooth() { Debug.WriteLine("[Connection] ConnectBluetooth -> " + e.Message); AddLog("(bluetooth) Server failed to connect valid client"); - Disconnect(); + await Disconnect(); return; } catch (SocketException e) { Debug.WriteLine("[Connection] ConnectBluetooth -> " + e.Message); AddLog("(bluetooth) Server failed to connect valid client"); - Disconnect(); + await Disconnect(); return; } // update status SetStatus(ConnectionStatus.Connected); AddLog("(bluetooth) Client connected\nClient Name: " + bthClient.RemoteMachineName + "\nClient Address: " + bthClient.RemoteEndPoint); - // start thread + // start task isConnectionAllowed = true; - bthThread = new Thread(() => + bthTask = Task.Factory.StartNew(async () => { // prepare data placeholders byte[] placeholder = new byte[4]; @@ -291,10 +307,11 @@ private void ConnectBluetooth() { while (isConnectionAllowed && bthClient != null) { + if (bthTaskToken.IsCancellationRequested) break; // read data into a buffer byte[] buffer = new byte[PACK_SIZE * (NUM_PACKS + 1)]; Array.Copy(lastPack, 0, buffer, 0, lastPackLength); // add last pack - int size = bthStream.Read(buffer, lastPackLength, PACK_SIZE * NUM_PACKS); + int size = await bthStream.ReadAsync(buffer, lastPackLength, PACK_SIZE * NUM_PACKS, bthTaskToken.Token); if (size <= 0) break; int totalSize = size + lastPackLength; // process data into data packs @@ -325,28 +342,24 @@ private void ConnectBluetooth() // check for remaining pack, and store for next loop Array.Copy(buffer, idx, lastPack, 0, totalSize - idx); lastPackLength = totalSize - idx; - Thread.Sleep(1); } } } catch (SocketException e) { - Debug.WriteLine("[Connection] ConnectBluetooth thread -> " + e.Message); + Debug.WriteLine("[Connection] ConnectBluetooth task -> " + e.Message); } catch (IOException e) { - Debug.WriteLine("[Connection] ConnectBluetooth thread -> " + e.Message); + Debug.WriteLine("[Connection] ConnectBluetooth task -> " + e.Message); } catch (ObjectDisposedException e) { - Debug.WriteLine("[Connection] ConnectBluetooth thread -> " + e.Message); + Debug.WriteLine("[Connection] ConnectBluetooth task -> " + e.Message); } - - Disconnect(); - }); - bthThread.Priority = ThreadPriority.AboveNormal; - bthThread.Start(); - Debug.WriteLine("[Connection] ConnectBluetooth thread started"); + await Disconnect(); + }, bthTaskToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + Debug.WriteLine("[Connection] ConnectBluetooth task started"); } /// @@ -355,7 +368,7 @@ private void ConnectBluetooth() /// public bool CheckWifi() { - if (mode != ConnectionMode.Wifi) + if (Mode != ConnectionMode.Wifi) return true; // get wifi IP address // reference: https://stackoverflow.com/questions/9855230/how-do-i-get-the-network-interface-and-its-right-ipv4-address @@ -411,11 +424,11 @@ public bool CheckWifi() /// /// connect with wifi server /// - private void ConnectWifi() + private async Task ConnectWifi() { AddLog("(wifi) Server network adapter = " + wifiAdapterName); AddLog("(wifi) Server IP Address = " + wifiAddress.ToString()); - if (wifiServer != null) Disconnect(); + if (wifiServer != null) await Disconnect(); // create server and start listening try { @@ -427,7 +440,7 @@ private void ConnectWifi() { Debug.WriteLine("[Connection] ConnectWifi -> " + e.Message); AddLog("(wifi) Server failed to start"); - Disconnect(); + await Disconnect(); return; } SetStatus(ConnectionStatus.Listening); @@ -453,26 +466,39 @@ private void ConnectWifi() catch (SocketException e) { Debug.WriteLine("[Connection] ConnectWifi -> " + e.Message); - Disconnect(); + await Disconnect(); return; } catch (ObjectDisposedException e) { Debug.WriteLine("[Connection] ConnectWifi -> " + e.Message); - Disconnect(); + await Disconnect(); return; } // check if target is found if (wifiTargetDeviceID == null) { - Disconnect(); + await Disconnect(); return; } - // prepare thread - if (wifiThread != null && wifiThread.IsAlive) + // prepare task + if (wifiTask?.IsCompleted == false) { isConnectionAllowed = false; - if (!wifiThread.Join(MAX_WAIT_TIME)) wifiThread.Abort(); + wifiTaskToken.Cancel(); + try + { + await wifiTask; + } + catch (OperationCanceledException e) + { + Debug.WriteLine("[Connection] ConnectWifi -> " + e.Message); + } + finally + { + wifiTask.Dispose(); + } + wifiTaskToken = new CancellationTokenSource(); } // try to accept client with same ID try @@ -490,22 +516,22 @@ private void ConnectWifi() { Debug.WriteLine("[Connection] ConnectWifi -> " + e.Message); AddLog("(wifi) Server failed to connect valid client"); - Disconnect(); + await Disconnect(); return; } catch (ObjectDisposedException e) { Debug.WriteLine("[Connection] ConnectWifi -> " + e.Message); AddLog("(wifi) Server failed to connect valid client"); - Disconnect(); + await Disconnect(); return; } // update status SetStatus(ConnectionStatus.Connected); AddLog("(wifi) Client connected\nClient Address: " + wifiClient.RemoteEndPoint); isConnectionAllowed = true; - // start thread - wifiThread = new Thread(() => + // start task + wifiTask = Task.Factory.StartNew(async () => { // prepare data placeholders byte[] placeholder = new byte[4]; @@ -516,10 +542,11 @@ private void ConnectWifi() { while (isConnectionAllowed && wifiClient != null) { + if (wifiTaskToken.IsCancellationRequested) break; // read data into a buffer byte[] buffer = new byte[PACK_SIZE * (NUM_PACKS + 1)]; Array.Copy(lastPack, 0, buffer, 0, lastPackLength); // add last pack - int size = wifiStream.Read(buffer, lastPackLength, PACK_SIZE * NUM_PACKS); + int size = await wifiStream.ReadAsync(buffer, lastPackLength, PACK_SIZE * NUM_PACKS, wifiTaskToken.Token); if (size <= 0) break; int totalSize = size + lastPackLength; // process data into data packs @@ -550,27 +577,24 @@ private void ConnectWifi() // check for remaining pack, and store for next loop Array.Copy(buffer, idx, lastPack, 0, totalSize - idx); lastPackLength = totalSize - idx; - Thread.Sleep(1); } } } catch (SocketException e) { - Debug.WriteLine("[Connection] ConnectWifi thread -> " + e.Message); + Debug.WriteLine("[Connection] ConnectWifi task -> " + e.Message); } catch (IOException e) { - Debug.WriteLine("[Connection] ConnectWifi thread -> " + e.Message); + Debug.WriteLine("[Connection] ConnectWifi task -> " + e.Message); } catch (ObjectDisposedException e) { - Debug.WriteLine("[Connection] ConnectWifi thread -> " + e.Message); + Debug.WriteLine("[Connection] ConnectWifi task -> " + e.Message); } - Disconnect(); - }); - wifiThread.Priority = ThreadPriority.AboveNormal; - wifiThread.Start(); - Debug.WriteLine("[Connection] ConnectWifi thread started"); + await Disconnect(); + }, wifiTaskToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); + Debug.WriteLine("[Connection] ConnectWifi task started"); } /// @@ -674,9 +698,9 @@ private bool TestClient(Socket client) /// /// disconnect server /// - public void Disconnect() + public async Task Disconnect() { - if (mode == ConnectionMode.Bluetooth) + if (Mode == ConnectionMode.Bluetooth) { if (bthClient != null) { @@ -684,10 +708,25 @@ public void Disconnect() bthClient.Close(); bthClient = null; } - // shut down thread - if (bthThread != null && bthThread.IsAlive) + // shut down task + if (bthTask?.IsCompleted == false) { - if (!bthThread.Join(MAX_WAIT_TIME)) bthThread.Abort(); + isConnectionAllowed = false; + bthTaskToken.Cancel(); + try + { + await bthTask; + } + catch (OperationCanceledException e) + { + Debug.WriteLine("[Connection] Disconnect -> " + e.Message); + } + finally + { + bthTask.Dispose(); + bthTask = null; + } + bthTaskToken = new CancellationTokenSource(); } // stop server if (bthListener != null) @@ -716,10 +755,25 @@ public void Disconnect() wifiClient.Close(); wifiClient = null; } - // shut down thread - if (wifiThread != null && wifiThread.IsAlive) + // shut down task + if (wifiTask?.IsCompleted == false) { - if (!wifiThread.Join(MAX_WAIT_TIME)) wifiThread.Abort(); + isConnectionAllowed = false; + wifiTaskToken.Cancel(); + try + { + await wifiTask; + } + catch (OperationCanceledException e) + { + Debug.WriteLine("[Connection] Disconnect -> " + e.Message); + } + finally + { + wifiTask.Dispose(); + wifiTask = null; + } + wifiTaskToken = new CancellationTokenSource(); } // stop server if (wifiServer != null) @@ -744,15 +798,15 @@ public void Disconnect() /// /// destroy connection service /// - public void Destroy() + public async Task Destroy() { isConnectionAllowed = false; // check bluetooth side - mode = ConnectionMode.Bluetooth; - Disconnect(); + Mode = ConnectionMode.Bluetooth; + await Disconnect(); // check wifi side - mode = ConnectionMode.Wifi; - Disconnect(); + Mode = ConnectionMode.Wifi; + await Disconnect(); } /// @@ -763,8 +817,8 @@ public void Destroy() /// whether status is updated private bool SetStatus(ConnectionStatus s, bool unlock = false) { - bool ret = s != status; - status = s; + bool ret = s != Status; + Status = s; Application.Current.Dispatcher.Invoke(new Action(() => { mainWindow.UpdateConnectButton(); diff --git a/Windows/SteeringWheel/Controller.cs b/Windows/SteeringWheel/Controller.cs index 0542006..b358f7f 100644 --- a/Windows/SteeringWheel/Controller.cs +++ b/Windows/SteeringWheel/Controller.cs @@ -1,6 +1,7 @@ using System; using System.Windows; using System.Threading; +using System.Threading.Tasks; using System.Diagnostics; using vJoyInterfaceWrap; @@ -89,10 +90,10 @@ public class Controller { private readonly MainWindow mainWindow; private readonly SharedBuffer sharedBuffer; - private Thread processThread; - private Thread updateThread; - private readonly int MAX_WAIT_TIME = 1500; - private bool isProcessAllowed = false; + private Task processTask; + private Task updateTask; + private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); + private bool isProcessAllowed; public float CAP_SteeringMin { get; set; } = -60.0f; public float CAP_SteeringMax { get; set; } = 60.0f; @@ -109,13 +110,14 @@ public class Controller private int axisMax = 0, axisMaxHalf = 0; public bool vJoyInitialized { get; private set; } private const int triggerInterval = 100; - private const int updateInterval = 5; + private const int updateInterval = 50; public Controller(MainWindow window, SharedBuffer buffer) { mainWindow = window; sharedBuffer = buffer; + isProcessAllowed = true; vJoyInitialized = false; joystick = new vJoy(); joyReport = new vJoy.JoystickState(); @@ -133,37 +135,48 @@ public Controller(MainWindow window, SharedBuffer buffer) public void Destroy() { isProcessAllowed = false; - if (processThread != null && processThread.IsAlive) - { - if (!processThread.Join(MAX_WAIT_TIME)) processThread.Abort(); - } - if (updateThread != null && updateThread.IsAlive) + if (processTask?.IsCompleted == false || updateTask?.IsCompleted == false) { - if (!updateThread.Join(MAX_WAIT_TIME)) updateThread.Abort(); + cancellationToken.Cancel(); + try + { + processTask?.Wait(); + updateTask?.Wait(); + } + catch (OperationCanceledException err) + { + Debug.WriteLine("[Controller] Destroy -> " + err.Message); + } + finally + { + processTask?.Dispose(); + updateTask?.Dispose(); + } } ResetVJoy(); joystick.RelinquishVJD(joystickID); } /// - /// setup background thread that write joystate + /// setup background task that write joystate /// private void SetupProcess() { isProcessAllowed = true; - processThread = new Thread(() => + processTask = Task.Factory.StartNew(async () => { while (isProcessAllowed) { + if (cancellationToken.IsCancellationRequested) break; var data = sharedBuffer.GetData(); if (data == null) { - Thread.Sleep(5); + await Task.Delay(5); continue; } if (data.IsButton) { - Debug.WriteLine("[Controller] processThread button pressed (" + data.Status + ")"); + Debug.WriteLine("[Controller] processTask button pressed (" + data.Status + ")"); switch ((MotionButton)data.Status) { case MotionButton.A: @@ -240,20 +253,17 @@ private void SetupProcess() break; } } - Thread.Sleep(1); } - }); - processThread.Priority = ThreadPriority.AboveNormal; - processThread.Start(); + }, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } /// - /// set up background thread that updates vJoy state + /// set up background task that updates vJoy state /// private void SetupUpdate() { isProcessAllowed = true; - updateThread = new Thread(() => + updateTask = Task.Factory.StartNew(async () => { while (isProcessAllowed) { @@ -262,15 +272,13 @@ private void SetupUpdate() if (!joystick.UpdateVJD(joystickID, ref joyReport)) { // AddLog("Failed to update vJoy controller state"); - Debug.WriteLine("[Controller] updateThread failed to update VJD"); + Debug.WriteLine("[Controller] updateTask failed to update VJD"); joystick.AcquireVJD(joystickID); } } - Thread.Sleep(updateInterval); + await Task.Delay(updateInterval); } - }); - updateThread.Priority = ThreadPriority.AboveNormal; - updateThread.Start(); + }, cancellationToken.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default); } /// @@ -405,18 +413,18 @@ private bool CheckDeviceSpecs(uint id) /// public void TriggerControl(ControlButton button) { - new Thread(() => + Task.Run(async () => { lock (joyReportLock) { joyReport.Buttons |= (uint)(0x1 << ((int)button - 1)); } - Thread.Sleep(triggerInterval); + await Task.Delay(triggerInterval); lock (joyReportLock) { joyReport.Buttons &= ~(uint)(0x1 << ((int)button - 1)); } - }).Start(); + }); } /// @@ -425,7 +433,7 @@ public void TriggerControl(ControlButton button) /// public void TriggerControl(ControlAxis axis) { - new Thread(() => + Task.Run(async () => { lock (joyReportLock) { @@ -463,7 +471,7 @@ public void TriggerControl(ControlAxis axis) break; } } - Thread.Sleep(triggerInterval); + await Task.Delay(triggerInterval); lock (joyReportLock) { switch (axis) @@ -494,7 +502,7 @@ public void TriggerControl(ControlAxis axis) break; } } - }).Start(); + }); } /// diff --git a/Windows/SteeringWheel/MainWindow.xaml.cs b/Windows/SteeringWheel/MainWindow.xaml.cs index e9f3e87..af154ae 100644 --- a/Windows/SteeringWheel/MainWindow.xaml.cs +++ b/Windows/SteeringWheel/MainWindow.xaml.cs @@ -6,6 +6,7 @@ using System.Windows.Controls; using System.Windows.Documents; using System.Threading; +using System.Threading.Tasks; namespace SteeringWheel { @@ -21,9 +22,9 @@ public partial class MainWindow : Window private readonly System.Windows.Forms.NotifyIcon notifyIcon; private bool notifDisplayedOnce = false; - private Thread connectThread; - private Thread disconnectThread; - private readonly int MAX_WAIT_TIME = 1500; + private Task connectTask; + private Task disconnectTask; + private readonly CancellationTokenSource connectionToken = new CancellationTokenSource(); [System.Runtime.InteropServices.DllImport("User32.dll")] private static extern bool SetForegroundWindow(IntPtr handle); @@ -66,12 +67,12 @@ public MainWindow() // load settings if (Properties.Settings.Default.MainWindowIsBluetoothSelected) { - connectionService.mode = ConnectionMode.Bluetooth; + connectionService.Mode = ConnectionMode.Bluetooth; RadioButtonBluetooth.IsChecked = true; } else { - connectionService.mode = ConnectionMode.Wifi; + connectionService.Mode = ConnectionMode.Wifi; RadioButtonWifi.IsChecked = true; } } @@ -125,14 +126,24 @@ private void SetupNotifyIcon() private void MainWindow_Closing(object sender, CancelEventArgs e) { controllerService.Destroy(); - connectionService.Destroy(); - if (connectThread != null && connectThread.IsAlive) + connectionService.Destroy().Wait(); + if (connectTask?.IsCompleted == false || disconnectTask?.IsCompleted == false) { - if (!connectThread.Join(MAX_WAIT_TIME)) connectThread.Abort(); - } - if (disconnectThread != null && disconnectThread.IsAlive) - { - if (!disconnectThread.Join(MAX_WAIT_TIME)) disconnectThread.Abort(); + connectionToken.Cancel(); + try + { + connectTask?.Wait(); + disconnectTask?.Wait(); + } + catch (OperationCanceledException err) + { + Debug.WriteLine("MainWindow_Closing -> " + err.Message); + } + finally + { + connectTask?.Dispose(); + disconnectTask?.Dispose(); + } } notifyIcon.Dispose(); Properties.Settings.Default.Save(); @@ -189,30 +200,28 @@ private void LogBlockScroll_ScrollChanged(object sender, ScrollChangedEventArgs /// private void ConnectButton_Click(object sender, RoutedEventArgs e) { - switch (connectionService.status) + switch (connectionService.Status) { case ConnectionStatus.Default: - if (connectThread == null || !connectThread.IsAlive) + if (connectTask?.IsCompleted != false) { if (!connectionService.CheckWifi()) return; LockRadioButtons(); - connectThread = new Thread(() => + connectTask = Task.Run(async () => { - connectionService.Connect(); - }); - connectThread.Start(); + await connectionService.Connect(); + }, connectionToken.Token); } else AddLog("Already connecting..."); break; case ConnectionStatus.Listening: case ConnectionStatus.Connected: - if (disconnectThread == null || !disconnectThread.IsAlive) + if (disconnectTask?.IsCompleted != false) { - disconnectThread = new Thread(() => + disconnectTask = Task.Run(async () => { - connectionService.Disconnect(); - }); - disconnectThread.Start(); + await connectionService.Disconnect(); + }, connectionToken.Token); } else AddLog("Already disconnecting..."); break; @@ -242,7 +251,7 @@ public void UnlockRadioButtons() /// public void UpdateConnectButton() { - switch (connectionService.status) + switch (connectionService.Status) { case ConnectionStatus.Default: ConnectButton.Content = "Connect"; @@ -311,12 +320,12 @@ private void RadioButton_Click(object sender, RoutedEventArgs e) { if ((sender as RadioButton) == RadioButtonBluetooth) { - connectionService.mode = ConnectionMode.Bluetooth; + connectionService.Mode = ConnectionMode.Bluetooth; Properties.Settings.Default.MainWindowIsBluetoothSelected = true; } else if ((sender as RadioButton) == RadioButtonWifi) { - connectionService.mode = ConnectionMode.Wifi; + connectionService.Mode = ConnectionMode.Wifi; Properties.Settings.Default.MainWindowIsBluetoothSelected = false; } } diff --git a/Windows/SteeringWheel/SteeringWheel.csproj b/Windows/SteeringWheel/SteeringWheel.csproj index 988e904..bf06ecc 100644 --- a/Windows/SteeringWheel/SteeringWheel.csproj +++ b/Windows/SteeringWheel/SteeringWheel.csproj @@ -30,7 +30,7 @@ Teamclouday false 0 - 2.1.0.0 + 2.2.0.0 false true true @@ -108,6 +108,7 @@ 7.3 prompt true + false