2015-07-24 3 views
1

Nous utilisons AppDomain.ExecuteAssembly() pour "fork" un exécutable de lui-même. Cela peut être utilisé pour mettre à jour dynamiquement app.config au démarrage (voir this ancien message).AppDomain.ExecuteAssembly définit le titre de la console

Apparemment, l'appel AppDomain.ExecuteAssembly() définit le titre de la fenêtre de console en cours à la valeur Assembly.CodeBase de l'assembly en cours d'exécution. Lorsque la fonction revient, c'est-à-dire que l'exécution reprend à l'AppDomain appelant, le titre est rétabli.

Bien qu'il ne soit pas vraiment "nuisible", c'est un peu gênant, quand vous avez un gros fichier batch qui appelle un tel exécutable plusieurs fois (le titre de la console change constamment).

Voici un exemple repro:

using System; 

public class Foo 
{ 
    public static int Main(string[] args) 
    { 
     if (AppDomain.CurrentDomain.FriendlyName.Equals("Test")) 
     { 
      Console.WriteLine("Inside"); 
      // While "in here", the console title is something 
      // like: "file:///C:/Sources/Foo.exe". 
      Console.ReadKey(); 
      return 0; 
     } 

     var domain = AppDomain.CreateDomain("Test", null, 
      AppDomain.CurrentDomain.SetupInformation); 
     return domain.ExecuteAssembly(typeof(Foo).Assembly.Location, (string[])null); 
    } 
} 

Je ne pouvais pas trouver toute la documentation à ce sujet, ou un code qui pourrait causer cela. La fonction réelle serait probablement _nExecuteAssembly() à l'intérieur du CLR, qui n'est pas inclus dans le CoreCLR open source, car cela ne prend pas en charge AppDomains. En outre, le code source SSCLI semble pour ne pas avoir de code pertinent (même si je ne suis pas sûr, ni peut vérifier, si ce comportement pourrait également être vu avec le CLR 2.x).

Mises à jour:

  • applications Windows (commutateur de compilateur /target:winexe) ne pas présentent ce comportement. Pour tester utiliser cet exemple:

    // Compile with "csc.exe /target:winexe foo.cs" 
    using System; 
    using System.Runtime.InteropServices; 
    
    public class Foo 
    { 
        [DllImport("Kernel32.dll")] 
        public static extern void AllocConsole(); 
    
        public static int Main(string[] args) 
        { 
         AllocConsole(); 
    
         if (AppDomain.CurrentDomain.FriendlyName.Equals("Test")) 
         { 
          Console.WriteLine("Inside"); 
          // While "in here", the console title is something 
          // like: "file:///C:/Sources/Foo.exe". 
          Console.ReadKey(); 
          return 0; 
         } 
    
         var domain = AppDomain.CreateDomain("Test", null, 
          AppDomain.CurrentDomain.SetupInformation); 
         return domain.ExecuteAssembly(typeof(Foo).Assembly.Location, (string[])null); 
        } 
    } 
    
  • En utilisant Mono au lieu de Microsoft CLR, fait aussi pas présentent ce comportement (même lorsque compilé en cours d'exécution executables avec le compilateur Microsoft). Donc, cela semble être un comportement (Microsoft) CLR.

Est-ce que quelqu'un peut confirmer/expliquer/reproduire ceci? Pouvez-vous trouver des traces de ce comportement dans le code source (de SSCLI, CoreCLR, etc.)?

Mise à jour Solution

(Note:. Je solution de contournement pour cela en utilisant AppDomain.SetData("key", Console.Title) et Console.Title = AppDomain.GetData("key"), respectivement, mais je suis curieux)

Depuis Hans a trouvé la raison précise cet être en effet un appel explicite à SetConsoleTitle (la base native de Console.Title), je voulais faire ma solution de contournement explicite:

using System; 

public class Foo 
{ 
    public static int Main(string[] args) 
    { 
     if (AppDomain.CurrentDomain.FriendlyName.Equals("Test")) 
     { 
      Console.Title = (string)AppDomain.CurrentDomain.GetData("foo:original-title"); 
      Console.WriteLine("Inside"); 
      Console.ReadKey(); 
      return 0; 
     } 

     var domain = AppDomain.CreateDomain("Test", null, AppDomain.CurrentDomain.SetupInformation); 
     domain.SetData("foo:original-title", Console.Title); 
     return domain.ExecuteAssembly(typeof(Foo).Assembly.Location, (string[])null); 
    } 
} 

En pratique, vous souhaiterez probablement effectuer un peu plus de vérification des erreurs et éventuellement empêcher les exceptions de Console.Title de provoquer la sortie de l'application, mais YMMV. Comme Hans l'a dit, comme indiqué dans l'exemple ci-dessus, vous pourriez aussi avoir compilé comme un "exécutable Windows" (/target:winexe) et appeler manuellement AllocConsole. Mais personnellement, j'ai trouvé l'approche ci-dessus plus raisonnable.

Répondre

3

Ceci est visible dans le code source CoreCLR pour le CLR, src/vm/appdomainnative.cpp, AppDomainNative::ExecuteAssembly function:

if (pAssembly->GetManifestFile()->GetSubsystem() == IMAGE_SUBSYSTEM_WINDOWS_CUI) 
{ 
    { 
     GCX_COOP(); 
     Security::CheckBeforeAllocConsole(pDomain, pAssembly); 
    } 
    bCreatedConsole = AllocConsole(); 
    StackSString codebase; 
    pAssembly->GetManifestFile()->GetCodeBase(codebase); 
    SetConsoleTitle(codebase); 
} 

Ce met en œuvre la promesse d'un ensemble compilé avec/target: exe est affiché dans une fenêtre de la console, créant si nécessaire. Probablement quelque chose que vous ne voyez pas puisque vous utilisez déjà une application en mode console. L'appel SetConsoleTitle() définit le titre de la fenêtre.

Aucun bouton à modifier, vous devez définir l'option de compilation/target sur toute autre chose. Soit le winexe ou library fait le travail, notez que l'extension du nom de fichier n'a pas d'importance.

+1

Merci Hans. Franchement, je ne comprends pas pourquoi je ne l'ai pas vu dans les sources de CoreCLR - regardé là d'abord et n'a même pas vu les sources natives AppDomain. Stupide moi :-) Ce que je ne comprends pas, c'est le "pourquoi". Le 'AllocConsole' est accordé - assurant que la fenêtre de la console est" là ", mais pourquoi définir le titre. Cela n'aurait pas été nécessaire. Quoi qu'il en soit, merci encore. –

+0

Cette question équivaut à peu près à "pourquoi pas?" :) Une application en mode console non gérée fait cela aussi, les fenêtres de console devraient avoir un titre approprié. Console.Title dans un programme .NET. –

+0

Oui, cela aurait du sens. Je pense que je voyais tout de mon propre point de vue, où j'utilise ExecuteAssembly ("self"), et donc le changement de titre est quelque peu "redondant" (et "CodeBase" semble tout simplement moche). Mais oui, toute la question «pourquoi/pourquoi pas» est bien sûr sans intérêt :-) –