March, 2009

  • Kirill Osenkov

    DLR Hosting in Silverlight

    • 9 Comments

    As you probably know, DLR is the dynamic language runtime that provides a common platform for dynamic languages and scripting in .NET. Their two main languages, IronPython and IronRuby, are available to develop your programs and also to be hosted in your programs. DLR hosting means that the users of your program can use scripting in any DLR language, for example to automate your program or to programmatically access the domain model of your application.

    I was thinking about adding a capability to plot function graphs like y = cos(x) to my Live Geometry app, so I thought of hosting the DLR in Silverlight to compile and evaluate mathematical expressions.

    Fortunately, DLR readily supports this scenario. And fortunately, Tomáš Matoušek, a developer on our IronRuby Team (part of Visual Studio Managed Languages), sits right around the corner from my office and was kind enough to provide great help when I had questions. Big thanks and kudos to Tomáš!

    So, to host the DLR in Silverlight, here's the file that I added to my project (you can view my full source code here: http://dynamicgeometry.codeplex.com/SourceControl/ListDownloadableCommits.aspx).

    All you need to do is to set up a script runtime, get your language's engine (here we use Python), create a scope for your variables and you're ready to evaluate, execute and compile!

    using System;
    using DynamicGeometry;
    using IronPython.Hosting;
    using Microsoft.Scripting.Hosting;
    using Microsoft.Scripting.Silverlight;
    
    namespace SilverlightDG
    {
        public class DLR : ExpressionCompiler
        {
            ScriptRuntime runtime;
            ScriptEngine engine;
            ScriptScope scope;
    
            public DLR()
            {
                var setup = new ScriptRuntimeSetup();
                setup.HostType = typeof(BrowserScriptHost);
                setup.LanguageSetups.Add(Python.CreateLanguageSetup(null));
    
                runtime = new ScriptRuntime(setup);
                engine = runtime.GetEngine("Python");
                scope = engine.CreateScope();
    
                engine.ImportModule("math");
            }
    
            public override Func<double, double> Compile(string expression)
            {
                var source = engine.CreateScriptSourceFromString(
                    string.Format(@"
    from math import *
    
    def y(x):
        return {0}
    
    func = y
    ", expression),
                    Microsoft.Scripting.SourceCodeKind.File);
    
                CompiledCode code = source.Compile();
                code.Execute(scope);
                var func = scope.GetVariable<Func<double, double>>("func");
                return func;
            }
        }
    }

    ExpressionCompiler is my own abstract class that I defined in the DynamicGeometry assembly:

    using System;
    
    namespace DynamicGeometry
    {
        public abstract class ExpressionCompiler
        {
            public abstract Func<double, double> Compile(string expression);
            public static ExpressionCompiler Singleton { get; set; }
        }
    }

    As you see, the service that I need from the DLR is to implement the Compile method, that compiles an expression down to a callable function delegate, which I can then use to evaluate a function at a point.

    Finally, just register the DLR as an implementation for my ExpressionCompiler:

    ExpressionCompiler.Singleton = new DLR();

    And we're ready to go.

    Let's go back to the DLR.cs and I'll comment a little more on what's going on. Essentially, to host the DLR you'd need 3 things:

    ScriptRuntime runtime;
    ScriptEngine engine;
    ScriptScope scope;

    Runtime is your "world". You load a language-specific engine (like PythonEngine) into the runtime. To create a runtime with a language, one way is to use:

    var setup = new ScriptRuntimeSetup();
    setup.HostType = typeof(BrowserScriptHost);
    setup.LanguageSetups.Add(Python.CreateLanguageSetup(null));
    runtime = new ScriptRuntime(setup);

    This will work fine in Silverlight, because we use a browser-specific BrowserScriptHost, which does not use the file system. One problem that I had is that I was trying to directly call:

    runtime = Python.CreateRuntime();

    Which didn't work because it used the default script host (which tried to access the file system) and not the BrowserScriptHost. After you have the runtime, you can get the engine and create a scope in that engine:

    engine = runtime.GetEngine("Python");
    scope = engine.CreateScope();

    Now you're ready to do things like:

    var five = engine.Execute("2 + 3", scope);
    You can go up to the first code example to see how I declared a function in Python, and converted it to a C# callable Func<double, double> delegate.

    Finally, here's the working application (which you can also find at http://geometry.osenkov.com). Press the y = f(x) toolbar button, enter sin(x) and press the Plot button:

  • Kirill Osenkov

    How to start Visual Studio programmatically

    • 6 Comments

    One of the ways we test Visual Studio is by automating the devenv.exe process using a library called DTE (Design Time Extensibility). To use this library from your .NET application, you’ll need to add a reference to the EnvDTE assembly (which is usually available on the .NET tab of the Add Reference dialog).

    Starting Visual Studio using DTE

    Here's a simple code snippet that starts Visual Studio and displays its main window:

    using System;
    using EnvDTE;
    
    class Program
    {
        static void Main(string[] args)
        {
            Type visualStudioType = Type.GetTypeFromProgID("VisualStudio.DTE.9.0");
            DTE dte = Activator.CreateInstance(visualStudioType) as DTE;
            dte.MainWindow.Visible = true;
        }
    }

    When the VS object is being created, a VS process (devenv.exe) starts in the background. You can make its main window visible using dte.MainWindow.Visible = true;

    Note that when the parent process (your program) ends, VS will close with it as well.

    To get an instance of an already running VS process, you can use the following snippet:

    EnvDTE80.DTE2 dte2 = (EnvDTE80.DTE2)
        System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.9.0");

    This snippet also demonstrates using DTE2, a newer version of the DTE interface that provides additional functionality.

    DTE interface

    Since DTE is COM based, we need to get the type that represents DTE from a well-known ProgID (“VisualStudio.DTE.9.0” that can be found in the registry). Once we have that type, we create an instance of it using Activator and cast it to the DTE interface. Contrary to what the name suggests, DTE is actually an interface and not a class:

    namespace EnvDTE
    {
        [CoClass(typeof(DTEClass))]
        [Guid("04A72314-32E9-48E2-9B87-A63603454F3E")]
        public interface DTE : _DTE
        {
        }
    } 

    DTE commands

    Now that you have DTE in your hands, you can do a whole lot of stuff, for example, execute a command:

    dte.ExecuteCommand("File.OpenFile", "");

    This one will execute the File.OpenFile command to display the open file dialog. There are plenty more Visual Studio commands that are really useful if you want to automate Visual Studio. You can look up a VS command from the Command Window: View –> Other Windows –> Command Window. Just start typing there and it will offer a completion list:

    image

    Also, you can use the Customize dialog (right-click on any VS menu) to get an idea of what commands are available:

    image

    Finally, you can see what command corresponds to an action if you start recording a macro, then just do an action manually, and then view the source code for that macro. As the macro is being recorded, VS registers all DTE command calls and writes them down in VBA source code. For example, ever wondered what command corresponds to the Rename refactoring? Record it and view the macro source, you’ll find out that there is a Refactor.Rename command.

    Other DTE API

    Apart from DTE.ExecuteCommand, there are a lot of other APIs to control the editor, ActiveDocument, ActiveWindow, Application, Debugger, Documents, ItemOperations, Solution, SourceControl, etc.

    However this deserves a separate post by itself. Who knows, if there is popular demand on how to automate Visual Studio, I might start a series of blog posts about that. However, for now, I’ll just link to MSDN articles on DTE:

  • Kirill Osenkov

    Visual Studio 2010 Screencast: C# 4.0 Language + IDE + WPF Shell + Editor

    • 13 Comments

    It so happened that I recorded a quick 30-minutes video (screencast) showing the new features in the language and the IDE – and I did all this on a recent internal build of Visual Studio 2010, which has the WPF UI enabled. The video is very basic, I don’t go into any details, it’s mainly a quick overview and how features look like:

    Get Microsoft Silverlight

    You can also download or view the .wmv file here: http://guilabs.de/video/CSharp4.wmv

    Features covered:

    1. Language (0:00)
      1. Dynamic (0:30)
      2. Named and optional (3:20)
      3. Co/Contravariance (11:10)
      4. NoPIA, omit ref etc. (16:35)
    2. IDE (18:45)
      1. Call Hierarchy (18:50)
      2. Quick Symbol Search (23:00)
      3. Highlight References (25:30)
      4. Crash!! (26:15)
      5. Generate From Usage (26:50)
      6. fix aggressive IntelliSense (consume first, list filtering) (29:50)
  • Kirill Osenkov

    Kirill’s Whitespace Guidelines for C#

    • 16 Comments

    I don’t remember seeing any explicit guidelines on whitespace formatting for C# programs, however it seems that experienced C# developers all format their C# code files in a very similar fashion, as if there are some implicit but widely-accepted rules. In this post, I’ll try to formalize my own rules that I use intuitively when I format C# code. I’ll add more to it as I discover new stuff and correct things based on your feedback.

    No two consecutive empty lines

    Bad:

       1:  static void Main(string[] args)
       2:  {
       3:      Main(null);
       4:  }
       5:   
       6:   
       7:  static void Foo()
       8:  {
       9:      Foo();
      10:  }

    No empty line before a closing curly

    Bad:

       1:          Main(null);
       2:   
       3:      }

    No empty line after an opening curly

    Bad:

       1:  class Program
       2:  {
       3:      
       4:      static void Main(string[] args)

    One empty line between same level type declarations

       1:  namespace Animals
       2:  {
       3:      class Animal
       4:      {
       5:      }
       6:   
       7:      class Giraffe : Animal
       8:      {
       9:      }
      10:  }

    One empty line between members of a type

       1:  class Animal
       2:  {
       3:      public Animal()
       4:      {
       5:      }
       6:   
       7:      public void Eat(object food)
       8:      {
       9:      }
      10:   
      11:      public string Name { get; set; }
      12:  }

    Whereas it’s OK to group single-line members:

       1:  class Customer
       2:  {
       3:      public string Name { get; set; }
       4:      public int Age { get; set; }
       5:      public string EMail { get; set; }
       6:   
       7:      public void Notify(string message)
       8:      {
       9:      }
      10:  }

    However every multi-line member must be surrounded by an empty line unless it’s the first or the last member, in which case there shouldn’t be a line between the member and the curly brace.

    One empty line after #region and before #endregion

    Usually #region should be treated as if it were the first construct from it’s content (in this example, a type member):

       1:  class Customer
       2:  {
       3:      #region Public properties
       4:   
       5:      public string Name { get; set; }
       6:      public int Age { get; set; }
       7:      public string EMail { get; set; }
       8:   
       9:      #endregion
      10:   
      11:      public void Notify(string message)
      12:      {
      13:      }
      14:  }

    Within a #region, it’s contents should be separated from the #region/#endregion by a single empty line. Usually #regions contain type members or whole types, less often parts of a method body.

    I think these are the major rules that come into mind for now. If I remember more, I’ll update this post. Also, definitely feel free to contribute any corrections/additions and I’ll update the post too. Thanks!

  • Kirill Osenkov

    A common globalization bug

    • 1 Comments

    I’ve just found and fixed a globalization bug in our test infrastructure where a feature of our testcase management system (resetting a testcase to re-run on a lab machine) just wouldn’t work on a Russian OS. Fortunately, the call stack was easy to investigate: (sorry it’s in Russian - globalization, what can you do…)

    System.InvalidCastException: Приведение строки "2.0" к типу "Double" является недопустимым. ---> System.FormatException: Входная строка имела неверный формат.
       в Microsoft.VisualBasic.CompilerServices.Conversions.ParseDouble(String Value, NumberFormatInfo NumberFormat)
       в Microsoft.VisualBasic.CompilerServices.Conversions.ToDouble(String Value, NumberFormatInfo NumberFormat)
       --- Конец трассировки внутреннего стека исключений ---
       в Microsoft.VisualBasic.CompilerServices.Conversions.ToDouble(String Value, NumberFormatInfo NumberFormat)
       в Microsoft.VisualBasic.CompilerServices.Conversions.ToDouble(String Value)
       в XXXXXXXX.Utilities.DotNetFramework.IsDotNetFramework35HigherInstalled()
       в XXXXXXXX.Result.BulkResetResultsHelper(...

    This essentially says: cannot convert a string “2.0” to double. Here’s the problem line of code (VB):

    version = CDbl(numbers(0) & "." & numbers(1))

    and here’s a fix:

    version = System.Double.Parse(numbers(0) & "." & numbers(1), System.Globalization.CultureInfo.InvariantCulture)

    The original code made an incorrect assumption that the decimal separator in the current culture is the ‘.’ character. However on German, Russian, Italian and some other OSs the default decimal separator is a ‘,’, not a ‘.’. By default, string operations use the current locale (and hence expect a comma as a decimal separator), so if you want to compose a string using a dot and convert it to a double, you have to use InvariantCulture, which uses a dot.

    I’ve seen this error quite a lot of times – this is probably the most common globalization bug out there. Keep in mind, it’s 21st century out there, it’s likely that your software will be used all over the world on all possible combinations of operating systems, languages, locales, encodings, RTL etc.

    A good read on this topic would be Jeffrey Richter’s CLR via C#, chapter 11 (Chars, Strings, and Working with Text), pages 264-268).

Page 1 of 1 (5 items)