Question

I'm looking to do some cleanup of a legacy ASP.Net web application, and the web.config file is cluttered with a large number of seemingly unused appSettings. Unfortunately a number of referenced third-party class libraries are dependent on some of these configuration values, so any cleanup carries the rsik that the application will fail somewhere down the road.

Is there a way to "go exploring" through a set of .dll files to determine which appSettings they may refer to? I would use this to determine which keys are missing and which keys are not referenced.

-Sigurd

Was it helpful?

Solution 2

Try using ILSpy, reflect into the DLLs and search for the values. This will be time consuming though

OTHER TIPS

You can use Mono.Cecil which makes it easier to go through the IL code in assemblies. With it you can find all calls to ConfigurationManager.AppSettings and then add them to a list or something.

Finally go through that list and check that only strings are used (so that no library have wrapped the configuration manager in another class).

Cecil can be found here: http://www.mono-project.com/Cecil

Working example:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using Mono.Cecil;
using Mono.Cecil.Cil;

namespace ConsoleApplication20
{
    internal class Program
    {
        public const string SomOtherKey = "hey";

        public static void Main(string[] args)
        {
            var key1 = "max1";
            var key2 = "max2";
            var key3 = "max3";
            var key4 = "max4";
            var key5 = "max5";
            Console.WriteLine(ConfigurationManager.AppSettings["hello"]);
            Console.WriteLine(ConfigurationManager.AppSettings[key2]);
            Console.WriteLine(ConfigurationManager.AppSettings[key4]);
            Console.WriteLine(ConfigurationManager.AppSettings[key1]);
            Console.WriteLine(ConfigurationManager.AppSettings[key3]);
            Console.WriteLine(ConfigurationManager.AppSettings[key5]);
            Console.WriteLine(ConfigurationManager.AppSettings[SomOtherKey]);

            var dlls = Directory.GetFiles(Environment.CurrentDirectory, @"*.exe");
            foreach (var dll in dlls)
            {
                var module = ModuleDefinition.ReadModule(dll);

                foreach (var type in module.Types)
                {
                    foreach (var method in type.Methods)
                    {
                        FindConfigurationManager(method);
                    }
                }
            }

            Console.ReadLine();
        }


        public static void FindConfigurationManager(MethodDefinition method)
        {
            for (var i = 0; i < method.Body.Instructions.Count; i++)
            {
                var instruction = method.Body.Instructions[i];
                if (instruction.OpCode == OpCodes.Call)
                {
                    var methodCall = instruction.Operand as MethodReference;
                    if (methodCall != null && methodCall.Name == "get_AppSettings")
                    {
                        var nextInstruction = method.Body.Instructions[i + 1];
                        var variable = "";

                        if (nextInstruction.OpCode == OpCodes.Ldloc_0)
                            variable = FindString(method.Body.Instructions, 0);
                        else if (nextInstruction.OpCode == OpCodes.Ldloc_1)
                            variable = FindString(method.Body.Instructions, 1);
                        else if (nextInstruction.OpCode == OpCodes.Ldloc_2)
                            variable = FindString(method.Body.Instructions, 2);
                        else if (nextInstruction.OpCode == OpCodes.Ldloc_3)
                            variable = FindString(method.Body.Instructions, 3);
                        else if (nextInstruction.OpCode == OpCodes.Ldloc_S)
                            variable = FindString(method.Body.Instructions, ((VariableReference) nextInstruction.Operand).Index);
                        else
                            variable = nextInstruction.Operand.ToString();

                        // next argument is a string value
                        Console.WriteLine("\t" + variable);
                    }
                }
            }
        }

        private static string FindString(IEnumerable<Instruction> instructions, int index)
        {
            var current = -1;
            foreach (var instruction in instructions)
            {
                if (instruction.OpCode != OpCodes.Ldstr)
                    continue;

                current++;
                if (current == index)
                    return instruction.Operand.ToString();
            }

            return "not found";
        }


    }
}

Output:

enter image description here

Disclaimer: I have not done anything like this before. The example solution is probably not the best, but it do work for some cases :) You might need to expand it.

how it works

I've just figured this out when answering this question, so what I write is maybe not 100% accurate. There are two different ways if accessing keys in the configuration manager.

The first one is by hard coding the string. In that case it's pretty easy since the ldstr (load string) instruction is used directly as the next instruction after the method reference. That's why we can do nextInstruction.Operand.ToString(); to load it.

The other way is to load the key name using a variable. In this case it's a bit trickier since there are several instructions which can be used to load a variable. Hence we need all those if's to be able to get the string value. What I do is to go through the entire method instruction set and simply count the number of ldstr instructions until I get the same index is being used in the method call.

Do note that I'm unsure if the second alternative will work in all cases.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top