Tuesday, March 20, 2007

Basic Assembly in .NET

 

+

Creating an assembly

Changing the search path for private assemblies

 

Creating strongly named assembly

Adding assembly into the global assembly cache

Creating a Delay signed shared assembly

 

Using Assembly Version Policy

Creating a Publisher Policy Assembly

 

Getting the types in an assembly

Creating assembly on the fly

Dynamic Binding a Assembly

 

Serializing using XmlSerializer

Serializing using SoapSerializer

Serializing using BinarSerializer

Creating a Domain

 

 

 

Module MyModule

          public sub Main(args() as string)

                   System.Console.WriteLine("Hello Vb")

          end sub

end Module

 

Using the command-line compiler provided with Visual Studio, such a program can be compiled with the command line directive

c:\> vbc Hello.vb

 

 

 

class CA

{

          public static void Main(string[] args)

          {

                   System.Console.WriteLine("Hello World");

          }

}

 

Using the command-line compiler provided with Visual Studio, such a program can be compiled with the command line directive

c:\> csc  Hello.cs

 

c:\> ildasm Hello.exe

 

Creating an assembly

 

namespace MathLibrary

{

          public class CMath

          {

                   public long AddFun(long x,long y)

                   {

                             Console.WriteLine("Addfun");

                             return x + y;

                   }

                   public long SubFun(long x,long y)

                   {

                             Console.WriteLine("AubFun");

                             return x - y;

                   }

          }

}

 

c:\> csc /t:libary Math.cs

 

 

public class CMath

          public function AddFun(x as long,y as long) as long

                   return x + y

          end function

end class

 

How Do I...Change the search path for private assemblies?

Private assemblies are assemblies that are visible to only one application. The .NET Framework enables developers to build applications that are isolated from changes made to the system by other applications. Private assemblies must be deployed in the directory structure of the containing application and are found during runtime through a process called probing, which is simply a mapping from an assembly's identity to a file on disk that contains the manifest.

 

By default, probing for private assemblies is done in the application base (the root directory of the application) and the subdirectories that follow naming conventions based on assembly name and culture. You can customize this behavior by specifying a privatePath in your application's configuration file. The privatePath is a semi-colon delimited list of directories in which the common language runtime will search for private assemblies. These directory names are relative to the application base - they cannot point outside the application base. The directories on privatePath will be searched after the application base itself. The following configuration file adds a bin directory to the assembly search path:

 

<configuration>  

    <runtime>

        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

     

            <probing privatePath="bin"/>

 

        </assemblyBinding>

    </runtime>

</configuration>

 

How Do I...Create an assembly with a strong name?

Assemblies can be assigned a cryptographic signature, called a strong name, which provides name uniqueness for the assembly and prevents someone from taking over the name of your assembly (name spoofing). If you are deploying an assembly that will be shared among many applications on the same machine, it must have a strong name. Even if you only use the assembly within your application, using a strong name ensures that the correct version of the assembly gets loaded

 

The first step in building an assembly with a strong name is to obtain a cryptographic key pair. The .NET Framework SDK includes a Strong Name tool (Sn.exe) that can be used to generate a key pair. The key pair that is generated by the Strong Name tool can be kept in a file or you can store it in your local machine's Crytographic Service Provider (CSP). The following command uses the Strong Name tool to generate a new key pair and store it in a file called TestKey.snk:

 

c:\> SN -k TestKey.snk

 

 

c:\> SN -tp TestKey.snk

 

Once you have obtained the key pair, you need to add the proper custom attribute to your source in order for the compiler to emit the assembly with a strong name. Choosing the correct attribute depends on whether the key pair used for the signing is contained in a file or in a key container within the CSP. For keys stored in a file, use System.Reflection.AssemblyKeyFileAttribute. For keys stored in the CSP use System.Reflection.AssemblyKeyNameAttribute

 

The following example uses AssemblyKeyFileAttribute to specify the name of the file containing the key pair.

 

using System;

using System.Reflection;

 

[assembly : AssemblyKeyFile("TestKey.snk ")]

 

namespace MathLibrary

{

          public class CMath

          {

                   public long AddFun(long x,long y)

                   {

                             Console.WriteLine("Addfun");

                             return x + y;

                   }

                   public long SubFun(long x,long y)

                   {

                             Console.WriteLine("AubFun");

                             return x - y;

                   }

          }

}

 

 

How to add assembly into the global assembly cache ?

 

The global assembly cache is a machine-wide store used to hold assemblies that are intended to be shared by several applications on the machine. The .NET Framework provides two tools for working with the cache. One is a Windows shell extension that allows you to work with the cache using a Graphical User Interface (GUI). The other is a command line tool, called the Global Assembly tool (Gacutil.exe), that is typically used in build and test scripts. The command line tool is called the Global Assembly Cache tool (Gacutil.exe).

 

All assemblies in the global cache must have strong names.

Viewing the Contents of the Assembly Cache

Navigate to %winnt%\assembly using the Windows Explorer to activate the shell extension. The default view shows the contents of the assembly cache. You can also view the contents of the cache with the /l option of the Global Assembly Cache tool:

 

gacutil /l

 

Installing Assemblies

To install an assembly using the shell extension simply drag and drop the file containing the assembly's manifest into the global assembly cache directory. To install an assembly using the Global Assembly Cache tool, use the /i option:

 

gacutil /i math.dll

 

Uninstalling Assemblies

To delete an assembly with the shell extension, right click on it and select Delete. The /u option of the Global Assembly Cache tool can also be used:

 

gacutil /u math,ver=1.0.0.0

 

The version of Windows Installer (1.5) that will ship in the Visual Studio .NET timeframe has native support for the assembly cache. When creating an Installer package with the Visual Studio Deployment tool or another setup tool, you can specify which assemblies you'd like installed in the cache. Using the Windows Installer to work with the assembly cache gives you the standard Installer benefits like install-on-demand, advertisement, publishing, and so on.

 

 

using System;

using MathLibrary;

 

class CMain

{

          public static void Main(string[] args)

          {

                   long x = long.Parse(args[0]);

                   long y = long.Parse(args[1]);

 

                   CMath obj = new CMath();

                  

                   long ans = obj.AddFun(x,y);

                  

          Console.WriteLine(" {0} + {1} = {2} ", x,y,ans);

          }

}

 

How to create a Delay signed shared assembly ?

 

Giving an assembly a strong name requires two cryptographic keys: a public key and a private key. This key pair is passed to the compiler at build time. However, the person building the assembly does not always have access to the private key required for strong naming. This is most common in corporations that have a central signing entity closely guards private keys. Only a few select people have access to these keys. Also, the process of assigning a strong name cannot be done after building because the public key is part of the assembly's identity and must be supplied at build time so that clients of the assembly can compile against the full assembly identity.

 

·         1. Creates a key-pair using sn -K.

·         2. Separates the public key from the private key and stores the public key in its own file.

·         3. Creates a delay signed assembly in either Visual Basic or C#.

·         4. Uses the Strong Name tool to request that signature verification be skipped for the assembly just generated.

·         5. Generates a valid signature using the Strong Name tool. This typically happens just before you ship the assembly.

 

The .NET Framework offers delay signing, which effectively splits the process of assigning the strong name into two steps:

·         1. At build time, the public key is given to the compiler so it can be recorded in the PublicKey field in the assembly manifest. Also, space is reserved in the file for the signature, although the actual signature is not generated at this time.

·         2. At a later time, the the actual signature is generated and stored in the file. Signature generation is done with the -R switch to the Strong Named tool (Sn.exe).

 

When you include the System.Reflection.AssemblyDelaySignAttribute in your source code, it indicates to the compiler that the assembly needs to be created with delay signing. You also need to include the public key, using AssemblyKeyFileAttribute. Typically, the signing entity will use the SN -k to generate a key pair and store it in a file. Next, it pulls the public key out of the file using SN -p. The public key can then be given out, with the private key still secret.

 

sn -k Testkey.snk

sn -p Testkey.snk TestPublicKey.snk

 

The following example uses AssemblyKeyFileAttribute and AssemblyDelaySignAttribute to create a delay signed assembly.

 

using System;

using System.Reflection;

 

[assembly:AssemblyKeyFileAttribute("TestPublicKey.snk")]

[assembly:AssemblyDelaySignAttribute(true)]

 

 

Since the assembly in the example does not have a valid signature, the signature validation performed by the common language runtime will fail when you try to install the assembly into the global assembly cache or load it from an application directory. However, the Strong Name tool can be used to disable signature verification of a particular assembly by using the -Vr option:

 

sn -Vr DelaySign.dll

 

A valid signature must be generated before the assembly is shipped to customers using sn -R. This is typically done by the company signing entity. You must supply the full key pair to create a valid signature.

 

sn -R DelaySign.dll Testkey.snk

 

To create and sign an assembly with a strong name using the Assembly Linker

 

At the command prompt, type the following command:

 

al /out:<assembly name> <module name> /keyfile:<file name>

 

In this command, assembly name is the name of the assembly to sign with a strong name, module name is the name of the code module used to create the assembly, and file name is the name of the container or file that contains the key pair.

 

The following example signs the assembly MyAssembly.dll with a strong name using the key file sgKey.snk.

 

al /out:MyAssembly.dll MyModule.netmodule /keyfile:sgKey.snk

 

 

How to Use Assembly Version Policy?

A primary goal of the deployment system in the .NET Framework is to eliminate conflicts between applications caused by shared components and shared states (or DLL conflicts). A key solution to this problem is a robust versioning system. The .NET Framework records information about an application's dependencies in the assembly manifest. This dependency information includes a version number that is used at runtime to load the proper version of a dependency.

 

 

By default, the common language runtime will load the version of a dependency that is specified in the manifest. This is preferred in the majority of scenarios. However, there are cases where running an application with a different version of a dependency can be useful. In order to accomplish this, version policies can be included in an application's configuration file. For example, the following XML code fragment redirects references to version 5.0.0.0 of a shared assembly called "caclR" up to version 6.0.0.0 of that assembly:

 

 

<configuration>  

    <runtime>

        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">

 

 

            <dependentAssembly>

                <assemblyIdentity name="calcR"

                                  publicKeyToken="a1690a5ea44bab32"

                                  culture=""/>

             

               

                <bindingRedirect oldVersion="5.0.0.0"

                                 newVersion="6.0.0.0"/>

 

 

            </dependentAssembly>

        </assemblyBinding>

    </runtime>

</configuration>

 

 

How to Create a Publisher Policy Assembly?

A Publisher policy statement describes the compatibility of an assembly issued by the publisher of that shared assembly. Publisher policy is commonly used in service pack scenarios. For example, a publisher may produce a number of small releases that enhances certain features for a particular customer. For maintenance reasons, the publisher may wish to collect all of these fixes into a single service pack release and have all exsiting customers upgrade to the new service pack.

 

A publisher policy statement is an XML configuration file wrapped as a separate assembly. There are three reasons that publisher policies are shipped as assemblies. The first is to ensure that the policy statement comes from the author of the assembly that the policy is making the compatibility statement about. This is accomplished by requiring that the policy assembly has a strong name generated with the same key-pair as the original assembly. The second reason is ease of deployment. Publishers or administrators can ship policy statements using the assembly deployment mechansims provided by the .NET Framework, including the Windows Installer and code download. Using the Windows Installer is particularly convenient because all policy assemblies must be installed in the global assembly cache. Finally, assemblies ship policy statements so that they can be versioned. This allows a publisher to ship a subsequent statement if a previous policy is found not to work in some scenarios. In effect, this allows a publisher to change his mind about the compatibility of his assembly independent of when it was shipped. The flexibility enabled by decoupling the compatibility statements from the code makes it much easier to fix broken applications in the .NET Framework. If multiple versions of a given policy assembly are found in the assembly cache, the .NET Framework will use the policy assembly with the highest version number.

 

In general, there are two steps required to create a publisher policy assembly:

·         1. Create the XML file containing the compatibility statement. You will have to use an XML editor to create this file.

·         2. Use the Assembly Generation tool (Al.exe) to create an assembly containing the XML file.

 

The format of the xml file, along with rules about how the elements relate, is described in detail in the .NET Framework SDK Guide. Here is an example file:

 

<configuration>

    <runtime>

        <assemblyBinding>

 

            <dependentAssembly>

                <assemblyIdentity name="myasm"

                                  publicKeyToken="e9b4c4996039ede8"

                                  culture="en-us"/>

 

                <bindingRedirect oldVersion="1.0.0.0-1.0.9.9"

                                 newVersion="2.0.0.0"/>

 

                <codeBase version="2.0.0.0"

                          href="http://www.foo.com/bar.dll"/>

            </dependentAssembly>

        </assemblyBinding>

    </runtime>

</configuration>

 

After the xml file is created, use the Assembly Generation tool to create a policy assembly. The following switches to the Assembly Generation tool are required:

 

1.      /link: links the xml file into the assembly.

2.      /out: gives the resulting policy assembly a name. Policy assemblies are found in the global assembly cache by naming convention. Therefore, their names must be:

policy.<major number>.<minor number>.<main assembly name>

For example, policy.2.0.myasm

3.      /keyfile: The key pair used to give the assembly a strong name (or at least the public key if delay signing is used). As described earlier, this key pair must be the same key pair used to sign the assembly to which this policy statement applies.

4.      /version: The version number of the policy assembly.

 

The following example shows a command line that uses the Assembly Generation tool:

 

Al /link:publisher.cfg /out:policy.2.0.myasm /keyfile:myasm.snk /version:2.0.0.0

 

 

In addition to policies specified at the application level, the .NET Framework also provides two other policy levels: publisher and administrator. A Publisher policy statement describes the compatibility of an assembly issued by the publisher of that shared assembly. Administrator policy is created using the same XML syntax as application level policy. The administrator policy file is called machine.config, and resides in the common language runtime install directory. The three policy levels are evaluated in the following order:

 

·         Application Policy

·         Publisher Policy

·         Administrator Policy

 

 

 

How Do I...Get the types in an assembly?

 

using System; Once you have an object reference to the assembly of interest, you can call the GetTypes method on that assembly, which returns an array of all the types in that assembly. You can use control logic to identify the more specific types in that array, and use iterating logic to parse your array, returning the type information to the user if needed. The ability to retrieve type information can be useful for determining alternative types you could use for a given task, or identifying existing elements which could provide you with the functionality you need.

 

The first thing to learn when retrieving types from a particular assembly is, how to identify an assembly.The first is by identifying a particular object that you want to find the assembly of, and requesting the assembly for the module of that object (remember that a module is a logical grouping of types and code, such as a .dll or .exe). The second is by using the LoadFrom method of the Assembly class to load a specific assembly for a named module (such as myapp.exe).

 

using System.Reflection;

 

class CMain

{

          static void Main(string[] args)

          {

                   //step 1 load an assembly

                   Assembly asm = Assembly.LoadFrom("c:\\dotnet\\MathLibrary.dll");

 

                   //step 2 get the modules

                   Module[] modules = asm.GetModules();

 

                   foreach(Module mod in modules)

                   {

                             Console.WriteLine("module {0}", mod.Name); 

 

                             //step 3 get types

                             Type[] types = mod.GetTypes();

 

                             foreach(Type t in types)

                             {

                                      Console.WriteLine("Type {0}", t.Name); 

 

                                      //step 4 get methods

                                      MethodInfo[] methods = t.GetMethods();

 

                                      foreach(MethodInfo method in methods)

                                      {

                                       Console.WriteLine("method : {0} ",method.Name); 

                                      }

 

                                      FieldInfo[] fields = t.GetFields();

 

                                      foreach(FieldInfo field in fields)

                                      {

                                      Console.WriteLine("field : {0} ",field.Name);

                                      }

                             }

                   }

 

          }

}

 

How Do I...Create a assembly On the fly?

 

using System;

using System.Reflection;

using System.Reflection.Emit;

 

class CMain

{

          static void Main(string[] args)

          {

                   //step 0 get domain ref in which asm will be created

                   AppDomain domain = AppDomain.CurrentDomain;

 

                   //step 1 create a name for the asm

                   AssemblyName asmname = new AssemblyName();

                   asmname.Name = "MyFirstAssembly";

 

                   //step 2 build a asm

AssemblyBuilder asmbuilder =

domain.DefineDynamicAssembly(asmname,AssemblyBuilderAccess.Save); 

                  

//step 3 build a module

                   ModuleBuilder modbuilder =

asmbuilder.DefineDynamicModule("mymodule","mymodule.netmodule");

 

                   //step 4 create a type

                   TypeBuilder typebuilder =

modbuilder.DefineType("CA",TypeAttributes.Public); 

 

                   //step 5 create a field

                   FieldBuilder field1 = typebuilder.DefineField

          ("empno", Type.GetType("System.String"),FieldAttributes.Private);  

 

                   //step 6 create a method

                   MethodBuilder method1 = typebuilder.DefineMethod

                                      ("fun",MethodAttributes.Public,null,new Type[]{});

 

                   //step 7 write code in the method

                   ILGenerator il = method1.GetILGenerator();

 

          il.Emit(OpCodes.Ret);

 

                   //step 8 save

                   typebuilder.CreateType();

                   asmbuilder.Save("myasm.dll");

                   }

          }

}

 

 

 

 

How Do I...Invoke methods?

 

In many coding scenarios, you know the task that you want to carry out before you want to do it. Therefore, you can specify the methods that you need to invoke, and the parameters you need to pass them. However, there are also situations where you might want to dynamically invoke methods, based upon specific scenarios, or user actions. This capability is available through the Reflection namespace, by using the InvokeMember method on the Type object.

 

You can also take other actions, such as getting or setting the value of a specified property. These actions are available through the BindingFlags enumeration. The second parameter of InvokeMethod is a combination of the BindingFlags actions you specify. For example, if you want to invoke a static method on a class, you would include the static element in BindingFlags, and the InvokeMethod BindingFlag. The following example demonstrates how to invoke a hypothetical method

 

 

// calling a static method, receiving no arguments

 

// don't forget that we are using object in the reflection namespace...

using System;

using System.Reflection;

 

public class Invoke {

 

public static void Main (String [] cmdargs) {

 

// Declare a type object, used to call our InvokeMember method...

Type t = typeof (TestClass);

 

// BindingFlags has three bitor'ed elements. Default indicates

// that default binding rules should be applied.

 

t.InvokeMember ("SayHello",

                BindingFlags.Default | BindingFlags.InvokeMethod

                | BindingFlags.Static, null,

                null, new object [] {});

        }

}

 

 

Take a quick look at the rest of the parameters that were passed to the Invoke method. The first null argument passed is requesting that the default binder be used to bind the method you are invoking. When you invoke the default binder, include the default BindingFlags. Instead of null as the third parameter, you can specify a Binder object that defines a set of properties and enables binding, which may involve selection of an overloaded method or coercion of argument types. The second null argument is the object on which to invoke the method you chose. Finally, pass an object array of the arguments that the member receives.

 

 

 

 

Named arguments

 

You can also use named arguments, in which case you need to use a different overloaded version of the InvokeMember method. Create the array of object arguments as you have been doing so far, and also create a string array of the names of the parameters being passed. The overloaded method you want to use accepts the list of parameter names as the last parameter, and the list of values you want to set as the fifth parameter

 

 

// Calling a method using named arguments

 

// the argument array, and the parameter name array.

// to determine the names of the parameters in advance

 

object[] argValues = new object [] {"Mouse", "Micky"};

String [] argNames = new String [] {"lastName", "firstName"};

 

 

t.InvokeMember ("PrintName",

                BindingFlags.Default | BindingFlags.InvokeMethod,

                null, null, argValues, null, null, argNames);

 

 

 

The final example uses a slightly different process to invoke a method. Rather than using the Type object directly, create a separate MethodInfo object directly to represent the method you will be invoking. Then, call the Invoke method on your MethodInfo object, passing an instance of the object you need to invoke your method on (if you are invoking an instance method, but null if your method is static). As before, an object array of the parameters is required.

 

// Invoking a ByRef member

MethodInfo m = t.GetMethod("Swap");

 

args = new object[2];

 

args[0] = 1; args[1] = 2;

 

m.Invoke(new TestClass(),args);

 

Console.WriteLine ("{0}, {1}", args[0], args[1]);

 

 

How do I Serialize using XmlSerializer

 

Serializes and deserializes objects into and from XML documents. The XmlSerializer enables you to control how objects are encoded into XML. XML serialization is the process of converting an object's public properties and fields to a serial format for storage or transport. Deserialization re-creates the object in its original state from the XML output. You can thus think of serialization as a way of saving the state of an object into a stream or buffer

 

 

 

using System;

using System.IO;

using System.Xml.Serialization;

 

public class Dept

{

          public long m_code;

private string m_name;

 

          public Dept(long code,string name)

          {

                   m_code = code; m_name = name;

          }

                  

          public void Display()

          {

          Console.WriteLine("dept code: {0}  name: {1} ",m_code,m_name);

          }

}

 

class CMain

{

         

          static void Main ()

          {

MainXmlSerialize();

MainXmlDeserialize();

 

}

 

          static void MainXmlDeserialize ()

          {

                   Dept obj;

 

                   //step 1 create        a serializer

                   XmlSerializer sr = new XmlSerializer(typeof(Dept));

         

                   //step 2 create a file

                   StreamReader file = new StreamReader("c:\\dotnet\\abc.xml");

 

                   //step 3

                   obj = (Dept) sr.Deserialize(file);

 

                   //step 4 close the file

                   file.Close();

 

                   obj.Display();

          }

                  

          static void MainXmlSerialize ()

          {

                   Dept obj = new Dept (101,"Purchase");

 

                   //step 1 create        a serializer

                   XmlSerializer sr = new XmlSerializer(obj.GetType());

         

                   //step 2 create a file

                   StreamWriter file = new StreamWriter("c:\\dotnet\\abc.xml");

 

                   //step 3 serialize

                   sr.Serialize(file,obj);

 

                   //step 4 close the file

                   file.Close();

          }

}

 

 

How do I Serialize using SoapSerializer

 

using System;

using System.IO;

using System.Runtime.Serialization.Formatters.Soap;    

 

[Serializable]

public class Dept

{

          public long m_code;

private string m_name;

 

          public Dept(long code,string name)

          {

                   m_code = code; m_name = name;

          }

                  

          public void Display()

          {

          Console.WriteLine("dept code: {0}  name: {1} ",m_code,m_name);

          }

}

 

class CMain

{

          static void MainSoapDeserialize()

          {

                   Dept obj;

 

                   //step 1 create        a serializer

                   SoapFormatter fm = new SoapFormatter();

 

                   //step 2 open a file

                   FileStream file = File.Open("c:\\dotnet\\abc.xml",FileMode.Open);

 

                   //step 3

                   obj = (Dept) fm.Deserialize(file);

                            

                   //step 4 close the file

                   file.Close();

                  

                   obj.Display();

          }

 

          static void MainSoapSerialize()

          {

                   Dept obj = new Emp(101,"Purchase");

 

                   //step 1 create        a serializer

                   SoapFormatter fm = new SoapFormatter();

 

                   //step 2 create a file

                   FileStream file = File.Create("c:\\dotnet\\abc.xml");

 

                   //step 3

                   fm.Serialize(file,obj);

                            

                   //step 4 close the file

                   file.Close();

          }

 

}

 

 

How do I Serialize using BinarSerializer

 

using System;

using System.IO;

using System.Runtime.Serialization.Formatters.Soap;    

 

 

[Serializable]

public class Dept

{

          public long m_code;

private string m_name;

 

          public Dept(long code,string name)

          {

                   m_code = code; m_name = name;

          }

                  

          public void Display()

          {

          Console.WriteLine("dept code: {0}  name: {1} ",m_code,m_name);

          }

}

         

class CMain

{

          static void Main()

          {

          }

 

          static void MainSoapDeserialize()

          {

                   Dept obj;

 

                   //step 1 create        a serializer

                   BinaryFormatter fm = new BinaryFormatter();

 

                   //step 2 open a file

                   FileStream file = File.Open("c:\\dotnet\\abc.xml",FileMode.Open);

 

                   //step 3

                   obj = (Dept) fm.Deserialize(file);

                            

                   //step 4 close the file

                   file.Close();

                  

                   obj.Display();

          }

 

          static void MainBinarySerialize()

          {

                   Dept obj = new Dept (10,"jack",101,"Purchase");

 

                   //step 1 create        a serializer

                   BinaryFormatter fm = new BinaryFormatter();

 

                   //step 2 create a file

                   FileStream file = File.Create("c:\\dotnet\\abc.dat");

 

                   //step 3

                   fm.Serialize(file,obj);

                            

                   //step 4 close the file

                   file.Close();

          }

}

 

 

Domain

 

Historically, process boundaries have been used to isolate applications running on the same computer. Each application is loaded into a separate process, which isolates the application from other applications running on the same computer.

 

The applications are isolated because memory addresses are process-relative; a memory pointer passed from one process to another cannot be used in any meaningful way in the target process. In addition, you cannot make direct calls between two processes. Instead, you must use proxies, which provide a level of indirection.

 

Managed code must be passed through a verification process before it can be run. The verification process determines whether the code can attempt to access invalid memory addresses or perform some other action that could cause the process in which it is running to fail to operate properly. Code that passes the verification test is said to be type-safe. The ability to verify code as type-safe enables the common language runtime to provide as great a level of isolation as the process boundary, at a much lower performance cost.

 

Application domains provide a secure and versatile unit of processing that the common language runtime can use to provide isolation between applications. You can run several application domains in a single process with the same level of isolation that would exist in separate processes, but without incurring the additional overhead of making cross-process calls or switching between processes. The ability to run multiple applications within a single process dramatically increases server scalability.

 

Isolating applications is also important for application security. For example, you can run controls from several Web applications in a single browser process in such a way that the controls cannot access each other's data and resources.

The isolation provided by application domains has the following benefits:

·         Faults in one application cannot affect other applications. Because type-safe code cannot cause memory faults, using application domains ensures that code running in one domain cannot affect other applications in the process.

·         Individual applications can be stopped without stopping the entire process. Using application domains enables you to unload the code running in a single application.

Note You cannot unload individual assemblies or types. Only a complete domain can be unloaded.

·         Code running in one application cannot directly access code or resources from another application. The common language runtime enforces this isolation by preventing direct calls between objects in different application domains. Objects that pass between domains are either copied or accessed by proxy.

·         The behavior of code is scoped by the application in which it runs. In other words, the application domain provides configuration settings such as application version policies, the location of any remote assemblies it accesses, and information about where to locate assemblies that are loaded into the domain.

·         Permissions granted to code can be controlled by the application domain in which the code is running.

All objects created in a remote domain are returned by reference and have to derive from MarshallByRefObject. Objects passed as parameters to a remote method call can be forwarded by value or by reference. The default behavior is pass by value provided the object in question is marked by the custom attribute [serializable]. Additionally, the object could implement the ISerializable interface, which provides flexibility in how the object should be serialized and deserialized. Objects that are not marshal by reference or marshal by value cannot be accessed across domains.

using System;

using System.Runtime.Remoting;

 

namespace BULibrary

{

            [Serializable]

          public class CBank

          {

                   public CBank()

                   {

                             Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);  

                   }

                   public void Credit()

                   {

                             Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);  

                             Console.WriteLine("credit");

                   }

          }

 

          public class CBankEx : MarshalByRefObject

          {

                   CBank m_bank = new CBank();

 

                   public CBankEx()

                   {

                             Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);  

                   }

                   public void Debit()

                   {

                             Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);  

                             Console.WriteLine("debit");

                   }

                   public CBank GetBank()

                   {

                             return m_bank;

                   }

          }

}

 

While a common language runtime host creates application domains automatically when they are needed, you can create your own application domains and load into them assemblies you want to personally manage. You can also create application domains from which you execute code.You create a new application domain using one of the overloaded CreateDomain methods in the System.AppDomain class. You can give the application domain a name and reference it by that name.

using System;

using System.Reflection;

 

namespace TestinterDomain

{

          class Class1

          {

                   static void Main(string[] args)

                   {

                   //step 1 create a domain

                   AppDomain domain = AppDomain.CreateDomain("MyDomain");

 

 

                   //step 2 load the assembly in the new domain

                   //step 3 create a instance of CBajnk

                   //step 4 get a serialized object in current domain

object obj =domain.CreateInstanceAndUnwrap("BULibrary","BULibrary.CBankEx");

 

                   //step 5 get the type

                   Type t = obj.GetType();

 

                   //step 6 call the method

                   t.InvokeMember("Debit",BindingFlags.InvokeMethod,null, obj, null);

 

                   //step 7 unload the domain

                   AppDomain.Unload(domain);

                   }

          }

}

 

 

 

 

 

 

 

 

 

 

Satellite Assembly

 

One time in .NET that you need to know about satellite assemblies is when you are dealing with localization. For localizing text, one doesn't hard code text on a page, but uses a key for that text. The text equivalent for the key is retrieved from a file called a resource file. A resource file is essentially a dictionary of associations between the keys and their textual values. You will have this resource file duplicated once for each language that you support. .NET will retrieve values from these multiple language-resource files based on the chosen language context.

 

 

String.txt

 

name=jack

key=value

 

Step 1: Use resgen to create a .resources file from a .resx file.

 

Resgen MyText.resx

The above command will create a file called:

 

MyText.resources

Step 2: Use al.exe to create the satellite assembly:

 

Al.exe

     /t:lib

     /embed:MyText.en-gb.Resources,MyApplication.MyText.en-gb.Resources

     /culture:hi-gb

     /out:MyApplication.resources.dll

There are a couple of things worth noting here:

/t:lib: Says you are interested in a .dll.

/embed:MyText.en-gb.Resources,MyApplication.MyText.en-gb.Resources : Embeds and renames the resource to a target name to match the Visual Studio IDE naming structure.

/culture:hi-gb : Identifies the culture in which you are interested.

/out:MyApplication.resources.dll : Name of the DLL in which you are interested.

The generated .dll has to have that naming convention for .NET to find it. Also notice that you have to specify the culture setting, even though the culture is available in the name of the resource files. So it has to be mentioned in both places.

 

Place the Satellite Assembly in the Appropriate Directory

Once the satellite assembly is created, physically copy the .dll to the following directory:

 

\MyApplication\bin\en-gb\MyApplication.Resources.DLL

 

 

 

 

c:/> resgen string.txt

 

c:/> al /t:library /embed:string.resource /out:string.resource.dll

 

 

 

using System;

using System.Resources;

 

namespace testResource

{

          class CMain

          {

                   static void Main(string[] args)

                   {

ResourceManager rm =

ResourceManager.CreateFileBasedResourceManager("mystring","c:\\dotnet",null);

 

                   Console.WriteLine(rm.GetString("name")); 

                   }

          }

}

 

 

Place the Satellite Assembly in the Appropriate Directory

Once the satellite assembly is created, physically copy the .dll to the following directory:

 

\MyApplication\bin\en-gb\MyApplication.Resources.DLL

This would have been identical if Visual Studio IDE had generated this file. Repeat this process for each languagein which you are interested.

 

Regards,

Sasikumar




----------------------------------------------------------------DISCLAIMER---------------------------------------------------------
Information transmitted by this EMAIL is proprietary to iGATE Group of Companies and is intended for use only by the individual 
or entity to whom it is addressed and may contain information that is privileged, confidential, or exempt from disclosure under 
applicable law. If you are not the intended recipient of this EMAIL immediately notify the sender at iGATE or mailadmin@igate.com 
and delete this EMAIL including any attachments

Assembly

FYI....




----------------------------------------------------------------DISCLAIMER---------------------------------------------------------
Information transmitted by this EMAIL is proprietary to iGATE Group of Companies and is intended for use only by the individual 
or entity to whom it is addressed and may contain information that is privileged, confidential, or exempt from disclosure under 
applicable law. If you are not the intended recipient of this EMAIL immediately notify the sender at iGATE or mailadmin@igate.com 
and delete this EMAIL including any attachments

Saturday, March 17, 2007

Inviting my friends & family...

I'm extending a personal invitation to my friends and family to make a difference without spending a penny. To see your invitation, click the link below, or copy and paste it into your browser's address field:

http://friends.unicefusa.org/r/dc7f64e02591102aa29c

If you would prefer not to receive invitations from Friends.UNICEFUSA.org please click here

http://friends.unicefusa.org/?PC=UNSUB&rh=bb7af4ba789a3d0b853b460d0b6d6c2d&sender=as_prabahar@yahoo.com&tc=11

----------------------------------------------------------
UNICEF USA
PMB# 210
2440 16th Street
San Francisco, CA 94103-4211

Wednesday, March 14, 2007

Hi,
Please look into the details below and let me know if you need more details. I am not able to find that project but the material I refered was the same. Check it out
Forms Authentication - This is a cookie based authentication system where the username and passport is stored in a text file or a database. We will be focusing on this authentication model in this tutorial.

Let's start!

web.config file
The web.config file is an XML based configuration file which exists for every web application. The web.config file typical resides in the application root directory although it is possible to have multiple web.config files. If there is another web.config file placed in a directory below the application root, it will use those setting instead. The web.config file is where you will tell a web application to use either of the three types of autentication types.

Here we show you a basic example of what a web.config file looks like when it has be set to use form authentication. I will go in further detail and explain the tags.
<configuration>
  <system.web>
    <authentication mode="Forms">
      <forms loginUrl="login.aspx" protection="All" timeout="30">
        <credentials passwordFormat="Clear">
          <user name="devhood" password="password"/>
          <user name="someguy" password="password"/>
        </credentials>
      </forms>
    </authentication>
    <authorization>
      <allow users="*" />
    </authorization>
  </system.web>
 
  <location path="admin/">
    <system.web>
      <authorization>
        <allow users="devhood" />
        <deny users="someguy" />
      </authorization>
    </system.web>
  </location>
  <location path="usersonly/">
    <system.web>
      <authorization>
        <deny users="?" />
      </authorization>
    </system.web>
  </location>
  <location path="public/">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>
</configuration>
 


The first tag in the web.config file is the
<configuration> tag. It is the base tag for the web.config file and will contain all your configuration settings in here. The first <system.web> tag specifies the settings that will apply to all the file in the same directory and the files below this directory.

<authentication> tag
Here we come to our first tag for authentication, which is thence called
<authentication>. We see that there is one attribute for this tag and it specifies the type of authentication that will be applied to this site. The choices are WindowsFormsPassportNone. This tutorial focuses on Forms authentication so that's what we got in there.

<forms> tag
Next we move to the
<forms> tag. This tag is unique to the Forms authentication mode and will specify things such as the loginUrl, the type of protection and the timeout of inactivity.
loginUrl attribute - when a user does not have the correct credentials to view a page, the user will be forwarded to this url.
protection attribute - there are four(4) types of protection modes, AllNoneEncryptionValidation. For simplicity sake, we're not going to go into this now, but if you want to know more, consult the MSDN documentation.
timeout attribute - this is an integer value specifying the number of minutes a user's cookie will be valid for. After this period of inactivity, the user will be forced to re-authenticate.

<credentials> tag
This is an optional section if you want to specify the username/password combinations in here. We will first discuss authentication with passwords in the web.config file and I will later highlight how you can store the usernames and passwords in a database or XML file. The credentials tag also has an attribute called passwordFormat. Your choices for password format are: ClearSHA1MD5. We still stick with clear text passwords for now and talk about encrypting the passwords further down.

<user> tag
This is also an optional tag (since it resides in the optional credentials tag). This tag is pretty straight forward, name attribute for the username and password attribute for the password.

<authorization> tag
Now that we have specified our authentication type and the user accounts, we have to specify how the authentication is to be applied to our website. We used the authorization tag to mark this. The autorization tag is placed between the system.web tags. In the example above, we see that the authorization tag contains the
<allow> tag. This allow tag will (as you can guess) specify which users have access to the website. And there is also a <deny> tag which will specify which users are denied access. The format of the users attributes is pretty simple. It's just a comma-delimited list of user names (i.e. users="jsmith, jdoe, blah"). There are also two special values that can be used in the users attribute. The first one is the * (asterix) character. This is used to denote "all users". So the example above allows access to all users. The second one is the ? (question mark) character. This is used to denote "anonymous" users. You can use this to deny anonymous access which will force users to authenticate before getting into some webpages (see the examples in the locations tags).

<location> tag
Now what happens when we want some parts of the website to be protected and others to not be protected? ASP.NET did think of that and handles that by the
<locations> tags. The location tag has one attribute, path, which is the path to apply a different set of security rules to. Inside the location tag, we have the system.web tag once again. The authorization tag is placed inside the system.web tag (just like the in first usage of <authorization>).

login.aspx file
Now that we have our web application all configured, we tackle the task of getting a user to authenticate themself by sending his/her username and password. In our
<forms> tag, we specified that the loginUrl attribute is login.aspx and here is an example of a login page:
<html>
<head>
    <title>Login</title>
    <script language="C#" runat="server">
        
        void Login_Click(Object sender, EventArgs e) {
            if (FormsAuthentication.Authenticate(username.Text, password.Text))
                FormsAuthentication.RedirectFromLoginPage(username.Text, true);
            else
                status.InnerHtml += "Invalid Login";
        }
        
    </script>
</head>
<body>
    <p class=title>Login</p> 
    <span id="status" class="text" runat="Server"/>
    <form runat="server">
    Username: <asp:textbox id=username cssclass="text" runat="Server"/><br />
    Password: <asp:textbox id=password textmode=Password cssclass="text" runat="Server"/><br />
    <asp:button id=login_button onclick="Login_Click" text="  Login  " cssclass="button" runat="Server"/>
    </form>
</body>
</html>
 


First, let's look at what the user sees. Our simple webpage example has two textboxes and a button. This webpage will be shown to the user anytime a request is made for a page and the user is does not have the proper credentials. This is a simple example, which you'll probably want to modify. Now we look at the code, this is where the authentication is done and the cookies are sent to the browser.

FormsAuthentication.Authenticate
The single login button on the webpage calls the Login_Click method when clicked. In this method, we use the FormsAuthentication.Authenticate(username,password) to get ASP.NET to check the credentials of the user. The parameters for this method is pretty straightforward and it just returns a boolean value.

FormsAuthentication.RedirectFromLoginPage
If the user is providing proper credentials, then we'll use the FormsAuthentication.RedirectFromLoginPage method. The parameters of this method are a username string and a boolean value. The first parameter is a username string and it is a name of the user for cookie authentication purposes. The value you put in there will be the user name that the client is associated with. It does not need to match the username used in the FormsAuthentication.Authenticate method but it is advisable to set the cookie to the username that was used to log in. The second parameter is a boolean value and it specifies whether or not a durable cookie (one that is saved across browser sessions) should be issued.
If you remember, when a user requests a page without proper authentication, they are redirected to the login page. After setting the cookie, the RedirectFromLoginPage will then send the user back to the page they came from.

There you go. You should have everything you need for a basic Forms based authentication system. Give it a try. If you want to extend the usage of authentication more, read on!
Thanks,
Thirumoorthy
private void btnLogin_Click(Object sender, EventArgs e)
{
// Initialize FormsAuthentication, for what it's worth
FormsAuthentication.Initialize();
// Create our connection and command objects
SqlConnection conn =
new SqlConnection("Data Source=localhost;Initial Catalog=web;");
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT roles FROM web WHERE username=@username " +
"AND password=@password";
// Fill our parameters
cmd.Parameters.Add("@username", SqlDbType.NVarChar, 64).Value =
Username.Value;
cmd.Parameters.Add("@password", SqlDbType.NVarChar, 128).Value =
FormsAuthentication.HashPasswordForStoringInConfigFile(
Password.Value, "md5"); // Or "sha1"
// Execute the command
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
// Create a new ticket used for authentication
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // Ticket version
Username.Value, // Username associated with ticket
DateTime.Now, // Date/time issued
DateTime.Now.AddMinutes(30), // Date/time to expire
true, // "true" for a persistent user cookie
reader.GetString(0), // User-data, in this case the roles
FormsAuthentication.FormsCookiePath);// Path cookie valid for
// Encrypt the cookie using the machine key for secure transport
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName, // Name of auth cookie
hash); // Hashed ticket
// Set the cookie's expiration time to the tickets expiration time
if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
// Add the cookie to the list for outgoing response
Response.Cookies.Add(cookie);
// Redirect to requested URL, or homepage if no previous page
// requested
string returnUrl = Request.QueryString["ReturnUrl"];
if (returnUrl == null) returnUrl = "/";
// Don't call FormsAuthentication.RedirectFromLoginPage since it
// could
// replace the authentication ticket (cookie) we just added
Response.Redirect(returnUrl);
}
else
{
// Never tell the user if just the username is password is incorrect.
// That just gives them a place to start, once they've found one or
// the other is correct!
ErrorLabel = "Username / password incorrect. Please try again.";
ErrorLabel.Visible = true;
}
reader.Close();
conn.Close();
}


Regards,
Sasikumar
----------------------------------------------------------------DISCLAIMER---------------------------------------------------------Information transmitted by this EMAIL is proprietary to iGATE Group of Companies and is intended for use only by the individual or entity to whom it is addressed and may contain information that is privileged, confidential, or exempt from disclosure under applicable law. If you are not the intended recipient of this EMAIL immediately notify the sender at iGATE or mailadmin@igate.com and delete this EMAIL including any attachments




It's here! Your new message!
Get new email alerts with the free Yahoo! Toolbar.

Using Visual Sourcesafe

[The following tutorial was something I knocked up for myself and my work colleagues, when we developed a need for a source control system. Clearly VSS is not the best ever source control system, but the integration with Visual Studio is very useful.]
Visual SourceSafe (VSS) is a client/server application which acts as a storage system for files. A file stored in a VSS server is not available via the standard file system, instead, it must be accessed through the VSS client tools - the VSS windows client, the VSS command-line tool, or else some application which integrates with or emulates these client tools.
The following discussion refers to version 6.0d. It contains three types of section:
- Discussions of important concepts, headed like this.
- Walk-throughs of development scenarios, headed like this.
- Practical advice and best-practice, which...
...appears in shaded boxes.
VSS Database
A VSS Database is a single instance of a VSS server - it's the big black box into which files get placed। All commands to VSS are directed towards a particular VSS Database, and wach database maintains a SRCSAFE।INI file which contains configuration information.
VSS Project
A VSS Database is organised as a tree structure, with each of the nodes of the tree being a VSS Project. Each database contains a single root project, which can branch (to a depth of 15 nodes) into sub-projects.
VSS Projects are misleadingly named; instead they should be thought of as directly analagous to filesystem directories, being unordered collections of up to 8000 files of any type. To illustrate this, note that where an application's source-code is organised into files that live in subdirectories off the main directory, these subdirectories have to be mapped onto subprojects of the application's main project directory.
Don't confuse Visual Studio 'projects' with Visual Sourcesafe 'projects'. The latter are more like directories, or folders. In fact, if you just think Visual Sourcesafe 'folder' where the documentation says Visual Sourcesafe 'project', it will be easier to follow.
Working Folder
Because the files stored in VSS are not directly exposed as files, development work on these files takes place on local copies, which are first checked out of VSS, changed, then checked in again. While a file is checked out of VSS, it is can be locked to other developers, preventing file overwriting. VSS also retains historical information about a file's changes, so it is possible to extract and use old versions of a file, or roll back unsuccessful changes.
The folder in which a user manipulates his local copy of VSS project files is known as his 'working folder'. Each user will often have a distinct, local working folder for each VSS project, the details of which are held by the VSS client in its SS.INI file.
Each working folder also gets populated with a VSSVER.SCC file, which contains version information about the files in that folder. This allows VSS quickly to determine whether or not local files are synchronised with those stored in the VSS database.
If it is possible that the source-code for your application may contain hard-coded file paths, it is important that developers' working folders are at the same location. One way of doing this is to require each application's working folder to be at the root of a particular drive.
Building and Deployment
For non-web applications, their compilation and deployment is not under the control of VSS. The process that builds and deploys an application needn't integrate with VSS except to extract the latest instances of the files into a local directory where they can be manipulated as required.
For web applications, VSS does support a 'deploy' command. This copies a project's files onto a webserver, either directly or via FTP. However, for various reasons one might choose to use other methods of deployment with web applications.
Development Scenario: A
Let's consider the situation in which two developers, UserA and UserB have to cooperatively develop a non-web application, are coming completely fresh to VSS, and neither uses an IDE to create the application.
In this scenario we shall assume that each developer has a development machine, and that there is a development server which both of them can access. This gives us the following three computers: DevelopmentUserA; DevelopmentUserB; DevelopmentServer
The first thing that needs to be done is for a VSS database to be installed on DevelopmentServer, and the VSS client tools to be installed on the two development machines. It's useful to know that the installation of VSS on DevelopmentServer sets up a network share to the installation directory, and the VSS client tools are available for users to install from there (this has the additional benefit that the client tools are set up with the DevelopmentServer database as the default).
Next, the VSS database has to be set up with appropriate users. VSS handles its own authentication, rather than integrating with Windows authentication (though file access is still subject to NTFS permissions, of course), so it is necessary to set up individual accounts in VSS for the two users.
Next, the application development should start, and the application files should be added to the VSS database. We shall assume that these application files are stored on UserA's personal development computer.
The advice from those with experience of developing using VSS is that before adding files to the VSS database it is useful to progress the development to a stage where the general structure of the application and its source code is mostly settled. This is because the process of renaming and moving application files can raise various small problems.
As he is not using a VSS-integrated IDE, UserA will use the VSS client tool to add the application file to the database. To do this, he will need to point the client tool at the appropriate VSS database, which involves executing File -> Open SourceSafe Database and navigating to the SRCSAFE.INI file on DevelopmentServer.
After identifying the appropriate database, the following procedure will add all of the application source files to the database:
  • select the root project ($/)
  • execute File -> Add Files
  • navigate to the root directory of the application files (leaving the Relevant Masks at *.*) and press Add
  • add a descriptive comment, tick 'recursive', and press 'Add'
  • agree to make the source directory the 'working directory' for the current project.
Alternatively, the required files may be dragged and dropped from Windows Explorer to the appropriate VSS project. This takes you to point 4 above.
Once the files are in the VSS project, UserB will be able to view the project via the client tool. When he tries to access these files, he will be prompted to nominate a local working directory, and will be able to work on from that point.
Getting vs Checking Out
If you 'Get' a file / project (which you do using the client tools by right-clicking on the item and selecting 'Get Latest Version'), a read-only copy is placed into your working folder. Changes you make to this file cannot be subsequently uploaded to the VSS database.
On the other hand, when you 'Check out' a file / project (which you do analagously to 'getting' it), a writable copy is placed into your working folder. By default, this locks the file to other users, so that while they may 'Get' a copy of the file they cannot themselves check it out. Notice that when you check out (and check in), you have the option to add a comment. This is to help developers keep track of the file changes.
When either of these options is being applied to a project, the procedure can be applied recursively, so that it is applied to subprojects of the chosen project.
Checking In
When you've finished making changes to a checked-out file / project, then you can check it in again, which writes the file changes to the VSS database. This procedure is also effected in the client tools using a right-click menu.
By checking project files out and in using VSS, developers can avoid the situation in which one overwrites the changes made by another.
Note that there are a few restrictions on filenames in VSS; for instance, they cannot contain the dollar sign ($).
Development Scenario: B
Let's now consider the situation in which our two developers have to cooperatively develop a non-web application using Visual Studio .NET. In this case the VSS commands are integrated with the IDE.
The recommended structure for a .NET solution is to locate each project folder under the solution folder (which is not the default: often, you create a project along with its solution files, and the two go into the same directory). It is easy to get the recommended structure when creating the project – the important check box to tick when creating the solution / project is revealed by clicking More, and reads Create directory for solution. Otherwise, it is possible to set up a blank solution first and add the project into it.
Let's assume that the VSS database and the VSS users have been set up correctly. Then the process of uploading the Visual Studio solution and its projects (recall that VS projects are different kind of things to VSS projects) will go via:
File -> Source Control -> Add Solution To Source Control
or possibly
File -> Source Control -> Add Selected Projects To Source Control
This action will first prompt for a VSS database path and user credentials, then (recursively) add one or more project to the chosen VSS database. It is possible to see the files uploaded to the database via the VSS client tool (though we found it necessary to close and open up the tool in order to see the added project – the refresh command did not seem effective).
Initially, the project files are in their checked-in state, demonstrated by a small blue padlock on the left-hand side of their icons. As has been said previously, in this state the project files cannot be amended. So a user who wants to amend a file has either to explicitly check it out from Visual Studio using one of the many menus that support this functionality, or else just attempt to edit it, which brings up a dialogue that helps him check it out. The checked-out status of the file is indicated with a small red exclamation mark.
Note that the compiled project files do not get placed into the VSS database. The generation and storage of these executables lies outside the purview of VSS. Furthermore, there are a number of project files generated by Visual Studio which contain information specific to an individual user, and these are not added to VSS either; such files have the extensions .suo, .user or .webinfo.
This deals with how UserA adds a project to Visual Studio project to VSS, but then how does UserB then access this existing project? The required command is:
File -> Source Control -> Open From Source Control
This then brings up a VSS dialogue box with the contained projects. When the user selects the required project, he is asked to nominate a local directory, which is then set as the user's project working folder.
Historically, people have had problems when attempting to share solution files (*.sln). It may be easier for developers to maintain their own solution files, and just pull in the project from VSS.
SS.exe
VSS also makes available a command-line program SS.exe to support its various functions. The main use of this tool is to allow integration of VSS functions into applications which, unlike Visual Studio, aren't integrated with VSS out of the box.
According to the VSS documentation, SS.exe "is usually in the Win32 subfolder under the folder you choose in the setup program". In our case, it was found at:
[D:\Program Files\Microsoft Visual Studio\Common\VSS\win32\SS.exe]
If you are going to make extensive use of SS.exe, you'll probably want to add its containing directory to the environment PATH variable.
It may also be necessary / useful to add a couple of VSS-specific environment variables. The first, SSUSER, relates to VSS authentication. According to the documentation, when the command-line tool is used, VSS picks up the user's logon name and tries to authenticate using that (apparently ignoring the password, which makes you wonder why it's there at all). If the VSS username is not the same as one's NT logon name, however, one can put the VSS username in the SSUSER environment variable, and this is used instead (again, without any password).
Note that in order to access the VSS database in the first place, users will have to have an NT login with appropriate access permissions. The authentication provided by VSS lives on top of standard Windows security.
The second environment variable, SSDIR, is used to inform SS.exe which VSS database it should use. If you install the client tools via the executable provided on the server hosting the database, then this may be set automatically to point to that database. Otherwise, you will probably need to set it up automatically. In our case the setting looks like:
SSDIR = \\nwk-s-web\sourcesafe\
Development Scenario: C
Let's consider the situation in which two developers are using a basic development tool like Textpad to develop a non-web application. One way in which they could proceed is to use the VSS client tool to check files in and out. But a prefered alternative might be to write a macro (or possibly, for Textpad, a 'user defined tool') utilising SS.exe.
There is no point in going through exactly how this is done in Textpad. Instead we'll take a quick look at the kind of SS.exe commands that would be used.
First, we should note that the command-line tool is able to keep track of the 'current project' across commands (recall that for VSS a project is an element in a hierarchy). The following command sets this current project to 'tempProject.root' - note the dollar sign and the forward slash in the code (the latter of which indicates that tempProject.root is itself located at the database root).
ss cp $/tempProject.root
The following command checks out the file tempProject.sln from VSS:
ss Checkout $tempProject/tempProject.sln
There are two important points to note about this. Firstly, we are identifying the file we want by providing a virtual path. Because we previously set the current project to be equal to
$/tempProject.root
the virtual path resolves – in the familiar way – to
$/tempProject.root/tempProject/tempProject.sln
The second point is that the command does not place the checked out files in any working folder that might have previously been specified for this project. Instead it places the files in the folder in which the user executes the command.
We can also check out project files recursively, which creates the appropriate local subfolders. For example, the following command (which uses an absolute project path) checks out all of project and subproject files under tempProject.root:
ss Checkout $/tempProject -R
(For some reason, if you don't provide the project / file path in the Checkout command it asks you to enter one, but then takes the current project setting if you press enter.)
Checking files in works in an analagous way, but uses the 'Checkin' command, eg.
ss Checkin -R
Getting (recursively) the latest version of project files is equally straightforward:
ss Get -R
Multiple Checkouts
By default, if you check out a file then no other user can check it out until you've checked it back in: the file becomes locked to changes by anyone other than you. But the VSS administrator may enable 'multiple checkouts', which (fairly obviously) allows multiple users simultaneously to check out files.
So, what happens when UserA and UserB each checks out a file to his local working folder, each updates it independently, and then each tries in turn to check it back in? What needs to be avoided here, obviously, is UserA's changes being overwritten by UserB's check-in. What VSS tries to do is to merge each user's changes into the master copy. This is easiest if users have made small changes to different parts of the file. However, if users have each changed the same part of the file then merging is not possible, so VSS rejects UserB's check-in attempt, with appropriate information about the problem, and requires him to resolve the conflict.
The people that I've talked to about this, who have been using VSS from its earliest versions, recommend against using multiple checkouts. They claim that the merging algorithm isn't 100% solid, and that there are management benefits to knowing exactly who is currently working on a file. However, it may be that where development involves a large, geographically distributed group of people that communication problems would offset any problems with multiple checkouts.
Web Deployment Models for VS.NET
The MSDN article Web Projects and Source Control Integration in Visual Studio.NET provides three useful models for collaboratively developing websites (in VS.NET, but the models are useful beyond this). The models differ crucially in where each developer can run and test changes. In each case the deployment of the production website from a latest version is independent of the model.
- Isolated Development
In this model, all developers maintain a working copy of the website on their own development machine (which must therefore be running web server software). Latest versions of the code can be brought down from VSS in the familiar way. This is the recommended development model.
- Semi-Isolated Development
In this model, all developers share a single development web server, such that each has a distinct working folder on that server. While users share a webserver, therefore, each user maintains his own copy of the web application. Latest versions of the code are brought down from VSS to these working folders.
One of the disadvantages of this model is that the debugging process freezes all the applications on the website, so at most one user can test / debug at a time.
- Non-Isolated Development
In this model, there is a single development web server, but developers must access the master copy of the code using Frontpage Extensions. Here developers both share a webserver and share a copy of the web application (although they may be able to download the files to local machines, when the developers run and test changes they are using the same code). In order for there to be VSS integration, the website needs to be a Frontpage web project (which can be set to integrate with VSS).
Development Scenario: D
Let's look at the situation where our two developers are creating a simple ASP.NET website with VS.NET. I shall assume that they use the 'isolated' development model.
When you create a new ASP.NET web application in VS.NET, the default location for the project files lies somewhere under the root folder of the default website, which will probably be something like this:
C:\inetpub\wwwroot
On the other hand, the location of the solution file is likely to be elsewhere. But Microsoft recommends that the solution file should be stored in a folder above the root project folder. The way round this problem is to create a blank solution located at a certain folder, then map a folder below it as a new virtual IIS directory, and use this virtual directory as the location for the web application.
So, let us suppose that DevelopmentUser A starts off by creating a blank solution, locating it at:
C:\Solutions\TestSolution1
He then creates a new folder
C:\Solutions\TestSolution1\TestProject1
maps this to the IIS virtual directory
http://localhost/TestProject1
and creates the new project TestProject1, locating it in this virtual directory.
To add this to VSS, DevelopmentUserA uses the standard command:
File -> Source Control -> Add Solution To Source Control
DevelopmentUserA ignores the warning that he will no longer be able to open the project using FrontPage Web access - accessing web projects using File Access is preferable where this is possible (basically this means that VS.NET just accesses the project files using the file system, rather than getting them from IIS via Frontpage Extensions). In the next dialogue boxes he enters the appropriate credentials to place the solution in the VSS database with the appropriate name, and the job is done.
According to the Microsoft document Team Development with Visual Studio.NET and Visual SourceSafe, the project structure in VSS should mirror the file structure in the working folder. But VS.NET seems very keen to place solution files in VSS projects parallel to those containing VS.NET project files. It is possible to manually move these folders in VSS, and then tell VS.NET about the move, but it's not clear why this is important.
Web Projects
VSS distinguishes between standard and 'web' projects. For the latter it has introduced – albeit in a fairly half-hearted way – some specific tools. So, for instance, there is a facility to create a site map, hyperlink checking, and some keyword expansion. Furthermore, there is also a 'deploy' command, which seems just to copy all the project files to location via FTP.
Sharing and Branching Files
It is possible to 'share' files between projects, so that there is a single master copy which appears in different places in the VSS project tree structure. Changes made to this copy from within one project will be seen in the other projects that share the file.
Right-clicking on a folder within VSS Explorer, or using the File -> Source Control menu VS.NET brings up the Share dialogue, from which one can choose files to share.
I have been informed that sharing of files can cause problems. In particular, the following scenario: a file is shared between projects A and B. It is checked out as part of project A but then by mistake checked back in as part of project B. (Exactly what problems this caused was left unspecified by my correspondant, but they seemed severe enough to warrant a warning).
Rolling Back
VSS maintains a history of changes to files, so that it is possible to roll back files to previous versions. From VSS Explorer you right-click the file and choose 'History', from which you can see a history of changes to the file along with possible actions. From VS.NET you use the File -> Source Control menu.
It doesn't look like it's possible to roll a whole project back to a previously known good point, however.
By default, rolling back a file erases all of the file history past that point. However, it is possible to perform a 'virtual rollback', in which the file history is not deleted.
Where files are shared between projects, rolling back a file from one project will result in the files branching.
Showing Differences
The VSS client tool is able to display the differences between different versions of text files, which can be useful for debugging purposes.
Pinning
It is possible to pin files so that they can't be changed (at least without first being unpinned).
Labelling
As well as commenting actions, users can add labels to files or projects. In the VSS client tool this is effected by right clicking or using File -> Label…
Labelling files is useful for marking certain versions as important. For instance, a project might be labelled as 'Release Candidate 1', and if one wanted to extract that version at a later date, then one could look for the label. Note that when a label is applied to an object, a separate entry is created within the history of that object.
Cloaking
Where an operation like 'Get' is used recursively, the operation is applied to all of the subprojects (in this case we can say that it is applied 'indirectly'). If a user wishes to turn this behaviour off for his own recursive operations, he can mark the subproject as 'cloaked'. Subprojects so marked won't have operations applied to them indirectly. They may still be usable directly, however.
Shadow Folders
A shadow folder is a nominated file directory which is kept up to date with copies of the most recent files in a VSS project, the idea being that this folder can be used to compile latest versions of code. Unfortunately, however, the updating process is not recursive.

Automation API

Although Microsoft doesn't advertise it very strongly, VSS can be Automated by referencing the ssapi.dll. There is a pretty full paper on automating VSS using VB 6.0.