About Me

My photo
Kozyatağı, İstanbul, Türkiye

Sunday, September 29, 2013

Compare IL Content of .dll and .exe Files (Assemblies)


In the programs we develop, sometimes we need to be sure if versions of the compiled code (.dll, .exe)  we wrote are equivalent of each other on various environments.

To concretize the scenario, let's consider this:
In enterprise solutions, generally there are many environments prior to production environment. In my case, these environmets are:

-- DEV: Development.
-- UAT: User Acceptance Test.
-- PREPROD: Pre-Production.
-- PROD: Production. (LIVE)

At the source control side, each of them has corresponding branch. (In my case, I have branches in TFS).
After developer is finished with development phase, he performs merge operations on branches in order to make his code tested and running on target environment.

Sometimes, developer cannot be sure if the running code is the same as he merged. So he needs to check, if the running assembly (.dll, .exe) version is the same as he coded, or some problem occured while merging the branches.

To smooth away these kind of suspicion, contents of assemblies can be compared to check if they are equal.
In a "headlong" way, if such a comparison is performed in bitwise manner, the result of the comparison will be incorrect. Because, compiler may put some code-irrelevant details into the assembly at the moment of compilation, which should not affect the result of comparison. For example, CSharp compiler injects date and time of build into the dll. These kind of details does NOT affect the code running in dll.

The comparison program should compare equality of running code in assembly, and only this.
So, the only way to check: Inspecting MSIL (Microsoft Intermediate Language), or IL in short, in the assembly, which is the output of compiler and input of JIT compiler.

We can retrieve IL content of an assembly through ildasm.exe, Microsoft IL disassembler tool which is shipped with Visual Studio.

After IL content is written to a text file, the program we write can compare to generated IL's line by line. If any line is different, then we can say these two assemblies are different.

Some points to consider:
-- The IL output of ildasm.exe contains comment lines. These lines need to be removed before comparison.
-- The compiler may inject GUID attributes to some types. So, the user may need to have an option of ignoring Guid attributes.

Steps of comparison
-- Call ildasm.exe to generate IL content and redirect the output text file
-- When .exe terminates, read text file newly created.
-- While reading the text file, line by line, remove comments by Regexy (//.*)
-- Merge lines and remove guid attributes with regex.
-- Compare two strings. If they are equal, then IL contents in asssemblies are equal.


Code:
        string GetIL_ContentOfAssembly(string assemblyPath, bool ignoreGuidAttribute)
        {
            string tmpFileNameWithoutExt = Guid.NewGuid().ToString();
            string tempFileNameIL = string.Format("TmpIL_{0}.il", tmpFileNameWithoutExt);
            StoreIL_InTextFile(assemblyPath, tempFileNameIL);
            StringBuilder sb = new StringBuilder();
            using (StreamReader sr = new StreamReader(tempFileNameIL))
            {
                string str = null;
                while ((str = sr.ReadLine()) != null)
                {
                    str = RemoveComments(str);
                    sb.AppendLine(str);
                }
            }
            string res = sb.ToString();
            if (ignoreGuidAttribute)
            {
                res = RemoveGuidAttributes(res);
            }
            return res;
        }


        string RemoveComments(string input)
        {
            return Regex.Replace(input,
                                 @"//.*",
                                 string.Empty);
        }

        string RemoveGuidAttributes(string input)
        {
            return Regex.Replace(input,
                                 @"(\.custom instance void \[mscorlib\]System\.Runtime\.InteropServices\.GuidAttribute::\.ctor\(string\) = \().[^.]*",
                                 string.Empty,
                                 RegexOptions.Multiline);
        }

        void StoreIL_InTextFile(string assemblyPath, string tempFileName)
        {
            ProcessStartInfo info = new ProcessStartInfo
            {
                FileName = ildasmPath,
                Arguments = string.Format("\"{0}\" /output:{1}", assemblyPath, tempFileName),
                CreateNoWindow = true,
            };
            Process proc = new Process();
            proc.StartInfo = info;
            proc.Start();
            proc.WaitForExit();
        }

The ildasmPath is read from .config file in this example, which is set to: C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\ildasm.exe

You can dowlonad the solution here

Sunday, September 22, 2013

Rule Expression User Input Validation

If you are developing a business rule engine; your application needs to have an admin page that rules are defined through.

For example, imagine we are creating campaign rules:
"If the sum of the amounts of customers transactions within last 24 hours exceeds 10.000 or count exceeds 10 then send him an SMS"

In the rule definition pane, admin user inputs an expression like: "(R1 || R2) && R3"
Rule expressions are generally written in a free text format by the user.  Because user may insert parentheses, group rules and combine them.

The input of user has to be validated before saving it. Because syntax of such an expression above may be incorrect, may have missing parentheses, etc.

The easiest and effortless way of performing this validation is, using JavaScript eval() function:

function IsValidRuleText() {
    var txt = $('#txtInput').val();
    if (txt.length == 0) {
        return false;
    }
    var replaced = txt.replace(/R\d+/gi, "true");
    try {
        if (eval(replaced) == true) {
            // Trivial Comparison. Just for test if throws error.
        }
    } catch (err) {
        return false;
    }
    return true;
}

All the numbered [R] expressions are determined with regex. Then regex matches are replaced with "true" string. Now, final string contains "true" words and punctuation characters ((, ), &&, || ). If the "eval()" function successfully evaluates the replaced expression, then the expression is valid. If it throws error, then the expression has syntax errors.

Counting Records Effectively: Reducing Redundant Counting

Sometimes, we need to query database for retrieving count of records that satistfies some condition.
If the count, which is returned from query, is used for comparing to a constant; there is no need to count all of the records.

Suppose that, you have a table in Sql Server as follows:

CREATE TABLE [Users]
(
   [Id]             [BIGINT],
   [Name]           [VARCHAR](100),
   [RegistrationDate] [DATETIME]
) 


And your application code needs to know if "the Count of users whose registration day is >= 2013-01-01, is more than 100 "

 The first query coming to mind is:

SELECT COUNT(*)
FROM   Users U
WHERE  U.RegistrationDate >= '20130101' 


Suppose, you have millions of users. The query above makes you check all, just for comparing to 100, which is a redundant activity.

Now, I am changing it to:

DECLARE @N INT = 100

SELECT COUNT(*)
FROM   (SELECT TOP(@N + 1) 1 AS CNT
        FROM   Users U
        WHERE  U.RegistrationDate >= '20130101')

X 


Now, Counting terminates at server side as soon as 101 records encountered.

The main idea here is that, if I know that the value is going to be compared to 100, so I do NOT need to count if I reach 101 records. Because all the numbers greater than 101 will not affect the result of comparison.