• Kirill Osenkov

    How to live forever (if you’re a CLR object)

    • 2 Comments

    Just subscribe to a static event, or an event of a long-lived object (such as a singleton instance). That long lived object will keep alive all objects that subscribed to any of its events (including you).

  • Kirill Osenkov

    Updated C# all-in-one file

    • 20 Comments

    Thanks to everyone who came up with suggestions on my Did I miss any C# syntax construct? post.

    The updated file is below.

    #error Error message
    #warning Warning message
    #pragma warning disable 414, 3021
    #pragma warning restore 3021
    #line 6
    #line 2 "test.cs"
    #line default
    #line hidden
    #define foo
    #if foo
    #else
    #endif
    #undef foo

    extern alias Foo;

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using M = System.Math;

    #if DEBUG || TRACE
    using System.Diagnostics;
    #elif SILVERLIGHT
    using System.Diagnostics;
    #else
    using System.Diagnostics;
    #endif

    #region Region

    #region more
    using ConsoleApplication2.Test;
    #endregion
    using X = int1;
    using X = ABC.X<int>;

    #endregion

    [assembly: System.Copyright(@"(C) 2009")]
    [module: System.Copyright("\n\t\u0123(C) 2009" + "\u0123")]

    class TopLevelType : IDisposable
    {
        void IDisposable.Dispose() { }
    }

    namespace My
    {
        using A.B;

        interface CoContra<out T, in K> { }
        delegate void CoContra2<out T, in K> () where T : struct;

        public unsafe partial class A : C, I
        {
            [method: Obsolete]
            public A([param: Obsolete] int foo) :
                base(1)
            {
            L:
                {
                    int i = sizeof(int);
                    ++i;
                }

    #if DEBUG
          Console.WriteLine(export.iefSupplied.command);
    #endif
                const int? local = int.MaxValue;
                const Guid? local0 = new Guid(r.ToString());

                var привет = local;
                var мир = local;
                var local3 = 0, local4 = 1;
                local3 = local4 = 1;
                var local5 = null as Action ?? null;
                var local6 = local5 is Action;

                var u = 1u;
                var U = 1U;
                long hex = 0xBADC0DE, Hex = 0XDEADBEEF, l = -1L, L = 1L, l2 = 2l;
                ulong ul = 1ul, Ul = 1Ul, uL = 1uL, UL = 1UL,
                    lu = 1lu, Lu = 1Lu, lU = 1lU, LU = 1LU;

                bool @bool;
                byte @byte;
                char @char = 'c', \u0066 = '\u0066', hexchar = '\x0130', hexchar2 = (char)0xBAD;
                string \U00000065 = "\U00000065";
                decimal @decimal = 1.44M;
                dynamic @dynamic;
                double @double = M.PI;
                float @float = 1.2f;
                int @int = local ?? -1;
                long @long;
                object @object;
                sbyte @sbyte;
                short @short;
                string @string = @"""/*";
                uint @uint;
                ulong @ulong;
                ushort @ushort;
               
                dynamic dynamic = local5;
                var add = 0;
                var ascending = 0;
                var descending = 0;
                var from = 0;
                var get = 0;
                var global = 0;
                var group = 0;
                var into = 0;
                var join = 0;
                var let = 0;
                var orderby = 0;
                var partial = 0;
                var remove = 0;
                var select = 0;
                var set = 0;
                var value = 0;
                var var = 0;
                var where = 0;
                var yield = 0;

                if (i > 0)
                {
                    return;
                }
                else if (i == 0)
                {
                    throw new Exception();
                }
                var o1 = new MyObject();
                var o2 = new MyObject(var);
                var o3 = new MyObject { A = i };
                var o4 = new MyObject(@dynamic)
                {
                    A = 0,
                    B = 0,
                    C = 0
                };
                var o5 = new { A = 0 };
                var dictionaryInitializer = new Dictionary<int, string>
                {
                    {1, ""},
                    {2, "a"}
                };
                float[] a = new float[]
                {
                    0f,
                    1.1f
                };
                int[] arrayTypeInference = new[] { 0, 1, };
                switch (i)
                {
                    case 1:
                        {
                            goto case 2;
                        }
                    case 2:
                        {
                            goto default;
                            break;
                        }
                    default:
                        {
                            return;
                        }
                }
                while (i < 10)
                {
                    ++i;
                }
                do
                {
                    ++i;
                }
                while (i < 10);
                for (int j = 0; j < 100; ++j)
                {
                    Console.WriteLine(j);
                }
                foreach (var i in Items())
                {
                    if (i == 7)
                        return;
                    else
                        continue;
                }
                checked
                {
                    checked(++i);
                }
                unchecked
                {
                    unchecked(++i);
                }
                lock (sync)
                    process();
                using (var v = BeginScope())
                using (A a = new A())
                using (BeginScope())
                    return;
                yield return this.items[3];
                yield break;
                fixed (int* p = stackalloc int[100])
                {
                    *intref = 1;
                }
                unsafe
                {
                    int* p = null;
                }
                try
                {
                    throw null;
                }
                catch (System.AccessViolationException av)
                {
                    throw av;
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                    try { } catch { }
                }
                var anonymous =
                {
                    A = 1,
                    B = 2,
                    C = 3,
                };
                var query = from c in customers
                            let d = c
                            where d != null
                            join c1 in customers on c1.GetHashCode() equals c.GetHashCode()
                            join c1 in customers on c1.GetHashCode() equals c.GetHashCode() into e
                            group c by c.Country
                                into g
                                orderby g.Count() ascending
                                orderby g.Key descending
                                select new { Country = g.Key, CustCount = g.Count() };
            }
            ~A()
            {
            }
            private readonly int f1;
            [Obsolete]
            [NonExisting]
            [Foo::NonExisting(var, 5)]
            [CLSCompliant(false)]
            [Obsolete, System.NonSerialized, NonSerialized, CLSCompliant(true || false & true)]
            private volatile int f2;
            [return: Obsolete]
            [method: Obsolete]
            public void Handler(object value)
            {
            }
            public int m<T>(T t)
              where T : class, new()
            {
                base.m(t);
                return 1;
            }
            public string P
            {
                get
                {
                    return "A";
                }
                set;
            }
            public abstract string P
            {
                get;
            }
            public abstract int this[int index]
            {
                protected internal get;
                internal protected set;
            }
            [method: Obsolete]
            [field: Obsolete]
            [event: Obsolete]
            public readonly event Event E;
            [event: Test]
            public event Action E1
            {
                [Obsolete]
                add { value = value; }
                [Obsolete]
                [return: Obsolete]
                remove { }
            }
            public static A operator +(A first, A second)
            {
                Delegate handler = new Delegate(Handler);
                return first.Add(second);
            }
            [method: Obsolete]
            [return: Obsolete]
            public static bool operator true(A a)
            {
                return true;
            }
            public static bool operator false(A a)
            {
                return false;
            }
            class C
            {
            }
        }
        public struct S : I
        {
            public S()
            {
            }
            private int f1;
            [Obsolete]
            private volatile int f2;
            public abstract int m<T>(T t)
              where T : struct
            {
                return 1;
            }
            public string P
            {
                get
                {
                    int value = 0;
                    return "A";
                }
                set;
            }
            public abstract string P
            {
                get;
            }
            public abstract int this[int index]
            {
                get;
                internal protected set;
            }
            public event Event E;
            public static A operator +(A first, A second)
            {
                return first.Add(second);
            }
            fixed int field[10];
            class C
            {
            }
        }
        public interface I
        {
            void A(int value);
            string Value
            {
                get;
                set;
            }
        }
        [type: Flags]
        public enum E
        {
            A,
            B = A,
            C = 2 + A,

    #if DEBUG
        D,
    #endif

        }
        public delegate void Delegate(object P);
        namespace Test
        {
            using System;
            using System.Collections;
            public class Список
            {
                public static IEnumerable Power(int number, int exponent)
                {
                    Список Список = new Список();
                    Список.Main();
                    int counter = 0;
                    int אתר = 0;
                    while (++counter++ < --exponent--)
                    {
                        result = result * number + +number+++++number;
                        yield return result;
                    }
                }
                static void Main()
                {
                    foreach (int i in Power(2, 8))
                    {
                        Console.Write("{0} ", i);
                    }
                }
            }
        }
    }

    namespace ConsoleApplication1
    {
        namespace RecursiveGenericBaseType
        {
            class A<T> : B<A<T>, A<T>>
            {
                protected virtual A<T> M() { }
                protected abstract B<A<T>, A<T>> N() { }
                static B<A<T>, A<T>> O() { }
            }

            sealed class B<T1, T2> : A<B<T1, T2>>
            {
                protected override A<T> M() { }
                protected sealed override B<A<T>, A<T>> N() { }
                new static A<T> O() { }
            }
        }

        namespace Boo
        {
            public class Bar<T> where T : IComparable
            {
                public T f;
                public class Foo<U> : IEnumerable<T>
                {
                    public void Method<K, V>(K k, T t, U u)
                        where K : IList<V>, IList<T>, IList<U>
                        where V : IList<K>
                    {
                        A<int> a;
                    }
                }
            }
        }

        class Test
        {
            void Bar3()
            {
                var x = new Boo.Bar<int>.Foo<object>();
                x.Method<string, string>(" ", 5, new object());

                var q = from i in new int[] { 1, 2, 3, 4 }
                        where i > 5
                        select i;
            }

            public static implicit operator Test(string s)
            {
                return new ConsoleApplication1.Test();
            }
            public static explicit operator Test(string s)
            {
                return new Test();
            }

            public int foo = 5;
            void Bar2()
            {
                foo = 6;
                this.Foo = 5.GetType(); Test t = "sss";
            }

            public event EventHandler MyEvent = delegate { };

            void Blah()
            {
                int i = 5;
                int? j = 6;

                Expression<Func<int>> e = () => i;
                Expression<Func<bool, Action>> e2 = b => () => { return; };
                Func<bool, bool> f = delegate (bool a)
                {
                    return !a;
                };
                Action a = Blah;
            }

            public Type Foo
            {
                [Obsolete("Name", error = false)]
                get
                {
                    return typeof(IEnumerable<>);
                }
                set
                {
                    var t = typeof(System.Int32);
                    t.ToString();
                    t = value;
                }
            }

            public void Constants()
            {
                int i = 1 + 2 + 3 + 5;
                global::System.String s = "a" + (System.String)"a" + "a" + "a" + "a" + "A";
            }

            public void ConstructedType()
            {
                List<int> i = null;
                int c = i.Count;
            }
        }
    }

    namespace Comments.XmlComments.UndocumentedKeywords
    {
        /// <summary>
        /// Whatever
        /// </summary>
        /// <!-- c -->
        /// <![CDATA[c]]> //
        /// <c></c> /* */
        /// <code></code>
        /// <example></example>
        /// <exception cref="bla"></exception>
        /// <include file='' path='[@name=""]'/>
        /// <permission cref=" "></permission>
        /// <remarks></remarks>
        /// <see cref=""/>
        /// <seealso cref=" "/>
        /// <value></value>
        /// <typeparam name="T"></typeparam>
        class /*///*/C<T>
        {
            void M<U>(T t, U u)
            {
                // comment
                /* *** / */
                /* //
                 */
                /*s*///comment
                // /***/
                /*s*/int /*s*/intValue = 0;
                intValue = intValue /*s*/+ 1;
                string strValue = /*s*/"hello";
                /*s*/MyClass c = new MyClass();
                string verbatimStr = /*s*/@"\\\\";
            }
        }

        //General Test F. Type a very long class name, verify colorization happens correctly only upto the correct size (118324)
        class TestClassXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/*Scen8*/{ }

        class TestClassXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX22/*Scen9*/{ }

        class yield
        {
            void Foo<U>(__arglist)
            {
                C<U> c = null;
                c.M<int>(5, default(U));
                TypedReference tr = __makeref(c);
                Type t = __reftype(tr);
                int j = __refvalue(tr, int);
                Params(a: t, b: t);
            }
            void Params(ref dynamic a, out dynamic b, params dynamic[] c) {}
            void Params(out dynamic a = 2, ref dynamic c = default(dynamic), params dynamic[][] c) {}

            public override string ToString() { return base.ToString(); }

            public partial void OnError();

            public partial void method()
            {
                int?[] a = new int?[5];/*[] bug*/ // YES []
                int[] var = { 1, 2, 3, 4, 5 };/*,;*/
                int i = a[i];/*[]*/
                Foo<T> f = new Foo<int>();/*<> ()*/
                f.method();/*().*/
                i = i + i - i * i / i % i & i | i ^ i;/*+ - * / % & | ^*/
                bool b = true & false | true ^ false;/*& | ^*/
                b = !b;/*!*/
                i = ~i;/*~i*/
                b = i < i && i > i;/*< && >*/
                int? ii = 5;/*? bug*/ // NO ?
                int f = true ? 1 : 0;/*? :*/   // YES :
                i++;/*++*/
                i--;/*--*/
                b = true && false || true;/*&& ||*/
                i << 5;/*<<*/
                i >> 5;/*>>*/
                b = i == i && i != i && i <= i && i >= i;/*= == && != <= >=*/
                i += 5.0;/*+=*/
                i -= i;/*-=*/
                i *= i;/**=*/
                i /= i;/*/=*/
                i %= i;/*%=*/
                i &= i;/*&=*/
                i |= i;/*|=*/
                i ^= i;/*^=*/
                i <<= i;/*<<=*/
                i >>= i;/*>>=*/
                object s = x => x + 1;/*=>*/
                Point point;
                unsafe
                {
                    Point* p = &point;/** &*/
                    p->x = 10;/*->*/
                }
                IO::BinaryReader br = null;
            }

            struct Point { public int X; public int Y; }
        }
  • Kirill Osenkov

    New IDE Features in Visual Studio 2010 for C# Developers

    • 1 Comments

    Check out this great overview about the new C# IDE features in Visual Studio 2010:

    http://blogs.msdn.com/csharpfaq/archive/2010/05/10/new-ide-features-in-visual-studio-2010-for-c-developers.aspx

    Before you go there, a little pop quiz to check if you already know and use these shortcuts. What do they do?

    1. Ctrl+,
    2. Ctrl+.
    3. Ctrl+Alt+Space
    4. Ctrl+Shift+Up/Ctrl+Shift+Down
    5. Ctrl+K,T
  • Kirill Osenkov

    Did I miss any C# syntax construct?

    • 14 Comments

    Dear readers, I’m asking for your valuable input again.

    I went through some of our test files and combined a single “all-in-one” C# syntax file that I want to contain all C# 4.0 constructs. I’m specifically not looking for combinations (because there’s an infinite number, obviously), it’s enough to just mention each syntax construct once.

    So, is there anything I’m missing? (Sorry, it’s a huge junkyard!)

    #error Error message
    #warning Warning message
    #pragma warning disable 414, 3021
    #pragma warning restore 3021
    #line 6
    #define foo
    #if foo
    #else
    #endif
    #undef foo
    //#warning foo
    //#error foo

    extern alias Foo;

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using M = System.Math;

    #if DEBUG || TRACE
    using System.Diagnostics;
    #elif SILVERLIGHT
    using System.Diagnostics;
    #else
    using System.Diagnostics;
    #endif

    #region Region

    #region more
    using ConsoleApplication2.Test;
    #endregion
    using X = int1;
    using X = ABC.X<int>;

    #endregion

    [assembly: System.Copyright(@"(C) 2009")]
    [module: System.Copyright("\n\t\u0123(C) 2009" + "\u0123")]

    namespace My
    {
        using A.B;

        interface CoContra<out T, in K> { }
        delegate void CoContra2<out T, in K> () where T : struct;

        public unsafe partial class A : C, I
        {
            [method: Obsolete]
            public A([param: Obsolete] int foo) :
                base(1)
            {
            L:
                {
                    int i = sizeof(int);
                    ++i;
                }

    #if DEBUG
          Console.WriteLine(export.iefSupplied.command);
    #endif
                const int? local = int.MaxValue;
                const Guid? local0 = new Guid(r.ToString());

                var привет = local;
                var мир = local;
                var local3 = 0, local4 = 1;
                var local5 = null as Action ?? null;
                var local6 = local5 is Action;

                var u = 1u;
                var U = 1U;
                long hex = 0xBADC0DE, Hex = 0XDEADBEEF, l = -1L, L = 1L;
                ulong ul = 1ul, Ul = 1Ul, uL = 1uL, UL = 1UL,
                    lu = 1lu, Lu = 1Lu, lU = 1lU, LU = 1LU;

                bool @bool;
                byte @byte;
                char @char = 'c', \u0066 = '\u0066', hexchar = '\x0130', hexchar2 = (char)0xBAD;
                decimal @decimal = 1.44M;
                dynamic @dynamic;
                double @double = M.PI;
                float @float;
                int @int = local ?? -1;
                long @long;
                object @object;
                sbyte @sbyte;
                short @short;
                string @string = @"""/*";
                uint @uint;
                ulong @ulong;
                ushort @ushort;
               
                dynamic dynamic = local5;
                var add = 0;
                var ascending = 0;
                var descending = 0;
                var from = 0;
                var get = 0;
                var global = 0;
                var group = 0;
                var into = 0;
                var join = 0;
                var let = 0;
                var orderby = 0;
                var partial = 0;
                var remove = 0;
                var select = 0;
                var set = 0;
                var value = 0;
                var var = 0;
                var where = 0;
                var yield = 0;

                if (i > 0)
                {
                    return;
                }
                else if (i = 0)
                {
                    throw new Exception();
                }
                var o1 = new MyObject();
                var o2 = new MyObject(var);
                var o3 = new MyObject { A = i };
                var o4 = new MyObject(@dynamic)
                {
                    A = 0,
                    B = 0,
                    C = 0
                };
                var o5 = new { A = 0 };
                int[] a = new int[]
          {
            0,
            1,
            2,
            3,
            4,
            5
          };
                switch (i)
                {
                    case 1:
                        {
                            goto case 2;
                        }
                    case 2:
                        {
                            goto default;
                            break;
                        }
                    default:
                        {
                            return;
                        }
                }
                while (i < 10)
                {
                    ++i;
                }
                do
                {
                    ++i;
                }
                while (i < 10);
                for (int j = 0; j < 100; ++j)
                {
                    Console.WriteLine(j);
                }
                foreach (var i in Items())
                {
                    if (i == 7)
                        return;
                    else
                        continue;
                }
                checked
                {
                    checked(++i);
                }
                unchecked
                {
                    unchecked(++i);
                }
                lock (sync)
                    process();
                using (var v = BeginScope())
                using (A a = new A())
                using (BeginScope())
                    return;
                yield return this.items[3];
                yield break;
                fixed (int* p = stackalloc int[100])
                {
                    *intref = 1;
                }
                unsafe
                {
                    int* p = null;
                }
                try
                {
                    throw null;
                }
                catch (System.AccessViolationException av)
                {
                    throw av;
                }
                catch (Exception)
                {
                    throw;
                }
                finally
                {
                }
                var anonymous =
                {
                    A = 1,
                    B = 2,
                    C = 3,
                };
                var query = from c in customers
                            let d = c
                            where d != null
                            join c1 in customers on c1.GetHashCode() equals c.GetHashCode()
                            join c1 in customers on c1.GetHashCode() equals c.GetHashCode() into e
                            group c by c.Country
                                into g
                                orderby g.Count() ascending
                                orderby g.Key descending
                                select new { Country = g.Key, CustCount = g.Count() };
            }
            ~A()
            {
            }
            private readonly int f1;
            [Obsolete]
            [NonExisting]
            [Foo::NonExisting(var, 5)]
            [CLSCompliant(false)]
            [Obsolete, System.NonSerialized, NonSerialized, CLSCompliant(true || false & true)]
            private volatile int f2;
            [return: Obsolete]
            [method: Obsolete]
            public void Handler(object value)
            {
            }
            public int m<T>(T t)
              where T : class, new()
            {
                base.m(t);
                return 1;
            }
            public string P
            {
                get
                {
                    return "A";
                }
                set;
            }
            public abstract string P
            {
                get;
            }
            public abstract int this[int index]
            {
                protected internal get;
                internal protected set;
            }
            [method: Obsolete]
            [field: Obsolete]
            [event: Obsolete]
            public readonly event Event E;
            [event: Test]
            public event Action E1
            {
                add { value = value; }
                remove { }
            }
            public static A operator +(A first, A second)
            {
                Delegate handler = new Delegate(Handler);
                return first.Add(second);
            }
            [method: Obsolete]
            [return: Obsolete]
            public static bool operator true(A a)
            {
                return true;
            }
            public static bool operator false(A a)
            {
                return false;
            }
            class C
            {
            }
        }
        public struct S : I
        {
            public S()
            {
            }
            private int f1;
            [Obsolete]
            private volatile int f2;
            public abstract int m<T>(T t)
              where T : struct
            {
                return 1;
            }
            public string P
            {
                get
                {
                    int value = 0;
                    return "A";
                }
                set;
            }
            public abstract string P
            {
                get;
            }
            public abstract int this[int index]
            {
                get;
                internal protected set;
            }
            public event Event E;
            public static A operator +(A first, A second)
            {
                return first.Add(second);
            }
            fixed int field[10];
            class C
            {
            }
        }
        public interface I
        {
            void A(int value);
            string Value
            {
                get;
                set;
            }
        }
        [type: Flags]
        public enum E
        {
            A,
            B = A,
            C = 2 + A,

    #if DEBUG
        D,
    #endif

        }
        public delegate void Delegate(object P);
        namespace Test
        {
            using System;
            using System.Collections;
            public class Список
            {
                public static IEnumerable Power(int number, int exponent)
                {
                    Список Список = new Список();
                    Список.Main();
                    int counter = 0;
                    int אתר = 0;
                    while (++counter++ < --exponent--)
                    {
                        result = result * number + +number+++++number;
                        yield return result;
                    }
                }
                static void Main()
                {
                    foreach (int i in Power(2, 8))
                    {
                        Console.Write("{0} ", i);
                    }
                }
            }
        }
    }

    namespace ConsoleApplication1
    {
        namespace RecursiveGenericBaseType
        {
            class A<T> : B<A<T>, A<T>>
            {
                protected virtual A<T> M() { }
                protected abstract B<A<T>, A<T>> N() { }
                static B<A<T>, A<T>> O() { }
            }

            class B<T1, T2> : A<B<T1, T2>>
            {
                protected override A<T> M() { }
                protected sealed override B<A<T>, A<T>> N() { }
                new static A<T> O() { }
            }
        }

        namespace Boo
        {
            public class Bar<T> where T : IComparable
            {
                public T f;
                public class Foo<U> : IEnumerable<T>
                {
                    public void Method<K, V>(K k, T t, U u)
                        where K : IList<V>, IList<T>, IList<U>
                        where V : IList<K>
                    {
                        A<int> a;
                    }
                }
            }
        }

        class Test
        {
            void Bar3()
            {
                var x = new Boo.Bar<int>.Foo<object>();
                x.Method<string, string>(" ", 5, new object());

                var q = from i in new int[] { 1, 2, 3, 4 }
                        where i > 5
                        select i;
            }

            public static implicit operator Test(string s)
            {
                return new ConsoleApplication1.Test();
            }
            public static explicit operator Test(string s)
            {
                return new Test();
            }

            public int foo = 5;
            void Bar2()
            {
                foo = 6;
                this.Foo = 5.GetType(); Test t = "sss";
            }

            void Blah()
            {
                int i = 5;
                int? j = 6;

                Expression<Func<int>> e = () => i;
            }

            public Type Foo
            {
                get
                {
                    return typeof(System.Int32);
                }
                set
                {
                    var t = typeof(System.Int32);
                    t.ToString();
                    t = value;
                }
            }

            public void Constants()
            {
                int i = 1 + 2 + 3 + 5;
                global::System.String s = "a" + (System.String)"a" + "a" + "a" + "a" + "A";
            }

            public void ConstructedType()
            {
                List<int> i = null;
                int c = i.Count;
            }
        }
    }

    namespace Comments.XmlComments.UndocumentedKeywords
    {
        /// <summary>
        /// Whatever
        /// </summary>
        /// <!-- c -->
        /// <![CDATA[c]]> //
        /// <c></c> /* */
        /// <code></code>
        /// <example></example>
        /// <exception cref="bla"></exception>
        /// <include file='' path='[@name=""]'/>
        /// <permission cref=" "></permission>
        /// <remarks></remarks>
        /// <see cref=""/>
        /// <seealso cref=" "/>
        /// <value></value>
        /// <typeparam name="T"></typeparam>
        class /*///*/C<T>
        {
            void M<U>(T t, U u)
            {
                // comment
                /* *** / */
                /* //
                 */
                /*s*///comment
                /*s*/int /*s*/intValue = 0;
                intValue = intValue /*s*/+ 1;
                string strValue = /*s*/"hello";
                /*s*/MyClass c = new MyClass();
                string verbatimStr = /*s*/@"\\\\";
            }
        }

        //General Test F. Type a very long class name, verify colorization happens correctly only upto the correct size (118324)
        class TestClassXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/*Scen8*/{ }

        class TestClassXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX22/*Scen9*/{ }

        class yield
        {
            void Foo<U>(__arglist)
            {
                C<U> c = null;
                c.M<int>(5, default(U));
                TypedReference tr = __makeref(c);
                Type t = __reftype(tr);
                int j = __refvalue(tr, int);
                Params(a: t, b: t);
            }
            void Params(ref dynamic a, out dynamic b, params dynamic[] c) {}
            void Params(out dynamic a = 2, ref dynamic c = default(dynamic), params dynamic[][] c) {}

            public override string ToString() { return base.ToString(); }

            public partial void method()
            {
                int?[] a = new int?[5];/*[] bug*/ // YES []
                int[] var = { 1, 2, 3, 4, 5 };/*,;*/
                int i = a[i];/*[]*/
                Foo<T> f = new Foo<int>();/*<> ()*/
                f.method();/*().*/
                i = i + i - i * i / i % i & i | i ^ i;/*+ - * / % & | ^*/
                bool b = true & false | true ^ false;/*& | ^*/
                b = !b;/*!*/
                i = ~i;/*~i*/
                b = i < i && i > i;/*< && >*/
                int? ii = 5;/*? bug*/ // NO ?
                int f = true ? 1 : 0;/*? :*/   // YES :
                i++;/*++*/
                i--;/*--*/
                b = true && false || true;/*&& ||*/
                i << 5;/*<<*/
                i >> 5;/*>>*/
                b = i == i && i != i && i <= i && i >= i;/*= == && != <= >=*/
                i += 5.0;/*+=*/
                i -= i;/*-=*/
                i *= i;/**=*/
                i /= i;/*/=*/
                i %= i;/*%=*/
                i &= i;/*&=*/
                i |= i;/*|=*/
                i ^= i;/*^=*/
                i <<= i;/*<<=*/
                i >>= i;/*>>=*/
                object s = x => x + 1;/*=>*/
                Point point;
                unsafe
                {
                    Point* p = &point;/** &*/
                    p->x = 10;/*->*/
                }
                IO::BinaryReader br = null;
            }

            struct Point { public int X; public int Y; }
        }
  • Kirill Osenkov

    Recording of my “Inside Live Geometry” talk now available

    • 0 Comments

    Boost Virtual Events and Tri-Digital have recorded the talk that I gave during Seattle CodeCamp 2010.

    Check out their interactive site built fully with Silverlight:

    http://events.boostweb20.com/Events/SeattleCodeCamp2010/

    The deep link to my talk recording is here:

    http://events.boostweb20.com/Events/SeattleCodeCamp2010/default.aspx#state=sessionCode%242011-6

    The talk is about an hour long, I briefly demo the application (http://livegeometry.com) and then walk the audience through the source code, explaining the purpose of the different parts of the application and how they work together.

  • Kirill Osenkov

    Stress testing Visual Studio 2010 – Part 3

    • 3 Comments

    Finally I have some time to continue my series on how we did stress testing on the languages team to uncover crashes, hangs and leaks in Visual Studio 2010.

    As a reminder, Part 1 gave a general overview of what we do for stress (8-hour runs) and Part 2 listed the parameters that we’re measuring (VM, working set, private memory, committed memory, managed memory, GC handles, CCW, GDI handles, USER objects, OS handles and the number of threads).

    In this part I will tell more about what types of tests we run and how we look for bugs. As a reminder, I was responsible for stress-testing four parts of VS: C# IDE, VB IDE, F# IDE and the Hostable Editor (Workflow designer can host our compilers to provide IntelliSense and colorization for their expression editors).

    On each team, we had one master test script that was invoking each feature sequentially in an end-to-end fashion. For example, for C# I had a test called ‘AllCSharpFeatures’ that invokes 28 features (we say it has 28 ‘scenarios’). Here they are:

    1. Formatting
    2. Colorization
    3. Completion List
    4. Quick Info
    5. Parameter Help
    6. Go To Definition
    7. Code Definition Window
    8. Find All References
    9. Navigate To
    10. Call Hierarchy Engine
    11. Call Hierarchy Toolwindow
    12. Highlight References
    13. Navigation Bar
    14. Metadata As Source
    15. Live Squiggles
    16. Generate From Usage
    17. Generate Type Dialog
    18. Add Using SmartTag
    19. Implement Interface
    20. += event handler generation
    21. Rename
    22. Rename SmartTag
    23. Extract Method
    24. Extract Interface
    25. Encapsulate Field
    26. Reorder / Remove Parameters
    27. Edit-and-Continue
    28. Build

    The test is written in such a way that if all the 28 scenarios are run sequentially, it models a typical user working with features (the scenarios are augmented with typing, inserting and removing text to keep the flow). Moreover, if you disable any of the scenarios, the remaining workflow can still execute (they’re independent of each other). Finally, each scenario is repeatable, so that you can call it twice or more times (“idempotent”).

    While the test is running, we record the parameters that I mentioned in Part 2 to an XML-based log file, before and after every scenario.

    Now with all that set up, we run the test in one of several modes. First mode is called end-to-end stress iterations – we just repeat the entire sequence 30 or 100 times or 8 hours and after it’s done, the XML log is converted to an Excel spreadsheet with charts like this:

    image

    This is an 8-hour run from November 19 2009. The main test has looped 52 times (a total of 1474 executed scenarios). We clearly see that we’re leaking about 70 MB of working set and about the same amount of VM. However from this chart it’s hard to tell which feature is leaking.

    Next step is that we run the test in ‘scenario iterations’ mode. First, we run the entire sequence once to “warm up” (load all modules, JIT all the code, fill the caches) and then we loop the first scenario (Formatting) 30 times, then we loop the second scenario (Colorization) 30 times, and so on. The fact that the scenarios are composable and repeatable allows us to do that. Now we get a different picture (notice the warm-up growth at the very beginning):

    image

    A handle is typically a native resource, such as a file, registry key, thread or synchronization object. This chart clearly shows that during our “Navigate To” scenario (that emulates the user pressing Ctrl+, searching for a member and navigating there) we lose around 600 OS handles, which, divided by 30 iterations, gives us a leak of 20 handles per iteration. Now we have a bug with repro steps (using Navigate To leaks 20 handles per iteration) and our developer can use htrace to find the leak.

    image

    The GC Handles page shows a leak of GC handles in += event handler generation code, as well as in our Edit-and-Continue scenario. We can then use !gchandles in sos.dll to find and isolate the bug. The huge spike in EnC means that when the debugger starts, we allocate a large amount of resources, and when it stops, we free almost all of them back. The difference (that the debugger didn’t release) is an actual leak. You can also see that the debugger leaks these continuously and not in one swoop because you can see the ragged upward slope when the debugger is running.

    image

    Finally, this sample shows a leak of Com-callable wrappers (CCW) in the Generate From Usage scenario.

    To reiterate, we can run our test in two modes: sequential loop to stress end-to-end feature usage, and stress each feature separately (with one warm-up iteration to separate the resource initialization from real leaks). David Nelson asked in the comments to Part 1, if we’re running our tests in combination with each other, as well as in isolation. This gives the answer that, yes, we do. We can run each of the scenarios for 8 hours separately to stress it on its own and determine an exact amount of leak without external noise, as well as run a more real-world stress in a loop, where scenarios work together and are invoked one after each other in combination.

    This way we can also measure the duration of each scenario and compare it to earlier builds to see if we have improved performance or if we have accidental slowdown in any of the scenarios. An interesting phenomena that I observed at the early stages is that all of our scenarios were mysteriously slowing down after hours of work. After a lot of head scratching I tracked it down to the function that was writing stress data to the XML log. After 5-6 hours of constant logging our XML stress log got into the sizes of 15-20 MB and we were using DOM to read the entire XML file, add an entry and write it all back. No wonder our scenarios slowed down! I replaced this code with a simple File.AppendAllText that works in O(1) and we were all good again (reminder: next time don’t forget to append </StressLog> at the end of the test!).

    To conclude, I’d like to list some pointers to different tools we use to measure resources and track down leaks.

  • Kirill Osenkov

    I’m speaking at Seattle CodeCamp 2010 on Saturday, April 17

    • 2 Comments

    Seattle CodeCamp 2010 will be held the weekend of April 17-18 at the Microsoft Campus in Redmond WA (“The Commons”). On Saturday, April 17 at 4pm I’ll be giving a talk titled “Inside LiveGeometry – architecture of a Silverlight application”.

    Abstract:

    Live Geometry (http://livegeometry.codeplex.com) is an open-source Silverlight application that lets you create interactive ruler-and-compass constructions and experiment with them. It is a CAD-like educational software for teachers and students that helps visualize and solve geometry problems.

    I will talk about the internals and implementation details:

    as well as dive into various details driven by the audience interests. You can also ask me questions related to Visual Studio, C# and VB languages, C# and VB IDE features, WPF and Silverlight, testing and other topics.

    The schedule is here: https://seattle.codecamp.us/schedule.aspx. Take a look, there will be many interesting talks if you’re in the area. Kevin Pilch-Bisson, our C# IDE Dev Lead, will be talking about C# IDE new features in VS 2010. It’s a great chance to talk to him about Visual Studio, our existing features and planning for the next VS release after 2010. Kevin is very knowledgeable and engaged, so don’t miss this one.

    The full list of sessions is here: https://seattle.codecamp.us/sessions.aspx.

  • Kirill Osenkov

    Creating images with XAML

    • 2 Comments

    In comments to my previous post Benjamin had asked how did I create an image like this:

    I used standard PowerPoint SmartArt graphics – it offers rich possibilities and is easy enough to use.

    But it suddenly occurred to me that it should be relatively straightforward to use XAML to create a graphic like this – all in all, this is what XAML is actually for. So here's a quick version of that graphic created in Visual Studio 2010 using XAML:

    image

    And here's the XAML for it (if you're using WPF version prior to 4.0 remove UseLayoutRounding and TextOptions.TextFormattingMode attributes):

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="287" Width="519"
            UseLayoutRounding="True"
            TextOptions.TextFormattingMode="Display"
            WindowStartupLocation="CenterScreen">
        <Window.Resources>
            <Style TargetType="TextBlock">
                <Setter Property="FontSize" Value="12" />
                <Setter Property="FontFamily" Value="Verdana" />
            </Style>
            <Style x:Key="Shadow" TargetType="FrameworkElement">
                <Setter Property="BitmapEffect">
                    <Setter.Value>
                        <DropShadowBitmapEffect
                            Color="DarkGray"
                            Direction="315"
                            Opacity="0.5"
                            Softness="0.2"
                            ShadowDepth="3"/>
                    </Setter.Value>
                </Setter>
            </Style>
            <Style x:Key="Byte" TargetType="Border" BasedOn="{StaticResource Shadow}">
                <Setter Property="Background" Value="White"/>
                <Setter Property="BorderThickness" Value="1" />
                <Setter Property="BorderBrush" Value="DimGray" />
                <Setter Property="Padding" Value="12,6" />
                <Setter Property="Margin" Value="4, 2, 0, 2" />
                <Setter Property="MinWidth" Value="35" />
            </Style>
            <LinearGradientBrush x:Key="Gradient1" StartPoint="0,0" EndPoint="0,1">
                <GradientStopCollection>
                    <GradientStop Color="Honeydew" Offset="0" />
                    <GradientStop Color="#D0FFC0" Offset="1" />
                </GradientStopCollection>
            </LinearGradientBrush>
            <Style x:Key="SelectedByte" BasedOn="{StaticResource Byte}" TargetType="Border">
                <Setter Property="Background" Value="{StaticResource Gradient1}" />
            </Style>
            <Style x:Key="Arrow" TargetType="Polygon" BasedOn="{StaticResource Shadow}">
                <Setter Property="Fill" Value="{StaticResource Gradient1}" />
            </Style>
        </Window.Resources>
        <Grid>
            <Canvas>
                <Polygon 
                    Style="{StaticResource Arrow}"
                    Canvas.Left="251"
                    Canvas.Top="36.5"
                    Fill="{StaticResource Gradient1}" 
                    StrokeThickness="1"
                    Stroke="DarkGreen" 
                    Points="10,0 10,25 5,25 15,40 25,25 20,25 20,0 20,0" />
                <Polygon
                    Canvas.Left="102"
                    Canvas.Top="111"
                    Fill="{StaticResource Gradient1}" 
                    Points="0,0 162,0 201,40 0,40" />
                <TextBlock 
                    Canvas.Left="282" 
                    Canvas.Top="36">
                    Replace continues from here,<LineBreak /> no further matches
                </TextBlock>
                <TextBlock 
                    Canvas.Left="199" 
                    Canvas.Top="197">
                    Need a second pass to replace this one
                </TextBlock>
                <StackPanel 
                    Canvas.Left="20" 
                    Canvas.Top="80" 
                    Orientation="Horizontal">
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>a</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>b</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>c</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>d</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock> </TextBlock>
                    </Border>
                </StackPanel>
                <StackPanel 
                    Canvas.Left="20" 
                    Canvas.Top="150" 
                    Orientation="Horizontal" 
                    VerticalAlignment="Top">
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>a</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>b</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock> </TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource SelectedByte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>\r</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>\n</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>c</TextBlock>
                    </Border>
                    <Border Style="{StaticResource Byte}">
                        <TextBlock>d</TextBlock>
                    </Border>
                </StackPanel>
                <Polyline
                    Canvas.Left="227"
                    Canvas.Top="186"
                    Points="0,0 0,5 159,5 159,0"
                    Stroke="Green"
                    StrokeThickness="2">
                </Polyline>
            </Canvas>
        </Grid>
    </Window>
  • Kirill Osenkov

    Interview answers

    • 8 Comments

    In the previous post, I’ve come up with this interview question:

    In a given .NET string, assume there are line breaks in standard \r\n form (basically Environment.NewLine).

    Write a method that inserts a space between two consecutive line breaks to separate any two line breaks from each other.

    Update: Rik Hemsley has posted an absolutely fantastic summary in form of a Visual Studio solution with all the answers from the comments, unit-tests and benchmarks:

    http://code.google.com/p/kirill-question/

    Jordan Terrell’s RegExp-based solution seems to be the fastest one.

    Thanks Rik! This is very helpful!

    Source of the problem

    First I’ll explain how I came up with the problem. I was writing some code that generated HTML, specifically, some text inside the <PRE> tag, interspersed with <BR /> tags. I’m not an expert in HTML, that’s why it came as a surprise to me that if you have multiple consecutive <BR /> elements, the browser will only render one line break (well, at least the browser I checked it on - IE8). My code generated the <BR /> tags from Environment.NewLine substrings in the source text. So, to preserve empty lines in the resulting HTML, I had to intersperse the line breaks with at least one space – then IE would keep the empty lines when rendering <PRE> contents. I didn’t keep direct line breaks because some HTML viewers that I care about don’t render them correctly.

    My original solution

    Anyway, I’ve gotten a lot of great responses that contain a lot of food for thought. Mostly I was amazed how the responses actually reflected my own thinking as I was coming up with a solution. My ultimate solution (which was also correctly suggested by ram) was this:

        public static string IntersperseLineBreaks(string source)
        {
            string result = source.Replace("\n\r", "\n \r");
            return result;
        }

    A temporary local variable here is for your debugging convenience until my team comes up with a way to inspect the return value of a method in the debugger.

    Ambiguous problem statement

    Of course, banko correctly noted that the problem is stated ambiguosly, since Environment.NewLine is “\r\n” on Windows, and “\n” on Unix. Besides, as Jordan Terrell mentions, the behavior is unclear on source texts that contain wild mixes like “\r\n\r” and “\r\n\n”. Jordan also suggests that I include unit-tests to specify correct solutions.

    Well, I apologize for a problem statement that is not specified well enough (I simply didn’t have time to be that thorough!). I didn’t do it on purpose, but now when I think about it, most real-world requirements are ambiguous and not well specified. It is always interesting to see how a candidate deals with ambiguity and unclear requirements. So be prepared to expect unclear problem statements in your interviews and deal with them gracefully.

    There are several good strategies to impress the interviewer in this case, the simplest being to ask clarifying questions in the places where you think the requirements are unclear. Let them know that you are trying to explicitly understand and model the requirements in your head and are filling in the gaps.

    Another approach is to explicitly identify and parameterize the variability in the requirements and give a generic solution that works in all cases. Finally, if the problem statement is unclear, you can totally assert, assume or demand requirements that make your life easier. For instance, I remember I said “let’s assume that C# has tuples” during my interview at Microsoft. The interviewer laughed and said OK. I produced a nice solution that used tuples, and then he asked how would I change it to not use tuples. But that’s a totally different question ;)

    Assessment criteria

    If I were an interviewer (and I’ve never been one yet) and I would happen to ask this question, I guess I would look at several things in answers:

    1. Code reuse (e.g. my solution above relies on the Replace implementation in the framework vs. reimplementing a custom version of it for this particular problem, which might be error prone). However the point of interview questions is usually to see how people implement algorithms, so I guess for a sorting question an answer like List<T>.Sort() would rate very high on code reuse, but pretty much useless otherwise :)
    2. Correctness – some people gave similar solutions to mine, which call replace twice, but are more resilient to “weird input streams” with non-normalized line breaks. Such solutions will be twice as slow (since they will do full string replacement twice), but if a candidate understands the trade-offs between performance and correctness, I’m totally fine with it.
    3. Performance – writing your own StringBuilder-based implementation of Replace specifically suited for this particular problem is totally OK. In fact, some people gave very efficient solutions using this approach (e.g. Jared Parsons (a developer who I work with, who sits in the office down the hall) had built a custom ‘replacer’ state machine in F#) and Thomas Levesque correctly used a StringBuilder to avoid unnecessary string allocations. Some people fell into the trap of using string concatenation instead of a StringBuilder, which is by far less efficient, since it allocates ~ O(n^2) memory vs. O(n log n) memory allocated by StringBuilder. I would want a .NET candidate to understand how strings work in .NET, what are allocations, how GC works, and why string concatenation is bad when used in a loop.
    4. Style – having a concise declarative version is great when performance is not critical. Having readable code that is expressive and easy to maintain can sometimes trump performance. Moreover, usually people who are able to express their thoughts in code concisely will likely also master the skill of performance tuning when necessary.

    So I guess it’s impossible to declare a single winner because different solutions win when different assessment criteria are used. In terms of readability, getting things done and reusing as much as possible, I like ram's solution (and mine :P), even if it won’t work in certain scenarios (I’m OK with making assumptions about the source text). In terms of algorithm implementation, other solutions are certainly much better, those that use a StringBuilder or build the resulting array char-by-char. Finally, in terms of expressiveness I really liked some declarative solutions (using LINQ), even if they’re less effective (e.g. use string concatenation).

    string.Replace(“\r\n\r\n”, “\r\n \r\n”)

    One solution deserves some special explanation since it nicely demonstrates the core of the problem. The Replace method continues matching substrings after the end of the replacement, so if your replacement patterns overlap, it won’t replace the second one:

    image

    To overcome this problem, some people suggested the following:

            while (str.Contains("\r\n\r\n"))
                str = str.Replace("\r\n\r\n", "\r\n \r\n");

    Note that this loop will never run more than twice (this is where I goofed myself). GSEJ corrected me, pointing out that only two passes are necessary – first pass leaves a maximum of two consecutive line breaks, second pass eliminates them all.

    However we notice that in the replacement pattern (“\r\n\r\n”, “\r\n \r\n”), the start and the end are the same (hence the first and last chars overlap with previous and next occurrences of the pattern). The pattern is redundant, so we can make it shorter and only replace the crux of the pattern: Replace(“\n\r”, “\n \r”):

    image

    This pattern is non-redundant and doesn’t overlap with the neighbors, hence no second pass required.

    Well, anyway, thanks everyone for your answers! It was truly interesting for me and hopefully educational and entertaining for some of you.

  • Kirill Osenkov

    Interview question

    • 47 Comments

    Here’s a nice simple interview question:

    In a given .NET string, assume there are line breaks in standard \r\n form (basically Environment.NewLine).

    Write a method that inserts a space between two consecutive line breaks to separate any two line breaks from each other.

    Also, anyone venture a guess what is a practical application for such a method?

  • Kirill Osenkov

    &apos; is in XML, in HTML use &#39;

    • 4 Comments

    I just got hit by a very confusing "by design" behavior and it took me a while to figure out what's going on.

    Here is the line of code:

        text = System.Security.SecurityElement.Escape(text);

    This method replaces invalid XML characters in a string with their valid XML equivalent.

    The problem that I had is that when escaping some VB code using this method and then pasting it into Windows Live Writer, VB comments ' became &amp;apos;.

    Well, it turns out, XML supports &apos; to denote the apostrophe symbol '. However HTML doesn't officially support &apos; and hence Live Writer "HTML-escaped" my already "XML-escaped" string.

    Solution:

        text = System.Security.SecurityElement.Escape(text);
        // HTML doesn't support XML's &apos;
        // need to use &#39; instead
        // http://www.w3.org/TR/html4/sgml/entities.html
        // http://lists.whatwg.org/pipermail/whatwg-whatwg.org/2005-October/004973.html
        // http://en.wikipedia.org/wiki/List_of_XML_and_HTML_character_entity_references
        // http://fishbowl.pastiche.org/2003/07/01/the_curse_of_apos/
        // http://nedbatchelder.com/blog/200703/random_html_factoid_no_apos.html
        text = text.Replace("&apos;", "&#39;");
  • Kirill Osenkov

    Stress testing Visual Studio 2010 – Part 2

    • 1 Comments

    Read part 1

    In the first part of this series I've started talking about our general approach to stress testing Visual Studio. In this post I'll talk about what parameters we're measuring. In the next post I’ll explain how we're measuring them and what tools we use for it.

    VM

    The main metric that we are tracking is the devenv.exe process virtual memory. Virtual memory describes the address space of the process, and it can be backed by RAM or page file on disk. Until a region in the address space is committed, it doesn’t consume any actual memory.

    Our primary goal is to not let VM grow over 1.5 GB, because otherwise the address space on 32-bit OS becomes so fragmented that we are unable to allocate new chunks of memory and crash with OOM (Out-Of-Memory). Fortunately we’re now fulfilling this goal even on very large projects. Typical Visual Studio 2010 VM size on startup is anywhere from 300-500 MB. Note however that this doesn’t mean that it consumes that much RAM, no. It merely means that the process reserved this much address space which it might or might not actually fill in the future with “real memory” (committed memory that actually consumes RAM or page file).

    Our secondary goal is that VM doesn’t keep growing if VS is not working with new data (e.g. there are no memory leaks).

    If VM grows in our tests, it typically grows in chunks of approximately 15-20 MB. VM growth is usually caused by the private/committed memory growing.

    Working set

    Working set is a subset of virtual memory currently being stored in the RAM. If the memory is swapped out to the page file, working set may drop to almost nothing. This is the memory that gets freed when you minimize your Windows application or call SetProcessWorkingSetSize. It’s not a very useful counter since depending on how much is swapped out to the page file working set size can differ dramatically.

    Private bytes

    A more useful measure is the actual amount of memory consumed by this particular instance of the application not shared with other processes. It can be backed by physical memory (RAM) or page file and serves as a more exact measurement of what does your process consume.

    http://www.itwriting.com/dotnetmem.php

    Process\Privates Bytes shows all private bytes used in the process (including native memory).

    Committed bytes

    The .NET CLR Memory\# Total Committed Bytes is the private bytes used for managed heap.

    Bytes in all 4 managed heaps

    Another useful measurement is the consumed managed memory. The CLR currently has 3 generations in the garbage collector: 0-gen heap, 1-gen heap and 2-gen heap (GC.MaxGeneration currently returns 2). Moreover, for objects larger than 85,000 bytes there is a special fourth heap called the “large object heap”. The LOH is not compacted.

    Objects in gen-0 are short lived, young, recently allocated instances. As they survive garbage collections, they get promoted to the first and then the second generation. Gen-2 heap contains long lived objects that aren’t collected very often. If you have a memory leak, your leaked objects will most likely end up being promoted to the Gen-2 heap and will stay there.

    GC Handles

    Since VS is a mixed managed-native application (increasingly managed with every release), COM interop still plays an important role. System.Runtime.InteropServices.GCHandle is a managed structure that provides a means to access a managed object from unmanaged memory. You use GC Handles to prevent the GC from collecting a managed object if it’s only being used from native code. GCHandles are also used to pin an object in memory and prevent the GC from moving it. Typically a VS process has around 30,000-40,000 GC Handles, which is normal. However if we see GC Handles grow up to 70,000 and beyond, we likely have a managed/native memory leak somewhere.

    CCW

    CCW, or COM-Callable-Wrapper, is another COM interop counter that we’re tracking. If it grows above 2000, I’ll likely start investigating a leak bug. CCW and GC Handles sometimes leak together (if one is leaking, then the other is leaking).

    GDI handles

    GDI handles are objects used by the operating system to describe brushes, pens, and other parts of GDI drawing system.

    A Windows process can allocate a max of 10,000 GDI handles.

    If there is a GDI leak, we usually notice it very soon since if there is a leak, 10,000 get exhausted relatively fast. If you notice drawing weirdness or some controls don’t refresh correctly, check the GDI objects column in the Task Manager for your process. Tracking down GDI leaks is relatively easy – find what operation increases the number of GDI handles, and go through the code – search for places where you forget to call ReleaseDC, DeleteObject and such.

    Many people are surprised that Visual Studio 2010, a WPF application, can still leak GDI handles. The explanation is that some remaining parts of the UI are still written in native code/GDI, and it’s them who is typically leaking, if any. WPF doesn’t consume GDI objects and hence can’t leak them.

    USER objects

    User objects are very similar to GDI in nature, they’re also used by the operating system. To be frank, I’ve never seen them leaking.

    Handles

    OS handles are typically created when you open a file, a registry key, a semaphore or a mutex, and the like, or create a thread for instance. Typically the code should close the handle after using it (ever forgot to close a file?). Handles are measured by the Task Manager or also check out the SysInternals Handle tool.

    Threads

    The number of threads is worth watching because a typical .NET thread allocates 1 MB for its stack. Threads and VM are typically related – if you’re seeing a sudden 1 or 2 MB jump in VM, check the threads - it’s likely the runtime has started 1 or 2 new threads.

    VS has typically around 30-50 threads. We’re working to bring this number down wherever we can, because it’s generally good to not start your own threads, but instead preferably use the thread pool (ThreadPool.QueueUserWorkItem) and abstractions such as the TPL (Task Parallel Library). Speaking of which, if you have a chance to take Jeffrey Richter’s training on threading, go take it. It’s an awesome experience.

    In the next post I will talk about how exactly we measure these values and what tools we’re using for this.

  • Kirill Osenkov

    LiveGeometry @ Coding4Fun

    • 0 Comments

    Check out this article I wrote for Coding4Fun:

    http://blogs.msdn.com/coding4fun/archive/2010/03/01/9971021.aspx

    Hope you like it!

  • Kirill Osenkov

    How to investigate and report Visual Studio issues

    • 7 Comments

    With the Visual Studio 2010 RC released, here are some tips to help you help us find and fix the remaining issues.

    We want Visual Studio to have no bugs. If you want the same and are willing to help us a little, read on! And thanks so much for trying it out!

    Help –> Customer Feedback Options

    First off, enabling the Customer Experience Improvement Program is a very helpful step!

    image

    image

    This will enable sending detailed error reports when Visual Studio crashes or hangs that GREATLY help us to identify the root cause of a problem.

    Send Error Report

    When Visual Studio crashes or hangs, you will be presented with the so-called "Watson dialog" – a choice to "Send Error Report" or "Do Not Send":

    image

    By clicking "Send Error Report" you greatly contribute to making Visual Studio better – this is the one most important thing that will help us the most.

    Watson Bucket ID

    After the crash has been submitted, two error entries are created in the Application Event Log in Control Panel –> Administrative Tools –> Event Viewer:

    Event 1000 and Event 1001 (may also be others, like Event 1023).

    The most interesting one is Event 1001, because it will contain the Watson Bucket ID:

    Bucket 1040873707, bucket table 1, faulting application devenv.exe, version 10.0.30128.1, stamp 488f2b50, faulting module cslangsvc.dll, version 10.0.30128.1, stamp 488f2dea, debug? 0, fault address 0x000ceedd.

    Knowing this bucket, Microsoft employees who are helping you with the problem can find the information you submitted in our tracking system. This is helpful during investigations.

    ActivityLog.xml

    If the error you're seeing reproduces consistently, you can usually get more information by starting Visual Studio with the /log switch like this: devenv.exe /log

    This MSDN page contains a nice description of the Visual Studio activity log: http://msdn.microsoft.com/en-us/library/ms241272.aspx

    You can find the log file in %APPDATA%\Microsoft\VisualStudio\10.0\ActivityLog.xml

    This log is especially useful if the error you're seeing happens at Visual Studio startup when it's loading packages.

    Search for the error text

    If you're getting an error message, try using your favorite search engine to search the internet for it – maybe someone has already encountered it before?

    Log a Connect bug

    To actually let us know that you've found a bug, the central location to do this is the Microsoft Connect website: https://connect.microsoft.com/visualstudio/feedback

    By opening a bug there it gets directly inside our bug tracking system.

    You Can Do More: minidump and call stack

    A while ago I wrote an article called How to debug crashes and hangs. This article explains in great detail what steps you need to do to create a minidump with heap of Visual Studio when it has crashed or is hanging.

    The simple way is, if you're running Windows Vista or later, just open Task Manager, click Processes, find the Visual Studio process (devenv.exe), right-click and select Create Dump File.

    That's it! It will create the large minidump file in your Temp directory and show a message box with the path where it saved the file.

    The original article can help if you're running Windows XP or in a more advanced case, when you also want to get the call stack, load symbols and so on. But usually a minidump file (.dmp) should be sufficient.

    How to send us the minidump

    The minidump file is usually huge (hundreds of megabytes), because it contains the exact snapshot of the full process memory of devenv.exe. To send us the minidump, open a bug on Connect and mention that you have a dump file. The MS employee who looks at the bug should give you a path to the temporary FTP website where you can upload the dump using FTP. If they don't give you this site or you have any other problems, just let me know.

    Debugging errors that are not crashes using First Chance Exceptions

    You might encounter an error which does not fully crash Visual Studio, and does not show the Watson dialog. In this case, there is still a good chance of finding what's going on and providing us with useful information about the error.

    For this you'll need to attach a debugger to the Visual Studio process (usually open another Visual Studio side by side). Again, a detailed tutorial is here. Just before the error is about to happen, break into debugger, and enable first chance exceptions (check everything in Debug –> Exceptions). Then resume debugging and let the error happen. Chances are that the exception will be thrown, but it will be subsequently suppressed. Enabling first-chance exceptions helps you to preview that exception before it gets swallowed. Once the exception happens, copy the Call Stack from the debugger Callstack toolwindow (Ctrl+A) and paste it into notepad. Sending us this call stack will be invaluable. Also, you can go to Debug –> Save Dump As and create a minidump with heap file (.dmp) from the current debuggee process state. Again, sending us this dump file and the call stack should really help a lot.

    One warning here is that there might be "red herrings" along the way – false first-chance exceptions which are normal and do not lead to an error. If you can't tell whether the first-chance exception you're seeing is normal or is the actual root cause of the error, send us the call stack for this and we'll be able to help further.

    devenv.exe /ResetSettings

    If you believe that you've gotten Visual Studio into a weird state with weird settings, one last resort is trying to start Visual Studio with the /ResetSettings switch:

    devenv.exe /ResetSettings

    This will reset the Visual Studio settings to the defaults and maybe help you solve the problem.

    devenv.exe /ResetSkipPkgs

    If you've had a Package Load Failure, running devenv.exe with the /ResetSkipPkgs flag will re-enable those failed packages and will try loading them again.

    devenv.exe /ResetUserData

    As a last resort, you can also try /ResetUserData.

    devenv.exe /SafeMode

    Or /SafeMode.

    devenv.exe Command Line Switches

    Here are other Visual Studio command line switches: http://msdn.microsoft.com/en-us/library/xee0c8y7.aspx

    New: Visual Studio Setup Log Collection Utility

    Check out the instructions here on how to use collect.exe to collect the logs: http://blogs.msdn.com/heaths/archive/2008/05/09/visual-studio-and-net-log-collection-utility.aspx

    Thanks!

    Thanks again for making Visual Studio a better product!

  • Kirill Osenkov

    Stress testing Visual Studio 2010

    • 18 Comments

    In the past several months Visual Studio and I have been really busy stress testing each other. This post is a general overview on what we've been up to and what kind of testing we're doing. I've learned a lot about stress testing and I have to say it's actually a lot of fun, so I guess it's worth sharing. I'll try to make this a series of several posts, diving into more technical details in the upcoming posts.

    Background

    During Beta 1 and Beta 2 it became painfully obvious that the new VS had an obesity problem: it was slow, consumed a lot of memory and the worst thing, with enough modules loaded it stopped fitting into the 2GB address space on 32-bit machines. There were several reasons for this, which Rico Mariani, Brian Harry and others have extensively blogged about. In a nutshell, with a lot of new functionality a lot more modules were loaded into memory. Besides, we now had to fully load the CLR and WPF at application startup. Moreover, there were all kinds of memory leaks all over the place.

    Making performance a top priority

    Of course this wasn't good, so our management made the right decision to make performance our top priority. Jason really took it seriously and we dedicated a lot of people to work fulltime to make Visual Studio fast and lean. As part of this effort I became a member of a virtual team called "Perf SWAT". This team is responsible for essentially three things: performance, memory consumption and design-time stress.

    Performance is clear: we need to be fast. Memory consumption is clear too: when we load, we need to take as little memory as possible, and avoid things such as double-loaded modules, loading both NGEN and IL versions of an assembly, and so on.

    Design-time stress on the VSL team

    As for design-time stress, the goal is once we're loaded into memory, jitted, warmed up and all the caches are filled, we should not continue to grow in consumption. This means find and eliminate all memory and resource leaks. Run-time stress means finding leaks in the CLR and BCL, design-time stress means finding leaks in the VS and tooling. I am responsible for design-time stress testing for the VSL team (managed languages). I need to make sure that there are no significant leaks in 4 areas:

    1. C# IDE and editor integration (C# code editor, navigation, refactorings and other core C# areas)
    2. VB IDE and editor integration
    3. F# IDE
    4. Hostable Editor (Workflow Designer in VS 2010 is essentially hosting a full-blown language service to show IntelliSense in the expression editor on the workflow diagram)

    Progress

    The good news is that we've made tremendous progress since Beta 2 and have brought the product into a much better state: it is much faster, more responsive, takes up much less memory and we also hope to have eliminated all major known memory leaks. A common complaint was that VS was growing in memory during usage and you had to restart it after a certain time. Right now we hope that you can mostly keep Visual Studio open for days (even weeks) without having to restart it.

    8 hour stress tests

    The official sign-off criteria is that the end user needs to be able to keep VS open for an entire work week without any noticeable performance degradation (this means 5 days times 8 hours a day). We've calculated that in average continuous human usage of 40 hours is equivalent to running our tests for 8 hours (tests are doing things faster than a human).

    We have identified and implemented a series of 22 tests for all the 4 language teams mentioned above. Each test covers one continuous kind of activity, e.g. CSharpStressEditing, CSharpStressNavigation, CSharpStressIntelliSense, CSharpStressDebugging, CSharpStressUI, VBStressEditing, VBStressProjectSystem, FSharpStressEditing, and so on.

    Each test runs for 8 hours on a machine in the lab and VS memory usage details are automatically logged. We've also developed tools to automatically analyze the stress logs and produce Excel spreadsheets and charts for analysis and reporting.

    Several months ago a typical test would start at about 300 MB ProcessWorkingSet and crash after several hours with OOM (Out-Of-Memory exception). None of the tests would even be able to run for 8 hours. After finding and fixing a lot (a lot!) of bugs, we were able to get it running for 8 hours – VS memory usage grew from about 300-400 MB of WorkingSet to over 1 GB over the period of 8 hours (that was anywhere from 200-500 stress iterations).

    Right now a typical test starts at about 150-200 MB and finishes 8 hours later at 200-300 MB. Also, instead of 500 iterations, it is able to do 3000-5000 iterations during 8 hours on the same hardware. Which means we made it considerably faster and also reduced the leaks in major feature areas to a minimum (right now a feature is considered not leaking if there is average increase of less then ~5KB per iteration).

    I'll try to continue blogging about our stress testing and dive more into the technical details: what we measure, how we measure, how we find bugs and how we'll know when we're eventually done.

  • Kirill Osenkov

    Unable to cast COM object … to IVsGlobalUndoCapableUndoManager

    • 3 Comments

    Hi all, sorry for the absense – we're all really busy fixing Visual Studio 2010 performance, memory consumption, improving stability and fixing last minute bugs.

    Good news: if you're seeing this:

    Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.VisualStudio.Editor.Implementation.IVsGlobalUndoCapableUndoManager'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{305E8709-55B7-4732-B872-1C073AB58047}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

    then we know what's going on.

    Workaround

    Try running devenv.exe /nosplash to bypass the splash screen, and things should work fine.

    WOOT??! How is the splash screen related??! :)

    For the curious ones, here's what's happening:

    …In my case I was investigating a fairly popular (5-6 new hits a day) Watson bucket. Ultimately I’ve found out, using some DistSvcs extension for WinDbg, that every dump has the splash screen thread as its main STA, followed by a crash. The symptoms are almost exactly the same as yours – IlocalRegistry::CreateInstance used on the WinMain thread, and returns a proxy for the newly created object. It seems to be a race condition between main thread and splash screen thread as to which one calls CoInitializeEx first (including implicit calls via various Win32 APIs), since we spin off the splash thread before we explicitly call OleInitialize (which calls CoInitialize) on the main thread.

    After a LOT of investigations, the solution was finally found by Pavel Minaev and Michael Lehenbauer, developers on the Visual Studio platform team. Huge thanks goes to them and to Viacheslav Ivanov, who helped a lot by reporting the issue and providing valuable call stacks, minidumps and trying out various things for us!

  • Kirill Osenkov

    Visual Studio 2010 Beta 2 Known Issues – Part 2

    • 23 Comments

    This post continues my unofficial list of known issues in VS 2010 Beta 2 (see Part 1). As we continue getting Beta 2 feedback and investigating issues, it's time for another update on the issues we've been seeing. Please keep in mind that due to the volume of the issues, I'm only mentioning the ones I was involved with or consider important.

    Crash during normal editor operation

    There is a rare situation where Beta 2 can crash during normal editing or viewing C# source code. Nevertheless, it happens often enough to become top 1 reported crash for C#.

    I'm very happy to say that we have identified the root cause and are working on a fix.

    Details: This is an extremely complex and hard to reproduce bug involving native code, threading, and working with Task Parallel Library from native code. The new C# IDE feature, Highlight References, is written in managed code and uses TPL (Task Parallel Library) to calculate references on a threadpool thread. However a certain portion of the language service (called the Language Analysis engine) is written in native code, so we had to do some cross-boundary and cross-thread manupulation. Our developers Ian (from C# compiler) and HeeJae (from C# IDE) worked very very hard to investigate the dumps that you folks were sending in and finally they've found the bug. The good news is that as more and more of Visual Studio gets rewritten in managed code, we hope to see less of such bad bugs in the future.

    So thanks to you for clicking that "Send Error Report" button – it really helped us to fix the top 1 reported C# crash!

    Cannot open C# source file: Error message "The operation could not be completed. Invalid pointer." or "Object reference not set to an instance of an object"

    This one is due to the Tools Options Fonts and Colors using a raster font instead of a TrueType font:

    http://blogs.msdn.com/visualstudio/archive/2009/10/27/VS-2010-Beta2_3A00_--Workaround-for-Raster-Font-Settings-Issue.aspx

    The fix is easy: switch to a TrueType font such as Consolas or Courier New.

    The editor team has fixed this one already.

    The application cannot start (VS black screen)

    Weston writes about this one in detail: http://blogs.msdn.com/visualstudio/archive/2009/10/29/how-to-fix-the-application-cannot-start-error.aspx 

    This is due to us being unable to read certain profile settings.

    The workaround is basically to run devenv /resetuserdata from the Visual Studio command prompt, but I encourage you to follow the link above for more information (e.g. how to save your settings beforehand).

    This is already fixed as well (now even if the profile contains the errors we will start successfully and deal with the errors).

    TextOptions does not appear in the IntelliSense in XAML designer

    The WPF team has completely rewritten the WPF text rendering to get rid of the font blurryness issue. The old style is still the default, to enable the new mode you need this simple attribute in your XAML: TextOptions.TextFormattingMode="Display". The problem is – TextOptions won't show up in IntelliSense!

    The workaround is to ignore IntelliSense and just build your project – things should just work.

    This has been fixed as well.

    All menus except the File menu disappear

    It happens extremely rarely, but we saw it three times already – the menus are just gone!

    image

    This one is very very hard to reproduce and we're still investigating this right now – for some unknown reason something is messing up the Style or the ControlTemplate for the menus. If you happen to run into this inside a virtual machine, you can really help us investigate the issue by pausing the machine in the state where VS is running and menus are gone.

    Feel free to open a bug on http://connect.microsoft.com/visualstudio/feedback or let me know and we'll send you instructions on how to upload the VM image.

    Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.VisualStudio.Editor.Implementation.IVsGlobalUndoCapableUndoManager'.

    ---------------------------

    Microsoft Visual Studio

    ---------------------------

    Unable to cast COM object of type 'System.__ComObject' to interface type 'Microsoft.VisualStudio.Editor.Implementation.IVsGlobalUndoCapableUndoManager'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{305E8709-55B7-4732-B872-1C073AB58047}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

    ---------------------------

    OK

    ---------------------------

     

    Right now we can't reproduce it so we're looking for any help – call stack, minidump with heap, ActivityLog.xml etc. You can read more about the ActivityLog here:

    http://msdn.microsoft.com/en-us/library/ms241272.aspx

    We'd appreciate any help!

    Expression.Compile generates incorrect code for (int?) conversion

    using System;
    using System.Linq.Expressions;
    
    class Program
    {
        static void Main()
        {
            Expression<Func<ValueType, int?>> expr = x => (int?)x;
            Func<ValueType, int?> converter1 = x => (int?)x;
            Func<ValueType, int?> converter2 = expr.Compile();
    
            int? a1 = converter1(null); // (int?) null
            int? a2 = converter2(null); // NRE
        }
    }

    Expected: both the direct delegate and the one generated with Expression Trees should work identically. Actual: the expression tree one throws a NullReferenceException.

    Thanks to controlflow for reporting this! It's a good bug, but unfortunately it will require a risky compiler change that we don't want to take so late in the product cycle. We've postponed the bug for a later release.

    Delegate.Combine doesn't support variance

    using System;
    
    class A { }
    class B : A { }
    
    class Program
    {
        static void Main()
        {
            Action<B> b = _ => { };
            Action<A> a = _ => { };
            b += a;
        }
    }

     

    This program compiles fine but causes an exception at runtime. The CLR team decided to postpone this to a future version.

    Completion List doesn't show up for certain lambda parameters in multiple overload scenarios

    using System;
    
    using System.Linq;
    using System.Threading.Tasks;
    
    class Program
    {
        static void Main(string[] args)
        {
            var query = Enumerable.Range(0, 100);
            Parallel.ForEach(query, (i, p) =>
            {
                Console.WriteLine(i);
                p.
            });
        }
    }

     

    In this code, IntelliSense doesn't show after p. Another great bug from controlflow. We've fixed this one (it had to deal with picking the right overload of ForEach given an incomplete lambda statement).

    Debugger data tips are leaking GDI handles

    Hovering the mouse cursor over variables during debugging and expanding the nodes in the tips will leak GDI handles. After the Visual Studio process will run out of GDI handles (and each process can only allocate 10,000 handles), it will crash. Usually this happens after 10-15 minutes of intensive debugging with datatips, depending on circumstances. You can watch the GDI handles of the devenv.exe process in the Task Manager (if you select GDI in View –> Select Columns).

    This is a stress bug that has been fixed by the debugger team recently.

    Tooltips on toolbar buttons are not displaying keyboard shortcuts

    As part of the WPF rewrite, we had postponed this work until very late, and only recently have fixed it (post Beta 2). Now all the keyboard shortcuts show up when you hover your mouse over a toolbar button. You will not be able to turn them off though :)

    Printing formatted source code with color is not supported

    This is actually not a bug, it's by design. We had to cut this low-impact feature from 2010 because of tight resources constraints as we've reimplemented the editor from scratch. This feature will hopefully come back in a later release of Visual Studio, maybe as an add-in.

    More High DPI and other accessibility issues

    I just keep finding more and more High DPI issues, hoping that if I find and report them now, you guys won't have to.

    Combobox missing right edge:

    image

    The shell team decided not to fix this because of tight schedule and resources.

    Snippets background poorly visible on High Contrast Black

    image

    The editor team has decided not to fix this because the colors are configurable.

    Silverlight Out-of-Browser settings dialog clips some controls under 120 DPI

    We're looking into fixing this right now.

  • Kirill Osenkov

    A million-dollar pen vs. a pencil

    • 5 Comments

    You know this legend about how NASA spent millions to develop a pen that can write in zero gravity, and the russians have just used a pencil? Well, I was reminded about this by Dejan’s post in response to my post about the Random Gradient Wallpaper Generator.

    I used an O(NxN) algorithm to generate a megapixel image that takes up about 5 MB on my desktop.

    Dejan generated a 2x2 bitmap that takes up 70 bytes and let Windows stretch it with the exactly same effect. If you read my original post, go read this one, you’ll like it :)

  • Kirill Osenkov

    Bezier Curves and Car Manufacturers

    • 3 Comments

    Michael has asked me if it was possible to add Bezier curves to Live Geometry. So I went ahead and added a Bezier tool (and it took me about 30 minutes to do so!):

    image

    This draws a cubic Bezier curve using four points:

    image

    There is something beautiful and elegant about these curves. No wonder industrial designers use it a lot. I actually felt so inspired that I went ahead and used the tool to design a car :) Here's the live view (you need Silverlight 3 to see this), and you can drag the points around:

    This is so fascinating. Maybe if I'm tired of programming I should just design cars? ;) Any recruiters from Subaru reading this?
     
    I also built in a possibility to pass arguments to Live Geometry using initParams:
    <object data="data:application/x-silverlight-2," 
    type="application/x-silverlight-2" width="100%" height="100%">
      <param name="source" value="LiveGeometry.xap"/>
      <param name="initParams" 
             value="LoadFile=http://guilabs.de/geometry/drawings/fun/car.lgf,ShowToolbar=false" />
    ...

    That's how I was able to embed Live Geometry into this page so that it doesn't show the toolbar and it automatically loads the car.lgf drawing.

    Finally, here are some important things to know if you want to host a .xap file from another domain:

    • That domain must have crossdomain.xml at its root (so http://livegeometry.com/crossdomain.xml should be accessible)
    • If you're testing this by loading an .html file from a local filesystem, it will just display a white background and won't load the app. Put the .html on a webserver, then it will work
  • Kirill Osenkov

    Irony

    • 7 Comments

    (Ironically, this post is not about irony in it's traditional sense)

    Irony (http://irony.codeplex.com) is an open-source .NET compiler construction framework written by Roman Ivantsov. It is a ".NET Language Implementation Toolkit". The language grammar is described in C# (or any other .NET language).

    So instead of generating the scanner and the parser from a grammar description written in an external DSL, Irony uses a .NET object graph to host the grammar description (internal DSL), and it uses this in-memory grammar description to drive the scanner and the parser at runtime.

    One huge advantage of this approach is that the language description becomes orthogonal to the scanner and parser implementation. Writing and maintaining a language becomes easier. See an example of grammar definition further down in this post.

    Arithmetic expression evaluation

    The reason I'm talking about all this is that I was looking around for an expression parser for Live Geometry. I was able to compile expressions earlier using DLR, but there were two issues with that.

    1. First, DLR has quite a footprint – adding a dependency on DLR to a Silverlight binary can grow the size of the .xap file from 400 KB to 1400 KB – and that's not good.
    2. Second, DLR didn't seem to expose the parse trees easily – and I do need the access to the parse trees for inspection and manipulation, such as calculating dependencies between formulas.

    So I started looking around. There are really a lot of related projects out there, I'll just mention the ones on CodePlex:

    1. http://antlrcsharp.codeplex.com
    2. http://csparser.codeplex.com
    3. http://expressioneval.codeplex.com
    4. http://flee.codeplex.com
    5. http://ilcalc.codeplex.com
    6. http://lazyparser.codeplex.com
    7. http://linqovercsharp.codeplex.com
    8. http://ncalc.codeplex.com
    9. http://simpleexpressioneval.codeplex.com
    10. http://simplemathparser.codeplex.com
    11. http://expressionscompiler.codeplex.com

    There might be more. My requirements were for the tool to output a parse tree and give me full access to it. Another requirement is that the grammar description has to be as simple as possible and as flexible and extensible as possible (because I planned to extend the language with custom elements such as method calls and property access). However full blown C# and LINQ parsers were an overkill. Other good projects were really fast, but didn't give me the parse tree.

    So I settled on Irony, and so far I'm pretty happy that I did. It didn't provide the Silverlight version out of the box, but I'm talking with Roman about Silverlight support. For now, it took me an hour to make the Irony sources build for Silverlight and I built a nice little Irony.Silverlight.dll for my own project.

    Expression parser in Irony

    Here's how I declared the grammar for the language that I need:

    // This grammar is based on the ExpressionEvaluatorGrammar from Irony.Samples
    // Copyright (c) Roman Ivantsov
    // Details at http://irony.codeplex.com
    [Language("Expression", "1.0", "Dynamic geometry expression evaluator")]
    public class ExpressionGrammar : Irony.Parsing.Grammar
    {
        public ExpressionGrammar()
        {
            this.GrammarComments = @"Arithmetical expressions for dynamic geometry.";
    
            // 1. Terminals
            var number = new NumberLiteral("number");
            var identifier = new IdentifierTerminal("identifier");
    
            // 2. Non-terminals
            var Expr = new NonTerminal("Expr");
            var Term = new NonTerminal("Term");
            var BinExpr = new NonTerminal("BinExpr");
            var ParExpr = new NonTerminal("ParExpr");
            var UnExpr = new NonTerminal("UnExpr");
            var UnOp = new NonTerminal("UnOp");
            var BinOp = new NonTerminal("BinOp", "operator");
            var PostFixExpr = new NonTerminal("PostFixExpr");
            var PostFixOp = new NonTerminal("PostFixOp");
            var AssignmentStmt = new NonTerminal("AssignmentStmt");
            var AssignmentOp = new NonTerminal("AssignmentOp");
            var PropertyAccess = new NonTerminal("PropertyAccess");
            var FunctionCall = new NonTerminal("FunctionCall");
    
            // 3. BNF rules
            Expr.Rule = Term | UnExpr | FunctionCall | PropertyAccess | BinExpr;
            Term.Rule = number | ParExpr | identifier;
            ParExpr.Rule = "(" + Expr + ")";
            UnExpr.Rule = UnOp + Term;
            UnOp.Rule = ToTerm("+") | "-" | "++" | "--";
            BinExpr.Rule = Expr + BinOp + Expr;
            BinOp.Rule = ToTerm("+") | "-" | "*" | "/" | "^";
            PropertyAccess.Rule = identifier + "." + identifier;
            FunctionCall.Rule = identifier + ParExpr;
            this.Root = Expr;
    
            // 4. Operators precedence
            RegisterOperators(1, "+", "-");
            RegisterOperators(2, "*", "/");
            RegisterOperators(3, Associativity.Right, "^");
    
            RegisterPunctuation("(", ")", ".");
            MarkTransient(Term, Expr, BinOp, UnOp, AssignmentOp, ParExpr);
        }
    }

    That's it! Note how Irony uses operator overloading for | and + to build rules. We need ToTerm("+") call on any of the operands to give the C# compiler a hint about the types so the operator overloading can succeed. This is a good example of an internal DSL hosted in C#.

    Consuming the parse tree

    Now, here's how to use the ExpressionGrammar class to create and use a parser:

    Grammar grammar = new ExpressionGrammar();
    Parser parser = new Parser(grammar);
    ParseTree parseTree = parser.Parse("sin(2 * x) + 1");

    Producing a LINQ Expression Tree from a parse tree

    Now, given the parse tree from the previous step, I just quickly wrote an ExpressionTreeBuilder that produces a bound expression tree out of it, and a Binder that helps resolve names. The Compiler class is a façade for the whole thing:


    public class Compiler
    {
        public static Func<double, double> CompileFunction(string functionText)
        {
            ParseTree ast = ParserInstance.Parse(functionText);
            ExpressionTreeBuilder builder = new ExpressionTreeBuilder();
            Expression<Func<double, double>> expression = builder.CreateFunction(ast.Root);
            Func<double, double> function = expression.Compile();
            return function;
        }
    
        static Parser ParserInstance = new Parser(ExpressionGrammar.Instance);
    }
    
    
    public class Binder { public void RegisterParameter(ParameterExpression parameter) { parameters.Add(parameter.Name, parameter); } ParameterExpression ResolveParameter(string parameterName) { ParameterExpression parameter; if (parameters.TryGetValue(parameterName, out parameter)) { return parameter; } return null; } Dictionary<string, ParameterExpression> parameters = new Dictionary<string, ParameterExpression>(); public Expression Resolve(string identifier) { return ResolveParameter(identifier); } public MethodInfo ResolveMethod(string functionName) { foreach (var methodInfo in typeof(System.Math).GetMethods()) { if (methodInfo.Name.Equals(functionName, StringComparison.InvariantCultureIgnoreCase)) { return methodInfo; } } return null; } }

    public class ExpressionTreeBuilder
    {
        public ExpressionTreeBuilder()
        {
            Binder = new Binder();
        }
    
        public Binder Binder { get; set; }
    
        public Expression<Func<double, double>> CreateFunction(ParseTreeNode root)
        {
            ParameterExpression parameter = Expression.Parameter(typeof(double), "x");
            Binder.RegisterParameter(parameter);
            Expression body = CreateExpression(root);
            var result = Expression.Lambda<Func<double, double>>(body, parameter);
            return result;
        }
    
        Expression CreateExpression(ParseTreeNode root)
        {
            if (root.Term.Name == "BinExpr")
            {
                return CreateBinaryExpression(root);
            }
    
            if (root.Term.Name == "identifier")
            {
                return Binder.Resolve(root.Token.Text);
            }
    
            if (root.Term.Name == "number")
            {
                return CreateLiteralExpression(Convert.ToDouble(root.Token.Value));
            }
    
            if (root.Term.Name == "FunctionCall")
            {
                return CreateCallExpression(root);
            }
    
            return null;
        }
    
        Expression CreateCallExpression(ParseTreeNode root)
        {
            string functionName = root.ChildNodes[0].Token.Text;
            Expression argument = CreateExpression(root.ChildNodes[1]);
            MethodInfo method = Binder.ResolveMethod(functionName);
            return Expression.Call(method, argument);
        }
    
        Expression CreateLiteralExpression(double arg)
        {
            return Expression.Constant(arg);
        }
    
        Expression CreateBinaryExpression(ParseTreeNode node)
        {
            Expression left = CreateExpression(node.ChildNodes[0]);
            Expression right = CreateExpression(node.ChildNodes[2]);
    
            switch (node.ChildNodes[1].Term.Name)
            {
                case "+":
                    return Expression.Add(left, right);
                case "-":
                    return Expression.Subtract(left, right);
                case "*":
                    return Expression.Multiply(left, right);
                case "/":
                    return Expression.Divide(left, right);
                case "^":
                    return Expression.Power(left, right);
            }
            return null;
        }
    }

    This just demonstrates the principle. One could easily extend this to write a full blown expression compiler, but this is good enough for my purposes for now. Live Geometry now uses this to evaluate math expressions and plot function graphs. As always, you can get the source from here.

  • Kirill Osenkov

    Indexed Properties in C# 4.0

    • 12 Comments

    Executive summary:

    1. In C# 4.0 you can call parameterized properties declared in COM using the indexer syntax, for instance instead of excel.get_Range("a") you can now write excel.Range["a"].
    2. You can’t declare your own indexed properties from C#. We have no plans of adding the ability to declare your own properties with parameters. Instead, the recommended way is to use a type with an indexer.
    3. The feature is immediately available in Visual Studio 2010 Beta 2 for your indexing pleasure.

    So I guess this hasn’t had a lot of press coverage so far (although Sam hinted about it). Even the published C# 4.0 language specification and the C# Future page still don’t mention it as of now. This is because we’ve implemented this feature very late in the cycle, as a DCR (Design Change Request). The language design team felt that we should complete the COM interop story in 4.0 and this one was the last missing piece of the puzzle. When Paul was in Redmond this summer, he was tasked with testing the IDE support for this feature, and I was helping out with the test infrastructure.

    The new syntax

    The pattern is very simple. Wherever you use COM interop and have to call get_X() and set_X(), now you can just call X[], which we feel is a more natural syntax:

    // before
    excel.get_Range("A1").set_Value(Type.Missing, "ID");
    
    // after
    excel.Range["A1"].Value = "ID";

    Let’s also take Scott Hanselman’s example from Beta 1:

    var excel = new Excel.Application();
    excel.Visible = true;
    excel.Workbooks.Add();
    excel.get_Range("A1").Value2 = "Process Name";
    excel.get_Range("B1").Value2 = "Memory Usage";

    Now you can simplify this even further:

    var excel = new Excel.Application();
    excel.Visible = true;
    excel.Workbooks.Add();
    excel.Range["A1"].Value = "Process Name";
    excel.Range["B1"].Value = "Memory Usage";

     

    This is just syntactic sugar – the compiler emits calls to the get_ and set_ accessors behind the stage.

    Omitting []

    In case that all the parameters are optional and none of the arguments are specified, you should omit the empty []. Having Value[] is illegal.

    This is the reason you can replace Value2 with Value in the example above: Value is an indexed property and you’re calling it without specifying any arguments – in this case we omit the brackets [] altogether. Earlier, without indexed properties support, we had to introduce the ugly Value2, because you otherwise had to call get_Value().

    IDE support

    My team, on the IDE side, provided IntelliSense support for this new language feature:

    • completion list:

    image

    • parameter help:

    image

    • however Quick Info still shows you that in reality the call simply binds to the get_Range method:

    image

    As we were designing the feature, it turned out that adding compiler support for it is not the only tricky part. There were a couple of interesting problems in the IDE space as well. For example, what do you show in Quick Info for the following intexed property call?

    A.B[C]++;

    Do we now show get_B or set_B?

    When the IDE can’t guess which accessor we’re talking about (for example, in incomplete code), we by default bind to the get_accessor.

    Backwards compatibility

    For backwards compatibility reasons, using the accessors get_ and set_ directly is still valid and available in IntelliSense, because we didn’t want to break all existing COM interop code out there.

    Why not allow declaring indexed properties in C#?

    A common question that we expect we’ll be getting is “why just consume? why not allow to declare such properties in C#?”. Well, the answer is not even that we first have to cost, design, spec, prototype, implement and test this feature, but rather that we think that declaring a type with an indexer is a preferred approach. It’s useful to separate the responsibilities:

    • The property is there to get an object. The property belongs to the parent object.
    • The indexer is there on the returned object to enumerate it. The indexer belongs to the returned object.

    We shouldn’t be mixing these together.

    Static or dynamic?

    Accessing indexed properties is supported from both static and dynamic code.

    kick it on DotNetKicks.com
  • Kirill Osenkov

    Visual Studio 2010 Beta 2 Known Issues

    • 14 Comments

    [This is Part 1. Read Part 2 here]

    Now that we’ve shipped Beta2 and the world is busy downloading the fresh new bits, I’m very excited to know what do you guys think? Will you like it? Will there be major issues that we missed? Time will show :)

    By definition, this Beta 2 release is not final, and there are still bugs lurking around out there. We are very busy fixing those bugs for RTM, but for now, there are some that we haven’t had time to fix before Beta2.

    This post lists some of the known issues that are in VS 2010 Beta 2. It’s not in my powers to maintain a comprehensive list here, I’ll just mention the ones which I was personally involved with in my day-to-day work. Apologies that I have found them too late, but I guess better late then never...

    Updates – new!

    Posted the Part 2 of the issues (updated 11/12/2009)

    http://blogs.msdn.com/kirillosenkov/archive/2009/11/12/visual-studio-2010-beta-2-known-issues-part-2.aspx

    All further updates will happen there or will wait until Part 3.

    The official Beta2 known issues list (updated 10/26/2009)

    http://go.microsoft.com/fwlink/?LinkID=166199

    Updating the .sln file from Beta1 to Beta2 (updated 10/26/2009)

    Need to upgrade .sln file from Beta2 in order to be able to double-click it. See Jon’s post here for more details:

    http://msmvps.com/blogs/jon_skeet/archive/2009/10/26/migrating-from-visual-studio-2010-beta-1-to-beta-2-solution-file-change-required.aspx

    Setup dialog UI layout wrong on High DPI

    image

    Embarrassing! Unfortunately, not everyone on the Visual Studio team is rigorous about non-default testing, such as High DPI, Accessibility, etc. We will hopefully fix this one before RTM. Also recent data shows that non-96 DPI is a very common setting, a lot of people actually use 120 DPI and others. I personally use 120 DPI, that’s how I find these bugs.

    SmartTag high DPI issues

    image

    With high DPI, the SmartTag menu is missing horizontal menu separator bars. Also the right vertical edge of the SmartTag button disappears. These bugs are a recent regression from WPF introducing the UseLayoutRounding API. The WPF team is looking into fixing these issues before the release.

    QuickInfo tooltip trimmed at high DPI

    image

    This is another regression from the new DWrite technology. The last word in the tooltip is missing! This one is fixed already.

    Thin line on top of the active document tab

    image

    Again, another high DPI issue. You can only notice this one if you look closely. We’ve fixed this already as well.

    Outlining Expand/Collapse glyphs are shifted several pixels up

    image

    Fixed already. A lot of people complained about this internally.

    Zoom combobox in the bottom-left corner of the editor isn’t aligned with the horizontal scrollbar

    image

    This is fixed.

    Don’t tear off the Call Hierarchy tool window!

    When you try to undock the C# Call Hierarchy toolwindow and drag it away (for example, to another monitor), VS will crash. This one is too embarrassing, because I was the one who is responsible for testing Call Hierarchy. To my defense, I was on vacation when this regressed and when I came back and found this, it was too late to fix. Shell UI team changed something about the WPF toolwindow implementation and we had some layout logic that didn’t expect double.Infinity as an argument, so we crashed. This one’s already fixed in recent builds.

    Vertical separator bar in the Call Hierarchy tool window doesn’t resize the details pane

    This also only reproes under 120 DPI and above. You can’t resize the two panes below by dragging the vertical bar:

    image

    And again, this is one that I should have caught and missed. When I caught this, it was too late to fix for Beta2. The WPF team is looking at this one right now.

    Error message connecting to a TFS server via HTTPS

    Well, guess what, the TFS client team has done it again! TFS HTTPS story was broken in Beta1, and it still has a bug in Beta2. However the good news is that there is a really easy workaround this time.

    When adding a new TFS HTTPS webserver (e.g. a Codeplex server at http://codeplex.com), after entering your credentials you will see this:

    image

    Don’t panic! Just enter your credentials again and things will work just fine. If you used the Tip (http://blogs.msdn.com/kirillosenkov/archive/2009/09/27/tip-don-t-enter-your-codeplex-credentials-every-time.aspx), then you will be unaffected by this and things will hopefully run smoothly.

    Ctrl+Alt+DOWN ARROW doesn’t bring up the active document list

    Ctrl+Alt+Down used to bring up the active document list, in Beta2 it doesn’t. This is fixed.

    Start Page: scroll wheel doesn’t work at the news feed

    Also the scrollbar thumb only moves in discrete steps. The Shell UI team decided not to fix this, because they don’t have time and resources for this. I actually was surprised to discover that the scrollbar didn’t work in 2008 either. Never noticed this until recently.

    VS Command prompt shortcut is not getting installed if you don’t install C++

    This one will be fixed as well.

    Hovering over ‘var’ during debugging doesn’t show the inferred type

    Jeffrey Richter told me about this minor annoyance. Although our language service correctly reports the inferred type when you hover the mouse cursor over ‘var’ in design time, it doesn’t work in debug mode. Since Jeff teaches a lot of courses in debugging and threading, this was bugging him ever since we shipped C# 3.0. Well, we finally fixed it for 2010 RTM. Thanks to Jeff for reporting this!

    Visual Studio doesn’t start maximized when first launched

    A while back I’ve logged a bug against the Shell team to “just start VS maximized for heaven’s sake”. They did the fix, but somehow it didn’t make it into the Beta2 branch. They’ll hopefully fix this for RTM.


    Well, these were the ones worth mentioning I guess. Apologies if you run into any of these or any other bugs for that matter. I will keep updating this post with more issues that I find or I think users should be aware of. Please keep in mind that this is an unofficial list.

    Please do let us know about any issues you find by submitting a connect bug. If the bug/suggestion/feedback is related to the C# language or IDE, also feel free to let me know directly or leave a comment on this blog. Since I work closely with the VS editor team, it’s worth watching their blog and giving them feedback: http://blogs.msdn.com/vseditor. Also, if you have any feedback about the new text rendering in WPF 4.0 and Visual Studio, the WPF Text team has a blog here: http://blogs.msdn.com/text.

  • Kirill Osenkov

    Saving images (.bmp, .png, etc) in WPF/Silverlight

    • 10 Comments

    I’ve recently added a new feature to Live Geometry that allows users to save the current drawing as a bitmap or a .png file. Just push the save button and pick the desired image format in the Save dialog:

    image

    Fortunately, both WPF and Silverlight support saving full visual contents of any visual into a file on disk. However the approach is somewhat different.

    Saving images in WPF

    WPF can save any Visual to an image and it supports several formats out of the box via a concept of Encoders. Here’s a sample for .bmp and .png:

    void SaveToBmp(FrameworkElement visual, string fileName)
    {
        var encoder = new BmpBitmapEncoder();
        SaveUsingEncoder(visual, fileName, encoder);
    }
    
    void SaveToPng(FrameworkElement visual, string fileName)
    {
        var encoder = new PngBitmapEncoder();
        SaveUsingEncoder(visual, fileName, encoder);
    }
    
    void SaveUsingEncoder(FrameworkElement visual, string fileName, BitmapEncoder encoder)
    {
        RenderTargetBitmap bitmap = new RenderTargetBitmap(
            (int)visual.ActualWidth,
            (int)visual.ActualHeight,
            96,
            96,
            PixelFormats.Pbgra32);
        bitmap.Render(visual);
        BitmapFrame frame = BitmapFrame.Create(bitmap);
        encoder.Frames.Add(frame);
    
        using (var stream = File.Create(fileName))
        {
            encoder.Save(stream);
        }
    }

    These types are all in System.Windows.Media.Imaging.

    Saving images in Silverlight 3

    In Silverlight, the encoders don’t come as part of the Silverlight runtime – but fortunately there is a project on CodePlex called ImageTools (http://imagetools.codeplex.com) that provides necessary support. You will need to download the following binaries and add them as references to your Silverlight project:

    • ICSharpCode.SharpZipLib.Silverlight
    • ImageTools
    • ImageTools.IO
    • ImageTools.IO.Png (only if you want .png support)
    • ImageTools.IO.Bmp (only if you want .bmp support)
    • ImageTools.Utils

    After that, you can call the ToImage() extension method on any Canvas:

    void SaveAsPng(Canvas canvas, SaveFileDialog dialog)
    {
        SaveToImage(canvas, dialog, new PngEncoder());
    }
    
    void SaveAsBmp(Canvas canvas, SaveFileDialog dialog)
    {
        SaveToImage(canvas, dialog, new BmpEncoder());
    }
    
    void SaveToImage(Canvas canvas, SaveFileDialog dialog, IImageEncoder encoder)
    {
        using (var stream = dialog.OpenFile())
        {
            var image = canvas.ToImage();
            encoder.Encode(image, stream);
        }
    }

    Since you can’t write to disk directly in Silverlight, you can pass a SaveFileDialog and use its stream, or you can obtain a stream elsewhere and pass that. The ToImage() extension method does the dirty work that we had to do ourselves in WPF.

    Big thanks to http://imagetools.codeplex.com for their awesome library and encoders!

  • Kirill Osenkov

    Links

    • 0 Comments

    There are several good folks out there who regularly accumulate interesting links about all things .NET and other stuff on their blogs (cast in alphabetical order):

    Alvin Ashcraft

    Alvin Ashcraft's Morning Dew

    .NET Development Resources from a Progressive.NET Perspective

    Arjan Zuidhof

    Arjan’s World

    Arjan Zuidhof’s opinionated linkblog, with a hang to Alt.NET

    Charlie Calvert

    Charlie Calvert's Community Blog

    Charlie is the C# Community Program Manager on our team and has a great series called “Community Convergence”. I hope he never runs out of roman numerals :)

    Chris Alcock

    Reflective Perspective

    The caffeine fueled thoughts of a UK Software Developer and home of ‘The Morning Brew’

    Jason Haley

    Interesting Finds

    Ramblings of a .Net developer
    .ver 3:0:0:0

    Scott Guthrie

    ScottGu’s Link Listing tag

    Yes, ScottGu is a linkblogger!

    Steve Pietrek

    A Continuous Learner’s weblog

    My continuous learning of .NET, C#, VB.NET, ASP.NET, Delphi, Business Intelligence, Software Design and Development, Project Management, Object Oriented Development, Unit Testing, Development Tools ramblings....

    And since I feel a little linkbloggy myself today, here it goes:

  • Kirill Osenkov

    Visual Studio 2010 New Editor screenshot

    • 18 Comments

    I’m very happy with how the Visual Studio editor improved since Beta1:

    image

    This is all WPF and includes the WPF 4.0 text rendering changes (DWrite integration). Note the new Zoom combobox in the bottom-left (which is the visual counterpart of Ctrl+ScrollWheel). Also the cross to close the tab is now located ON the tab, and not on the rightmost edge of the document. It takes about a day to get used to, but after you get used to it, it’s really awesome.

    I believe that the editor team has done a phenomenal job making the editor better for all of us. Note that although the screenshot was made from a remote desktop to a virtual machine, it still looks much better than in Beta1. And believe me, it is way faster and more reliable now.

    I’m very excited for Beta2 and can’t wait to get the bits out for you to play with!

Page 3 of 7 (153 items) 12345»