// Licensed to the .NET Foundation under one or more agreements.// The .NET Foundation licenses this file to you under the MIT license.usingSystem;usingSystem.Diagnostics;usingSystem.Diagnostics.Tracing;usingSystem.Diagnostics.CodeAnalysis;usingSystem.IO;usingSystem.Reflection;usingSystem.Runtime.Loader;namespaceSystem{internalstaticclassStartupHookProvider{privateconststringStartupHookTypeName="StartupHook";privateconststringInitializeMethodName="Initialize";privateconststringDisallowedSimpleAssemblyNameSuffix=".dll";privatestaticboolIsSupported=>AppContext.TryGetSwitch("System.StartupHookProvider.IsSupported",outboolisSupported)?isSupported:true;privatestructStartupHookNameOrPath{publicAssemblyNameAssemblyName;publicstringPath;}// Parse a string specifying a list of assemblies and types// containing a startup hook, and call each hook in turn.privatestaticvoidProcessStartupHooks(){string?startupHooksVariable=AppContext.GetData("STARTUP_HOOKS")asstring;...// Parse startup hooks variablestring[]startupHookParts=startupHooksVariable.Split(Path.PathSeparator);StartupHookNameOrPath[]startupHooks=newStartupHookNameOrPath[startupHookParts.Length];for(inti=0;i<startupHookParts.Length;i++){stringstartupHookPart=startupHookParts[i];...if(Path.IsPathFullyQualified(startupHookPart)){startupHooks[i].Path=startupHookPart;}else{// The intent here is to only support simple assembly names, but AssemblyName .ctor accepts// lot of other forms (fully qualified assembly name, strings which look like relative paths and so on).// So add a check on top which will disallow any directory separator, space or comma in the assembly name.for(intj=0;j<disallowedSimpleAssemblyNameChars.Length;j++){if(startupHookPart.Contains(disallowedSimpleAssemblyNameChars[j])){thrownewArgumentException(SR.Format(SR.Argument_InvalidStartupHookSimpleAssemblyName,startupHookPart));}}if(startupHookPart.EndsWith(DisallowedSimpleAssemblyNameSuffix,StringComparison.OrdinalIgnoreCase)){thrownewArgumentException(SR.Format(SR.Argument_InvalidStartupHookSimpleAssemblyName,startupHookPart));}try{// This will throw if the string is not a valid assembly name.startupHooks[i].AssemblyName=newAssemblyName(startupHookPart);}catch(ExceptionassemblyNameException){thrownewArgumentException(SR.Format(SR.Argument_InvalidStartupHookSimpleAssemblyName,startupHookPart),assemblyNameException);}}}// Call each hook in turnforeach(StartupHookNameOrPathstartupHookinstartupHooks){CallStartupHook(startupHook);}}// Load the specified assembly, and call the specified type's// "static void Initialize()" method. [RequiresUnreferencedCode("The StartupHookSupport feature switch has been enabled for this app which is being trimmed. " +
"Startup hook code is not observable by the trimmer and so required assemblies, types and members may be removed")]privatestaticvoidCallStartupHook(StartupHookNameOrPathstartupHook){Assemblyassembly;try{if(startupHook.Path!=null){Debug.Assert(Path.IsPathFullyQualified(startupHook.Path));assembly=AssemblyLoadContext.Default.LoadFromAssemblyPath(startupHook.Path);}elseif(startupHook.AssemblyName!=null){Debug.Assert(startupHook.AssemblyName!=null);assembly=AssemblyLoadContext.Default.LoadFromAssemblyName(startupHook.AssemblyName);}...Typetype=assembly.GetType(StartupHookTypeName,throwOnError:true)!;// Look for a static method without any parametersMethodInfo?initializeMethod=type.GetMethod(InitializeMethodName,BindingFlags.Public|BindingFlags.NonPublic|BindingFlags.Static,null,// use default binderType.EmptyTypes,// parametersnull);// no parameter modifiers...initializeMethod.Invoke(null,null);}}}