RSS

Category Archives: .NET

Windows Automation: Automating Windows 7 Notepad within S# Script


Today we have updated S# with another sample covering the use of Windows Automation API from within a common S# script.

Auto# (AutoSharp)

This is a demo project shows how easily your S# scripts can be enhanced with a set of functions that target Windows UI Automation API and the ways of forming a QA-friendly language on the top of S# runtime. Full source code for this sample is available within the latest S# runtime update here.

Note: Sample project targets Windows 7 Notepad and may not work properly on other versions of Windows because of control ids difference.

To find Windows Automation API for your version of Windows please refer to this article.

Auto# introduces 9 functions that provide Windows Automation support S# scripts:

  • Kill – close all running instances of some process
  • Launch – launch a new process and return its main window
  • Wait – sleep for some time, often required when waiting application response
  • FindByClassName – finds element by Class Name
  • FindById – finds element by Automation ID
  • FindByName – finds element by its Name
  • Expand – expands menu element
  • InvokeById – invokes element by Automation ID
  • FocusEditor – focuses textbox editor

The following workflow is executed in order to automate Notepad:

  1. Kill all running Notepad instances
  2. Launch a new Notepad process
  3. Type some text
  4. Click “File” – “Save As” in the main menu
  5. Type destination file name
  6. Agree to overwrite existing file if confirmation dialog appears
  7. Click “File” – “Exit” to close Notepad

Here’s how the S# script may look like:

// Close existing instances of Notepad

Kill(“notepad”);

 

// Launch a new Notepad instance and get main window

window = Launch(“notepad”);

// Wait 1 second

Wait(1000);

 

// Get main editor region

edit = FindByClassName(window, “Edit”);

 

// focus main editor

FocusEditor(edit);

 

// Send sample text to the editor region

SendKeys.SendWait(“Automating Notepad using Windows UI Automation and S#”);

Wait(3000);

 

// Find [File] menu

mnuFile = FindById(window, “Item 1″);

 

// Expand [File] menu

Expand(mnuFile);

Wait(1000);

 

// Invoke [Save As] menu item

InvokeById(window, “Item 4″);

Wait(1000);

 

// Get [Save As] dialog

saveAsDialog = FindByName(window, “Save As”);

 

// Get access to [FileName] textbox

saveAsName = FindById(saveAsDialog, “1001″);

 

// Focus filename editor

FocusEditor(saveAsName);

 

// Write down file name

SendKeys.SendWait(“D:\\MyTextFile”);

// Send [Enter] keypress

SendKeys.SendWait(“{ENTER}”);

Wait(1000);

 

// Check whether Overwrite Dialog appeared

confirmSaveAs = FindByName(saveAsDialog, “Confirm Save As”);

if (confirmSaveAs != null)

{

    // Click [OK] button

    InvokeById(confirmSaveAs, “CommandButton_6″);

    Wait(1000);

}

 

// Expand [File] menu

Expand(mnuFile);

Wait(1000);

 

// Click [Exit] item

InvokeById(window, “Item 7″);

 

 

Demo project also includes a very simply UI providing possibility to alter the script above and execute it.

Unleash your fantasy :)

 
2 Comments

Posted by on December 8, 2009 in .NET

 

S# 1.1 and Processing# Visualization Language for Silverlight 3.0


We are happy to announce that S# 1.1 was released today.

“Extension Methods”

It is now possible extending .NET types with custom methods that can be invoked during script execution. It is not the support for.NET extension methods but functionality is somewhat similar.

Let’s take a .NET “System.Math” class. It is a static class that “Provides constants and static methods for trigonometric, logarithmic, and other common mathematical functions”. You can freely access all its members from S# script (omitting namespace) but at some point you may want to extend existing set of methods with some custom or missing (in your opinion) ones. For example conversion to radians, mapping/constraint of numeric values, distance calculation, etc. But “System.Math” is a static class and cannot be inherited for customization purpose.

In S# 1.0 we used the following workaround for such cases: we would have created a “MathEx” class to mimic “System.Math” one with same methods being re-directed to original “Math” plus containing additional members like “Distance()”, “Radians()” etc. After that we’ve been suppressing original “Math” class with our own implementation with the help of RuntimeHost features like “AddType”: RuntimeHost.AddType(“Math”, typeof(MathEx)); From that moment all “Math.*” calls were redirected to a custom implementation and original “System.Math” was hidden from the scripts.

Though the approach above is still valid and you can easily suppress any type with your own implementation we decided to provide an out-of-box support in S# 1.1 for such scenarios:


RuntimeHost.TypeManager

.AddExtensionMethod(typeof(Math), “Constrain”, new
MathExtensions.Constrain())

.AddExtensionMethod(typeof(Math), “Degrees”, new
MathExtensions.Degrees())

.AddExtensionMethod(typeof(Math), “Distance”, new
MathExtensions.Distance())

.AddExtensionMethod(typeof(Math), “Distance2D”, new
MathExtensions.Distance2D())

.AddExtensionMethod(typeof(Math), “Distance3D”, new
MathExtensions.Distance3D())

.AddExtensionMethod(typeof(Math), “Map”, new
MathExtensions.Map())

.AddExtensionMethod(typeof(Math), “Radians”, new
MathExtensions.Radians());

Now you can create invokable functions and bind them to ANY .net type with a single line of code. The sample above demonstrates how easily “System.Math” type is enriched with 7 new methods. Here’s how I’m using it now within my scripts:

ang1 = Math.Radians(i); // convert degrees to radians

ang2 = Math.Radians(j); // convert degrees to radians

pos1 = Surface.Width/2 + (sc * Math.Cos(ang1));

pos2 = Surface.Width/2 + (sc * Math.Sin(ang1));

pos3 = Surface.Width/2 + (sc * Math.Cos(ang2));

pos4 = Surface.Width/2 + (sc * Math.Sin(ang2));

We hope this feature will greatly reduce development efforts for numerous scenarios as well as well as enrich your scripts.

Processing# Visualization Language for Silverlight 3.0

“Processing#” is a showcase sample demonstrating most of the powerful capabilities of S#. Being greatly inspired by “Processing” language implementation in Java we decided to port it to Silverlight powering with S# runtime. After installing S# 1.1 runtime you will find the complete source code under the “Samples\Silverlight 3.0″ folder.

Solution contains a simple console where you can write and immediately execute scripts. There are more than 30 “Processing.org” samples that were successfully ported to “Processing#” and that are available within the Silverlight console. The syntax was made slightly different by intention and will be getting more parity in later versions.

Source code also contains a ported to Silverlight demo from “OpenProcessing.org” to demonstrate the capabilities of Processing# powered by S# 1.1 runtime:

function Setup()

{

 

Surface.Size(320, 480);

 

//Main Body

Surface.Defaults.StrokeThickness = 2;

Surface.Defaults.Fill = new SolidColorBrush(Color.FromArgb(255, 203, 224, 142));

 

using (Surface)

{

    BeginShape();

    Vertex(154, 65);

    Vertex(182, 71);

    Vertex(225, 82);

    Vertex(250, 90);

    Vertex(258, 96);


The image above is rendered by Silverlight 3 and has no visual difference comparing to that created by Java.

You can download the latest runtime with Processing# demo here: Orbifold S# Runtime 1.1

 
Leave a comment

Posted by on December 2, 2009 in .NET

 

Introducing S# scripting language and runtime


Today we are very excited and proud to announce that we finished testing and moved to the packaging procedure for our next brainchild called “Orbifold S#“. The purpose of this article is to give a brief overview of what S# actually is, key features and possible use cases.

S# is a weakly-typed dynamic language and runtime infrastructure to make your applications extendable, customizable and highly flexible. It allows introducing expressions and large code blocks evaluation within your applications in the similar way Microsoft Office deals with VBScript, gives you possibilities providing rich formula evaluation capabilities like it can be seen in MS Excel, etc.

“Orbifold S# Runtime” will be distributed absolutely free for academic and non-commercial usage!. You can download runtime right now from here.

The key principles of S# are:

  • Be simple
  • Be efficient
  • Be intuitive

S# runtime has been designed to be easily hosted by applications. Minimum script execution scenario requires two lines of code! The important part of S# is its well-defined extendable runtime engine together with the application programming interface that allows full bi-directional communication between script and application code. In particular it is easy to extend S# by embedding external functions and functional objects, shared static or dynamic variables, operator handlings and type filters from the host application. Moreover the execution semantics of some language constructs has extensibility mechanisms available externally. This enables developers to create user-friendly executable business/domain specific languages on S# basis.

S# can work in single-expression mode in order to execute string expressions to values. This is especially helpful when application should allow users executing only light-weight portions of the functionality. S# is a pure .NET interpreted language completely written in C#.

For the moment it is ported to the following platforms:

  • Microsoft .NET 3.5 (SP1)
  • Microsoft Silverlight 3.0
  • Microsoft .NET Compact Framework
  • Microsoft XNA Framework
  • MONO

This means that S# runtime can be hosted by applications based on .NET like Console, Windows Forms, ASP.NET, Silverlight 2 and 3, Windows Presentation Foundation (WPF), XNA (both PC and XBox scenarios) and MONO (Linux).

Key features that will be dwelled on in the upcoming articles are:

  • Designed to be easily embedded into applications
  • Highly extensible grammar and language
  • Rich and controlled communication between runtime and host application in both directions
  • Sits on the top of .NET, has support for interaction with .NET code
  • Works in a single-expression evaluation mode to execute string expressions to values and return results to application
  • No code emitting, no CodeDom or background compilation, 100% interpreted language (own Virtual Machine and Debugging facilities are in progress)
  • Completely in-memory execution, does not require temp files, local system access, etc
  • C# and Java-like language syntax designed to be familiar to .NET and Java developers; easy to study for non-developer users
  • Various platforms are supported
  • Fully remotable; expressions and scripts can be sent across the wire and executed on a remote machine
  • Weakly-typed as IronPython and IronRuby for .NET

Installation Experience

You will need a couple of clicks in order to install S# runtime on your machine. For the moment runtime requires less than 1 MB on you hard drive.

SSharp_Installation_1

SSharp_Installation_2

SSharp_Installation_3

SSharp_Installation_4

For the moment you will have runtimes only for .NET 3.5 and Silverlight 3.0 installed by default. Both components are automatically registered within the “Add Reference” dialog in Visual Studio 2008 (note this requires installation to be started by a user with administrator account).

When creating a new Silverlight application you will be able adding S# reference without any additional implications related to finding the assembly installed on the local drive:

SSharp_Installation_VS

From Installation right to Execution

I am going to show you how easily you can start hosting S# runtime right after installation process is finished. You need to create an empty “Silverlight 3″ application project and add reference to “Orbifold S# for Silverlight” assembly using “Add Reference” dialog in Visual Studio 2008.

Next open “MainPage.xam” file and add a Canvas control. We are going to execute scripts that perform some operations against the Canvas element.

SSharp_Intro_1

After that you will need opening the code-behind file “MainPage.xaml.cs”. S# runtime hosting will require at least 2 namespaces involved: “Orbifold.SSharp” and “Orbifold.SSharp.Runtime”

SSharp_Intro_2

Note that S# runtime should be initialized before executing scripts. This procedure should be done once and I would recommend placing “RuntimeHost.Initialize()” method call somewhere in “App.xaml.cs” in the future.

Now it’s time to do some scripting on the “MainPage.Loaded” event:

SSharp_Intro_3

S# runtime supports a large amount of Scoping facilities (local scopes, global scopes, dynamic or application-controlled scopes). The same goes to execution contexts. Execution Contexts is a highly valuable and extensible area that deserves a separate article. All this will be  described in future articles and in API guides.

As I want promoting my “canvas” element to the runtime I need creating a simple execution context for my future script and provide possibility accessing “canvas” during runtime. This is achieved by adding a “canvas” variable to the scope that references our “canvas” element inside application. Note that you can provide any “alias” you want and it may not carry the same name as host-side variable/element.

Next step is defining the body of the script. I’m going to assign a new background brush for the Canvas and add a Rectangle element. Note that I don’t have to use full namespace paths for .NET classes as they are resolved automatically by S# runtime. You may forget about endless “using” clauses here.

After that I create a Rectanlge, configure it’s properties (including attached ones) and add it to the Children collection of the Canvas control by referencing “canvas” variable.

Finally I call “Script.RunCode” providing the body of the script and root execution context. When launching this Silverlight application you will see the following result:

SSharp_Intro_4

Yes, everything works perfect. Script got access to application structures and even managed to create and add new objects using core .NET Silverlight functionality. I would like to highlight “namespace resolving” feature once more as it really simplifies coding efforts. Let’s extend the script above to apply rotation for the Rectangle shape. “RotateTransform” class resides in “System.Windows.Media” namespace in Silverlight 3.0.

SSharp_Intro_5

As S# is a weakly-typed language you don’t have a need worrying about value types as they will be resolved automatically during runtime.

When running modified script you should see the following results:

SSharp_Intro_6

Extending Language with Custom Functions

Though S# supports a huge part of C# syntax constructions it may be required adding some domain-specific constructions or functions that simplify script building for end-users.

I am going to create two new functions: “CreateRectangle” that creates a Rectangle shape in a simplified manner and “Rotate” that will rotate FrameworkElement.

CreateRectangleFunction

SSharp_Ext_2

RotateElementFunction

SSharp_Ext_3

Both functions are fairly simple and don’t require comments. Please note that each S# function element should expose “IInvokable” interface. This is the only requirement for external functions integration.

In order to promote these functions you will need to include them into the scope the same fashion “Canvas” element was done – ScriptContext.SetItem() method. However to reduce development time there are also generics-based methods:

SSharp_Ext_1

Note that you can provide any name for the function. In this case I used “CreateRectangle” and “Rotate”. Now let’s modify the code from the samples above in order to consume new functions:

SSharp_Ext_4

This time the code becomes less complex but more efficient. I can create a Rectangle shape providing position and size and can call “Rotate” in order to apply Rotate Transformation.

Just for demonstration purposes I’ve added a function usage sample. I’ve introduced a function (event handler) called “OnCanvasClick” that is wired with “MouseLeftButtonDown” event of Canvas. Function requests access to global variable scope via the “global” keyword (similar to PHP)

SSharp_Ext_5

Now if you execute a script Rectangle will rotate 10 degrees each time you click design surface with a left button of the mouse. Another important point is that weakly-typed and dynamic nature of S# allows me accessing Angle value of RenderTransform property as of RotateTransform actually. By default RenderTransform property is of Transform type. S# didn’t require me doing type casting or conversion in order to set property value and this is really helpful in many scenarios.

The S# language has a great number of features and runtime is extremely extensible. We will unveil more details, links and samples for S# very soon.

UPDATE: Meanwhile we are finishing all the packaging work you can download the raw runtime (without documentation, samples and some other stuff) right now here.

 
4 Comments

Posted by on November 8, 2009 in .NET, Info, Silverlight, WPF

 

Tags: , ,

Silverlight 3 PropertyGrid Development Progress


There was no information about Silverlight version of the PropertyGrid control for some period. I would like to leak some information towards the current development progress as well as highlight some of the most remarkable features.

Multiple layouts support

Silverlight PropertyGrid will support at least Alphabetical and Categorized layouts out-of-box. There will be a possibility to write your own layouts with least amount of efforts possible.

Here’s how the the “Categorized” one looks like and defined in XAML:

SPG01

SPG02

“Categorized” layout will be the default one. This means that you won’t need defining anything if “Categorized” behavior is those you expect to have by default.

Note that control will support “Description Box” similar to native Windows Forms PropertyGrid control.

Here’s how the “Alphabetical” layout looks like and defined in XAML:

SPG03

SPG04

Both POCO (Plain Old CLR Objects) and Dependency objects are to be supported. Here’s the Button control bound to PropertyGrid:

SPG05

Property Filtering feature will provide exactly the same functionality Microsoft Expression Blend’s PropertyGrid control gives. It will be possible to filter out properties according to their names and types:

SPG06

SPG07

After Silverlight 3 went RTM the Silverlight PropertyGrid development progress went rapid and smooth and hopefully the control will be available very soon.

More news are coming soon…

 
4 Comments

Posted by on October 31, 2009 in .NET, Silverlight

 

Reflection Limitation in .NET 3.5/4.0


Recently a colleague of mine pointed to a strange behavior in .NET Reflection  that pushed me doing a lot of additional investigations and finding out a very serious limitation that might raise a lot of issues if not being taken into account.

When you declare property as “virtual” beware that custom attributes won’t be fetched for the derived one if the property is being overrided.

This can be easily tested. First you need creating two custom attributes that will target two cases: virtual property and virtual method.

  [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
  public class MyPropertyAttribute : Attribute
  {
  }

  [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
  public class MyMethodAttribute : Attribute
  {
  }

Now you can create two classes: “BaseClass” that will contain a single virtual property and single virtual method and “DerivedClass” that will override both members.

 public class BaseClass
  {
    private string _PropertyVirtual;

    [MyProperty]
    public virtual string PropertyVirtual
    {
      get { return _PropertyVirtual; }
      set { _PropertyVirtual = value; }
    }

    [MyMethod]
    public virtual void MethodVirtual()
    {
    }
  }

  public class DerivedClass : BaseClass
  {
    public override string PropertyVirtual
    {
      get { return base.PropertyVirtual; }
      set { base.PropertyVirtual = value; }
    }

    public override void MethodVirtual()
    {
      base.MethodVirtual();
    }
  }

The problem starts when you try fetching custom attributes for “PropertyVirtual” from within a DerivedClass because you will never get them using PropertyInfo.GetCustomAttributes method call. Here’s a sample Console Application to ensure the limitation is alive:

  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Checking virtual property attributes:\r\n");

      Console.WriteLine(
        "BaseClass.PropertyVirtual attributes count: {0}",
        typeof(BaseClass).GetProperty("PropertyVirtual").GetCustomAttributes(true).Length);

      Console.WriteLine(
        "DerivedClass.PropertyVirtual attributes count: {0}",
        typeof(DerivedClass).GetProperty("PropertyVirtual").GetCustomAttributes(true).Length);

      Console.WriteLine("\r\nChecking virtual method attributes:\r\n");

      Console.WriteLine(
        "BaseClass.MethodVirtual attributes count: {0}",
        typeof(BaseClass).GetMethod("MethodVirtual").GetCustomAttributes(true).Length);

      Console.WriteLine(
        "DerivedClass.MethodVirtual attributes count: {0}",
        typeof(DerivedClass).GetMethod("MethodVirtual").GetCustomAttributes(true).Length);

      Console.ReadLine();
    }

Upon running this sample you should see the following results:


Checking virtual property attributes:

BaseClass.PropertyVirtual attributes count: 1
DerivedClass.PropertyVirtual attributes count: 0

Checking virtual method attributes:

BaseClass.MethodVirtual attributes count: 1
DerivedClass.MethodVirtual attributes cound: 1

As you can ensure there are no custom attributes returned for overrided virtual property while the expected result is fetched for overrided virtual method.

You can easily find what is happening under the hood if switching to MSIL representation of the classes coded above. Here’s how the “BaseClass” attribute usage looks like:

Reflection Issue 1

As you can see on the IL level “MethodVirtual” declares “MyMethodAttribute” instance right within its own body. The same goes for “PropertyVirtual” – a getter, a setter and attribute declaration.

However both members look like the following when derived:

Reflection Issue 2

While “MethodVirtual” still refers to “base.MethodVirtual” and theoretically can invoke attribute declaration,

note that the following overriding will still work for methods:

  public class DerivedClass : BaseClass
  {
    public override string PropertyVirtual
    {
      get { return base.PropertyVirtual; }
      set { base.PropertyVirtual = value; }
    }

    public override void MethodVirtual()
    {
      //base.MethodVirtual();
    }
  }

“PropertyVirtual” is no longer related to a base implementation. We have a completely new property definition that is wired with base getter and setter. But no custom attribute declaration!

This means that the default code written for MemberInfo class won’t be able fetching anything except empty object array.

There are two workarounds I found that help switching to another MS developer(s) work, custom attribute fetching seems to be completely different in the following approaches: TypeDescriptor and old-fashioned Attribute class.

      Console.WriteLine(
        "DerivedClass.PropertyVirtual attributes count: {0}",
        Attribute.GetCustomAttributes(typeof(DerivedClass).GetProperty("PropertyVirtual"), typeof(MyPropertyAttribute)).Length);

The code above will be able to fetch and display custom attributes for a virtual property. Same can be achieved by means of property descriptors:

      Console.WriteLine(
        "DerivedClass.PropertyVirtual attributes count: {0}",
        TypeDescriptor.GetProperties(typeof(DerivedClass))["PropertyVirtual"].Attributes[typeof(MyPropertyAttribute)] != null);

However the last one will have a great performance overhead in comparison with the “Attribute.GetCustomAttributes” usage.

I’ve tested the same issue with .NET 4.0 (beta 2) and it is still there. I really hope this article will help you detecting problems with your software and eliminating issues related to this very case.

You can find the source code for this article here: MetadataIssue

 
2 Comments

Posted by on October 26, 2009 in .NET

 

Immediate Window. Generating and executing lambdas across application domains.


First I’d like to say thank you words to Igor Moochnick. His article CodeDom extensions and dynamic LINQ (string/script to LINQ emitting) gave me a lot of pleasure extending it a bit and creating shorter code in a Linq manner flow. Refer to his article for more details on CodeDom and extension methods he introduced.

Introduction

Due to some purposes I needed creating a quick implementation of some kind of "Immediate Window" that could allow me executing methods and setting properties for object instances at live running application. I tried several approaches. Among them were "Dynamic Methods", extending "Linq Dynamic API" and parsing commands and property paths to be able using them against reflection. But at last moved to code generation as the most easiest and quickest way of accomplishing the task.

Imagine you have a small TextBox based pad in the running WPF application similar to "Immediate Window" pad in Visual Studio and you are willing to execute the following snippet against the main application window:

this.Title = "NewTitle"

(this.FindName("btnExecute") as Button).SetValue(Button.ContentProperty, "NewContent")

(this.FindName("stackPanel") as StackPanel).Children.Add(new Button() { Content="xxx" })

So these lines should be transformed into the code from their string representation and executed in runtime against the running Window as the code is supposed to perform some actions against the Window instance.

What I’ve decided to do was similar to the following ideas:

1. We should have the dynamically generated method that will have our text line as it’s body (though with some possible transformations)

2. Method should accept our Window instance as parameter and perform defined text line against parameter.

3. It should return something if we are requesting the property value.

So we need here something similar to the code we could write down manually in the application wrapped into a method, for example

this.Title = "NewTitle" can be imagined as following:

public static void SetTitle(Window obj, string value)
{
  obj.Title = value;
}

So upon processing the command our environment has to generate some method called for example "SetTitle", provide the execution context (in this case a Window) and pass the command typed by the user. During that it should redirect all "this" aliases to the execution context detected (in this case all "this" should be translated into "obj"). Regard this as you are typing the body of some method without body declaration and are sure that all the missing stuff will be automatically generated.

Implementation

Code generation for this case will require dealing with separate application domains due to assembly management issues and possible memory leaks. The steps will be as following:

1. Transform and wrap user defined method/function body with appropriate method, class, namespace, etc. declarations using the CodeDom (so to get the ready to compile sources either into memory or physical file)

2. Compile the resulting Dom into the assembly (I’ll be using physical files)

3. Create a separate application domain and load compiled assembly into it.

4. Extract the generated method from the loaded assembly and invoke it against the execution context (in this case it will be a Window)

5. Free resources, unload second application domain and perform a temporary files cleanup.

This is similar to implementing an Add-In or Plug-In support for your application where your external algorithms may be added to main application, executed against some provided execution context and finally destroyed without any impact on main application.

It is obvious that you might don’t want passing whole your window across application domain boundaries (i.e. .net Remoting scenario) and you don’t want marking anything as Serializable to pass into another domain. So all we need here is getting some delegate or lambda somewhere outside for applying it against our Window locally.

Void execution

Under Void execution I mean some operation that is invoked against your execution context and doesn’t return any results. For example setting the property value.

So what happens when user types this.Title = "New Title" and presses Enter or some "Execute" button

1. "this" alias is converted into "context"

2. Lambda action is generated that performs your logic against some "context" parameter:

Action<T> action = context => { context.Title = "New Title"; };

3. Wrapper method and class container is generated that will help you getting lambda from another domain:

namespace GraphSquare.AutoScript
{
  using System;
  using System.Windows;

  public class Script
  {
    public static Action<T> CreateLambda<T>() where T : Window
    {
      Action<T> action = context => { context.Title = "New Title"; }; 
      return action;
    }
  }
}

As you can see this method will be valid for all the types based on Window, so you’ll have possibilities running the resulting lambda against your different custom windows and dialogs that inherit Window type. Note that this rules are introduced for clarification purposes and <T> constraint may differ. According to this constraint we ensure that the method will be compiled successfully as each Window object has a Title property.

CodeDom for this method is generated using the Igor’s CodeDom extensions I mentioned at the beginning of the article:

public static CodeDom GenerateActionDom<T>(string actionBody)
{
  string body = string.Format(
    "Action<T> action = context => {{ {0}; }}; return action;",
    actionBody.Replace("this", "context"));

  CodeDom c = new CodeDom();
  c.AddReference(DefaultReferences)
      .AddNamespace(DefaultNamespace)
        .Imports(DefaultImports)
          .AddClass(
            c.Class(DefaultClassName)
              .AddMethod(c.LambdaAction("CreateLambda", typeof(T).Name, null, body))
           );

  return c;
}

I won’t dwell on extension methods implementation and my contribution to it as it is more understandable when looking through the sources. Please refer to the source code link at the end of this article. In two words you are creating the source for the class mentioned earlier and specifying the <T> constraint passing the generic type provided for the method. Also "this" alias is converted into "context" and passed to CodeDom as a body of newly generated method.

Function execution

If you want invoking the command that user had typed and expect some results to be returned and displayed you might want in this case getting Func<T,K> lambda, running it against some T parameter and present the K result back to user. For simplicity purposes I’ve decided to reuse native .net "return" keyword and so being close to method body declaration in a common way it is used.

So what happens when user types return this.Title and presses Enter or some "Execute" button:

1. "this" is converted into "context"

2. Lambda function is generated that performs some logic against your "context" and returns results

Func<T, object> func = context => { return context.Title; };

3. Wrapper function is generated for getting lambda from another domain, constraints and types are defined according to the calling application

namespace GraphSquare.AutoScript
{
  using System;
  using System.Windows;

  public class Script
  {
    public static Func<T, object> CreateLambda<T>() where T : Window
    {
      Func<T, object> func = context => { return context.Title; };
      return func;      
    }
  }
}

Here’s how CodeDom is generated to get such a class:

public static CodeDom GenerateFunctionDom<T>(string fucntionBody)
{
  string body = string.Format(
    "Func<T,object> func = context => {{ {0}; }}; return func;",
    fucntionBody.Replace("this", "context"));

  CodeDom c = new CodeDom();
  c.AddReference(DefaultReferences)
      .AddNamespace(DefaultNamespace)
        .Imports(DefaultImports)
          .AddClass(
            c.Class(DefaultClassName)
              .AddMethod(c.LambdaFunction("CreateLambda", typeof(T).Name, null, body))
           );

  return c;
}

Compilation and assembly loading

I’ve provided additional method for CodeDom class providing capabilities compiling sources to file without keeping the strong reference to Assembly file at the end so providing possibilities of removing the assembly later on when it is no more in use. You’ll be calling CodeDom.CompileAssembly method that gets string parameter for full final assembly path and returns CompilerResults for results processing in order compilation exceptions.

According to common approach patterns for loading assemblies inside separate application domains you need implementing a proxy Loader class to serve also as a contract and bridge between your two domains. So exactly the loader being instantiated in the second application domain will load compiled script assembly, extract the desired lambda and execute it against your context or simply return you the lambda so you can execute it inside the main application domain whenever required.

Assembly Loader

using System;
using System.IO;
using System.Reflection;

namespace GraphSquare.Scripting.CodeDom
{
  [Serializable]
  public class AssemblyLoader
  {
    Assembly assembly;

    public AssemblyLoader()
    {
      assembly = null;
    }

    // Loads the content of a file to a byte array to
    // prevent file locking, so user can remove or modify assembly 
    // while using it loaded into memory
    static byte[] LoadFile(string filename)
    {      
      FileStream fs = new FileStream(filename, FileMode.Open);
      byte[] buffer = new byte[(int)fs.Length];
      fs.Read(buffer, 0, buffer.Length);
      fs.Close();
      return buffer;
    }
    
    public void Load(string assemblyPath)
    {
      byte[] rawAssembly = LoadFile(assemblyPath);
      this.assembly = Assembly.Load(rawAssembly);
    }
  }
}

This is a basic implementation providing only assembly loading facilities. It will be extended later on. Note that it is situated in the separate assembly in order to serve a contract between different domains.

To create additional application domain our main application will have the following method:

private static AppDomain CreateNewDomain()
{
  // Create a simple application domain
  return AppDomain.CreateDomain(
    typeof(AssemblyLoader).Name, 
    AppDomain.CurrentDomain.Evidence, 
    AppDomain.CurrentDomain.SetupInformation);
}

This is the most basic implementation of second domain that takes Evidence and Setup Information from the main one and so loads all the references that the main domain has. In our case it will also load the contract library with the AssemblyLoader so we could establish communication between domains easily.

To create a valid loader in the second domain and loading external assembly file into it we’ll be using the following method:

private static AssemblyLoader CreateLoader(AppDomain domain, string assemblyPath)
{
  // Create a new instance of AssemblyLoader in the separate domain
  AssemblyLoader loader = domain.CreateInstanceAndUnwrap(
    "GraphSquare.Scripting", 
    typeof(AssemblyLoader).FullName) as AssemblyLoader;

  // Load compiled assembly into the loader
  loader.Load(assemblyPath);      
  return loader;
}

Extracting compiled lambdas

Fine now we have two possible members of the generated wrapper class needed to be discovered and loaded (action and function) to be used in lambda generation. As we have a separate domain with compiled assembly loaded into it and know exactly the namespace, type and name of the wrapper class and even the name of the method to generate lambda – "CreateLambda" (as we did it’s generation actually) the process of it’s loading and invokation becomes quite trivial.

Using Assembly.GetType(string…) method it becomes easy loading types and activating objects. For each type you can call GetMethod(…) to get the necessary method info to be used in invokaction. Thus discovering and invoking generic methods are a bit different. Here’s how can extract our generated "CreateLambda" method from AssemblyLoader:

public Action<T> GetLambdaAction<T>(string type, string action)
{
  // Extract type method info from assembly
  MethodInfo methodInfo = assembly.GetType(type).GetMethod(
    action, 
    BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

  // Create generic arguments for generic method
  Type[] genericArguments = new Type[] { typeof(T) };

  // Create generic function info for invokaction
  MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);

  // Invoke generic function and get return value as Action<T>
  Action<T> returnValue = (Action<T>)genericMethodInfo.Invoke(null, null);

  // return results
  return returnValue;
}

As compiled assembly is loaded into the second application domain where the AssemblyLoader is already present so loader is capable of discovering our generated type to work with further.

When executed this method returns us ready to use lambda with the body we defined earlier. In most of the cases guess you’ll have to get compiled lambda, execute it against some context and dispose it. So we can introduce two more members for the AssemblyLoader for invoking actions and functions directly from AssemblyLoader.

public bool InvokeLambdaAction<T>(string type, string action, T argument)
{      
  // Extract type method info from assembly
  MethodInfo methodInfo = assembly.GetType(type).GetMethod(
    action, 
    BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

  // Create generic arguments for generic method
  Type[] genericArguments = new Type[] { typeof(T) };

  // Create generic function info for invokaction
  MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);

  // Invoke generic function and get return value as Action<T>
  Action<T> returnValue = (Action<T>)genericMethodInfo.Invoke(null, null);
  if (returnValue != null)
  {
    // Execute received action against argument provided
    returnValue(argument);
    return true;
  }
  return false;
}

public object InvokeLambdaFunc<T>(string type, string function, T argument)
{
  // Extract type method info from assembly
  MethodInfo methodInfo = assembly.GetType(type).GetMethod(
    function, 
    BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

  // Create generic arguments for generic method
  Type[] genericArguments = new Type[] { typeof(T) };
  // Create generic function info for invokaction
  MethodInfo genericMethodInfo = methodInfo.MakeGenericMethod(genericArguments);
  // Invoke generic function and get return value as Func<T,K>
  Func<T, object> func = (Func<T, object>)genericMethodInfo.Invoke(null, null);
  //return execution result of func against the argument provided
  return func(argument);
}

As you can see both methods are not so different. Both of them require full type name of the class holding the method, method or function name to be searched and argument of your needed type to be processed as an execution context.

So the process of executing user typed command that doesn’t require results can be as following (at main application side):

// Generate CodeDom
CodeDom c = CodeDomSmith.GenerateActionDom<T>(methodBody);
string assemblyPath = GenerateAssemblyPath();
// Compile sources into assembly
CompilerResults results = c.CompileAssembly(assemblyPath);

// Process compilation errors
if (results.Errors.Count > 0)
  return false;

// Create separate application domain
AppDomain domain = CreateNewDomain();
// Create loader proxy based on application domain created 
// and load compiled assembly to it
AssemblyLoader proxy = CreateLoader(domain, assemblyPath);

try
{
  // Extract and execute lambda against the context object
  proxy.InvokeLambdaAction<T>(
    CodeDomSmith.DefaultClassFullname, "CreateLambda", context);
}
catch (Exception ex)
{
  return false;
}

if (domain != null)
{
  // Unload and dispose second application domain
  AppDomain.Unload(domain);
  domain = null;
}
if (File.Exists(assemblyPath))
  // Remove used assembly
  File.Delete(assemblyPath);

The only difficulty that arise is determining what type of lambda to generate according to the user input. I mean how to decide whether he wants executing a Void or getting some results. I’ve accomplished that again as I’ve mentioned earlier by reusing the "return" keyword. So due to this sample when the user starts the command from "return" keyword we are generating the Func<T,K> otherwise Action<T>

Samples:

Here’s the sample environment I’ve prepared for the article:

image

You’ll have the Window that will be assigned as an execution context for all upcoming commands typed in the bottom TextBox control.

"Execute" button compiles and executes the command putting all the output to the "Debug window" TextBox control. You will also be able to see the content of the class generated and executed for development purposes.

"Help" button shows a list of dummy samples that can be copy-pasted and tested against the window.

"Execution Result" TextBox shows the results of the command execution. This can be either "void()" if method was compiled and executed or some value.

Here’s the possible result of execution of the command:

this.Title = "GraphSquare Scripting"

image

Note that execution result is "void()" as we didn’t try to return anything and the Title of the window has changed from "Window1" to "GraphSquare Scripting"

Let’s try running the application once again and executing the following command:

return this.Title == "Window1"

image

As you can see command was successfully evaluated and returned "True" value that is a valid result as the Window’s Title property value is "Window1"

Now let’s try typing a command that cannot be valid and compiled by default, something like

this.ExecuteMissingCommand()

If you remember the execution context for our commands is being set to Window. This means that as a common Window class does not contain the method typed the resulting code won’t be compiled and cannot be executed against a window. This brings to a compilation exception:

image

The exception message will be the following:

error CS1061: ‘T’ does not contain a definition for ‘ExecuteMissingCommand’ and no extension method ‘ExecuteMissingCommand’ accepting a first argument of type ‘T’ could be found (are you missing a using directive or an assembly reference?)

This is mainly for your debugging purposes. The real life implementation can hide the developer oriented details and present more user-friendly message.

Also as you might have noticed all lambda generation methods are generic ones, for example

public static bool InvokeContextMethod<T>(T context, string methodBody, ...)

this means that you can specify different execution contexts or even change them on the fly.

As for the suggestions I think there could be some contracts to serve as execution context instead of applications or windows. Remember not providing the end-user more power that he is expected to have. As you will see from the samples provided within the "Help" section of this tool you can simply add or remove new elements to the window, rename buttons, etc.

Hope this helps someone enriching his tools with runtime command support or Debug features.

The source code for the article can be found here

 
Leave a comment

Posted by on April 26, 2008 in .NET

 

Using Reflection for binding classes to .ini files


I’m sure the title of the article will appear strange and weird for you. Why should I use ".ini" files nowadays having all the required configuration infrastructure and a pile of Microsoft patterns for doing the configuration stuff. I definitely need to provide some background…

2 of my recent jobs were tightly connected with gambling industry. Me and my partner Alex (you can jump to his space from my "Friends" space section) were working under enormous pressure developing software for video slots and roulette tables, hardware programming and Windows XP Embedded targeting. Guess I’ll dwell on it some other day when I’ll be in a good mood for speaking about XNA and OpenGL, maths, combinational and permutation algorithms, etc.

Once we were finishing another video slot machine. We’ve been working for a whole a day and a night in a non-stop mode and at last when all the tests were performed except one. The machine couldn’t survive the power failure test. All the settings were lost, settings files were damaged and registry hives remained unchanged. It took me several hours to find the solution for that and at the end I made a strong decision for  ".ini" files usage as the best approach for storing configuration settings when speaking of embedded systems.

Weak sides of the configuration approaches when getting power failure situation

1. Registry

I must state that registry really sucks when speaking about sensitive information. It’s caching driver mechanism lives it’s own life. It usually saves information on Windows "Reset" or "Shut Down" performed in a proper way. Try to configure desktop and plug off the power manually. You’ll see the old preferences and nothing will be saved. So registry takes the first place to be avoided.

2. Application Settings

When speaking .net application settings I must admit that the older framework becomes the harder it gets to understand what is going on with the lowest layers. It happens very often that you have to dig all the abstraction layers, providers, wrappers and services to find out "Where the hell is IO operations"? As always Reflector stays the best tool to find out the truth. This approach doesn’t survive the power failures either. Almost all the times you get the damaged data because of incomplete flush operations.

3. IO operations and file streams

The same as above. You can do everything you want with the file streams and use any classes from System.IO namespace but the result is the same as mentioned. The files damage from time to time because of incomplete flush operations though not so often as using Application Settings approach.

You may wonder why do I argue against the above approaches and what kind of code did I do that flushing was my horror. The answer is quite simple.

According to some strict hardware specifications my application had to control the work of bill acceptor, coin acceptor and several money counters. Information should be accepted processed and sent at least every 7-10 ms at a separate thread. So each 7-10 ms I had a strong need of dumping and caching to hard drive to protect machine from "power failure hacks". I’ve realized at the end of testing that the best way of solving the task that was keeping closer to kernel.

After testing the ".ini" via the kernel32 I found out the amazing thing that our machine survived all the power failures. We couldn’t damage our settings with that approach anymore.

Though I’ll give you not the same sample we used, guess you’ll get the main idea. I’ve made it more object oriented and more complicated to cover the reflection power as well.

 

Going unmanaged

The ".ini" processing is rather simple and has always the same declaration to remember

#region External API
[DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int GetPrivateProfileString(
  string lpAppName,
  string lpKeyName,
  string lpDefault,
  string lpReturnString,
  int nSize,
  string lpFilename);

[DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
private static extern int WritePrivateProfileString(
  string lpAppName,
  string lpKeyName,
  string lpString,
  string lpFilename);        
#endregion

GetPrivateProfileStringW gets the value from file and WritePrivateProfileStringW writes a value to it.

So as you can see you can quickly save the required data to file from any part of code. But imagine that you have about 300 values to be processed all over the application. Even if you create one class for holding it, the process of loading and saving properties’ values will bring you crazy.

As I went crazy very quickly I’ve decided to automate the process using one custom attribute and all the power of reflection.

 

Moving params to custom attribute

You have to remember 4 strings to store any property to file: Filename (physical path of ".ini" file, will be created if not found), Application Name (anything that will be wrapped with "[ ]" brackets, don’t know why MS didn’t call it category or something like that), Key Name and Key Value. First 3 of them are usually static for an application. So the idea is very simple – why not to move them to a custom attribute.

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class IniFileKeyBinding : Attribute
{
    private string _Section;
    public string Section
    {
        get { return _Section; }
        set { _Section = value; }
    }

    private string _Key;        
    public string Key
    {
        get { return _Key; }
        set { _Key = value; }
    }

    private string _DefaultValue;
    public string DefaultValue
    {
        get { return _DefaultValue; }
        set { _DefaultValue = value; }
    }


    public IniFileKeyBinding(string section, string key)
    {
        _Section = section;
        _Key = key;            
    }
}

So now you can mark some properties in our class to be ready for saving/loading. Here comes my settings provider to hold this stuff. There will be no supplementary information because it is overcommented I think :)

 

IniFileSettingsProvider.cs

using System;
using System.Reflection;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;

namespace IniSettings
{
    public class IniFileSettingsProvider
    {
        #region External API
        [DllImport("KERNEL32.DLL", EntryPoint = "GetPrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern int GetPrivateProfileString(
          string lpAppName,
          string lpKeyName,
          string lpDefault,
          string lpReturnString,
          int nSize,
          string lpFilename);

        [DllImport("KERNEL32.DLL", EntryPoint = "WritePrivateProfileStringW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)]
        private static extern int WritePrivateProfileString(
          string lpAppName,
          string lpKeyName,
          string lpString,
          string lpFilename);        
        #endregion

        /// <summary>
        /// This is a flag for denying data changes. It is mandatory setting public properties using reflection.
        /// </summary>
        protected bool locked;

        #region Local properties
        private string _FileName;
        public string FileName
        {
            get { return _FileName; }
            protected set { _FileName = value; }
        } 
        #endregion

        #region Constructors
        public IniFileSettingsProvider(string filename)
        {
            if (string.IsNullOrEmpty(filename))
                throw new ArgumentNullException("filename");

            _FileName = filename;
            DataBind();
        } 
        #endregion

        /// <summary>
        /// Load and setup all the properties marked with IniFileKeyBinding custom attribute
        /// </summary>
        protected virtual void DataBind()
        {
            // Get current object type
            Type t = this.GetType();

            // Get our mandatory field for temporary locking of data changing
            FieldInfo f_locked = t.GetField("locked", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase);

            // If there's no locking field we don't support data binding
            if (f_locked == null)
                throw new NotSupportedException("Data binding is not supported for objects that do not support property locking!");

            // Lock data changes by setting our lock to true
            f_locked.SetValue(this, true);

            // Get the type of our custom attribute
            Type aType = typeof(IniFileKeyBinding);
            // Get all the public propeties of the required type
            PropertyInfo[] properties = t.GetProperties(); 

            // Configure a flag determining whether to load default values for properties if requested ini file was not found
            bool defaultOnly = !File.Exists(_FileName);

            // Start looping the public properties
            foreach (PropertyInfo pi in properties)
            {
                // Skip processing readonly properties
                if (!pi.CanWrite) continue;

                // If the propety exposes our IniFileKeyBinding attribute...
                if (pi.IsDefined(aType, false))
                {
                    // cee1faffe2ebffe5ec efe5f0e5ece5ededf3fe e4ebff f5f0e0ede5ede8ff e7ede0f7e5ede8ff e8e7 ini f4e0e9ebe0
                    // String holder for ini file value (values are always got as string)
                    string iniValue;
                    // Object holder for the final value or the property
                    object propValue;                    

                    // Extract our IniFileKeyBinding attribute info for current property looped
                    IniFileKeyBinding attrInfo = (IniFileKeyBinding)pi.GetCustomAttributes(typeof(IniFileKeyBinding), false)[0];                    
                    
                    // If we don't require default value initialization
                    if (!defaultOnly)
                        // Load ini file value based on attribute info
                        iniValue = GetValue(this._FileName, attrInfo.Section, attrInfo.Key, attrInfo.DefaultValue);
                    else
                        // else set the predefined default value
                        iniValue = attrInfo.DefaultValue;
                    
                    // If we got an empty string... we get the empty string in 2 cases:
                    // 1. Value doesn't exist at all in the ini file
                    // 2. Value is supposed to be empty
                    // We must test this in order to avoid problems...
                    if (string.IsNullOrEmpty(iniValue))
                    {
                        // If current property is of string type
                        if (pi.PropertyType == typeof(string))
                            // If the default value is not defined in the custom attribute we set null value otherwise the predefined attribute value
                            propValue = (attrInfo.DefaultValue == null) ? null : attrInfo.DefaultValue;
                        else
                            // Otherwise we setup the possible default value of given type
                            propValue = @default(pi.PropertyType);
                    }
                    else
                        // Change the type of received value to the current propery type
                        propValue = Convert.ChangeType(iniValue, pi.PropertyType, CultureInfo.InvariantCulture);
                    
                    // Finally set the property value
                    pi.SetValue(this, propValue, null);
                }
            }

            // Unlock the data changing by removing the lock
            f_locked.SetValue(this, false);
        }
                
        /// <summary>
        /// Svae the property value to corresponding ini file. Property should be marked with IniFileKeyBinding attribute to be saved.
        /// </summary>
        /// <param name="propertyName">The name of the property to be saved.</param>
        protected virtual void SaveProperty(string propertyName)
        {
            // Skip the save procedure in case of lock enabled
            if (locked) return;

            if (string.IsNullOrEmpty(propertyName))
                throw new ArgumentNullException("propertyName");

            // Get the type for current object
            Type t = this.GetType();    

            // Extract all the neccessary information for the given property
            PropertyInfo pi = t.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);

            if (pi == null)
                throw new NullReferenceException("Class does not contain property named '" + propertyName + "'.");

            // Get the type of our custom attribute
            Type aType = typeof(IniFileKeyBinding);

            if (!pi.IsDefined(aType, false))
                throw new NotSupportedException("Saving properties not marked with IniFileKeyBinding attribute is not supported by this method.");

            // Extract our custom attribute from the given property
            IniFileKeyBinding attrInfo = (IniFileKeyBinding)pi.GetCustomAttributes(typeof(IniFileKeyBinding), false)[0];
            // Get the property's value
            object propValue = pi.GetValue(this, null);
            // Convert the property value to string for saving to ini file
            string iniValue = (string)Convert.ChangeType(propValue, typeof(string), CultureInfo.InvariantCulture);
            // Finally save the string value to ini file
            WritePrivateProfileString(attrInfo.Section, attrInfo.Key, iniValue, _FileName);
        }

        /// <summary>
        /// Save all the properties marked with IniFileKeyBinding attribute to file.
        /// </summary>
        public virtual void Save()
        {
            Save(_FileName);
        }

        /// <summary>
        /// Save all the properties marked with IniFileKeyBinding attribute to file.
        /// </summary>
        /// <param name="filename">File to be used for saving the properties.</param>
        public virtual void Save(string filename)
        {
            if (string.IsNullOrEmpty(filename))
                throw new ArgumentNullException("filename");

            Type t = this.GetType();
            Type aType = typeof(IniFileKeyBinding);
            PropertyInfo[] properties = t.GetProperties();

            foreach (PropertyInfo pi in properties)
            {
                if (pi.IsDefined(aType, false))
                {
                    IniFileKeyBinding attrInfo = (IniFileKeyBinding)pi.GetCustomAttributes(typeof(IniFileKeyBinding), false)[0];
                    object propValue = pi.GetValue(this, null);
                    string iniValue = (string)Convert.ChangeType(propValue, typeof(string), CultureInfo.InvariantCulture);
                    WritePrivateProfileString(attrInfo.Section, attrInfo.Key, iniValue, filename);
                }
            }
        }

        #region Utils
        // This method loads the value from the ini file
        private static string GetValue(string filename, string section, string key, string defaultValue)
        {
            string retval = new string(' ', 1024);
            GetPrivateProfileString(section, key, defaultValue, retval, 1024, filename);
            return retval.Split('')[0];            
        }

        // Get the default value for the type provided
        public static object @default(Type t)
        {
            if (!t.IsValueType)
                return null;
            else
                return Activator.CreateInstance(t);
        }
        #endregion
    }
}

This will be the base class for your own configuration holders. All is left to do for you is implementation of your own settings providers based on it. I’ll give you the basic sample.

MySettings.cs

using System;

namespace SomeMyProjectNamespace
{
    public class MySettings : IniFileSettingsProvider
    {
        public MySettings(string filename) : base(filename) { }

        private int _ScreenWidth;
        [IniFileKeyBinding("Graphics", "ScreenWidth", DefaultValue="1024")]
        public int ScreenWidth
        {
            get { return _ScreenWidth; }
            set 
            { 
                _ScreenWidth = value;
                SaveProperty("ScreenWidth");                
            }
        }

        private int _ScreenHeight;
        [IniFileKeyBinding("Graphics", "ScreenHeight")]
        public int ScreenHeight
        {
            get { return _ScreenHeight; }
            set 
            { 
                _ScreenHeight = value;
                SaveProperty("ScreenHeight");
            }
        }
    }
}

All the properties are automatically bound upon class creation. All your properties are automatically saved upon value changes. Values are being saved according to an attribute configuration. You should only take care of what to mark and what default values to provide.

This approach saved a lot of my time and hope will help someone too.

 
Leave a comment

Posted by on October 23, 2007 in .NET

 

Getting Best of Generics for Type Conversion


Not a long time ago I worked hard with reflection and type conversion and decided to save the time getting some C# snippets for doing the type conversion. I don’t like the standard System.Convert to be honest so I was looking for some generics implementation. Imagine my surprise when Googling gave me nothing interesting towards my request. This is rather common stuff I guess and there was so much talking about generics that I would better think that Google was buggy that day ;)

I managed to find only one sample that was almost close to what I wanted. I won’t give the sources but in two words: the guy implemented a generic method that was getting a type. Inside he did the switch case or multiple if-else clauses to check it’s generic attributes and give the appropriate result. Good beginning and so stupid end :)

I’ve placed my approach to the couple of forums some months before I started to fill my space, and to be honest I didn’t check today whether any good solutions appeared, but I strongly want to put it here…

So meet the raw generics wrapper for System.Convert. I think this is the smallest implementation possible in this case.

public static class Converter
{
    public static T Convert<T>(object value) where T : IConvertible
    {
        return (T)System.Convert.ChangeType(value, typeof(T), System.Globalization.CultureInfo.InvariantCulture);
    }

    public static T Convert<T, K>(K value)
        where T : IConvertible
        where K : IConvertible
    {
        return (T)System.Convert.ChangeType(value, typeof(T), System.Globalization.CultureInfo.InvariantCulture);
    }
}

No multiple ifs and no switches :)

Hope it will save someone’s time one day…

 
Leave a comment

Posted by on October 23, 2007 in .NET

 
 
Follow

Get every new post delivered to your Inbox.

Join 71 other followers

%d bloggers like this: