Thursday, August 20, 2009

How safe is Marshal class?

Hello...

How safe is this piece of code?
//allocate 10 bytes
IntPtr p = Marshal.AllocHGlobal(10);
byte[] a = new byte[20];
for (byte i = 0; i < a.Length; ++i) a[i] = i;
//copy the buffer
Marshal.Copy(a, 0, p, a.Length);

Here's some pictures that shows the memory before and ...
after the copying...

Thursday, August 13, 2009

Project management resource

Hello...

I just found a website with some Project Management resources. ProjectSmart is the name of the project and you can find it here.

Have fun!

Tuesday, August 11, 2009

Create objects in remote/other AppDomains + unload assemblie(2)

Hi there...

In my previous post I showed how can you unload a assembly within an AppDomain and rebuild the assembly project without stoping the process that loaded the assembly.

In this post I'll show you how can you pass parameters to the newly created AppDomain. For this, you have to create a class that is decorated with Serializable attribute and create some properties that are the actual parameters (it is passed by value. If you want to pass by refrence, you must inherit from MarshalRefByObject - but you will stay in the main AppDomain).
[Serializable]
internal class CrossDomainObject
{
internal string Path { get; set; }
internal string Type { get ; set; }
internal object ObjectSent { get; set; }

internal void DomainCallBack()
{
//load assembly
Assembly assembly = Assembly.LoadFrom(Path);
AppDomain.CurrentDomain.Load(assembly.FullName);

//get type
Type type = assembly.GetType(Type);
if (type == null)
throw new NullReferenceException(
string.Format("Unable to get type: {0}", Type));

//create instance in remote domain
object instance= Activator.CreateInstance(type);

if (instance == null)
throw new NullReferenceException("Unable to create object in remote domain");

//get Controller property
PropertyInfo p = commandType.GetProperty("MyProperty");

if (p == null)
throw new NullReferenceException("Unable to get MyProperty property");

if (ObjectSent == null)
throw new NullReferenceException("Convert to ObjectSent failed!");

//Set value to the remote controller
p.SetValue(instance, ObjectSent , null);
}
}

The CrossAppDomainDelegate will invoke the DomainCallBack.

CrossDomainObject obj = new CrossDomainObject { ObjectSent = new List<object>(), Path = pathToAssembly, Type = typeOfInstance };
domain.DoCallBack(new CrossAppDomainDelegate(obj.DomainCallBack));

That's about it...

Monday, August 10, 2009

Create objects in remote/other AppDomains + unload assemblies

Hi there...
Today I wanted know how can I unload an assembly. After some Google/Bing/MSDN search, I realized that unloading an assembly is possible only and if only you create a new AppDomain, load the assembly in it and then Unload the AppDomain.

Some articles were about domain.CreateInstanceAndUnwrap(assemblyName, typeName) but the result was always the same: the assembly was loaded in the primary application domain. If you want to rebuild the assembly/project you will find out that it's not possible because the assembly was loaded in the main application domain and you will get and Error 2: file is being used by another process. But there's a trick.

In .NET Framework you can comunicate with others AppDomains using .NET Remoting. The comunication can be done using IPC Remoting (if you have a server and a client) or using the CrossAppDomainDelegate.

This blog entry will conver the case when you want to create a new AppDomain and create some objects in it without stoping the process that has the main application domain.

First of all, you will have to inherit your cross appdomain objects from MarshalByRefObject.
namespace ClassLibrary1
{
public class Class1 : MarshalByRefObject
{
public object MyProperty
{
set
{
object a = value;
Console.WriteLine(string.Format("Remote AppDomain ID: {0}", AppDomain.CurrentDomain.Id));
}
}
}
}

After that, you will have to create a new AppDomain next to your main AppDomain.
static void Main(string[] args)
{
Console.WriteLine(string.Format("Local AppDomain ID: {0}{1}", AppDomain.CurrentDomain.Id, Environment.NewLine));

while (Console.ReadKey().Key != ConsoleKey.Escape)
{
Console.WriteLine("Load in other AppDomain... ");
LoadInOtherAppDomain();
Console.WriteLine("Done... Press ESC to exit!");
}
}

private static void LoadInOtherAppDomain()
{
try
{
//create AppDomain
AppDomain domain = AppDomain.CreateDomain("MyTestDomain");
domain.DoCallBack(new CrossAppDomainDelegate(LoadAssembly));

//unload domain
AppDomain.Unload(domain);
domain = null;
}
catch
{
throw;
}
}

In the LoadAssembly method, you will have to create a new assembly, load the assembly in the newly created AppDomain, find the type of the object that you want to instantiate find methods/properties and call them.

static void LoadAssembly()
{
Assembly assembly = Assembly.LoadFrom("ClassLibrary1.dll");
AppDomain.CurrentDomain.Load(assembly.FullName);

Type t = Array.Find(assembly.GetTypes(), ts => ts.FullName == "ClassLibrary1.Class1");

if(t == null)
throw new NullReferenceException("Unable to get type!");

//create instance
object instance = Activator.CreateInstance(t);

if(instance == null)
throw new NullReferenceException("Unable to create an instance!");
//get MyProperty property
PropertyInfo p = instance.GetType().GetProperty("MyProperty");

if (p == null)
throw new NullReferenceException("Unable to get MyProperty property!");

//Set value to the remote property
p.SetValue(instance, new List<object>, null);n
}

You will see that the result is:
Local AppDomain ID: 1

Load in other AppDomain...
Remote AppDomain ID: 2
Done... Press ESC to exit!
Load in other AppDomain...
Remote AppDomain ID: 3
Done... Press ESC to exit!
Load in other AppDomain...
Remote AppDomain ID: 4
Done... Press ESC to exit!

Well, that's about it...

The nice thing here is that you won't have to stop the process to debug/rebuild the library/assembly that is dynamically loaded! :)

Nice, right?

Happy coding...