Search

A generic HTTP Module for Microsoft Dynamics CRM

When reading the CRM newsgroups you often find questions like "How can I make JavaScript functions available in all forms" or "Is it possible to change the color of a row in a view". There are two types of answers:
  1. Not in any supported way.
  2. Use an HTTP module.
While answer number 2 is unsupported as well, it is a good alternative. However beside the answer "Use an HTTP module" I never saw an example or any hints on how to do it..

What is an HTTP module? 

An HTTP module is an extension for IIS and it's general purpose is to hook into the stream between IIS and the client to log or modify data. A request to a file on the web server is first served by IIS. It loads the file and if it contains server code (ASP, ASP.NET), the code is executed. Once that's done, the document is sent to the client.
If you install an HTTP module, it is invoked on each request, so after all of the server side processing is done, the response stream is passed to all HTTP modules. After all of them have finished, the final result is passed to the client. Each HTTP module can change the content of the stream, so let's start to build a module allowing us to include additional JavaScript include files and links to cascading style sheets.

Implementing the IHttpModule interface

Creating an HTTP module is very easy. All you have to do is to implement the System.Web.IHttpModule interface. It only has two methods, Init and Dispose. Unless you have something to dispose, you only need to care about the Init method. It is called once and serves as the general initialization routine. You use the context parameter to add event handlers and they are invoked whenever a request is made. That's said an HTTP module must be fast, otherwise it may slow down your server dramatically.

Here's the implementation I'm using is this sample:


Our only subscription is BeginRequest (our OnBeginRequest event handler). Once applied, the OnBeginRequest method is called for every single file requested by a client, containing images, JavaScript files, style sheets, HTML controls and of course ASP.NET pages. We all know that CRM forms are ASP.NET web pages and they all have an extension of ".aspx". Only these must be evaluated and therefore we only apply a filter for .aspx pages.


What is a filter?

A filter (the context.Response.Filter) is a Stream. It is not a standard stream, like a FileStream or a MemoryStream. It is a class deriving from the abstract Stream class. If a filter is applied, the stream operations are passed to the filter instead of using the response stream directly.

Here's the interesting part of the CrmFilter class:

Our only subscription is BeginRequest (our OnBeginRequest event handler). Once applied, the OnBeginRequest method is called for every single file requested by a client, containing images, JavaScript files, style sheets, HTML controls and of course ASP.NET pages. We all know that CRM forms are ASP.NET web pages and they all have an extension of ".aspx". Only these must be evaluated and therefore we only apply a filter for .aspx pages.

What to include?

The easiest implementation would be to include a single link to a JavaScript file in all ASP.NET pages. However I thought it would be better to differentiate a bit and added a very simple configuration file:
This configuration file must be placed in /MyConfig/config and must be named config.xml. However you can change that in the Config class:
string configFilePath = webSitePath + "MyConfig\\config\\config.xml";
The above configuration file defines the following: 
  • Include files listed in the All section are added to all HTML files. In the sample configuration above, the following line is injected into the head of the html document: <link rel="stylesheet" type="text/css" href="/MyConfig/config/css/styles.css">.
  • Include files listed in the AllEntities section are added to all CRM forms. That's the reason why I'm looking for the entity type name when parsing the HTML content. In the sample configuration above, the following line is injected into the head of the html document:<script language="javascript" src="/MyConfig/config/scripts/global.js" />
  • Finally, include files listed in the Entities section are added only if the HTML document is a CRM form of the specified type. In the sample configuration above, the following line is injected: <script language="javascript" src="/MyConfig/config/scripts/account.js" />.

You can add as many entries as you like. The xml structure simply is a serialized Config class and the "Current" accessor deserializes it. Feel free to change it to your needs. Also note that I have included some very basic logging. By default it logs to C:\CRMModule\log.txt and only when running the debug version. You can change this in the Log class.

How to install it

You have to place the assembly into two locations on your CRM server:
  1. /bin
  2. /mscrmservices/bin

If you have modified the web to contain additional bin directories, you may have to copy them in there too. As an alternative you can add the assembly to the GAC. For this reason I added a strong name to the assembly, as unsigned assemblies are not allowed in the GAC.
Finally, open the web.config in the CRM root and make the highlighted changes:

<system.web>
    ...
    <httpModules>
        <add name="MyConfig.Crm.HttpModule" type="MyConfig.Crm.HttpModule, MyConfig.Crm.HttpModule, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1ed2e80199a69f48"/>
    </httpModules>
</system.web>

Note that there may be two system.web sections, one for the main CRM application and one for the reporting services, which is identified by the following xml element:
<location path="Reports">
Be sure to add the module to the main CRM configuration and do not put it into the reports section.

And finally: How to test it

Create a new JavaScript file and add the following function:
function MyConfig_Test(message) {
    alert(message);
}

Save it at /MyConfig/config/scripts/global.js.

You can of course use any name for your function, but to not conflict with functions in other include files, I'm using a unique prefix. I leave it up to you to follow that or not.

Use the following configuration file (/MyConfig/config/config.xml):
<?xml version="1.0" encoding="utf-8" ?>
    <Config xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <All/>
        <AllEntities>
            <Include href="/MyConfig/config/scripts/global.js" type="js" />
        </AllEntities>
        <Entities/>
</Config>

Open a CRM form and add the following to your OnLoad event:

MyConfig_Test("It works!");

If it doesn't work, try resetting IIS. If it indeed does work and you come across some cool functions, I would love to see them to build some kind of function repository that everyone can download (of course the poster will get full credits for it).

Playing with the old CDO object

Here is an example that might work if the system supports CDO object

C# Events



/*C# Events
______________

Events notify the program when the state of the control changes.*/

/*Declaring an Event:
Events are decalared by uding a delegate object. A delegate object encapsulates
a method so that it can be called anonymously from the application. When an event
occurs, the client application invokes the corresponding delegate for the event
to which the delegate is attached.
Before you declare an event inside a class you need to declare a delegate by
using the delegate keyword. For every event in C#, there is a corresponding
method declaration that handles it.*/

public delegate void ModifiedEventhandler();

/*The delegate type consists of the set of arguments that it passes to the method
that handles the event. Multiple events can share the same delegate. In other
words by using a single delegate, you can invoke different methods corresponding
to different events.*/

public event ModidifedEventhandler Changed;

/*The event is declared using the event keyword. The event keyword follows the
access modifiers. Events are usually public, but you can specify any access modifier.
The event is declared as a field of the delegate type. The above example displays
the declaration of the event named changed as a field of the ModifiedEventHandler
delegate type.*/

if(Changed != null)
Changed();

/*After you declare an event in a class, the class treats the event as a field of
the declared delegate. Declaring events as a field of the delegate type allows
you to call the event directly like a method. The field Changed refers to a
delegate that is called when the event occurs. The above example shows that an
event is involved after checking whether or not the event is null. You invoke
an event only when it's not null.*/

You can perform the following operations on an event that is involved
1. Compose a new delegate onto that field
2. Remove a delegate from a composed field

List.Changed += new ChangedEventHandler(ListChanged);
List.Changed -= new ChangedEventHandler(ListChanged);

/*You composed a delegate by using the + operator.You remove a delegate by using
the - operator. To receive the event invocation, the client application, the
application that raises the event, creates a delegate of the event type. This
event refers to the method that is called when the event in invoked. Then, the
client application composes the new delegate with the existing delegate in the
event. When the client application no longer receives event invocations, it
removes the delegates from the event.*/

protected virtual void onChanged()
{
if(Changed != null)
Changed();
}

public override int Add(object value)
{
int i = base.Add(value);
onChanged();
return i;
}

/*You can invoke events only within the class in which the event is decalred.
You cannot directly call the events declared in the base class from the
derived class. To call a base class event from derived class, you create a
protected method in the application that invokes the event. For Example: the
state of a list box changes whenever you add an item to the list or delete an
item from the list.*/

Get the Computer, User and domain name using Javascript

This come quite easy, you just have to initialize an ActiveX object for "WScript.Network"
 
/* Jscript - ComputerName, Username & DoaminName */

var oWshNet = new ActiveXObject("WScript.Network");
var computerNmae = oWshNet.ComputerName;
var userName = oWshNet.UserName;
var domainName = oWshNet.UserDomain;


Note: This works only with ActiveX in IE.

Interface in C#

An interface contains only the signatures of methods, delegates or events.
  • The implementation of the methods is done in the class that implements the interface, as shown in the following example.
  • An interface can be a member of a namespace or a class and can contain signatures of Methods.properties.Indexes & Events.
  • An interface can inherit from one or more base interfaces.
  • When a base type list contains a base class and interfaces, the base class must come first in the list.
  • A class that implements an interface can explicitly implement members of that interface.
  • An explicitly implemented member cannot be accessed through a class instance, but only through an instance of the interface.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    interface ISampleInterface
    {
    void SampleMethod();
    }

    namespace Tutorial
    {
    class Program : ISampleInterface
    {
    // Explicit interface member implementation:
    void ISampleInterface.SampleMethod()
    {
    // Method implementation.
    Console.WriteLine("Method implementation.");
    }

    static void Main(string[] args)
    {
    // Declare an interface instance.
    ISampleInterface obj = new Program();
    // Call the member.
    obj.SampleMethod();
    Console.ReadLine();
    }
    }
    }





    The following example demonstrates interface implementation. In this example, the interface IPoint contains the property declaration, which is responsible for setting and getting the values of the fields.

    The class Point contains the property implementation. random



    using System;
    using System.Collections.Generic;
    using System.Text;

    interface IPoint
    {
    // Property signatures:
    int x
    {
    get;
    set;
    }
    int y
    {
    get;
    set;
    }
    }// interface

    class Point : IPoint
    {
    // Fields:
    private int _x;
    private int _y;
    // Constructor:
    public Point(int x, int y)
    {
    _x = x;
    _y = y;
    }
    // Property implementation:

    public int x
    {
    get
    {
    return _x;
    }
    set
    {
    _x = value;
    }
    }
    public int y
    {
    get
    {
    return _y;
    }
    set
    {
    _y = value;
    }
    }
    }// class

    namespace IndiLogiX.Tutorial
    {
    class Program
    {
    static void PrintPoint(IPoint p)
    {
    Console.WriteLine("x={0}, y={1}", p.x, p.y);
    }
    static void Main(string[] args)
    {
    Point p = new Point(2, 3);
    Console.Write("My Point: ");
    PrintPoint(p);
    Console.ReadLine();
    }
    }
    }



  • Collection Class in C#

    This tutorial shows how to implement a collection class that can be used with the foreach statement.

    The foreach statement is a convenient way to iterate over the elements of an array. It can also enumerate the elements of a collection, provided that the collection class has implemented the System.Collections.IEnumerator and System.Collections.IEnumerable interfaces.

    Example 1

    The following code sample illustrates how to write a collection class that can be used with foreach. The class is a string tokenizer, similar to the C run-time function strtok.

     

    // tokens.cs
    using System;
    // The System.Collections namespace is made available:
    using System.Collections;

    // Declare the Tokens class:
    public class Tokens : IEnumerable
    {
    private string[] elements;

    Tokens(string source, char[] delimiters)
    {
    // Parse the string into tokens:
    elements = source.Split(delimiters);
    }

    // IEnumerable Interface Implementation:
    // Declaration of the GetEnumerator() method
    // required by IEnumerable
    public IEnumerator GetEnumerator()
    {
    return new TokenEnumerator(this);
    }

    // Inner class implements IEnumerator interface:
    private class TokenEnumerator : IEnumerator
    {
    private int position = -1;
    private Tokens t;

    public TokenEnumerator(Tokens t)
    {
    this.t = t;
    }

    // Declare the MoveNext method required by IEnumerator:
    public bool MoveNext()
    {
    if (position < t.elements.Length - 1)
    {
    position++;
    return true;
    }
    else
    {
    return false;
    }
    }

    // Declare the Reset method required by IEnumerator:
    public void Reset()
    {
    position = -1;
    }

    // Declare the Current property required by IEnumerator:
    public object Current
    {
    get
    {
    return t.elements[position];
    }
    }
    }

    // Test Tokens, TokenEnumerator

    static void Main()
    {
    // Testing Tokens by breaking the string into tokens:
    Tokens f = new Tokens("This is a well-done program.",
    new char[] {' ','-'});
    foreach (string item in f)
    {
    Console.WriteLine(item);
    }
    }
    }

     


    Unboxing in C#

    Unboxing is an explicit conversion from the type object to a value type or from an interface type to a value type that implements the interface. An unboxing operation consists of:
  • Checking the object instance to make sure that it is a boxed value of the given value type.
  • Copying the value from the instance into the value-type variable.
    The following statements demonstrate both boxing and unboxing operations:
     int i = 123;      // a value type 
    object o = i; // boxing
    int j = (int)o; // unboxing

    The following figure demonstrates the result of the previous statements.
    Unboxing Conversion
    UnBoxing Conversion graphic


  • For the unboxing of value types to succeed at run time, the item being unboxed must be a reference to an object that was previously created by boxing an instance of that value type. Attempting to unbox null causes a NullReferenceException. Attempting to unbox a reference to an incompatible value type causes an InvalidCastException.

    Boxing in C#

    Boxing is used to store value types in the garbage-collected heap. Boxing is an implicit conversion of a value type to the type object or to any interface type implemented by this value type. Boxing a value type allocates an object instance on the heap and copies the value into the new object.
    Consider the following declaration of a value-type variable:
    The following statement implicitly applies the boxing operation on the variable i:
    int i = 123;
    
    
    
    // Boxing copies the value of i into object o. 
    object o = i;
    
    
    

    The result of this statement is creating an object reference o, on the stack, that references a value of the type int, on the heap. This value is a copy of the value-type value assigned to the variable i. The difference between the two variables, i and o, is illustrated in the following figure.
    Boxing Conversion

    BoxingConversion graphic
    It also possible to perform the boxing explicitly as in the following example, but explicit boxing is never required:
    int i = 123;
    object o = (object)i;  // explicit boxing
    
    
    
    
    
    Example:
    class TestBoxing
    {
    static void Main()
    {
    int i = 123;
    
    // Boxing copies the value of i into object o. 
    object o = i;  
    
    // Change the value of i.
    i = 456;  
    
    // The change in i does not effect the value stored in o.
    System.Console.WriteLine("The value-type value = {0}", i);
    System.Console.WriteLine("The object-type value = {0}", o);
    }
    }
    /* Output:
    The value-type value = 456
    The object-type value = 123
    */
    
    
    
    
    The following example demonstrates a case of invalid unboxing and the resulting InvalidCastException. Using try and catch, an error message is displayed when the error occurs.
    class TestUnboxing
    {
    static void Main()
    {
    int i = 123;
    object o = i;  // implicit boxing 
    
    try
    {
    int j = (short)o;  // attempt to unbox
    
    System.Console.WriteLine("Unboxing OK.");
    }
    catch (System.InvalidCastException e)
    {
    System.Console.WriteLine("{0} Error: Incorrect unboxing.", e.Message);
    }
    }
    }
    
    This program outputs:
    Specified cast is not valid. Error: Incorrect unboxing.
    If you change the statement:
    int j = (short) o;
    to
    int j = (int) o;
    the conversion will be performed, and you will get the output:
    Unboxing OK.

    Boxing and Unboxing in C#

    Boxing is the process of converting a value type to the type object or to any interface type implemented by this value type. When the CLR boxes a value type, it wraps the value inside a System.Object and stores it on the managed heap. Unboxing extracts the value type from the object. Boxing is implicit; unboxing is explicit. The concept of boxing and unboxing underlies the C# unified view of the type system, in which a value of any type can be treated as an object. In the following example, the integer variable i is boxed and assigned to object o.



    int i = 123;
    // The following line boxes i.
    object o = i;






    The object o can then be unboxed and assigned to integer variable i:


    o = 123;
    i = (int)o; // unboxing






    The following examples illustrate how boxing is used in C#.


    // String.Concat example.
    // String.Concat has many versions. Rest the mouse pointer on
    // Concat in the following statement to verify that the version
    // that is used here takes three object arguments. Both 42 and
    // true must be boxed.
    Console.WriteLine(String.Concat("Answer", 42, true));


    // List example.
    // Create a list of objects to hold a heterogeneous collection
    // of elements.
    List mixedList = new List();

    // Add a string element to the list.
    mixedList.Add("First Group:");

    // Add some integers to the list.
    for (int j = 1; j < 5; j++)
    {
    // Rest the mouse pointer over j to verify that you are adding
    // an int to a list of objects. Each element j is boxed when
    // you add j to mixedList.
    mixedList.Add(j);
    }

    // Add another string and more integers.
    mixedList.Add("Second Group:");
    for (int j = 5; j < 10; j++)
    {
    mixedList.Add(j);
    }

    // Display the elements in the list. Declare the loop variable by
    // using var, so that the compiler assigns its type.
    foreach (var item in mixedList)
    {
    // Rest the mouse pointer over item to verify that the elements
    // of mixedList are objects.
    Console.WriteLine(item);
    }

    // The following loop sums the squares of the first group of boxed
    // integers in mixedList. The list elements are objects, and cannot
    // be multiplied or added to the sum until they are unboxed. The
    // unboxing must be done explicitly.
    var sum = 0;
    for (var j = 1; j < 5; j++)
    {
    // The following statement causes a compiler error: Operator
    // '*' cannot be applied to operands of type 'object' and
    // 'object'.
    //sum += mixedList[j] * mixedList[j]);

    // After the list elements are unboxed, the computation does
    // not cause a compiler error.
    sum += (int)mixedList[j] * (int)mixedList[j];
    }

    // The sum displayed is 30, the sum of 1 + 4 + 9 + 16.
    Console.WriteLine("Sum: " + sum);






    In relation to simple assignments, boxing and unboxing are computationally expensive processes. When a value type is boxed, a new object must be allocated and constructed. To a lesser degree, the cast required for unboxing is also expensive computationally.


    Casting in C#

    Converting between data types can be done explicitly using a cast, but in some cases, implicit conversions are allowed. For example:
    static void TestCasting()
    {
    int i = 10;
    float f = 0;
    f = i;  // An implicit conversion, no data will be lost.
    f = 0.5F;
    i = (int)f;  // An explicit conversion. Information will be lost.
    }
    
    
    
    
    


    A cast explicitly invokes the conversion operator from one type to another. The cast will fail if no such conversion operator is defined. You can write custom conversion operators to convert between user-defined types. For more information about defining a conversion operator, see explicit (C# Reference) and implicit (C# Reference).

    The following program casts a double to an int. The program will not compile without the cast.


    class Test
    {
    static void Main()
    {
    double x = 1234.7;
    int a;
    a = (int)x;  // cast double to int
    System.Console.WriteLine(a);
    }
    }
    

    Polymorphism in C#

    Through inheritance, a class can be used as more than one type; it can be used as its own type, any base types, or any interface type if it implements interfaces. This is called polymorphism. In C#, every type is polymorphic. Types can be used as their own type or as a Object instance, because any type automatically treats Object as a base type.

    Polymorphism is important not only to the derived classes, but to the base classes as well. Anyone using the base class could, in fact, be using an object of the derived class that has been cast to the base class type. Designers of a base class can anticipate the aspects of their base class that are likely to change for a derived type. For example, a base class for cars might contain behavior that is subject to change when the car in question is a minivan or a convertible. A base class can mark those class members as virtual, allowing derived classes representing convertibles and minivans to override that behavior.

    Overview

    When a derived class inherits from a base class, it gains all the methods, fields, properties and events of the base class. To change the data and behavior of a base class, you have two choices: you can replace the base member with a new derived member, or you can override a virtual base member.

    Replacing a member of a base class with a new derived member requires the new keyword. If a base class defines a method, field, or property, the new keyword is used to create a new definition of that method, field, or property on a derived class. The new keyword is placed before the return type of a class member that is being replaced. For example:


    public class BaseClass
    {
    public void DoWork() { }
    public int WorkField;
    public int WorkProperty
    {
    get { return 0; }
    }
    }

    public class DerivedClass : BaseClass
    {
    public new void DoWork() { }
    public new int WorkField;
    public new int WorkProperty
    {
    get { return 0; }
    }
    }





    When the new keyword is used, the new class members are called instead of the base class members that have been replaced. Those base class members are called hidden members. Hidden class members can still be called if an instance of the derived class is cast to an instance of the base class. For example:



    DerivedClass B = new DerivedClass();
    B.DoWork(); // Calls the new method.

    BaseClass A = (BaseClass)B;
    A.DoWork(); // Calls the old method.



    In order for an instance of a derived class to completely take over a class member from a base class, the base class has to declare that member as virtual. This is accomplished by adding the virtual keyword before the return type of the member. A derived class then has the option of using the override keyword, instead of new, to replace the base class implementation with its own. For example:

    public class BaseClass
    {
    public virtual void DoWork() { }
    public virtual int WorkProperty
    {
    get { return 0; }
    }
    }
    public class DerivedClass : BaseClass
    {
    public override void DoWork() { }
    public override int WorkProperty
    {
    get { return 0; }
    }
    }





    Fields cannot be virtual; only methods, properties, events and indexers can be virtual. When a derived class overrides a virtual member, that member is called even when an instance of that class is being accessed as an instance of the base class. For example:



    DerivedClass B = new DerivedClass();
    B.DoWork(); // Calls the new method.

    BaseClass A = (BaseClass)B;
    A.DoWork(); // Also calls the new method.




    Virtual methods and properties allow you to plan ahead for future expansion. Because a virtual member is called regardless of which type the caller is using, it gives derived classes the option to completely change the apparent behavior of the base class.


    Virtual members remain virtual indefinitely, no matter how many classes have been declared between the class that originally declared the virtual member. If class A declares a virtual member, and class B derives from A, and class C derives from B, class C inherits the virtual member, and has the option to override it, regardless of whether class B declared an override for that member. For example:




    public class A
    {
    public virtual void DoWork() { }
    }
    public class B : A
    {
    public override void DoWork() { }
    }

    //--
    public class C : B
    {
    public override void DoWork() { }
    }





    A derived class can stop virtual inheritance by declaring an override as sealed. This requires putting the sealed keyword before the override keyword in the class member declaration. For example:




    public class C : B
    {
    public sealed override void DoWork() { }
    }




    In the previous example, the method DoWork is no longer virtual to any class derived from C. It is still virtual for instances of C, even if they are cast to type B or type A. Sealed methods can be replaced by derived classes using the new keyword, as the following example shows:




    public class D : C
    {
    public new void DoWork() { }
    }





    In this case, if DoWork is called on D using a variable of type D, the new DoWork is called. If a variable of type C, B, or A is used to access an instance of D, a call to DoWork will follow the rules of virtual inheritance, routing those calls to the implementation of DoWork on class C.


    A derived class that has replaced or overridden a method or property can still access the method or property on the base class using the base keyword. For example:

    public class A
    {
    public virtual void DoWork() { }
    }
    public class B : A
    {
    public override void DoWork() { }
    }


    //--2


    public class C : B
    {
    public override void DoWork()
    {
    // Call DoWork on B to get B's behavior:
    base.DoWork();

    // DoWork behavior specific to C goes here:
    // ...
    }
    }


    Create a multidimensional array

    This C# code snippet creates a multidimensional Array of the specified Type, dimension lengths, and lower bounds.
    using System;
     
    public class MultidimensionalArray
    {
       public static void Main()
       {
          int[] lowerBounds = {1, 2, 4};
          int[] lengths     = {4, 2, 1};
          Console.WriteLine
             ("One dimensional array lower bound = {0}", 
             new string[10].GetLowerBound (0));      
          Array array = Array.CreateInstance 
             (typeof (int), lengths, lowerBounds);
          Console.WriteLine
             ("Bounds dimension {0} = [{1},{2}]", 
             0, array.GetLowerBound (0), array.GetUpperBound (0)); 
          Console.WriteLine
             ("Bounds dimension {0} = [{1},{2}]", 
             1, array.GetLowerBound (1), array.GetUpperBound (1)); 
          Console.WriteLine
             ("Bounds dimension {0} = [{1},{2}]", 
             2, array.GetLowerBound (2), array.GetUpperBound (2));
       } 
    }
    

    CRM 4 Plugin Template

    using System;
    using System.Collections.Generic;
    using Microsoft.Win32;
    
    // Microsoft Dynamics CRM namespaces
    using Microsoft.Crm.Sdk;
    using Microsoft.Crm.SdkTypeProxy;
    using Microsoft.Crm.SdkTypeProxy.Metadata;
    
    namespace My.Plugin
    {
    public class MyPlugin : IPlugin
    {
    // Configuration information that can be passed to a plug-in at run-time.
    private string _secureInformation;
    private string _unsecureInformation;
    
    // Note: Due to caching, Microsoft Dynamics CRM does not invoke the plug-in 
    // constructor every time the plug-in is executed.
    
    // Related SDK topic: Writing the Plug-in Constructor
    public MyPlugin(string unsecureInfo, string secureInfo)
    {
    _secureInformation = secureInfo;
    _unsecureInformation = unsecureInfo;
    }
    
    // Related SDK topic: Writing a Plug-in
    public void Execute(IPluginExecutionContext context)
    {
    DynamicEntity entity = null;
    
    // Check if the InputParameters property bag contains a target
    // of the current operation and that target is of type DynamicEntity.
    if (context.InputParameters.Properties.Contains(ParameterName.Target) &&
    context.InputParameters.Properties[ParameterName.Target] is DynamicEntity)
    {
    // Obtain the target business entity from the input parameters.
    entity = (DynamicEntity)context.InputParameters.Properties[ParameterName.Target];
    
    // TODO Test for an entity type and message supported by your plug-in.
    // if (entity.Name != EntityName.account.ToString()) { return; }
    // if (context.MessageName != MessageName.Create.ToString()) { return; }
    }
    else
    {
    return;
    }
    
    try
    {
    // Create a Microsoft Dynamics CRM Web service proxy.
    // TODO Uncomment or comment out the appropriate statement.
    
    // For a plug-in running in the child pipeline, use this statement.
    // CrmService crmService = CreateCrmService(context, true);
    
    // For a plug-in running in the parent pipeline, use this statement.
    ICrmService crmService = context.CreateCrmService(true);
    
    // TODO Plug-in business logic goes here.
    
    }
    catch (System.Web.Services.Protocols.SoapException ex)
    {
    throw new InvalidPluginExecutionException(
    String.Format("An error occurred in the {0} plug-in.",
    this.GetType().ToString()),
    ex);
    }
    }
    
    #region Private methods
    /// 
    /// Creates a CrmService proxy for plug-ins that execute in the child pipeline.
    /// 
    /// The execution context that was passed to the plug-ins Execute method.
    /// Set to True to use impersontation.
    /// A CrmServce instance.
    private CrmService CreateCrmService(IPluginExecutionContext context, Boolean flag)
    {
    CrmAuthenticationToken authToken = new CrmAuthenticationToken();
    authToken.AuthenticationType = 0;
    authToken.OrganizationName = context.OrganizationName;
    
    // Include support for impersonation.
    if (flag)
    authToken.CallerId = context.UserId;
    else
    authToken.CallerId = context.InitiatingUserId;
    
    CrmService service = new CrmService();
    service.CrmAuthenticationTokenValue = authToken;
    service.UseDefaultCredentials = true;
    
    // Include support for infinite loop detection.
    CorrelationToken corToken = new CorrelationToken();
    corToken.CorrelationId = context.CorrelationId;
    corToken.CorrelationUpdatedTime = context.CorrelationUpdatedTime;
    corToken.Depth = context.Depth;
    
    RegistryKey regkey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM");
    
    service.Url = String.Concat(regkey.GetValue("ServerUrl").ToString(), "/2007/crmservice.asmx");
    service.CorrelationTokenValue = corToken;
    
    return service;
    }
    
    /// 
    /// Creates a MetadataService proxy for plug-ins that execute in the child pipeline.
    /// 
    /// The execution context that was passed to the plug-ins Execute method.
    /// Set to True to use impersontation.
    /// A MetadataServce instance.
    private MetadataService CreateMetadataService(IPluginExecutionContext context, Boolean flag)
    {
    CrmAuthenticationToken authToken = new CrmAuthenticationToken();
    authToken.AuthenticationType = 0;
    authToken.OrganizationName = context.OrganizationName;
    
    // Include support for impersonation.
    if (flag)
    authToken.CallerId = context.UserId;
    else
    authToken.CallerId = context.InitiatingUserId;
    
    MetadataService service = new MetadataService();
    service.CrmAuthenticationTokenValue = authToken;
    service.UseDefaultCredentials = true;
    
    RegistryKey regkey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\MSCRM");
    
    service.Url = String.Concat(regkey.GetValue("ServerUrl").ToString(), "/2007/metadataservice.asmx");
    
    return service;
    }
    #endregion Private Methods
    }
    }
    

    Read & Write Registry entries with VBScript


    '[VBScript]
    Dim WshShell, bKey
    Set WshShell = WScript.CreateObject("WScript.Shell")
    WshShell.RegWrite "HKCU\Software\ACME\FortuneTeller\", 1, "REG_BINARY"
    WshShell.RegWrite "HKCU\Software\ACME\FortuneTeller\MindReader", "Goocher!","REG_SZ"

    bKey = WshShell.RegRead("HKCU\Software\ACME\FortuneTeller\")
    WScript.Echo WshShell.RegRead("HKCU\Software\ACME\FortuneTeller\MindReader")
    WshShell.RegDelete "HKCU\Software\ACME\FortuneTeller\MindReader"
    WshShell.RegDelete "HKCU\Software\ACME\FortuneTeller\"
    WshShell.RegDelete "HKCU\Software\ACME\"