C# Basic (10)

1. Creating and executing code at run time

Although the code in this example is extremely simple, I’ve separated the server code—a DLL that contains a class creates a method called HelloWorld—from the client code, an application that instantiates the code-generating class and calls its HelloWorld method. (Note that the compiler switches are in the code comments.) An explanation follows for the DLL code, which is here:

using System;
using System.Reflection;
using System.Reflection.Emit;

namespace ILGenServer
{
    public class CodeGenerator
    {
        public CodeGenerator()
        {
            // Get current currentDomain.
            currentDomain = AppDomain.CurrentDomain;

            // Create assembly in current currentDomain.
            assemblyName = new AssemblyName();
            assemblyName.Name = "TempAssembly";

            assemblyBuilder =
                    currentDomain.DefineDynamicAssembly
                    (assemblyName, AssemblyBuilderAccess.Run);

            // create a module in the assembly
            moduleBuilder = assemblyBuilder.DefineDynamicModule
                                ("TempModule");

            // create a type in the module
            typeBuilder = moduleBuilder.DefineType
                              ("TempClass",
                              TypeAttributes.Public);
            // add a member (a method) to the type
            methodBuilder = typeBuilder.DefineMethod
                                 ("HelloWorld",
                                  MethodAttributes.Public,
                                  null,null);

            // Generate MSIL.
            msil = methodBuilder.GetILGenerator();
            msil.EmitWriteLine("Hello World");
            msil.Emit(OpCodes.Ret);

            // Last "build" step : create type.
            t = typeBuilder.CreateType();
        }

        AppDomain currentDomain;
        AssemblyName assemblyName;
        AssemblyBuilder assemblyBuilder;
        ModuleBuilder moduleBuilder;
        TypeBuilder typeBuilder;
        MethodBuilder methodBuilder;
        ILGenerator msil;
        object o;

        Type t;
        public Type T
        {
            get
            {
                return this.t;
            }
        }
    }
}

Now all the client has to do is retrieve the CodeGenerator ‘s Type member, create an Activator instance, instantiate a MethodInfo object from the type, and then invoke the method. Here’s the code to do that, with a little error checking added to make sure things work as they should:

using System;
using System.Reflection;
using ILGenServer;

public class ILGenClientApp
{
    public static void Main()
    {
        Console.WriteLine("Calling DLL function to generate " +
                          "a new type and method in memory...");
        CodeGenerator gen = new CodeGenerator();

        Console.WriteLine("Retrieving dynamically generated type...");
        Type t = gen.T;
        if (null != t)
        {
            Console.WriteLine("Instantiating the new type...");
            object o = Activator.CreateInstance(t);

            Console.WriteLine("Retrieving the type's " +
                              "HelloWorld method...");
            MethodInfo helloWorld = t.GetMethod("HelloWorld");
            if (null != helloWorld)
            {
                Console.WriteLine("Invoking our dynamically " +
                                  "created HelloWorld method...");
                                  helloWorld.Invoke(o, null);
            }
            else
            {
                Console.WriteLine("Could not locate " +
                                  "HelloWorld method");
            }
        }
        else
        {
            Console.WriteLine("Could not access Type from server");
        }
    }
}

Now if you build and execute this application, you will see the following output:

Calling DLL function to generate a new type and method in memory...
Retrieving dynamically generated type...
Instantiating the new type...
Retrieving the type's HelloWorld method...
Invoking our dynamically created HelloWorld method...
Hello World

2. Unmanaged code refers to code that is not managed, or controlled, by the .NET runtime.

This attribute is called the DllImport attribute, and its syntax is shown here:

[DllImport(dllName)]
accessModifier static extern retValue dllFunction ( param1, param2,...);

Pay particular attention to the fact that the static and extern modifiers must also be used on the function that you’re defining.

using System;
using System.Runtime.InteropServices;

class PInvoke1App
{
    [DllImport("user32.dll")]
    static extern int MessageBoxA(int hWnd,
                                 string msg,
                                 string caption,
                                 int type);

    public static void Main()
    {
        MessageBoxA(0,
                    "Hello, World!",
                    "This is called from a C# app!",
                    0);
    }
}

Running this application results in the expected message box popping up with the “Hello, World!” statement.

using an internal name of MsgBox while still calling the DLL’s MessageBoxA function.

using System;
using System.Runtime.InteropServices;

class PInvoke2App
{
    [DllImport("user32.dll", EntryPoint="MessageBoxA")]
    static extern int MsgBox(int hWnd, 
                             string msg,
                             string caption,
                             int type);

    public static void Main()
    {
        MsgBox(0, 
               "Hello, World!",
               "This is called from a C# app!",
               0);
    }
}

As you can see, I need only specify the DllImport attribute’s EntryPoint named parameter to be able to name my internal equivalent of the external DLL function anything I like.

3. The following code enumerates and prints out the captions of all the windows in the system:

using System;
using System.Runtime.InteropServices;
using System.Text;

class CallbackApp
{
 [DllImport("user32.dll")]
 static extern int GetWindowText(int hWnd, StringBuilder text, int
 count);

 delegate bool CallbackDef(int hWnd, int lParam);

 [DllImport("user32.dll")]
 static extern int EnumWindows (CallbackDef callback, int lParam);

 static bool PrintWindow(int hWnd, int lParam)
    {
        StringBuilder text = new StringBuilder(255);
        GetWindowText(hWnd, text, 255);

        Console.WriteLine("Window caption: {0}", text);
        return true;
    }

    static void Main()
    {
        CallbackDef callback = new CallbackDef(PrintWindow);
        EnumWindows(callback, 0);
    }
}

the DLL function is not permitted to alter a string, so you can’t use that type. And even if you attempt to pass by reference, there’s no way for the calling code to initialize a string to the correct size. That’s where the StringBuilder class comes in. A StringBuilder object can be dereferenced and modified by the called function, as long as the length of the text does not exceed the maximum length passed to the StringBuilder constructor.

4. unsafe code is not code that is inherently unsafe and untrustworthy—it’s code for which the .NET runtime will not be controlling the allocation and deallocation of memory. You write unsafe code by using two keywords: unsafe and fixed.

Pointers can be acquired only for value types, arrays, and strings. in the case of arrays the first element must be a value type.

// Compile this application with the /unsafe option.

using System;

class Unsafe1App
{
    public static unsafe void GetValues(int* x, int* y)
    {
        *x = 6;
        *y = 42;
    }

    public static unsafe void Main()
    {
        int a = 1;
        int b = 2;
        Console.WriteLine("Before GetValues() : a = {0}, b = {1}",
                          a, b);
        GetValues(&a, &b);
        Console.WriteLine("After GetValues() : a = {0}, b = {1}",
                          a, b);
    }
}

This sample needs to be compiled with the /unsafe compiler option. The output from this application should be the following:

Before GetValues() : a = 1, b = 2
After GetValues() : a = 6, b = 42

The fixed statement has the following syntax:

fixed (type * ptr = expression) statement

As I mentioned, the statement tells the GC not to bother with the specified variable. Note that type is an unmanaged type or void, expression is any expression that results in a type pointer, and statement refers to the block of code for which the pinning of the variable is applicable.

 

5. an assembly is the packaging of a manifest, one or more modules, and, optionally, one or more resources. Using assemblies allows you to semantically group functional units into a single file for purposes of deployment, versioning, and maintenance.

 

6. “DLL hell” refers to the situation in which one application overwrites a DLL needed by another application, usually with an earlier version of the same DLL, breaking the first application.

 

Advertisements
This entry was posted in C#. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s