`GlobalExclusiveDeviceAccess` added to Visual Studio extension (#834)

This commit is contained in:
Frank Robijn 2024-11-07 19:34:57 +01:00 коммит произвёл GitHub
Родитель f434dac04d
Коммит 639d6d86b9
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
11 изменённых файлов: 915 добавлений и 576 удалений

213
.editorconfig Normal file
Просмотреть файл

@ -0,0 +1,213 @@
# EditorConfig for Visual Studio 2022: https://learn.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options?view=vs-2022
# This is a top-most .editorconfig file
root = true
#=====================================================
#
# nanoFramework specific settings
#
#
#=====================================================
[*]
# Generic EditorConfig settings
end_of_line = crlf
charset = utf-8-bom
# Visual Studio spell checker
spelling_languages = en-us
spelling_checkable_types = strings,identifiers,comments
spelling_error_severity = information
spelling_exclusion_path = spelling_exclusion.dic
#=====================================================
#
# Settings copied from the .NET runtime
#
# https://github.com/dotnet/runtime
#
#=====================================================
# Default settings:
# A newline ending every file
# Use 4 spaces as indentation
insert_final_newline = true
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
# Generated code
[*{_AssemblyInfo.cs,.notsupported.cs,AsmOffsets.cs}]
generated_code = true
# C# files
[*.cs]
# New line preferences
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = false
csharp_indent_switch_labels = true
csharp_indent_labels = one_less_than_current
# Modifier preferences
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion
# avoid this. unless absolutely necessary
dotnet_style_qualification_for_field = false:suggestion
dotnet_style_qualification_for_property = false:suggestion
dotnet_style_qualification_for_method = false:suggestion
dotnet_style_qualification_for_event = false:suggestion
# Types: use keywords instead of BCL types, and permit var only when the type is clear
csharp_style_var_for_built_in_types = false:suggestion
csharp_style_var_when_type_is_apparent = false:none
csharp_style_var_elsewhere = false:suggestion
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
dotnet_style_predefined_type_for_member_access = true:suggestion
# name all constant fields using PascalCase
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
dotnet_naming_symbols.constant_fields.applicable_kinds = field
dotnet_naming_symbols.constant_fields.required_modifiers = const
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
# static fields should have s_ prefix
dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion
dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields
dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style
dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected
dotnet_naming_style.static_prefix_style.required_prefix = s_
dotnet_naming_style.static_prefix_style.capitalization = camel_case
# internal and private fields should be _camelCase
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
# Code style defaults
csharp_using_directive_placement = outside_namespace:suggestion
dotnet_sort_system_directives_first = true
csharp_prefer_braces = true:silent
csharp_preserve_single_line_blocks = true:none
csharp_preserve_single_line_statements = false:none
csharp_prefer_static_local_function = true:suggestion
csharp_prefer_simple_using_statement = false:none
csharp_style_prefer_switch_expression = true:suggestion
dotnet_style_readonly_field = true:suggestion
# Expression-level preferences
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_prefer_collection_expression = when_types_exactly_match
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
dotnet_style_prefer_auto_properties = true:suggestion
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
dotnet_style_prefer_conditional_expression_over_return = true:silent
csharp_prefer_simple_default_expression = true:suggestion
# Expression-bodied members
csharp_style_expression_bodied_methods = true:silent
csharp_style_expression_bodied_constructors = true:silent
csharp_style_expression_bodied_operators = true:silent
csharp_style_expression_bodied_properties = true:silent
csharp_style_expression_bodied_indexers = true:silent
csharp_style_expression_bodied_accessors = true:silent
csharp_style_expression_bodied_lambdas = true:silent
csharp_style_expression_bodied_local_functions = true:silent
# Pattern matching
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
# Null checking preferences
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# Other features
csharp_style_prefer_index_operator = false:none
csharp_style_prefer_range_operator = false:none
csharp_style_pattern_local_over_anonymous_function = false:none
# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = do_not_ignore
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false
# License header
file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license.
# C++ Files
[*.{cpp,h,in}]
curly_bracket_next_line = true
indent_brace_style = Allman
# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]
indent_size = 2
[*.{csproj,vbproj,proj,nativeproj,locproj}]
charset = utf-8
# Xml build files
[*.builds]
indent_size = 2
# Xml files
[*.{xml,stylecop,resx,ruleset}]
indent_size = 2
# Xml config files
[*.{props,targets,config,nuspec}]
indent_size = 2
# YAML config files
[*.{yml,yaml}]
indent_size = 2
# Shell scripts
[*.sh]
end_of_line = lf
[*.{cmd,bat}]
end_of_line = crlf

Просмотреть файл

@ -1,24 +1,23 @@
//
// Copyright (c) .NET Foundation and Contributors
// See LICENSE file in the project root for full license information.
//
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using GalaSoft.MvvmLight.Messaging;
using Microsoft.VisualStudio.Shell;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.VisualStudio.Extension.FirmwareUpdate;
using nanoFramework.Tools.VisualStudio.Extension.ToolWindow.ViewModel;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.Messaging;
using Microsoft.VisualStudio.Shell;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.Debugger.NFDevice;
using nanoFramework.Tools.VisualStudio.Extension.FirmwareUpdate;
using nanoFramework.Tools.VisualStudio.Extension.ToolWindow.ViewModel;
using Task = System.Threading.Tasks.Task;
namespace nanoFramework.Tools.VisualStudio.Extension.AutomaticUpdates
{
public class UpdateManager
{
private const int ExclusiveAccessTimeout = 3000;
private static UpdateManager s_instance;
private ViewModelLocator ViewModelLocator;
private readonly Package _package;
@ -32,7 +31,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension.AutomaticUpdates
}
public static void Initialize(
AsyncPackage package,
AsyncPackage package,
ViewModelLocator vmLocator)
{
s_instance = new UpdateManager(package)
@ -101,257 +100,279 @@ namespace nanoFramework.Tools.VisualStudio.Extension.AutomaticUpdates
}
}
#endif
// check if DebugEngine is available
if (nanoDevice.DebugEngine == null)
{
nanoDevice.CreateDebugEngine();
}
if (nanoDevice.DebugEngine == null)
{
// can't create it, quit update now
return;
}
// add this device to the updatING list
if (!devicesUpdatING.TryAdd(deviceId, new object()))
{
// fail to add device to list
#if DEBUG
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} update already in progress.");
#endif
// quit, never mind, this is not critical whatsoever
return;
}
// better wrap this on a try-finally because a lot of things can go wrong in the process
GlobalExclusiveDeviceAccess exclusiveAccess = null;
try
{
await Task.Yield();
var fwPackage = await GetFirmwarePackageAsync(
nanoDevice.TargetName,
nanoDevice.Platform);
await Task.Yield();
//////////////////////////////
// STM32 targets
if (fwPackage is Stm32Firmware)
// Get exclusive access to the device, but don't wait forever
exclusiveAccess = GlobalExclusiveDeviceAccess.TryGet(nanoDevice, ExclusiveAccessTimeout);
if (exclusiveAccess is null)
{
// sanity check
if (nanoDevice.DebugEngine == null)
{
// Can't get access, quit update for now
#if DEBUG
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} debug engine is not ready.");
Console.WriteLine($"[Automatic Updates] Cannot access {nanoDevice.Description}, another application is using the device");
#endif
// quit
return;
}
return;
}
if (nanoDevice.DebugEngine.Connect(
1000,
true,
true))
// check if DebugEngine is available
if (nanoDevice.DebugEngine == null)
{
nanoDevice.CreateDebugEngine();
}
if (nanoDevice.DebugEngine == null)
{
// can't create it, quit update now
return;
}
// add this device to the updatING list
if (!devicesUpdatING.TryAdd(deviceId, new object()))
{
// fail to add device to list
#if DEBUG
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} update already in progress.");
#endif
// quit, never mind, this is not critical whatsoever
return;
}
// better wrap this on a try-finally because a lot of things can go wrong in the process
try
{
await Task.Yield();
var fwPackage = await GetFirmwarePackageAsync(
nanoDevice.TargetName,
nanoDevice.Platform);
await Task.Yield();
//////////////////////////////
// STM32 targets
if (fwPackage is Stm32Firmware)
{
Version currentClrVersion = null;
// try to store CLR version
if(nanoDevice.DebugEngine.IsConnectedTonanoCLR)
// sanity check
if (nanoDevice.DebugEngine == null)
{
if (nanoDevice.DeviceInfo.Valid)
{
currentClrVersion = nanoDevice.DeviceInfo.SolutionBuildVersion;
}
#if DEBUG
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} debug engine is not ready.");
#endif
// quit
return;
}
// update conditions:
// 1. Running CLR _and_ the new version is higher
// 2. Running nanoBooter and there is no version information on the CLR (presumably because there is no CLR installed)
if (fwPackage.Version > nanoDevice.CLRVersion)
if (nanoDevice.DebugEngine.Connect(
1000,
true,
true))
{
bool attemptToLaunchBooter = false;
Version currentClrVersion = null;
// try to store CLR version
if (nanoDevice.DebugEngine.IsConnectedTonanoCLR)
{
// any update has to be handled by nanoBooter, so let's have it running
try
if (nanoDevice.DeviceInfo.Valid)
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Launching nanoBooter...");
currentClrVersion = nanoDevice.DeviceInfo.SolutionBuildVersion;
}
}
attemptToLaunchBooter = nanoDevice.ConnectToNanoBooter();
// update conditions:
// 1. Running CLR _and_ the new version is higher
// 2. Running nanoBooter and there is no version information on the CLR (presumably because there is no CLR installed)
if (fwPackage.Version > nanoDevice.CLRVersion)
{
bool attemptToLaunchBooter = false;
if (!attemptToLaunchBooter)
if (nanoDevice.DebugEngine.IsConnectedTonanoCLR)
{
// any update has to be handled by nanoBooter, so let's have it running
try
{
// check for version where the software reboot to nanoBooter was made available
if (currentClrVersion != null &&
nanoDevice.DeviceInfo.SolutionBuildVersion < new Version("1.6.0.54"))
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] The device is running a version that doesn't support rebooting by software. Please update your device using 'nanoff' tool.");
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Launching nanoBooter...");
await Task.Yield();
attemptToLaunchBooter = nanoDevice.ConnectToNanoBooter();
if (!attemptToLaunchBooter)
{
// check for version where the software reboot to nanoBooter was made available
if (currentClrVersion != null &&
nanoDevice.DeviceInfo.SolutionBuildVersion < new Version("1.6.0.54"))
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] The device is running a version that doesn't support rebooting by software. Please update your device using 'nanoff' tool.");
await Task.Yield();
}
}
}
catch
{
// this reboot step can go wrong and there's no big deal with that
}
}
catch
else
{
// this reboot step can go wrong and there's no big deal with that
attemptToLaunchBooter = true;
}
}
else
{
attemptToLaunchBooter = true;
}
// check if the device is still there
if(ViewModelLocator.DeviceExplorer.AvailableDevices.FirstOrDefault(d => d.DeviceUniqueId == deviceUniqueId) == null)
{
// check if the device is still there
if (ViewModelLocator.DeviceExplorer.AvailableDevices.FirstOrDefault(d => d.DeviceUniqueId == deviceUniqueId) == null)
{
#if DEBUG
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} is not available anymore.");
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} is not available anymore.");
#endif
return;
}
if (attemptToLaunchBooter &&
nanoDevice.Ping() == Debugger.WireProtocol.ConnectionSource.nanoBooter)
{
// get address for CLR block expected by device
var clrAddress = nanoDevice.GetCLRStartAddress();
// compare with address on the fw packages
if (clrAddress !=
(fwPackage as Stm32Firmware).ClrStartAddress)
{
// CLR addresses don't match, can't proceed with update
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Can't update device. CLR addresses are different. Please update nanoBooter manually.");
return;
}
await Task.Yield();
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Starting update to CLR v{fwPackage.Version}.");
try
if (attemptToLaunchBooter &&
nanoDevice.Ping() == Debugger.WireProtocol.ConnectionSource.nanoBooter)
{
// get address for CLR block expected by device
var clrAddress = nanoDevice.GetCLRStartAddress();
// compare with address on the fw packages
if (clrAddress !=
(fwPackage as Stm32Firmware).ClrStartAddress)
{
// CLR addresses don't match, can't proceed with update
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Can't update device. CLR addresses are different. Please update nanoBooter manually.");
return;
}
await Task.Yield();
// create a progress indicator to be used by deployment operation to post debug messages
var progressIndicator = new Progress<string>(m => MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] {m}"));
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Starting update to CLR v{fwPackage.Version}.");
if (nanoDevice.DeployBinaryFile(
(fwPackage as Stm32Firmware).nanoClrFileBin,
(fwPackage as Stm32Firmware).ClrStartAddress,
progressIndicator))
try
{
await Task.Yield();
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Update successful.");
// create a progress indicator to be used by deployment operation to post debug messages
var progressIndicator = new Progress<string>(m => MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] {m}"));
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
// if this is the selected device...
if (ViewModelLocator.DeviceExplorer.SelectedDevice?.DeviceUniqueId == deviceUniqueId)
{
// ...reset property to force that device capabilities to be retrieved on next connection
ViewModelLocator.DeviceExplorer.LastDeviceConnectedHash = 0;
}
if (attemptToLaunchBooter)
{
// try to reboot target
// remove it from updatING list
devicesUpdatING.TryRemove(deviceId, out var dummy);
// check if the device is still there
if (ViewModelLocator.DeviceExplorer.AvailableDevices.FirstOrDefault(d => d.DeviceUniqueId == Guid.Parse(deviceId)) == null)
if (nanoDevice.DeployBinaryFile(
(fwPackage as Stm32Firmware).nanoClrFileBin,
(fwPackage as Stm32Firmware).ClrStartAddress,
progressIndicator))
{
return;
await Task.Yield();
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Update successful.");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Rebooting...");
// if this is the selected device...
if (ViewModelLocator.DeviceExplorer.SelectedDevice?.DeviceUniqueId == deviceUniqueId)
{
// ...reset property to force that device capabilities to be retrieved on next connection
ViewModelLocator.DeviceExplorer.LastDeviceConnectedHash = 0;
}
nanoDevice.DebugEngine.RebootDevice(RebootOptions.NormalReboot);
if (attemptToLaunchBooter)
{
// try to reboot target
// remove it from updatING list
devicesUpdatING.TryRemove(deviceId, out var dummy);
// check if the device is still there
if (ViewModelLocator.DeviceExplorer.AvailableDevices.FirstOrDefault(d => d.DeviceUniqueId == Guid.Parse(deviceId)) == null)
{
return;
}
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Rebooting...");
nanoDevice.DebugEngine.RebootDevice(RebootOptions.NormalReboot);
}
}
catch (Exception ex)
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Exception occurred when performing update ({ex.Message}).");
}
}
catch (Exception ex)
else
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Exception occurred when performing update ({ex.Message}).");
if (attemptToLaunchBooter)
{
// only report this as an error if the launch was successful
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Failed to launch nanoBooter. Quitting update.");
}
}
}
else
{
if (attemptToLaunchBooter)
// just to make sure that the CLR version is the latest, so we don't check it over and over
if (nanoDevice.DebugEngine.IsConnectedTonanoCLR &&
(fwPackage.Version == nanoDevice.DeviceInfo.ClrBuildVersion))
{
// only report this as an error if the launch was successful
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Failed to launch nanoBooter. Quitting update.");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
}
}
else
{
// just to make sure that the CLR version is the latest, so we don't check it over and over
if (nanoDevice.DebugEngine.IsConnectedTonanoCLR &&
(fwPackage.Version == nanoDevice.DeviceInfo.ClrBuildVersion))
{
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Can't connect to device. Quitting update.");
}
}
///////////////////////////////////
// ESP32 targets
else if (fwPackage is Esp32Firmware)
{
// TODO
// not supported yet
MessageCentre.OutputFirmwareUpdateMessage("The ability to update ESP32 targets is not currently available. Yet...");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
///////////////////////////////////////
// TI CC13x26x2
else if (fwPackage is CC13x26x2Firmware)
{
// TODO
// not supported yet
MessageCentre.OutputFirmwareUpdateMessage("The ability to update CC13x26x2 targets is not currently available. Yet...");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
else
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Can't connect to device. Quitting update.");
// shouldn't be here....
}
}
///////////////////////////////////
// ESP32 targets
else if (fwPackage is Esp32Firmware)
catch (Exception ex)
{
// TODO
// not supported yet
MessageCentre.OutputFirmwareUpdateMessage("The ability to update ESP32 targets is not currently available. Yet...");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Exception occurred when performing update ({ex.Message}).");
}
///////////////////////////////////////
// TI CC13x26x2
else if (fwPackage is CC13x26x2Firmware)
finally
{
// TODO
// not supported yet
MessageCentre.OutputFirmwareUpdateMessage("The ability to update CC13x26x2 targets is not currently available. Yet...");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
// remove it from updatING list
devicesUpdatING.TryRemove(deviceId, out var dummy);
}
else
{
// shouldn't be here....
}
}
catch (Exception ex)
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Exception occurred when performing update ({ex.Message}).");
}
finally
{
// remove it from updatING list
devicesUpdatING.TryRemove(deviceId, out var dummy);
nanoDevice.DebugEngine?.Stop();
exclusiveAccess?.Dispose();
}
}
});
}
internal static async Task<FirmwarePackage> GetFirmwarePackageAsync(
string targetName,
string targetName,
string platformName)
{
if (platformName.StartsWith("STM32"))

Просмотреть файл

@ -1,24 +1,24 @@
//
// Copyright (c) .NET Foundation and Contributors
// See LICENSE file in the project root for full license information.
//
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using GalaSoft.MvvmLight.Messaging;
using Microsoft.VisualStudio.Shell;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.VisualStudio.Extension.FirmwareUpdate;
using nanoFramework.Tools.VisualStudio.Extension.ToolWindow.ViewModel;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.Messaging;
using Microsoft.VisualStudio.Shell;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.Debugger.NFDevice;
using nanoFramework.Tools.VisualStudio.Extension.FirmwareUpdate;
using nanoFramework.Tools.VisualStudio.Extension.ToolWindow.ViewModel;
using Task = System.Threading.Tasks.Task;
namespace nanoFramework.Tools.VisualStudio.Extension.AutomaticUpdates
{
public class UpdateManager
{
private const int ExclusiveAccessTimeout = 3000;
private static UpdateManager s_instance;
private ViewModelLocator ViewModelLocator;
private readonly Package _package;
@ -32,7 +32,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension.AutomaticUpdates
}
public static void Initialize(
AsyncPackage package,
AsyncPackage package,
ViewModelLocator vmLocator)
{
s_instance = new UpdateManager(package)
@ -102,256 +102,278 @@ namespace nanoFramework.Tools.VisualStudio.Extension.AutomaticUpdates
}
#endif
// check if DebugEngine is available
if (nanoDevice.DebugEngine == null)
{
nanoDevice.CreateDebugEngine();
}
if (nanoDevice.DebugEngine == null)
{
// can't create it, quit update now
return;
}
// add this device to the updatING list
if (!devicesUpdatING.TryAdd(deviceId, new object()))
{
// fail to add device to list
#if DEBUG
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} update already in progress.");
#endif
// quit, never mind, this is not critical whatsoever
return;
}
// better wrap this on a try-finally because a lot of things can go wrong in the process
GlobalExclusiveDeviceAccess exclusiveAccess = null;
try
{
await Task.Yield();
var fwPackage = await GetFirmwarePackageAsync(
nanoDevice.TargetName,
nanoDevice.Platform);
await Task.Yield();
//////////////////////////////
// STM32 targets
if (fwPackage is Stm32Firmware)
// Get exclusive access to the device, but don't wait forever
exclusiveAccess = GlobalExclusiveDeviceAccess.TryGet(nanoDevice, ExclusiveAccessTimeout);
if (exclusiveAccess is null)
{
// sanity check
if (nanoDevice.DebugEngine == null)
{
// Can't get access, quit update for now
#if DEBUG
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} debug engine is not ready.");
Console.WriteLine($"[Automatic Updates] Cannot access {nanoDevice.Description}, another application is using the device");
#endif
// quit
return;
}
return;
}
if (nanoDevice.DebugEngine.Connect(
1000,
true,
true))
// check if DebugEngine is available
if (nanoDevice.DebugEngine == null)
{
nanoDevice.CreateDebugEngine();
}
if (nanoDevice.DebugEngine == null)
{
// can't create it, quit update now
return;
}
// add this device to the updatING list
if (!devicesUpdatING.TryAdd(deviceId, new object()))
{
// fail to add device to list
#if DEBUG
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} update already in progress.");
#endif
// quit, never mind, this is not critical whatsoever
return;
}
// better wrap this on a try-finally because a lot of things can go wrong in the process
try
{
await Task.Yield();
var fwPackage = await GetFirmwarePackageAsync(
nanoDevice.TargetName,
nanoDevice.Platform);
await Task.Yield();
//////////////////////////////
// STM32 targets
if (fwPackage is Stm32Firmware)
{
Version currentClrVersion = null;
// try to store CLR version
if(nanoDevice.DebugEngine.IsConnectedTonanoCLR)
// sanity check
if (nanoDevice.DebugEngine == null)
{
if (nanoDevice.DeviceInfo.Valid)
{
currentClrVersion = nanoDevice.DeviceInfo.SolutionBuildVersion;
}
#if DEBUG
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} debug engine is not ready.");
#endif
// quit
return;
}
// update conditions:
// 1. Running CLR _and_ the new version is higher
// 2. Running nanoBooter and there is no version information on the CLR (presumably because there is no CLR installed)
if (fwPackage.Version > nanoDevice.CLRVersion)
if (nanoDevice.DebugEngine.Connect(
1000,
true,
true))
{
bool attemptToLaunchBooter = false;
Version currentClrVersion = null;
// try to store CLR version
if (nanoDevice.DebugEngine.IsConnectedTonanoCLR)
{
// any update has to be handled by nanoBooter, so let's have it running
try
if (nanoDevice.DeviceInfo.Valid)
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Launching nanoBooter...");
currentClrVersion = nanoDevice.DeviceInfo.SolutionBuildVersion;
}
}
attemptToLaunchBooter = nanoDevice.ConnectToNanoBooter();
// update conditions:
// 1. Running CLR _and_ the new version is higher
// 2. Running nanoBooter and there is no version information on the CLR (presumably because there is no CLR installed)
if (fwPackage.Version > nanoDevice.CLRVersion)
{
bool attemptToLaunchBooter = false;
if (!attemptToLaunchBooter)
if (nanoDevice.DebugEngine.IsConnectedTonanoCLR)
{
// any update has to be handled by nanoBooter, so let's have it running
try
{
// check for version where the software reboot to nanoBooter was made available
if (currentClrVersion != null &&
nanoDevice.DeviceInfo.SolutionBuildVersion < new Version("1.6.0.54"))
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] The device is running a version that doesn't support rebooting by software. Please update your device using 'nanoff' tool.");
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Launching nanoBooter...");
await Task.Yield();
attemptToLaunchBooter = nanoDevice.ConnectToNanoBooter();
if (!attemptToLaunchBooter)
{
// check for version where the software reboot to nanoBooter was made available
if (currentClrVersion != null &&
nanoDevice.DeviceInfo.SolutionBuildVersion < new Version("1.6.0.54"))
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] The device is running a version that doesn't support rebooting by software. Please update your device using 'nanoff' tool.");
await Task.Yield();
}
}
}
catch
{
// this reboot step can go wrong and there's no big deal with that
}
}
catch
else
{
// this reboot step can go wrong and there's no big deal with that
attemptToLaunchBooter = true;
}
}
else
{
attemptToLaunchBooter = true;
}
// check if the device is still there
if(ViewModelLocator.DeviceExplorer.AvailableDevices.FirstOrDefault(d => d.DeviceUniqueId == deviceUniqueId) == null)
{
// check if the device is still there
if (ViewModelLocator.DeviceExplorer.AvailableDevices.FirstOrDefault(d => d.DeviceUniqueId == deviceUniqueId) == null)
{
#if DEBUG
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} is not available anymore.");
Console.WriteLine($"[Automatic Updates] {nanoDevice.TargetName} is not available anymore.");
#endif
return;
}
if (attemptToLaunchBooter &&
nanoDevice.Ping() == Debugger.WireProtocol.ConnectionSource.nanoBooter)
{
// get address for CLR block expected by device
var clrAddress = nanoDevice.GetCLRStartAddress();
// compare with address on the fw packages
if (clrAddress !=
(fwPackage as Stm32Firmware).ClrStartAddress)
{
// CLR addresses don't match, can't proceed with update
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Can't update device. CLR addresses are different. Please update nanoBooter manually.");
return;
}
await Task.Yield();
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Starting update to CLR v{fwPackage.Version}.");
try
if (attemptToLaunchBooter &&
nanoDevice.Ping() == Debugger.WireProtocol.ConnectionSource.nanoBooter)
{
// get address for CLR block expected by device
var clrAddress = nanoDevice.GetCLRStartAddress();
// compare with address on the fw packages
if (clrAddress !=
(fwPackage as Stm32Firmware).ClrStartAddress)
{
// CLR addresses don't match, can't proceed with update
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Can't update device. CLR addresses are different. Please update nanoBooter manually.");
return;
}
await Task.Yield();
// create a progress indicator to be used by deployment operation to post debug messages
var progressIndicator = new Progress<string>(m => MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] {m}"));
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Starting update to CLR v{fwPackage.Version}.");
if (nanoDevice.DeployBinaryFile(
(fwPackage as Stm32Firmware).nanoClrFileBin,
(fwPackage as Stm32Firmware).ClrStartAddress,
progressIndicator))
try
{
await Task.Yield();
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Update successful.");
// create a progress indicator to be used by deployment operation to post debug messages
var progressIndicator = new Progress<string>(m => MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] {m}"));
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
// if this is the selected device...
if (ViewModelLocator.DeviceExplorer.SelectedDevice?.DeviceUniqueId == deviceUniqueId)
{
// ...reset property to force that device capabilities to be retrieved on next connection
ViewModelLocator.DeviceExplorer.LastDeviceConnectedHash = 0;
}
if (attemptToLaunchBooter)
{
// try to reboot target
// remove it from updatING list
devicesUpdatING.TryRemove(deviceId, out var dummy);
// check if the device is still there
if (ViewModelLocator.DeviceExplorer.AvailableDevices.FirstOrDefault(d => d.DeviceUniqueId == Guid.Parse(deviceId)) == null)
if (nanoDevice.DeployBinaryFile(
(fwPackage as Stm32Firmware).nanoClrFileBin,
(fwPackage as Stm32Firmware).ClrStartAddress,
progressIndicator))
{
return;
await Task.Yield();
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Update successful.");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Rebooting...");
// if this is the selected device...
if (ViewModelLocator.DeviceExplorer.SelectedDevice?.DeviceUniqueId == deviceUniqueId)
{
// ...reset property to force that device capabilities to be retrieved on next connection
ViewModelLocator.DeviceExplorer.LastDeviceConnectedHash = 0;
}
nanoDevice.DebugEngine.RebootDevice(RebootOptions.NormalReboot);
if (attemptToLaunchBooter)
{
// try to reboot target
// remove it from updatING list
devicesUpdatING.TryRemove(deviceId, out var dummy);
// check if the device is still there
if (ViewModelLocator.DeviceExplorer.AvailableDevices.FirstOrDefault(d => d.DeviceUniqueId == Guid.Parse(deviceId)) == null)
{
return;
}
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] Rebooting...");
nanoDevice.DebugEngine.RebootDevice(RebootOptions.NormalReboot);
}
}
catch (Exception ex)
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Exception occurred when performing update ({ex.Message}).");
}
}
catch (Exception ex)
else
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Exception occurred when performing update ({ex.Message}).");
if (attemptToLaunchBooter)
{
// only report this as an error if the launch was successful
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Failed to launch nanoBooter. Quitting update.");
}
}
}
else
{
if (attemptToLaunchBooter)
// just to make sure that the CLR version is the latest, so we don't check it over and over
if (nanoDevice.DebugEngine.IsConnectedTonanoCLR &&
(fwPackage.Version == nanoDevice.DeviceInfo.ClrBuildVersion))
{
// only report this as an error if the launch was successful
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Failed to launch nanoBooter. Quitting update.");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
}
}
else
{
// just to make sure that the CLR version is the latest, so we don't check it over and over
if (nanoDevice.DebugEngine.IsConnectedTonanoCLR &&
(fwPackage.Version == nanoDevice.DeviceInfo.ClrBuildVersion))
{
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Can't connect to device. Quitting update.");
}
}
///////////////////////////////////
// ESP32 targets
else if (fwPackage is Esp32Firmware)
{
// TODO
// not supported yet
MessageCentre.OutputFirmwareUpdateMessage("The ability to update ESP32 targets is not currently available. Yet...");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
///////////////////////////////////////
// TI CC13x26x2
else if (fwPackage is CC13x26x2Firmware)
{
// TODO
// not supported yet
MessageCentre.OutputFirmwareUpdateMessage("The ability to update CC13x26x2 targets is not currently available. Yet...");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
}
else
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Can't connect to device. Quitting update.");
// shouldn't be here....
}
}
///////////////////////////////////
// ESP32 targets
else if (fwPackage is Esp32Firmware)
catch (Exception ex)
{
// TODO
// not supported yet
MessageCentre.OutputFirmwareUpdateMessage("The ability to update ESP32 targets is not currently available. Yet...");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Exception occurred when performing update ({ex.Message}).");
}
///////////////////////////////////////
// TI CC13x26x2
else if (fwPackage is CC13x26x2Firmware)
finally
{
// TODO
// not supported yet
MessageCentre.OutputFirmwareUpdateMessage("The ability to update CC13x26x2 targets is not currently available. Yet...");
// add it to the list of devices updatED with the update time stamp
devicesUpdatED.TryAdd(deviceDescription, DateTime.UtcNow);
// remove it from updatING list
devicesUpdatING.TryRemove(deviceId, out var dummy);
}
else
{
// shouldn't be here....
}
}
catch (Exception ex)
{
MessageCentre.OutputFirmwareUpdateMessage($"[{deviceDescription}] ERROR: Exception occurred when performing update ({ex.Message}).");
}
finally
{
// remove it from updatING list
devicesUpdatING.TryRemove(deviceId, out var dummy);
nanoDevice.DebugEngine?.Stop();
exclusiveAccess?.Dispose();
}
}
});
}
internal static async Task<FirmwarePackage> GetFirmwarePackageAsync(
string targetName,
string targetName,
string platformName)
{
if (platformName.StartsWith("STM32"))

Просмотреть файл

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<ImageManifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.microsoft.com/VisualStudio/ImageManifestSchema/2014">
<Symbols>
<String Name="Resources" Value="/nanoFramework.Tools.VS2022.Extension;v2022.3.0.92;C78BE3280FBDE583;Component/Resources" />
<String Name="Resources" Value="/nanoFramework.Tools.VS2022.Extension;v2022.3.0.103;C78BE3280FBDE583;Component/Resources" />
<Guid Name="NanoFrameworkCatalog" Value="{23cf437f-5e0e-4b0c-8aa4-ceec5b5f8679}" />
<ID Name="DeviceConnected" Value="20" />
<ID Name="DeviceDisconnected" Value="30" />

2
spelling_exclusion.dic Normal file
Просмотреть файл

@ -0,0 +1,2 @@
nano

Просмотреть файл

@ -1,20 +1,17 @@
//
// Copyright (c) .NET Foundation and Contributors
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
// See LICENSE file in the project root for full license information.
//
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using CorDebugInterop;
using Microsoft.VisualStudio.Debugger.Interop;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.Debugger.Extensions;
using nanoFramework.Tools.Debugger.WireProtocol;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using CorDebugInterop;
using Microsoft.VisualStudio.Debugger.Interop;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.Debugger.Extensions;
using nanoFramework.Tools.Debugger.WireProtocol;
using BreakpointDef = nanoFramework.Tools.Debugger.WireProtocol.Commands.Debugging_Execution_BreakpointDef;
namespace nanoFramework.Tools.VisualStudio.Extension
@ -79,15 +76,15 @@ namespace nanoFramework.Tools.VisualStudio.Extension
_syncTerminatingObject = new object();
}
public void Dispose()
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
Dispose(true);
GC.SuppressFinalize(this);
}
bool m_fDisposed = false;
protected virtual void Dispose(bool disposing)
protected virtual void Dispose(bool disposing)
{
if (!m_fDisposed)
{
@ -97,14 +94,14 @@ namespace nanoFramework.Tools.VisualStudio.Extension
}
// free "unmanaged" stuff
StopDebugging();
StopDebugging();
m_fDisposed = true;
}
}
~CorDebugProcess()
{
Dispose (false);
Dispose(false);
}
@ -126,11 +123,11 @@ namespace nanoFramework.Tools.VisualStudio.Extension
public bool IsDebugging => _corDebug != null;
public void SetCurrentAppDomain( CorDebugAppDomain appDomain )
public void SetCurrentAppDomain(CorDebugAppDomain appDomain)
{
if(appDomain != _appDomainCurrent)
if (appDomain != _appDomainCurrent)
{
if(appDomain != null && Engine.Capabilities.AppDomains)
if (appDomain != null && Engine.Capabilities.AppDomains)
{
Engine.SetCurrentAppDomain(appDomain.Id);
}
@ -161,7 +158,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// catch all as this can throw and we need to continue
}
if(!isProcess)
if (!isProcess)
{
// try sender as Engine
try
@ -355,7 +352,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
}
else
{
MessageCentre.InternalErrorWriteLine($"Device is running nanoCLR, requesting a restart and pause of debugger ({retry + 1}/{ maxOperationRetries }).");
MessageCentre.InternalErrorWriteLine($"Device is running nanoCLR, requesting a restart and pause of debugger ({retry + 1}/{maxOperationRetries}).");
bool rebootSuccessful = _engine.RebootDevice(RebootOptions.ClrOnly | RebootOptions.WaitForDebugger);
@ -373,7 +370,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
}
else if (_engine.IsConnectedTonanoBooter)
{
MessageCentre.InternalErrorWriteLine($"Device is running nanoBooter, requesting to launch nanoCLR ({retry + 1}/{ maxOperationRetries }).");
MessageCentre.InternalErrorWriteLine($"Device is running nanoBooter, requesting to launch nanoCLR ({retry + 1}/{maxOperationRetries}).");
// this is telling nanoBooter to enter CLR
_engine.ExecuteMemory(0);
@ -416,7 +413,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
public Engine AttachToEngine()
{
for(int retry = 0; retry < maxOperationRetries; retry++)
for (int retry = 0; retry < maxOperationRetries; retry++)
{
if (ShuttingDown)
{
@ -425,7 +422,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
break;
}
MessageCentre.InternalErrorWriteLine($"Attempting to connect the debugger engine ({retry + 1}/{ maxOperationRetries })");
MessageCentre.InternalErrorWriteLine($"Attempting to connect the debugger engine ({retry + 1}/{maxOperationRetries})");
try
{
@ -497,7 +494,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
var executionMode = Commands.DebuggingExecutionChangeConditions.State.SourceLevelDebugging;
// check if we need to disable the stack trace in exceptions
if(_engine.ThrowOnCommunicationFailure)
if (_engine.ThrowOnCommunicationFailure)
{
executionMode |= Commands.DebuggingExecutionChangeConditions.State.NoStackTraceInExceptions;
}
@ -517,14 +514,14 @@ namespace nanoFramework.Tools.VisualStudio.Extension
{
DetachFromEngine();
if(!ShuttingDown)
if (!ShuttingDown)
{
Thread.Sleep(10);
}
}
}
if(_engine != null && !_engine.IsConnected)
if (_engine != null && !_engine.IsConnected)
{
DetachFromEngine();
@ -555,21 +552,21 @@ namespace nanoFramework.Tools.VisualStudio.Extension
bool fContinue = false;
ManagedCallbacks.ManagedCallback mc = null;
lock(this)
lock (this)
{
if(_cStopped == 0 && AnyQueuedEvents)
if (_cStopped == 0 && AnyQueuedEvents)
{
Interlocked.Increment( ref _cStopped );
Interlocked.Increment(ref _cStopped);
mc = (ManagedCallbacks.ManagedCallback)_events.Dequeue();
}
}
if(mc != null)
if (mc != null)
{
DebugAssert(ShuttingDown || IsExecutionPaused || mc is ManagedCallbacks.ManagedCallbackDebugMessage, "Error on FlushEvent");
mc.Dispatch( _corDebug.ManagedCallback );
mc.Dispatch(_corDebug.ManagedCallback);
fContinue = true;
}
@ -685,7 +682,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
try
{
EnqueueStartupEventsAndWait();
MessageCentre.DebugMessage(Resources.ResourceStrings.AttachingToDevice);
if (AttachToEngine() == null)
@ -699,7 +696,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
if (_fLaunched)
{
//This will reboot the device if start debugging was done without a deployment
MessageCentre.DebugMessage(Resources.ResourceStrings.WaitingDeviceInitialization);
EnsureProcessIsInInitializedState();
}
@ -725,7 +722,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
}
}
}
catch(Exception ex)
catch (Exception ex)
{
MessageCentre.DebugMessage(Resources.ResourceStrings.InitializationFailed);
@ -737,15 +734,15 @@ namespace nanoFramework.Tools.VisualStudio.Extension
private void UpdateThreadList()
{
/*
This is a bit of a hack (or performance improvement, if you prefer)
The nanoCLR creates threads with wild abandon, but ICorDebug specifies that
thread creation/destruction events should stop the CLR, and provide callbacks
This can slow down debugging anything that makes heavy use of threads
For example...managed drivers, timers, etc...
So we are faking the events just in time, in a couple of cases --
when a real breakpoint gets hit, when execution is stopped via BreakAll, etc..
*/
/*
This is a bit of a hack (or performance improvement, if you prefer)
The nanoCLR creates threads with wild abandon, but ICorDebug specifies that
thread creation/destruction events should stop the CLR, and provide callbacks
This can slow down debugging anything that makes heavy use of threads
For example...managed drivers, timers, etc...
So we are faking the events just in time, in a couple of cases --
when a real breakpoint gets hit, when execution is stopped via BreakAll, etc..
*/
MessageCentre.InternalErrorWriteLine(Resources.ResourceStrings.RunningThreadsInformation);
@ -789,7 +786,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
Init(corDebug, fLaunch);
_threadDispatch = new Thread(delegate()
_threadDispatch = new Thread(delegate ()
{
try
{
@ -842,7 +839,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
{
AD_PROCESS_ID id = new AD_PROCESS_ID();
id.ProcessIdType = (uint) AD_PROCESS_ID_TYPE.AD_PROCESS_ID_SYSTEM;
id.ProcessIdType = (uint)AD_PROCESS_ID_TYPE.AD_PROCESS_ID_SYSTEM;
id.dwProcessId = _pid;
return id;
}
@ -875,7 +872,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
if (wr != null)
{
CorDebugValue val = (CorDebugValue) wr.Target;
CorDebugValue val = (CorDebugValue)wr.Target;
if (val != null)
{
@ -945,7 +942,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
m_scratchPad[index] = wr;
}
CorDebugValue val = (CorDebugValue) wr.Target;
CorDebugValue val = (CorDebugValue)wr.Target;
if (val == null)
{
@ -1081,12 +1078,12 @@ namespace nanoFramework.Tools.VisualStudio.Extension
public CorDebugAssembly AssemblyFromIdx(uint idx)
{
return CorDebugAssembly.AssemblyFromIdx( idx, _assemblies );
return CorDebugAssembly.AssemblyFromIdx(idx, _assemblies);
}
public CorDebugAssembly AssemblyFromIndex(uint index)
{
return CorDebugAssembly.AssemblyFromIndex( index, _assemblies );
return CorDebugAssembly.AssemblyFromIndex(index, _assemblies);
}
public void RegisterBreakpoint(object o, bool fRegister)
@ -1103,17 +1100,17 @@ namespace nanoFramework.Tools.VisualStudio.Extension
DirtyBreakpoints();
}
internal ArrayList GetBreakpoints( Type t, CorDebugAppDomain appDomain )
internal ArrayList GetBreakpoints(Type t, CorDebugAppDomain appDomain)
{
ArrayList al = new ArrayList( _breakpoints.Count );
ArrayList al = new ArrayList(_breakpoints.Count);
foreach(CorDebugBreakpointBase breakpoint in _breakpoints)
foreach (CorDebugBreakpointBase breakpoint in _breakpoints)
{
if(t.IsAssignableFrom( breakpoint.GetType() ))
if (t.IsAssignableFrom(breakpoint.GetType()))
{
if(appDomain == null || appDomain == breakpoint.AppDomain)
if (appDomain == null || appDomain == breakpoint.AppDomain)
{
al.Add( breakpoint );
al.Add(breakpoint);
}
}
}
@ -1121,22 +1118,22 @@ namespace nanoFramework.Tools.VisualStudio.Extension
return al;
}
private CorDebugBreakpointBase[] FindBreakpoints( BreakpointDef breakpointDef )
private CorDebugBreakpointBase[] FindBreakpoints(BreakpointDef breakpointDef)
{
//perhaps need to cheat for CorDebugEval.Breakpoint for uncaught exceptions...
ArrayList breakpoints = new ArrayList( 1 );
if( IsDebugging )
ArrayList breakpoints = new ArrayList(1);
if (IsDebugging)
{
foreach( CorDebugBreakpointBase breakpoint in _breakpoints )
foreach (CorDebugBreakpointBase breakpoint in _breakpoints)
{
if( breakpoint.IsMatch( breakpointDef ) )
if (breakpoint.IsMatch(breakpointDef))
{
breakpoints.Add( breakpoint );
breakpoints.Add(breakpoint);
}
}
}
return (CorDebugBreakpointBase[])breakpoints.ToArray( typeof( CorDebugBreakpointBase ) );
return (CorDebugBreakpointBase[])breakpoints.ToArray(typeof(CorDebugBreakpointBase));
}
private bool BreakpointHit(BreakpointDef breakpointDef)
@ -1147,23 +1144,23 @@ namespace nanoFramework.Tools.VisualStudio.Extension
CorDebugBreakpointBase[] breakpoints = FindBreakpoints(breakpointDef);
bool fStopExecution = false;
for(int iBreakpoint = 0; iBreakpoint < breakpoints.Length; iBreakpoint++)
for (int iBreakpoint = 0; iBreakpoint < breakpoints.Length; iBreakpoint++)
{
CorDebugBreakpointBase breakpoint = breakpoints[iBreakpoint];
if(breakpoint.ShouldBreak(breakpointDef))
if (breakpoint.ShouldBreak(breakpointDef))
{
fStopExecution = true;
break;
}
}
if(fStopExecution)
if (fStopExecution)
{
for(int iBreakpoint = 0; iBreakpoint < breakpoints.Length; iBreakpoint++)
for (int iBreakpoint = 0; iBreakpoint < breakpoints.Length; iBreakpoint++)
{
CorDebugBreakpointBase breakpoint = breakpoints[iBreakpoint];
breakpoint.Hit( breakpointDef );
breakpoint.Hit(breakpointDef);
}
}
@ -1172,36 +1169,36 @@ namespace nanoFramework.Tools.VisualStudio.Extension
public void UpdateBreakpoints()
{
if(!IsAttachedToEngine || !_fUpdateBreakpoints || ShuttingDown)
if (!IsAttachedToEngine || !_fUpdateBreakpoints || ShuttingDown)
return;
//Function breakpoints are set for each AppDomain.
//No need to send all duplicates to the nanoCLR
ArrayList al = new ArrayList( _breakpoints.Count );
for(int i = 0; i < _breakpoints.Count; i++)
ArrayList al = new ArrayList(_breakpoints.Count);
for (int i = 0; i < _breakpoints.Count; i++)
{
CorDebugBreakpointBase breakpoint1 = ((CorDebugBreakpointBase)_breakpoints[i]);
bool fDuplicate = false;
for(int j = 0; j < i; j++)
for (int j = 0; j < i; j++)
{
CorDebugBreakpointBase breakpoint2 = ((CorDebugBreakpointBase)_breakpoints[j]);
if(breakpoint1.Equals( breakpoint2 ))
if (breakpoint1.Equals(breakpoint2))
{
fDuplicate = true;
break;
}
}
if(!fDuplicate)
if (!fDuplicate)
{
al.Add( breakpoint1.Debugging_Execution_BreakpointDef );
al.Add(breakpoint1.Debugging_Execution_BreakpointDef);
}
}
BreakpointDef[] breakpointDefs = (BreakpointDef[])al.ToArray( typeof( BreakpointDef ) );
BreakpointDef[] breakpointDefs = (BreakpointDef[])al.ToArray(typeof(BreakpointDef));
Engine.SetBreakpoints(breakpointDefs);
_fUpdateBreakpoints = false;
@ -1320,7 +1317,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// Utility.Kernel32.DuplicateHandle(Utility.Kernel32.GetCurrentProcess(), emuProcess.Handle,
// Utility.Kernel32.GetCurrentProcess(), out lpProcessInformation.hProcess,
// 0, false, DUPLICATE_SAME_ACCESS);
// lpProcessInformation.dwProcessId = (uint)emuProcess.Id;
// CreateDummyThread(out lpProcessInformation.hThread, out lpProcessInformation.dwThreadId);
// }
@ -1347,18 +1344,18 @@ namespace nanoFramework.Tools.VisualStudio.Extension
}
private void InternalCreateProcess(
DebugPort port,
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
int bInheritHandles,
uint dwCreationFlags,
DebugPort port,
string lpApplicationName,
string lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
int bInheritHandles,
uint dwCreationFlags,
System.IntPtr lpEnvironment,
string lpCurrentDirectory,
string lpCurrentDirectory,
ref _STARTUPINFO lpStartupInfo,
ref _PROCESS_INFORMATION lpProcessInformation,
uint debuggingFlags
uint debuggingFlags
)
{
//if (port.IsLocalPort)
@ -1375,7 +1372,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
CommandLineBuilder cb = new CommandLineBuilder(lpCommandLine);
string[] args = cb.Arguments;
string deployDeviceName = args[args.Length-1];
string deployDeviceName = args[args.Length - 1];
//Extract deployDeviceName
if (!deployDeviceName.StartsWith(CorDebugProcess.DeployDeviceName))
@ -1397,7 +1394,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
return process;
}
internal ulong FakeLoadAssemblyIntoMemory( CorDebugAssembly assembly )
internal ulong FakeLoadAssemblyIntoMemory(CorDebugAssembly assembly)
{
ulong address = _fakeAssemblyAddressNext;
@ -1420,7 +1417,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
DebugAssert(assemblies.Count > 0, "Error loading assemblies. Assemblies count is 0.");
if(assemblies.Count == 0)
if (assemblies.Count == 0)
{
// if debug was started, presumably after a successful deployment, there have to be assemblies on the device
// so, if there are none, probably the command above failed, anyway we can't proceed with debugging
@ -1480,11 +1477,11 @@ namespace nanoFramework.Tools.VisualStudio.Extension
}
}
public CorDebugAppDomain GetAppDomainFromId( uint id )
public CorDebugAppDomain GetAppDomainFromId(uint id)
{
foreach(CorDebugAppDomain appDomain in _appDomains)
foreach (CorDebugAppDomain appDomain in _appDomains)
{
if(appDomain.Id == id)
if (appDomain.Id == id)
{
return appDomain;
}
@ -1565,10 +1562,16 @@ namespace nanoFramework.Tools.VisualStudio.Extension
{
_engine.ThrowOnCommunicationFailure = false;
// need to reboot device to clear memory leaks which are caused by the running app stopping execution and leaving C/C++ vars orphaned in the CRT heap
_engine.RebootDevice(RebootOptions.NormalReboot);
DetachFromEngine();
try
{
// need to reboot device to clear memory leaks which are caused by the running app stopping execution and leaving C/C++ vars orphaned in the CRT heap
_engine.RebootDevice(RebootOptions.NormalReboot);
}
finally
{
// Make sure the engine is detached (disposes the global exclusive access)
DetachFromEngine();
}
}
}
catch (Exception ex)
@ -1585,9 +1588,9 @@ namespace nanoFramework.Tools.VisualStudio.Extension
{
if (_threads != null && _threads.Count > 0)
{
if(_appDomains != null && _appDomains.Count > 0)
if (_appDomains != null && _appDomains.Count > 0)
{
EnqueueEvent( new ManagedCallbacks.ManagedCallbackDebugMessage( (CorDebugThread)_threads[0], (CorDebugAppDomain)_appDomains[0], "nanoCLR_Message", text, LoggingLevelEnum.LStatusLevel0 ) );
EnqueueEvent(new ManagedCallbacks.ManagedCallbackDebugMessage((CorDebugThread)_threads[0], (CorDebugAppDomain)_appDomains[0], "nanoCLR_Message", text, LoggingLevelEnum.LStatusLevel0));
}
}
else
@ -1693,9 +1696,9 @@ namespace nanoFramework.Tools.VisualStudio.Extension
public void OnNoise(byte[] buf, int offset, int count)
{
if(buf != null && (offset + count) <= buf.Length)
if (buf != null && (offset + count) <= buf.Length)
{
MessageCentre.InternalErrorWriteLine( System.Text.UTF8Encoding.UTF8.GetString(buf, offset, count) );
MessageCentre.InternalErrorWriteLine(System.Text.UTF8Encoding.UTF8.GetString(buf, offset, count));
}
}
@ -1718,21 +1721,21 @@ namespace nanoFramework.Tools.VisualStudio.Extension
private uint m_tkCLR;
private CorDebugClass m_class;
public BuiltinType( CorDebugAssembly assembly, uint tkCLR, CorDebugClass cls )
public BuiltinType(CorDebugAssembly assembly, uint tkCLR, CorDebugClass cls)
{
m_assembly = assembly;
m_tkCLR = tkCLR;
m_class = cls;
}
public CorDebugAssembly GetAssembly( CorDebugAppDomain appDomain )
public CorDebugAssembly GetAssembly(CorDebugAppDomain appDomain)
{
return appDomain.AssemblyFromIdx( m_assembly.Idx );
return appDomain.AssemblyFromIdx(m_assembly.Idx);
}
public CorDebugClass GetClass( CorDebugAppDomain appDomain )
public CorDebugClass GetClass(CorDebugAppDomain appDomain)
{
CorDebugAssembly assembly = GetAssembly( appDomain );
CorDebugAssembly assembly = GetAssembly(appDomain);
return assembly.GetClassFromTokenCLR(m_tkCLR);
}
@ -1748,7 +1751,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
uint tkCLR = MetaData.Helper.ClassTokenFromName(assm.MetaDataImport, type);
CorDebugClass c = assm.GetClassFromTokenCLR(tkCLR);
BuiltinType builtInType = new BuiltinType( assm, tkCLR, c );
BuiltinType builtInType = new BuiltinType(assm, tkCLR, c);
_tdBuiltin[o] = builtInType;
}
@ -1775,7 +1778,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
DebugAssert(assmCorLib != null, "Error resolving built-in type. Couldn't find mscorlib");
AddBuiltInType(CorElementType.ELEMENT_TYPE_BOOLEAN, assmCorLib, "System.Boolean");
AddBuiltInType(CorElementType.ELEMENT_TYPE_CHAR, assmCorLib, "System.Char");
AddBuiltInType(CorElementType.ELEMENT_TYPE_CHAR, assmCorLib, "System.Char");
AddBuiltInType(CorElementType.ELEMENT_TYPE_I1, assmCorLib, "System.SByte");
AddBuiltInType(CorElementType.ELEMENT_TYPE_U1, assmCorLib, "System.Byte");
AddBuiltInType(CorElementType.ELEMENT_TYPE_I2, assmCorLib, "System.Int16");
@ -1798,7 +1801,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
AddBuiltInType(ReflectionDefinition.Kind.REFLECTION_METHOD, assmCorLib, "System.Reflection.RuntimeMethodInfo");
AddBuiltInType(ReflectionDefinition.Kind.REFLECTION_CONSTRUCTOR, assmCorLib, "System.Reflection.RuntimeConstructorInfo");
AddBuiltInType(nanoClrDataType.DATATYPE_TRANSPARENT_PROXY, assmCorLib, "System.Runtime.Remoting.Proxies.__TransparentProxy" );
AddBuiltInType(nanoClrDataType.DATATYPE_TRANSPARENT_PROXY, assmCorLib, "System.Runtime.Remoting.Proxies.__TransparentProxy");
}
return (BuiltinType)_tdBuiltin[o];
@ -2177,7 +2180,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
#region ICorDebugProcess2 Members
int ICorDebugProcess2.GetVersion( out _COR_VERSION version )
int ICorDebugProcess2.GetVersion(out _COR_VERSION version)
{
version = new _COR_VERSION();
version.dwMajor = 1; //This is needed to handle v1 exceptions.
@ -2185,33 +2188,33 @@ namespace nanoFramework.Tools.VisualStudio.Extension
return COM_HResults.S_OK;
}
int ICorDebugProcess2.GetThreadForTaskID( ulong taskid, out ICorDebugThread2 ppThread )
int ICorDebugProcess2.GetThreadForTaskID(ulong taskid, out ICorDebugThread2 ppThread)
{
ppThread = null;
return COM_HResults.E_NOTIMPL;
}
int ICorDebugProcess2.SetUnmanagedBreakpoint( ulong address, uint bufsize, byte[] buffer, out uint bufLen )
int ICorDebugProcess2.SetUnmanagedBreakpoint(ulong address, uint bufsize, byte[] buffer, out uint bufLen)
{
bufLen = 0;
return COM_HResults.E_NOTIMPL;
}
int ICorDebugProcess2.GetDesiredNGENCompilerFlags( out uint pdwFlags )
int ICorDebugProcess2.GetDesiredNGENCompilerFlags(out uint pdwFlags)
{
pdwFlags = 0;
return COM_HResults.E_NOTIMPL;
}
int ICorDebugProcess2.SetDesiredNGENCompilerFlags( uint pdwFlags )
int ICorDebugProcess2.SetDesiredNGENCompilerFlags(uint pdwFlags)
{
return COM_HResults.E_NOTIMPL;
}
int ICorDebugProcess2.ClearUnmanagedBreakpoint( ulong address )
int ICorDebugProcess2.ClearUnmanagedBreakpoint(ulong address)
{
return COM_HResults.E_NOTIMPL;
}
@ -2277,17 +2280,17 @@ namespace nanoFramework.Tools.VisualStudio.Extension
{
ArrayList appDomains = _appDomains;
if(!IsAttachedToEngine)
if (!IsAttachedToEngine)
{
//need to fake this in order to get the Attach Dialog to work.
DebugAssert( appDomains == null, "Error enumerating programs. AppDomain is null.");
DebugAssert(appDomains == null, "Error enumerating programs. AppDomain is null.");
appDomains = new ArrayList
{
new CorDebugAppDomain(this, 1)
};
}
ppEnum = new CorDebugEnum( appDomains, typeof( IDebugProgram2 ), typeof( IEnumDebugPrograms2 ) );
ppEnum = new CorDebugEnum(appDomains, typeof(IDebugProgram2), typeof(IEnumDebugPrograms2));
return COM_HResults.S_OK;
}
@ -2330,7 +2333,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
if ((Fields & enum_PROCESS_INFO_FIELDS.PIF_FLAGS) != 0)
{
if(_executionPaused)
if (_executionPaused)
pi.Flags = enum_PROCESS_INFO_FLAGS.PIFLAG_PROCESS_STOPPED;
else
pi.Flags = enum_PROCESS_INFO_FLAGS.PIFLAG_PROCESS_RUNNING;
@ -2417,23 +2420,23 @@ namespace nanoFramework.Tools.VisualStudio.Extension
private string GetCommandName(uint cmd)
{
switch(cmd)
switch (cmd)
{
case Commands.c_Debugging_Execution_BreakpointStatus:
return "BreakpointStatus";
case Commands.c_Debugging_Execution_QueryCLRCapabilities:
return "QueryCLRCapabilities";
case Commands.c_Debugging_Execution_Allocate:
return "Allocate";
case Commands.c_Debugging_Execution_ChangeConditions:
return "ChangeConditions";
default:
return $"0x{cmd:X8}";
}
}
}
}
}

Просмотреть файл

@ -1,20 +1,19 @@
//
// Copyright (c) .NET Foundation and Contributors
// See LICENSE file in the project root for full license information.
//
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using GalaSoft.MvvmLight.Ioc;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.Debug;
using Microsoft.VisualStudio.ProjectSystem.VS.Debug;
using Microsoft.VisualStudio.Threading;
using nanoFramework.Tools.VisualStudio.Extension.ToolWindow.ViewModel;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.Debug;
using Microsoft.VisualStudio.ProjectSystem.VS.Debug;
using Microsoft.VisualStudio.Threading;
using nanoFramework.Tools.Debugger.NFDevice;
using nanoFramework.Tools.VisualStudio.Extension.ToolWindow.ViewModel;
namespace nanoFramework.Tools.VisualStudio.Extension
{
@ -22,6 +21,8 @@ namespace nanoFramework.Tools.VisualStudio.Extension
[AppliesTo(NanoCSharpProjectUnconfigured.UniqueCapability)]
internal partial class NanoDebuggerLaunchProvider : DebugLaunchProviderBase
{
private const int ExclusiveAccessTimeout = 3000;
private static AssemblyInformationalVersionAttribute _informationalVersionAttribute;
[ImportingConstructor]
@ -50,35 +51,62 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// get device
var device = SimpleIoc.Default.GetInstance<DeviceExplorerViewModel>().SelectedDevice;
// check for debug engine
if (device.DebugEngine == null)
var exclusiveAccess = GlobalExclusiveDeviceAccess.TryGet(device, ExclusiveAccessTimeout);
if (exclusiveAccess is null)
{
device.CreateDebugEngine();
#pragma warning disable S112 // OK to use Exception here
throw new Exception($"Can't get access to {deployDeviceName}, another application is using the device!");
#pragma warning restore S112 // General exceptions should never be thrown
}
// update stack trace processing option
device.DebugEngine.NoStackTraceInExceptions = !NanoFrameworkPackage.DebuggingOptions.ProcessStackTraceOption;
// make sure that the device is connected
if (device.DebugEngine.Connect(
false,
true))
else
{
string commandLine = await GetCommandLineForLaunchAsync();
commandLine = string.Format("{0} \"{1}{2}\"", commandLine, CorDebugProcess.DeployDeviceName, deployDeviceName);
var stopDebugEngine = true;
var settings = new DebugLaunchSettings(launchOptions)
try
{
Executable = typeof(CorDebugProcess).Assembly.Location,
Arguments = commandLine,
LaunchOperation = DebugLaunchOperation.CreateProcess,
PortSupplierGuid = DebugPortSupplier.PortSupplierGuid,
PortName = NanoFrameworkPackage.NanoDeviceCommService.Device.Description,
Project = VsHierarchy,
LaunchDebugEngineGuid = CorDebug.EngineGuid
};
// check for debug engine
if (device.DebugEngine == null)
{
device.CreateDebugEngine();
}
return new IDebugLaunchSettings[] { settings };
// update stack trace processing option
device.DebugEngine.NoStackTraceInExceptions = !NanoFrameworkPackage.DebuggingOptions.ProcessStackTraceOption;
// make sure that the device is connected
if (device.DebugEngine.Connect(
false,
true))
{
string commandLine = await GetCommandLineForLaunchAsync();
commandLine = string.Format("{0} \"{1}{2}\"", commandLine, CorDebugProcess.DeployDeviceName, deployDeviceName);
var settings = new DebugLaunchSettings(launchOptions)
{
Executable = typeof(CorDebugProcess).Assembly.Location,
Arguments = commandLine,
LaunchOperation = DebugLaunchOperation.CreateProcess,
PortSupplierGuid = DebugPortSupplier.PortSupplierGuid,
PortName = NanoFrameworkPackage.NanoDeviceCommService.Device.Description,
Project = VsHierarchy,
LaunchDebugEngineGuid = CorDebug.EngineGuid
};
stopDebugEngine = false;
return new IDebugLaunchSettings[] { settings };
}
}
finally
{
if (stopDebugEngine)
{
// On success, the debug engine does not have to be stopped, it will be stopped in the CorDebugProcess
// and the global exclusive access is terminated there.
device.DebugEngine?.Stop();
}
exclusiveAccess?.Dispose();
}
}
#pragma warning disable S112 // OK to use Exception here

Просмотреть файл

@ -1,16 +1,6 @@
//
// Copyright (c) .NET Foundation and Contributors
// Portions Copyright (c) Microsoft Corporation. All rights reserved.
// See LICENSE file in the project root for full license information.
//
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.Build;
using Microsoft.VisualStudio.Shell;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.VisualStudio.Extension.ToolWindow.ViewModel;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
@ -19,6 +9,14 @@ using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.CSharp;
using Microsoft.VisualStudio.ProjectSystem;
using Microsoft.VisualStudio.ProjectSystem.Build;
using Microsoft.VisualStudio.Shell;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.Debugger.NFDevice;
using nanoFramework.Tools.VisualStudio.Extension.ToolWindow.ViewModel;
using Task = System.Threading.Tasks.Task;
namespace nanoFramework.Tools.VisualStudio.Extension
@ -27,6 +25,8 @@ namespace nanoFramework.Tools.VisualStudio.Extension
[AppliesTo(NanoCSharpProjectUnconfigured.UniqueCapability)]
internal class DeployProvider : IDeployProvider
{
private const int ExclusiveAccessTimeout = 3000;
private static ViewModelLocator _viewModelLocator;
private static Package _package;
@ -126,6 +126,12 @@ namespace nanoFramework.Tools.VisualStudio.Extension
bool needsToCloseMessageOutput = false;
// Get exclusive access to the device, but don't wait forever
MessageCentre.InternalErrorWriteLine("Try to get exclusive access to the nanoDevice");
using var exclusiveAccess = GlobalExclusiveDeviceAccess.TryGet(device, ExclusiveAccessTimeout)
?? throw new DeploymentException($"Couldn't access the device {device.Description}, it is used by another application!");
try
{
MessageCentre.InternalErrorWriteLine("Starting debug engine on nanoDevice");
@ -404,6 +410,8 @@ namespace nanoFramework.Tools.VisualStudio.Extension
}
finally
{
device.DebugEngine?.Stop();
MessageCentre.StopProgressMessage();
}
}

Просмотреть файл

@ -1,15 +1,13 @@
//
// Copyright (c) .NET Foundation and Contributors
// See LICENSE file in the project root for full license information.
//
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.Debugger.WireProtocol;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.Debugger.WireProtocol;
namespace nanoFramework.Tools.VisualStudio.Extension
{
@ -22,7 +20,5 @@ namespace nanoFramework.Tools.VisualStudio.Extension
bool SelectDevice(string description);
TaskAwaiter GetAwaiter();
bool ConnectTo(string description = null, int timeout = 5000);
}
}

Просмотреть файл

@ -1,14 +1,12 @@
//
// Copyright (c) .NET Foundation and Contributors
// See LICENSE file in the project root for full license information.
//
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using nanoFramework.Tools.Debugger;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using nanoFramework.Tools.Debugger;
namespace nanoFramework.Tools.VisualStudio.Extension
{
@ -84,17 +82,5 @@ namespace nanoFramework.Tools.VisualStudio.Extension
return true;
}
public bool ConnectTo(string deviceId = null, int timeout = 5000)
{
if (deviceId == null)
{
return Device.DebugEngine.Connect(timeout, true);
}
else
{
return DebugClient.NanoFrameworkDevices.FirstOrDefault(d => d.Description == deviceId).DebugEngine.Connect(timeout, true);
}
}
}
}

Просмотреть файл

@ -1,19 +1,18 @@
//
// Copyright (c) .NET Foundation and Contributors
// See LICENSE file in the project root for full license information.
//
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.ComponentModel.Design;
using System.Text;
using System.Windows;
using GalaSoft.MvvmLight.Ioc;
using GalaSoft.MvvmLight.Messaging;
using Microsoft.VisualStudio.Shell;
using nanoFramework.Tools.Debugger;
using nanoFramework.Tools.Debugger.Extensions;
using nanoFramework.Tools.Debugger.NFDevice;
using nanoFramework.Tools.Debugger.WireProtocol;
using nanoFramework.Tools.VisualStudio.Extension.ToolWindow.ViewModel;
using System;
using System.ComponentModel.Design;
using System.Text;
using System.Windows;
using Task = System.Threading.Tasks.Task;
namespace nanoFramework.Tools.VisualStudio.Extension
@ -83,6 +82,9 @@ namespace nanoFramework.Tools.VisualStudio.Extension
public const int ShowInternalErrorsCommandID = 0x0300;
public const int ShowSettingsCommandID = 0x0420;
// Timeout for exclusive access
private const int ExclusiveAccessTimeout = 3000;
INanoDeviceCommService NanoDeviceCommService;
OleMenuCommandService MenuCommandService;
@ -301,6 +303,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
var descriptionBackup = ViewModelLocator.DeviceExplorer.SelectedDevice.Description;
MessageCentre.StartProgressMessage($"Pinging {descriptionBackup}...");
GlobalExclusiveDeviceAccess exclusiveAccess = null;
try
{
// disable buttons
@ -309,6 +312,14 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// make sure this device is showing as selected in Device Explorer tree view
ViewModelLocator.DeviceExplorer.ForceNanoDeviceSelection();
// Get exclusive access to the device, but don't wait forever
exclusiveAccess = GlobalExclusiveDeviceAccess.TryGet(ViewModelLocator.DeviceExplorer.SelectedDevice, ExclusiveAccessTimeout);
if (exclusiveAccess is null)
{
MessageCentre.OutputMessage($"Cannot access {descriptionBackup}, another application is using the device.");
return;
}
// check if debugger engine exists
if (NanoDeviceCommService.Device.DebugEngine == null)
{
@ -348,6 +359,8 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// disconnect device
NanoDeviceCommService.Device?.DebugEngine?.Stop(true);
exclusiveAccess?.Dispose();
// enable buttons
await UpdateDeviceDependentToolbarButtonsAsync(true);
@ -375,6 +388,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
await Task.Run(async delegate
{
GlobalExclusiveDeviceAccess exclusiveAccess = null;
try
{
// disable buttons
@ -386,6 +400,14 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// only query device if it's different
if (descriptionBackup.GetHashCode() != ViewModelLocator.DeviceExplorer.LastDeviceConnectedHash)
{
// Get exclusive access to the device, but don't wait forever
exclusiveAccess = GlobalExclusiveDeviceAccess.TryGet(ViewModelLocator.DeviceExplorer.SelectedDevice, ExclusiveAccessTimeout);
if (exclusiveAccess is null)
{
MessageCentre.OutputMessage($"Cannot access {descriptionBackup}, another application is using the device.");
return;
}
// check if debugger engine exists
if (NanoDeviceCommService.Device.DebugEngine == null)
{
@ -543,6 +565,8 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// disconnect device
NanoDeviceCommService.Device?.DebugEngine?.Stop(true);
exclusiveAccess?.Dispose();
// enable buttons
await UpdateDeviceDependentToolbarButtonsAsync(true);
@ -574,6 +598,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
await Task.Run(async delegate
{
GlobalExclusiveDeviceAccess exclusiveAccess = null;
try
{
// disable buttons
@ -582,6 +607,14 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// make sure this device is showing as selected in Device Explorer tree view
ViewModelLocator.DeviceExplorer.ForceNanoDeviceSelection();
// Get exclusive access to the device, but don't wait forever
exclusiveAccess = GlobalExclusiveDeviceAccess.TryGet(ViewModelLocator.DeviceExplorer.SelectedDevice, ExclusiveAccessTimeout);
if (exclusiveAccess is null)
{
MessageCentre.OutputMessage($"Cannot access {descriptionBackup}, another application is using the device.");
return;
}
// check if debugger engine exists
if (NanoDeviceCommService.Device.DebugEngine == null)
{
@ -639,6 +672,8 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// disconnect device
NanoDeviceCommService.Device?.DebugEngine?.Stop(true);
exclusiveAccess?.Dispose();
// enable buttons
await UpdateDeviceDependentToolbarButtonsAsync(true);
@ -665,6 +700,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
await Task.Run(async delegate
{
GlobalExclusiveDeviceAccess exclusiveAccess = null;
try
{
// disable buttons
@ -673,6 +709,17 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// make sure this device is showing as selected in Device Explorer tree view
ViewModelLocator.DeviceExplorer.ForceNanoDeviceSelection();
// Get exclusive access to the device, but don't wait forever
exclusiveAccess = GlobalExclusiveDeviceAccess.TryGet(ViewModelLocator.DeviceExplorer.SelectedDevice, ExclusiveAccessTimeout);
if (exclusiveAccess is null)
{
_ = MessageBox.Show($"Cannot access {ViewModelLocator.DeviceExplorer.SelectedDevice.Description}, another application is using the device.",
".NET nanoFramework Device Explorer",
MessageBoxButton.OK,
MessageBoxImage.Error);
return;
}
// check if debugger engine exists
if (NanoDeviceCommService.Device.DebugEngine == null)
{
@ -761,6 +808,8 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// disconnect device
NanoDeviceCommService.Device?.DebugEngine?.Stop(true);
exclusiveAccess?.Dispose();
// enable buttons
await UpdateDeviceDependentToolbarButtonsAsync(true);
@ -785,6 +834,7 @@ namespace nanoFramework.Tools.VisualStudio.Extension
await Task.Run(async delegate
{
GlobalExclusiveDeviceAccess exclusiveAccess = null;
try
{
// disable buttons
@ -793,6 +843,14 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// make sure this device is showing as selected in Device Explorer tree view
ViewModelLocator.DeviceExplorer.ForceNanoDeviceSelection();
// Get exclusive access to the device, but don't wait forever
exclusiveAccess = GlobalExclusiveDeviceAccess.TryGet(ViewModelLocator.DeviceExplorer.SelectedDevice, ExclusiveAccessTimeout);
if (exclusiveAccess is null)
{
MessageCentre.OutputMessage($"Cannot access {ViewModelLocator.DeviceExplorer.SelectedDevice.Description}, another application is using the device.");
return;
}
// check if debugger engine exists
if (NanoDeviceCommService.Device.DebugEngine == null)
{
@ -881,6 +939,8 @@ namespace nanoFramework.Tools.VisualStudio.Extension
// disconnect device
NanoDeviceCommService.Device?.DebugEngine?.Stop(true);
exclusiveAccess?.Dispose();
// enable buttons
await UpdateDeviceDependentToolbarButtonsAsync(true);
}