Renvoyez Thread.Abort () Ă  .NET Core. Livraison d'applications avec sa version de CoreCLR et CoreFX

Lors de la migration du .NET Framework vers le .NET Core , certains moments désagréables peuvent survenir. Par exemple, si votre application utilise des domaines, vous devrez réécrire la logique. Une situation similaire avec Thread.Abort () : Microsoft est tellement détesté par cette pratique (et à juste titre) qu'ils ont d'abord déclaré cette méthode obsolète , puis l'ont complètement supprimée du cadre et maintenant, elle lance de manière perfide une exception PlatformNotSupportedException .

Mais que se passe-t-il si votre application utilise Thread.Abort () et que vous souhaitez vraiment la traduire en .NET Core sans réécrire quoi que ce soit? Eh bien, nous savons très bien que la plate-forme prend même en charge cette fonctionnalité, je peux donc vous plaire: il existe un moyen de sortir, il vous suffit d'assembler votre propre version du CLR .

Avertissement: Il s'agit d'un article purement pratique avec un minimum de théorie, conçu uniquement pour démontrer de nouvelles options d'interaction entre le développeur et l'environnement .NET. Ne faites jamais cela en production. Mais si vous voulez vraiment ...



Cela a été rendu possible grâce à deux choses: la volonté de Microsoft de multiplier les plates-formes .NET Core et le travail effectué par les développeurs pour transférer le code source du framework en accès ouvert. Profitons de cela.

Minimum théorique:

  • dotnet publish nous permet de publier une application autonome : le framework sera livrĂ© avec, et non recherchĂ© quelque part dans le GAC
  • La version de CoreCLR sur laquelle l'application s'exĂ©cutera, sous certaines conditions, peut ĂŞtre dĂ©finie Ă  l'aide de runtimeconfig.json
  • Nous pouvons crĂ©er notre propre CoreFX : github.com/dotnet/corefx
  • Nous pouvons construire notre propre CoreCLR : github.com/dotnet/coreclr

Personnalisez CoreFX


Avant de passer à notre objectif principal - retourner Thread.Abort ( ) - changeons quelque chose de fondamental dans CoreFX pour nous réchauffer et tester la fonctionnalité de tous les outils. Par exemple, sur les traces de mon précédent article sur la dynamique , nous interdisons complètement son utilisation dans l'application. Pourquoi? Parce que nous le pouvons.

Prérequis


Tout d'abord, nous installerons tout le nécessaire pour le montage:

  • CMake
  • Aperçu de Visual Studio 2019
  • Dernier SDK .NET Core (.NET Core 3.0 Preview)

Visual Studio 2019 - Charges de travail


Développement de bureau .NET

  • Tous les composants requis
  • .NET Framework 4.7.2 Outils de dĂ©veloppement

Développement de bureau avec C ++

  • Tous les composants requis
  • VC ++ 2019 v142 Toolset (x86, x64)
  • SDK Windows 8.1 et SDK UCRT
  • VC ++ 2017 v141 Toolset (x86, x64)

Développement multiplateforme .NET Core

  • Tous les composants requis

Visual Studio 2019 - Composants individuels


  • Compilateurs Roslyn C # et Visual Basic
  • Outils d'analyse statique
  • Pack de ciblage de bibliothèque portable .NET
  • SDK Windows 10 ou SDK Windows 8.1
  • FonctionnalitĂ©s principales de Visual Studio C ++
  • VC ++ 2019 v142 Toolset (x86, x64)
  • VC ++ 2017 v141 Toolset (x86, x64)
  • Msbuild
  • Pack de ciblage .NET Framework 4.7.2
  • SDK CRT universel Windows

Clone corefx :

git clone https://github.com/dotnet/corefx.git 

Désactivez maintenant la dynamique . Nous allons ouvrir pour cela (ci-après je vais indiquer les chemins par rapport à la racine du référentiel)

 corefx\src\System.Linq.Expressions\src\System\Runtime\CompilerServices\CallSite.cs 

Et à la fin de la fonction CallSite <T> .Create, insérez le code brut:

 throw new PlatformNotSupportedException("No way"); 

Nous revenons à corefx et exécutons build.cmd . Une fois l'assemblage terminé, créez un nouveau projet .NET Core dans Visual Studio avec le contenu suivant:

 public int Test { get; set; } public static void Main(string[] args) { try { dynamic a = new Program(); a.Test = 120; } catch (Exception e) { Console.WriteLine(e); } //,    foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { Console.WriteLine(asm.Location); } } 

Nous compilons notre projet. Maintenant, allez Ă 

 corefx\artifacts\packages\Debug\NonShipping 

et nous y trouvons un package qui ressemble à ceci: Microsoft.Private.CoreFx.NETCoreApp. 5.0.0-dev.19465.1 .nupkg . Nous ouvrons .csproj de notre projet et y insérons les lignes suivantes:

 <PropertyGroup> <PackageConflictPreferredPackages>Microsoft.Private.CoreFx.NETCoreApp;runtime.$(RuntimeIdentifiers).Microsoft.Private.CoreFx.NETCoreApp;$(PackageConflictPreferredPackages)</PackageConflictPreferredPackages> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Private.CoreFx.NETCoreApp" Version="5.0.0-dev.19465.1" /> </ItemGroup> 

La version doit être la même que dans le nom du package assemblé. Dans mon cas, 5.0.0-dev.19465.1 .



Accédez aux paramètres de nuget pour notre projet et ajoutez-y deux nouveaux chemins:

 corefx\artifacts\packages\Debug\NonShipping corefx\artifacts\packages\Debug\Shipping 

Et décochez toutes les autres.



Accédez au dossier du projet et exécutez

 dotnet publish --runtime win-x64 --self-contained 

C'est fait! Il ne reste plus qu'à exécuter:



Ça marche! Les bibliothèques ne sont pas extraites du GAC , la dynamique ne fonctionne pas.

Rendre CoreCLR grand Ă  nouveau


Passons maintenant à la deuxième partie, en retournant Thread.Abort () . Une désagréable surprise nous attend ici: l'implémentation des threads réside dans CoreCLR , qui ne fait pas partie de CoreFX et est préinstallé sur la machine séparément. Créez d'abord un projet de démonstration:

 var runtimeInformation = RuntimeInformation.FrameworkDescription; Console.WriteLine(runtimeInformation); var thr = new Thread(() => { try { while (true) { Console.WriteLine("."); Thread.Sleep(500); } } catch (ThreadAbortException) { Console.WriteLine("Thread aborted!"); } }); foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) { Console.WriteLine(asm.Location); } thr.Start(); Thread.Sleep(2000); thr.Abort(); 

Dégonfler le coreclr . Trouvez le fichier

 coreclr\src\System.Private.CoreLib\shared\System\Threading\Thread.cs 

Et remplacez Abort () par

 [SecuritySafeCritical] [SecurityPermissionAttribute(SecurityAction.Demand, ControlThread = true)] public void Abort() { AbortInternal(); } [System.Security.SecurityCritical] // auto-generated [ResourceExposure(ResourceScope.None)] [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern void AbortInternal(); 

Nous devons maintenant renvoyer les attributs et l'implémentation c ++ . Je l'ai collecté en morceaux à partir de divers référentiels ouverts du .NET Framework et pour plus de commodité, j'ai conçu toutes les modifications sous la forme d'une demande d'extraction .

Remarque: lors de la construction, il y avait des problèmes de ressources dans les «nouveaux» attributs,
"Fixe" avec des bouchons. Gardez cela à l'esprit si vous souhaitez utiliser ce code ailleurs que dans l'expérimentation à domicile.

Après avoir intégré ces modifications dans le code, exécutez build.cmd à partir de coreclr . L'assemblage dans les étapes ultérieures peut commencer à parsemer d'erreurs, mais ce n'est pas effrayant, l'essentiel pour nous est que CoreCLR puisse s'assembler. Il se situera dans:

 coreclr\bin\Product\Windows_NT.x64.Debug 

Moyen facile


Nous réalisons

 dotnet publish --runtime win-x64 --self-contained 

Les fichiers de Windows_NT.x64.Debug sont déposés dans le dossier avec l'application publiée.

Chemin difficile


Une fois les bibliothèques assemblées, accédez à

 C:\Program Files\dotnet\shared\Microsoft.NETCore.App 

et clonez le dossier 3.0.0. Nous l'appellerons, par exemple, 5.0.1. Nous copions tout ce qui se trouve dans Windows_NT.x64.Debug , Ă  l'exception des dossiers. Maintenant, notre version de CoreCLR sera disponible via runtimeconfig.

Monter notre projet. Ajoutez Ă  .csproj :

 <PropertyGroup> <DisableImplicitFrameworkReferences>true</DisableImplicitFrameworkReferences> <PackageConflictPreferredPackages>Microsoft.Private.CoreFx.NETCoreApp;runtime.$(RuntimeIdentifiers).Microsoft.Private.CoreFx.NETCoreApp;$(PackageConflictPreferredPackages)</PackageConflictPreferredPackages> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.Private.CoreFx.NETCoreApp" Version="5.0.0-dev.19465.1" /> </ItemGroup> 

Nous répétons les manipulations avec nuget de la partie précédente de l'article. Publier

 dotnet publish --runtime win-x64 --self-contained 

Dans runtimeconfig.json, entrez la configuration suivante:

 { "runtimeOptions": { "tfm": "netcoreapp3.0", "framework": { "name": "Microsoft.NETCore.App", "version": "5.0.1" } } } 

Résultat


Lancez!



La magie s'est produite. Maintenant, dans nos applications .NET Core , Thread.Abort () est à nouveau en cours d'exécution. Mais, bien sûr, uniquement sur Windows .

Source: https://habr.com/ru/post/fr467475/


All Articles