Microsoft Agent; Providing a Custom Popup Menu

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

A second honeymoon with Microsoft Agent. Do you remember our article Programming Microsoft Agent in WinForms and our sample application PartIt? After you have included your desired agent in your application, are you feeling bad with the default popup menu? If so, then you are in the right place (welcome :).)

Enough talking, let’s get to work! First, prepare your code that loads the Microsoft Agent and brings it to the screen.

After that, create your System.Windows.Forms.ContextMenuStrip and add your required items (well, including ‘Hide’ maybe) and finish the item event handlers.

Now, let’s complete it. Get to the code that loads the agent character (e.g. calls the Characters.Load() method of the agent control object, AxAgentObjects.AxAgent) and just disable the AutoPopupMenu flag/property of the character object, AgentObjects.IAgentCtlCharacterEx. This flag/property determines whether to allow the default popup menu or not.

For example, the following code disables this property:

    AxAgentObjects.AxAgent agentCtl;
    AgentObjects.IAgentCtlCharacterEx agentChar;

    // initializing 'agentCtl'
    // . . .

    agentCtl.Characters.Load("Merlin", "merlin.acs");
    agentChar = agentCtl.Characters.Character("Merlin");
    agentChar.AutoPopupMenu = false;

Next comes the interesting point. When the character is clicked, the ClickEvent event of the agent control (AxAgent) fires. So the next step is to handle this event and to provide your code that brings up your custom context menu. Consider the following code:

// agentCtl.ClickEvent += agent_ClickEvent;

public void agentCtl_ClickEvent(object sender, AxAgentObjects._AgentEvents_ClickEvent e)
{
    if (e.characterID == "Merlin")  // check for this if you have many characters
    {
        if (e.button == 2) // 1 = left, 2 = right
        {
            myContextMenu.Show(e.x, e.y);
        }
    }
}

Well done!

Have a nice Sunday!

9 Rules about Constructors, Destructors, and Finalizers

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Overview

First, this writing concentrates of and compares between three programming languages, C#, C++/CLI, and ISO/ANSI C++. It discusses 9 rules that every developer should keep in mind while working with constructors, destructors, and finalizers and class hierarchies:

  • Rule #1: Contrsuctors are called in descending order
  • Rule #2: In C# lexicology, a destructor and a finalizer refer to the same thing
  • Rule #3: Destructors are called in ascending order
  • Rule #4: Finalizers are a feature of GC-managed objects only
  • Rule #5: You cannot determine when finalizers would be called
  • Rule #6: C++/CLI differs between destructors and finalizers
  • Rule #7: In C++/CLI and classic C++, you can determine when destructors are called
  • Rule #8: In C++/CLI, destructors and finalizers are not called together
  • Rule #9: Beware of virtual functions in constructors

Rule #1: Constructors are called in descending order

Rule #1: Constructors are called in descending order; starting from the root class and stepping down through the tree to reach the last leaf object that you need to instantiate. Applies to C#, C++/CLI, and ANSI C++.

Let’s consider a simple class hierarchy like this:

    class BaseClass
    {
        public BaseClass()
        {
            Console.WriteLine("ctor of BaseClass");
        }
    }

    class DerivedClass : BaseClass
    {
        public DerivedClass()
        {
            Console.WriteLine("ctor of DerivedClass");
        }
    }

    class ChildClass : DerivedClass
    {
        public ChildClass()
        {
            Console.WriteLine("ctor of ChildClass");
        }
    }

ChildClass inherits from DerivedClass, and DerivedClass, in turn, inherits from BaseClass.

When we create a new instance of ChildClass using a simple line like this:

    static void Main()
    {
        ChildClass cls = new ChildClass();
    }

the code outputs the following results:

ctor of BaseClass
ctor of DerivedClass
ctor of ChildClass

Rule #2: In C# lexicology, a destructor and a finalizer refer to the same thing

Rule #2: In C# lexicology, a destructor and a finalizer refer to the same thing; the function called before the object is fully-removed from the memory (i.e. GC-collected). Applies to C# only.

Let’s consider the same class hierarchy but with destructors:

    class BaseClass
    {
        public ~BaseClass()
        {
            Console.WriteLine("dtor of BaseClass");
        }
    }

    class DerivedClass : BaseClass
    {
        public ~DerivedClass()
        {
            Console.WriteLine("dtor of DerivedClass");
        }
    }

    class ChildClass : DerivedClass
    {
        public ~ChildClass()
        {
            Console.WriteLine("dtor of ChildClass");
        }
    }

When you define a class destructor with that C++-alike syntax (preceding the function name with a ~) the compiler actually replaces your destructor with an override of the virtual Object.Finalize() function. That is, before the object is removed (i.e. GC-collected) from the memory, the finalizer (i.e. destructor) is called first. This finalizer first executes your code. After that it calls the finalizer of the base type of your object. If we could decompile our assembly, we would see that our destructor in the ChildClass (so other classes) has been replaced with this function:

        protected virtual void Finalize()
        {
            try
            {
                Console.WriteLine("dtor of ChildClass");
            }
            finally
            {
                base.Finalize();
            }
        }

Rule #3: Destructors are called in ascending order

Rule #3: Destructors are called in ascending order, starting from the leaf object that you need to instantiate and moving up through the tree to reach the very first base class of your object. In reverse of constructors calling order. Applies to C#, C++/CLI, and ANSI C++.

Now, instantiate your class:

    static void Main()
    {
        ChildClass cls = new ChildClass();

        // 'cls' is removed from memory here
    }

the code should outputs the following results:

dtor of ChildClass
dtor of DerivedClass
dtor of BaseClass

Rule #4: Finalizers are a feature of GC-managed objects

Rule #4: Finalizers are a feature of GC managed objects (i.e. managed classes). That’s because the finalizer is called only when the GC removes the object from the memory (i.e. frees memory associated with).

Now, try to create a simple structure with a destructor:

    struct MyStruct
    {
        ~MyStruct()
        {
            Console.WriteLine("dtor of MyStruct");
        }
    }

The code won’t compile. That’s because that GC doesn’t handle structures.

Rule #5: You can’t determine when finalizers would be called

That’s because you don’t know when the next garbage collection would occur, even if you performed a manual garbage collection (using System.GC.Collect() function) you won’t know exactly when memory would be released. In addition, GC always delay releasing of finalizable object, it puts them in a special GC queue called freachable (pronounced ef-reachable, F stands for Finalize) queue. Applies to C# and C++/CLI (.NET.)

Rule #6: C++/CLI differs between destructors and finalizers

Rule #6: C++/CLI differs between destructors and finalizers. That is, finalizers are called by GC, and destructors are called when you manually delete the object.

Let’s consider the same example but in C++/CLI:

ref class BaseClass
{
public:
	BaseClass()
	{
		Console::WriteLine("ctor of BaseClass");
	}

	~BaseClass()
	{
		Console::WriteLine("dtor of BaseClass");
		GC::ReRegisterForFinalize(this);
	}
};

ref class DerivedClass : BaseClass
{
public:
	DerivedClass()
	{
		Console::WriteLine("ctor of DerivedClass");
	}

	~DerivedClass()
	{
		Console::WriteLine("dtor of DerivedClass");
		GC::ReRegisterForFinalize(this);
	}
};

ref class ChildClass : DerivedClass
{
public:
	ChildClass()
	{
		Console::WriteLine("ctor of ChildClass");
	}

	~ChildClass()
	{
		Console::WriteLine("dtor of ChildClass");
		GC::ReRegisterForFinalize(this);
	}
};

When we run the code:

int main()
{
	ChildClass^ cls = gcnew ChildClass();
}

it outputs the following results:

ctor of BaseClass
ctor of DerivedClass
ctor of ChildClass

The destructors are not called. Why? Unlike C#, in C++/CLI there is a big difference between destructors and finalizers. As you know, the finalizer is called when the GC removes the object from the memory. Destructors, on the other hand, are called when you destroy the object yourself (e.g. use the delete keyword.)

Now, try to change the test code to the following:

int main()
{
	ChildClass^ cls = gcnew ChildClass();
	delete cls;
}

Run the code. Now, destructors are called.

Next, let’s add finalizers to our objects. The code should be like the following:

ref class BaseClass
{
public:
	BaseClass()
	{
		Console::WriteLine("ctor of BaseClass");
	}

	~BaseClass()
	{
		Console::WriteLine("dtor of BaseClass");
		GC::ReRegisterForFinalize(this);
	}
	!BaseClass()
	{
		Console::WriteLine("finz of BaseClass");
	}
};

ref class DerivedClass : BaseClass
{
public:
	DerivedClass()
	{
		Console::WriteLine("ctor of DerivedClass");
	}

	~DerivedClass()
	{
		Console::WriteLine("dtor of DerivedClass");
		GC::ReRegisterForFinalize(this);
	}
	!DerivedClass()
	{
		Console::WriteLine("finz of DerivedClass");
	}
};

ref class ChildClass : DerivedClass
{
public:
	ChildClass()
	{
		Console::WriteLine("ctor of ChildClass");
	}

	~ChildClass()
	{
		Console::WriteLine("dtor of ChildClass");
		GC::ReRegisterForFinalize(this);
	}
	!ChildClass()
	{
		Console::WriteLine("finz of ChildClass");

	}
};

As you see, the syntax of constructors, destructors, and finalizers are very similar.

Now, let’s try the code:

int main()
{
	ChildClass^ cls = gcnew ChildClass();
}

GC would call finalizers and the code would outputs the following:

ctor of BaseClass
ctor of DerivedClass
ctor of ChildClass
finz of ChildClass
finz of DerivedClass
finz of BaseClass

Rule #7: In C++/CLI and C++, you can determine when destructors are called

Now, try to destroy the object yourself:

int main()
{
	ChildClass^ cls = gcnew ChildClass();
	delete cls;
}

The delete statement calls object destructors and removes the object from memory.

Or else, declare the object with stack-semantics:

int main()
{
	ChildClass cls;
}

Now, destructors are called when the scope of the object ends.

Rule #8: In C++/CLI, destructors and finalizers are not called together

Rule #8: In C++/CLI, destructors and finalizers are not called together. Only destructors or finalizers are called. If you manually delete the object or you declare it with stack-semantics, destructors are called. If you leaved the object for GC to handle, finalizers are called.

Now try to run the code. The code should outputs the following results:

ctor of BaseClass
ctor of DerivedClass
ctor of ChildClass
dtor of ChildClass
dtor of DerivedClass
dtor of BaseClass

Rule #9: Beware of virtual functions in constructors

Rule #9: Beware of virtual (overridable) functions in constructors. In .NET (C# and C++/CLI,) the overload of the most derived object (the object to be instantiated) is called. In traditional C++ (ISO/ANSI C++,) the overload of the current object constructed is called.

Let’s update our C# example:

class BaseClass
{
    public BaseClass()
    {
        Foo();
    }

    public virtual void Foo()
    {
        Console.WriteLine("Foo() of BaseClass");
    }
}

class DerivedClass : BaseClass
{
    public DerivedClass()
    {
    }

    public override void Foo()
    {
        Console.WriteLine("Foo() of DerivedClass");
    }
}

class ChildClass : DerivedClass
{
    public ChildClass()
    {
    }

    public override void Foo()
    {
        Console.WriteLine("Foo() of ChildClass");
    }
}

When you execute the code:

    static void Main()
    {
        ChildClass cls = new ChildClass();
    }

you would get the following results:

Foo() of ChildClass

The same code in C++/CLI:

ref class BaseClass
{
public:
	BaseClass()
	{
		Foo();
	}

	virtual void Foo()
	{
		Console::WriteLine("Foo() of BaseClass");
	}
};

ref class DerivedClass : BaseClass
{
public:
	DerivedClass()
	{
	}

	virtual void Foo() override
	{
		Console::WriteLine("Foo() of DerivedClass");
	}
};

ref class ChildClass : DerivedClass
{
public:
	ChildClass()
	{
	}

	virtual void Foo() override
	{
		Console::WriteLine("Foo() of ChildClass");
	}
};

The code outputs the same results.

But what if you need to call the virtual function of the BaseClass? Just change the code to the following:

ref class BaseClass
{
public:
	BaseClass()
	{
		BaseClass::Foo();
	}

	virtual void Foo()
	{
		Console::WriteLine("Foo() of BaseClass");
	}
};

Now, the code outputs:

Foo() of BaseClass

Let’s consider the same example but in classic ISO/ANSI C++:

class CBaseClass
{
public:
	CBaseClass()
	{
		Foo();
	}
	virtual void Foo()
	{
		cout << "Foo() of CBaseClass" << endl;
	}
};

class CDerivedClass : CBaseClass
{
public:
	CDerivedClass()
	{
	}

	virtual void Foo() override
	{
		cout << "Foo() of CDerivedClass" << endl;
	}
};

class CChildClass : CDerivedClass
{
public:
	CChildClass()
	{
	}

	virtual void Foo() override
	{
		cout << "Foo() of CChildClass" << endl;
	}
};

Now, run the code. It should outputs:

Foo() of BaseClass

In classic C++, the overload of the function of the class being constructed is called unlike C# and C++/CLI (.NET in general.)

Video: What’s New in C# 2/3/4

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Here’s the complete “What’s New in C#” webcast series of Bruce Kyle (ISV Architect Evangelist of Microsoft) from Channel 9 blog:

What’s new in C# 2:

We will try to update this list as soon as new items release.

Creating a Simple Twitter Client Application

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Code: Geming.twitoo.msi

Contents

Contents of this writing:

  • Contents
  • Overview
  • Introduction
  • Twitter API
    • What Twitter API is
    • API Documentation
    • Things to be Kept into mind
  • API Methods
    • Method Address
    • Methods with Arguments
    • Methods Require Authentication
  • Twitter and .NET
    • Accessing the API
    • Authentication
    • Encoding URIs
    • Business Objects
    • Retrieving Data
  • twittoo; Sample Application
    • Snapshots
    • Description
    • Interface
    • URL Shortening
    • Download

Overview

This writing discusses the Twitter API and how you can utilize it in your managed application. It begins by a brief discussion of the API and the methods used. After that, it digs into the discussion of how you can utilize the API into your application with help of code samples and examples. At the end of this writing, there’s a nice open-source Twitter client application available for download. Worth mentioning that this article focuses on the REST API set of Twitter, specifically XML endpoints.)

Introduction

Today, we are talking about Twitter as it is one of the most famous social networking services. This article discusses how you can create Twitter clients using .NET Framework. Oh, before we start, I’d like to introduce you my twitter account first; it’s @elsheimy.

Twitter API

What Twitter API is

Before we dig into the discussion of Twitter API we need to know first what the Twitter API is.

Twitter API is a set of endpoints (or methods) that allow applications to work with Twitter data.

Twitter API consists of three parts:

  • Two REST APIs:
    • REST API:
      This is the core API set; it allows developers to access core Twitter data, it contains most (if not all) of the methods and functions you would use to utilize Twitter data in your application, and it supports (mostly) three formats (or endpoints) for each method: XML, Atom, and JSON formats.
    • Search API:
      Fully integrating search methods allows you to search for tweets based on specific criteria and to search for daily/weekly hot trends. It supports only the JSON format. This set of API was originally developed by Summize, Inc. and integrated into the Twitter API. (Twitter is looking forward to unify the REST API.)
  • Streaming API:
    A set of API methods that allow near-realtime access to various subsets of Twitter public statuses. All methods are in JSON format, no XML or ATOM endpoints available.

Actually, the REST API would be very sufficient for you unless you find it easier to handle the JSON format.

It is worth mentioning that the REST API provides very handful and sufficient search methods that your application can use. If you need more searching capabilities (like searching for tweets that confirm to a set of criteria,) you can dig into the REST Search API.

You are not required to stick to a specific set of API methods. You can mix the methods you like from the three APIs.

Wikipedia: REST (Representational State Transfer) is a style of software architecture for distributed hypermedia systems such as the World Wide Web.

Wikipedia: JSON (JavaScript Object Notation) is a lightweight text-based open standard designed for human-readable data interchange based on the JavaScript programming language.

Wikipedia: Atom Publishing Protocol (AtomPub or APP) is a simple HTTP-based protocol for creating and updating web resources.

This writing focuses on the XML endpoints of the REST API.

API Documentation

Twitter has with a very nice API documentation that documents each and all methods and endpoints of the three parts of the API. This documentation can be found here: http://apiwiki.twitter.com.

To avoid duplication, we are not going to discuss the Twitter API again in this writing; it is already documented and available for all users. Instead, we will take a brief look at the API and discuss how we can utilize it in our .NET application. For simplicity, we will focus on XML endpoints. Therefore, all of our code would rely on the XML features of .NET Framework 2.0 (i.e. System.Xml.dll library.) Oh, you are free to write the code the way you like (e.g. integrating LINQ for XML into the code.)

Now, we are going to look inside the Twitter API.

Things to be Kept into Mind

There are some basics of the Twitter API that a developer should keep into his mind.

  • Calls to the Twitter API are limited:
    Don’t expect to post unlimited updates or to follow thousands of users in just an hour! Many methods have a call limit (hourly/daily.) For example, at the time of this writing, you have only 150 API requests per hour, further requests would return an exception. In addition, you can send only 1,000 updates and 250 direct messages per day (can we remove this word ‘only€?) You can check Twitter limits here.
  • Every endpoint has its HTTP methods that need to be set (e.g. GET, POST, and DELETE) in the request. The documentation of each endpoint lists the HTTP methods required for that endpoint. For more information about HTTP and HTTP methods check the RFC 2616 Section 9 document.
  • Some methods (like methods send updates) require user authentication and others not. Authentication is discussed soon.

API Methods

The following is a discussion about Twitter API methods and how you can call them.

Method Address

Every method has an address (i.e. URI). The address of a method is something like that: http://api.twitter.com/1/statuses/public_timeline.format (for the statuses/public timeline method.) We replace the format field with the format we like to work with (e.g. xml, atom, json.)

Now, try to copy the method address http://api.twitter.com/1/statuses/public_timeline.xml and paste it into your browser. You should get something like that:


    
        Wed Apr 14 19:32:07 +0000 2010
        12179999634
        35 Ingenious Examples of Footwear - http://su.pr/1JxMAr
        <a href="http://apiwiki.twitter.com/" rel="nofollow">API</a>
        false
        
        
        false
        
        
            15736190
            Smashing Magazine
            smashingmag
            Freiburg, Germany
            Vitaly Friedman, editor-in-chief of SmashingMagazine.com ...
            http://a3.twimg.com/profile_images/572829723/...
            http://www.smashingmagazine.com
            false
            166019
            B2DFDA
            333333
            f51616
            ffffff
            eeeeee
            394
            Tue Aug 05 14:00:40 +0000 2008
            4
            -10800
            <time>Greenland</time>
            http://s.twimg.com/a/...
            true
            false
            true
            false
            false
            8703
            en
            false
        
        
        
        
        
    
    . . .

This method returns the 20 most recent statuses from arbitrarily-selected non-protected Twitter users; this list is called the Public Timeline. Because we have selected the XML format (or endpoint) we end up with XML data.

From the data returned we can extract the structure of statuses and users. Twitter API is good enough; it uses the same structure for all data returned. In other words, all methods (in the REST API, remember?) use the same structure (i.e. schema) to represent status and user objects. Those structures are covered soon.

Methods with Arguments

Most of methods accept arguments, some are optional and others are required. An example of a function requires a single argument is the users/show method that returns profile information about a specific user.

This function accepts one of three arguments:

  • id:
    The ID or screen name of the user.
  • user_id:
    The ID of the user.
  • screen_name:
    The screen name of the user.

We set arguments the same way we set web pages query strings. Considering my twitter account @elsheimy as an example, we could query user profile using one of four ways:

All of the four calls return the same results that should be like this:


    19645411
    Mohammad Elsheimy
    Elsheimy
    KB, Egypt
    Technology evangelist from Egypt born in 1991
    http://a1.twimg.com/profile_images/833061500/...
    http://justlikeamagic.com
    false
    278
    DBE9ED
    333333
    CC3366
    E6F6F9
    DBE9ED
    179
    Wed Jan 28 10:47:36 +0000 2009
    7
    7200
    <time>Cairo</time>
    http://s.twimg.com/a/1271811071/...if
    false
    
    true
    false
    
    1877
    en
    false
    
        Wed Apr 21 16:21:46 +0000 2010
        12585240751
        I'm reading this, it's really hot: #RFC 2616 - Hypertext Transfer Protocol ...
        <a href="http://bit.ly" rel="nofollow">bit.ly</a>
        false
        
        
        false
        
        
        
        
        
    

As you can see, it returns user information and the last update of that user. Notice that the structure of the data is the same in all methods.

Methods Require Authentication

Some methods require user authentication. Examples are functions update status, send direct messages, retrieve friends’ timeline, etc.

Let’s take an example. Bring up your browser and browse to the address of statuses/friends timeline method that returns user’s friends’ timeline, http://api.twitter.com/1/statuses/friends_timeline.xml. A small window like this should appear that asks you your Twitter username and password.

Figure 1. IE Authentication

Provide your Twitter username and password to pass to the results. If authentication was OK, you should receive the most recent 20 statuses of your friend timeline in the same structure (schema) as the Public Timeline method. On the other hand, if the authentication process failed, you should receive an error:


    /1/statuses/friends_timeline.xml
    Could not authenticate you.

Let’s consider another example, the direct_messages/new method. This function sends a new direct message to a specific user from the current user authenticated.

This function accepts two required arguments:

  • user:
    The ID or screen name of the recipient user. You can use one of two arguments in place of this argument:

    • screen_name:
      Screen name of the user.
    • user_id:
      The ID of the user.
  • text:
    Message body, not longer than 140 UTF-8-encoded characters.

The following two calls send the same message (‘hi, how is it going€) to the same user:

Notice that we need to encode arguments before we pass them to the server.

Because this function updates data, it requires HTTP POST method. Therefore, you won’t be able to try it from your browser unless your browser allows you to set HTTP methods in the request.

If the function succeeded, it should return the message object that has been sent, and it should be like this:


    88619848
    1401881
    all your bases are belong to us.
    7004322
    Wed Apr 08 20:30:04 +0000 2009
    dougw
    igudo
    
        1401881
        Doug Williams
        dougw
        San Francisco, CA
        Twitter API Support. Internet, greed, users, dougw ...
        http://s3.amazonaws.com/twitter_production/...
        http://www.igudo.com
        false
        1036
        9ae4e8
        000000
        0000ff
        e0ff92
        87bc44
        290
        Sun Mar 18 06:42:26 +0000 2007
        0
        -18000
        <time>Eastern Time (US &amp; Canada)</time>
        http://s3.amazonaws.com/...
        false
        3394
        false
        false
        true
    
    
        7004322
        Doug Williams
        igudo
        North Carolina
        A character.
        http://s3.amazonaws.com/...
        http://www.igudo.com
        false
        19
        69A1AA
        000000
        F00
        ACBEC1
        8A8F85
        3
        Thu Jun 21 21:16:21 +0000 2007
        0
        -18000
        <time>Eastern Time (US &amp; Canada)</time>
        http://static.twitter.com/...
        false
        382
        false
        true
        true
    

Notice that the sender and recipient are just user objects. It is worth mentioning that the direct_messages function return a list of direct_message objects (like the timeline functions that return list of status objects.)

Twitter and .NET

Now, you have a solid understanding of the Twitter API and how you can access it. Now, let’s utilize this API into our .NET applications.

Accessing the API

To access the API from your .NET application you need to create a HTTP request and send it to the server and wait for server response. Let’s consider an example. The following code snippet connects to the server and retrieves a System.Xml.XmlDocument that contains the returned data.

// C# Code

public static Main()
{
    GetResponse("http://api.twitter.com/1/statuses/public_timeline.xml");
}

public static XmlDocument GetResponse(string uri)
{
    WebRequest req = WebRequest.Create(new Uri(uri));

    XmlDocument doc = new XmlDocument();
    doc.Load(req.GetResponse().GetResponseStream());
    return doc;
}
' VB.NET Code

Sub Main()
    GetResponse("http://api.twitter.com/1/statuses/public_timeline.xml")
End Sub

Public Function GetResponse(ByVal uri As String) As XmlDocument

    Dim req As WebRequest = WebRequest.Create(New Uri(uri))

    Dim doc As New XmlDocument()
    doc.Load(req.GetResponse().GetResponseStream())

    Return doc
End Function

We have used the System.Xml.WebRequest class to create the request and to get the response (as an instance of System.Xml.WebResponse class) from the server. Once we get the XmlDocument, we can walk through the data and retrieve it.

Authentication

You can take one of two approaches to authenticate Twitter users:

  • OAuth Authentication:
    An authentication protocol that allows users to approve application to act on their behalf without sharing their password. As this function requires more overhead and requires your application to be registered in the Twitter clients’ directory, we would better use the second approach in our examples.
  • Basic Authentication:
    To provide authentication information in each request you make to the server. Unfortunately, Twitter announced that this method will not be available later.

Considering a method like statuses/update method that updates the status information of the user (i.e. sends a tweet) we would develop our previous code to be like this:

// C# Code

public static void Main()
{
    GetResponse("http://api.twitter.com/1/statuses/update.xml?status=hello%20from%20the%20API",
        "elsheimy", "b@zzword", true);
}

public static XmlDocument GetResponse(string uri, string username, string password, bool post)
{
    WebRequest req = WebRequest.Create(new Uri(uri));
    if (post)
        req.Method = "POST";
    if ((username != null) && (username.Trim() != String.Empty) && (!String.IsNullOrEmpty(password)))
        req.Credentials = new NetworkCredential(username.Trim(), password);

    XmlDocument doc = new XmlDocument();
    doc.Load(req.GetResponse().GetResponseStream());
    return doc;
}
' VB.NET Code

Sub Main()
    GetResponse("http://api.twitter.com/1/statuses/update.xml?status=hello%20from%20the%20API", "elsheimy", "b@zzword", True)
End Sub

Public Function GetResponse(ByVal uri As String, ByVal username As String, ByVal password As String, ByVal post As Boolean) As XmlDocument
    Dim req As WebRequest = WebRequest.Create(New Uri(uri))
    If (post) Then
        req.Method = "POST"
    End If
    If ((username  Nothing) And (username.Trim()  String.Empty) And (Not String.IsNullOrEmpty(password))) Then
        req.Credentials = New NetworkCredential(username.Trim(), password)
    End If

    Dim doc As XmlDocument = New XmlDocument()
    doc.Load(req.GetResponse().GetResponseStream())

    Return doc
End Function

Notice how we set the HTTP method based on the function requirements. It is worth mentioning that a status should not exceed 140 UTF-8-encoded characters.

Encoding URIs

Have you noticed the previous code? It tries to post the update ‘hello from the API€. Because the text is included with the URI, special handling to the text should be carried. This special handling for text included in URIs is usually called Percent-encoding or URL Encoding. This encoding replaces unsafe characters with their hexadecimal values preceded by percentage (%) signs. Unsafe characters are those somewhat conflicted with URI special characters. For example, if we would encode the text ‘hello from the API€ we would end up with ‘hello%20from%20the%20API€.

There are many unsafe characters that should be percent-encoded including $, +, &, :, and =. A nice discussion of URL Encoding can be found in the article URL Encoding by Brian Wilson.

Once we get the idea, we can create our percent-encoding class that encodes/decodes strings:

// C# Code

private static string[,] _chars = new string[,]
        {
        { "%", "%25" },     // this is the first one
        { "$" , "%24" },
        { "&", "%26" },
        { "+", "%2B" },
        { ",", "%2C" },
        { "/", "%2F" },
        { ":", "%3A" },
        { ";", "%3B" },
        { "=", "%3D" },
        { "?", "%3F" },
        { "@", "%40" },
        { " ", "%20" },
        { """ , "%22" },
        { "", "%3E" },
        { "#", "%23" },
        { "{", "%7B" },
        { "}", "%7D" },
        { "|", "%7C" },
        { "\", "%5C" },
        { "^", "%5E" },
        { "~", "%7E" },
        { "[", "%5B" },
        { "]", "%5D" },
        { "`", "%60" } };

public static string EncodeUrl(string url)
{
    for (int i = 0; i < _chars.GetUpperBound(0); i++)
        url = url.Replace(_chars[i, 0], _chars[i, 1]);

    return url;
}

public static string DecodeUrl(string url)
{
    for (int i = 0; i < _chars.GetUpperBound(0); i++)
        url = url.Replace(_chars[i, 1], _chars[i, 0]);

    return url;
}

' VB.NET Code
Public Module UrlEncoder

Private _chars(,) As String = _
{ _
{"%", "%25"}, _
{"$", "%24"}, _
{"&", "%26"}, _
{"+", "%2B"}, _
{",", "%2C"}, _
{"/", "%2F"}, _
{":", "%3A"}, _
{";", "%3B"}, _
{"=", "%3D"}, _
{"?", "%3F"}, _
{"@", "%40"}, _
{" ", "%20"}, _
{"", "%22"}, _
{"”, “%3E”}, _
{“#”, “%23”}, _
{“{“, “%7B”}, _
{“}”, “%7D”}, _
{“|”, “%7C”}, _
{“”, “%5C”}, _
{“^”, “%5E”}, _
{“~”, “%7E”}, _
{“[“, “%5B”}, _
{“]”, “%5D”}, _
{“‘”, “%60”}}

Public Function EncodeUrl(ByVal url As String) As String
For i As Integer = 0 To _chars.GetUpperBound(0) – 1
url = url.Replace(_chars(i, 0), _chars(i, 1))
Next

Return url
End Function

Public Function DecodeUrl(ByVal url As String) As String
For i As Integer = 0 To _chars.GetUpperBound(0) – 1
url = url.Replace(_chars(i, 1), _chars(i, 0))
Next

Return url
End Function

End Module

For clearness, we have included the encoded string of each character along with the character itself. You don’t have to do this. You can convert the character to a number and just output the number in hex format.

Now we could change the code that updates the status to the following:

// C# Code

public static void Main()
{
    string uri;
    string text = UrlEncoder.EncodeUrl("hello from the API");
    uri = "http://api.twitter.com/1/statuses/update.xml?status=" + text;
    GetResponse(uri, "elsheimy", "b@zzwrd", true);
}
' VB.NET Code

Public Sub Main()

    Dim uri As String
    Dim text As String = UrlEncoder.EncodeUrl("hello from the API")
    uri = "http://api.twitter.com/1/statuses/update.xml?status=" & text
    GetResponse(uri, "elsheimy", "b@zzwrd", True)
End Sub

.NET includes a nice function that escapes (encodes) a URI, System.Uri.EscapeUriString() function. However, this function does not encode all unsafe characters.

Business Objects

Once you know the structure of the XML data returned, you can create your business objects that would encapsulate this data. The following are the three classes that would represent our three core objects, the user, the status, and the message.

// C# Code

public structure TwitterUser
{
    public long ID;
    public string Name;
    public string ScreenName;
    public string Location;
    public string Description;
    public string ProfileImage;
    public string Url;
    public bool IsProtected;
    public long FollowersCount;
    public long FriendsCount;
    public string CreatedAt;
    public long FavoritesCount;
    public bool Verified;
    public bool Following;
    public long StatusCount;
}

public structure TwitterStatus
{
    public string CreatedAt;
    public long ID;
    public string Text;
    public string Source;
    public bool Truncated;
    public long InReplyToStatusID;
    public long InReplyToUserID;
    public bool Favorited;
    public string InReplyToScreenName;
    public TwitterUser User;
}

public structure TwitterMessage
{
    public long ID;
    public long SenderID;
    public long SenderScreenName;
    public long RecipientID;
    public long RecipientScreenName;
    public string Text;
    public string CreatedAt;
    public TwitterUser Sender;
    public TwitterUser Recipient;
}
' VB.NET Code

Public Structure TwitterUser
    Public ID As Long
    Public Name As String
    Public ScreenName As String
    Public Location As String
    Public Description As String
    Public ProfileImage As String
    Public Url As String
    Public IsProtected As Boolean
    Public FollowersCount As Long
    Public FriendsCount As Long
    Public CreatedAt As String
    Public FavoritesCount As Long
    Public Verified As Boolean
    Public Following As Boolean
    Public StatusCount As Long
End Structure

Public Structure TwitterStatus
    Public CreatedAt As String
    Public ID As Long
    Public Text As String
    Public Source As String
    Public Truncated As Boolean
    Public InReplyToStatusID As Long
    Public InReplyToUserID As Long
    Public Favorited As Boolean
    Public InReplyToScreenName As String
    Public User As TwitterUser
End Structure

Public Structure TwitterMessage

    Public ID As Long
    Public SenderID As Long
    Public SenderScreenName As Long
    Public RecipientID As Long
    Public RecipientScreenName As Long
    Public Text As String
    Public CreatedAt As String
    Public Sender As TwitterUser
    Public Recipient As TwitterUser
End Structure

Retrieving Data

Now you can walk through the XML data and get that data inside your objects. The following code returns a list of statuses from your friends’ timeline.

// C# Code

public static void Main()
{
    GetStatuses("elsheimy", "b@zzword");
}
public static List GetStatuses(string username, string password)
{
    XmlNode node = GetResponse("http://api.twitter.com/1/statuses/friends_timeline.xml",
        username, password, true);

    List lst = new List(node.ChildNodes.Count);

    foreach (XmlNode nd in node.ChildNodes)   // for each status
        lst.Add(HandleStatus(nd));

    return lst;
}

public static TwitterStatus HandleStatus(XmlNode nd)
{
    // HandleNumber, FormatText, HandleBool
    // are just functions that converts strings
    // to numbers, decoded strings, and bool
    TwitterStatus status = new TwitterStatus(
        nd["created_at"].InnerText,
        HandleNumber(nd["id"]),
        FormatText(nd["text"]),
        FormatText(nd["source"]),
        HandleBool(nd["truncated"]),
        HandleNumber(nd["in_reply_to_status_id"]),
        HandleNumber(nd["in_reply_to_user_id"]),
        HandleBool(nd["favorited"]),
        FormatText(nd["in_reply_to_screen_name"]),
        HandleUser(nd["user"]));
    return status;
}

public static TwitterUser HandleUser(XmlNode nd)
{
    // HandleNumber, FormatText, HandleBool
    // are just functions that converts strings
    // to numbers, decoded strings, and bool
    long id = HandleNumber(nd["id"]);
    TwitterUser user;

    user = new TwitterUser(
        id,
        FormatText(nd["name"]),
        FormatText(nd["screen_name"]),
        FormatText(nd["location"]),
        FormatText(nd["description"]),
        nd["profile_image_url"].InnerText,
        nd["url"].InnerText,
        HandleBool(nd["protected"]),
        HandleNumber(nd["followers_count"]),
        HandleNumber(nd["friends_count"]),
        nd["created_at"].InnerText,
        HandleNumber(nd["favourites_count"]),
        HandleBool(nd["verified"]),
        HandleBool(nd["following"]),
        HandleNumber(nd["statuses_count"]));

    return user;
}
' VB.NET Code

Sub Main()
    GetStatuses("elsheimy", "b@zzword")
End Sub

Public Function GetStatuses(ByVal username As String, ByVal password As String) As List(Of TwitterStatus)
    Dim node As XmlNode = GetResponse("http://api.twitter.com/1/statuses/friends_timeline.xml", username, password, True)

    Dim lst As New List(Of TwitterStatus)(node.ChildNodes.Count)

    For Each nd As XmlNode In node.ChildNodes
        lst.Add(HandleStatus(nd))
    Next

    Return lst
End Function

Public Function HandleStatus(ByVal nd As XmlNode) As TwitterStatus
    ' HandleNumber, FormatText, HandleBool
    ' are just functions that converts strings
    ' to numbers, decoded strings, and bool

    Dim status As New TwitterStatus( _
            nd("created_at").InnerText, _
            HandleNumber(nd("id")), _
            FormatText(nd("text")), _
            FormatText(nd("source")), _
            HandleBool(nd("truncated")), _
            HandleNumber(nd("in_reply_to_status_id")), _
            HandleNumber(nd("in_reply_to_user_id")), _
            HandleBool(nd("favorited")), _
            FormatText(nd("in_reply_to_screen_name")), _
            HandleUser(nd("user")))

    Return status
End Function

Public Function HandleUser(ByVal nd As XmlNode) As TwitterUser
    ' HandleNumber, FormatText, HandleBool
    ' are just functions that converts strings
    ' to numbers, decoded strings, and bool
    Dim id As Long = HandleNumber(nd("id"))

    Dim user As New TwitterUser( _
        id, _
        FormatText(nd("name")), _
        FormatText(nd("screen_name")), _
        FormatText(nd("location")), _
        FormatText(nd("description")), _
        nd("profile_image_url").InnerText, _
        nd("url").InnerText, _
        HandleBool(nd("protected")), _
        HandleNumber(nd("followers_count")), _
        HandleNumber(nd("friends_count")), _
        nd("created_at").InnerText, _
        HandleNumber(nd("favourites_count")), _
        HandleBool(nd("verified")), _
        HandleBool(nd("following")), _
        HandleNumber(nd("statuses_count")))
    Return user
End Function

twittoo; Sample Application

twitto, is our WinForms sample application that utilizes the Twitter API. This is just a very simple application with basic functionalities.

Snapshots

The following are snapshots of the application:

Figure 2 - twitto 0
Figure 3 - twitto 1
Figure 4 - twitto 2
Figure 5 - twitto 3
Figure 6 - twitto 4
Figure 7 - twitto 5

Description

This application was created using C# and WinForms 2.0 technology; it allows the user to navigate through his friends’ timeline, mentions, direct messages, retweets, and friends, and to update his status, reply to tweets, retweets, and to direct messages. Data is not refreshed automatically, the user have to click the ‘refresh€ button. (You can create your own routine that creates a timer that updates the data automatically.)

Interface

As you see, the application uses just the Windows Common Controls all around the application; no 3rd party controls were used.

To represent a status, message, or a user, the application overuses the System.Windows.Forms.TableLayoutPanel control to represent each status, message, or user. It consists of four columns and two rows. The following figure shows how the control is laid-out.

Figure 8 - TableLayoutPanel Status Template

URL Shortening

twittoo, has a very nice feature, it allows the user to insert a shortened URL into his tweets. For this to work, the application makes use of http://is.gd URL shortening service. The following is the function that utilizes the http://is.gd API:

// C# Code

public static string Shorten(string url)
{
    if (!System.Text.RegularExpressions.Regex.IsMatch
        (url, @"(http|ftp|https)://[w-_]+(.[w-_]+)+([w-.,@?^=%&:/~+#]*[w-@?^=%&/~+#])?"))
        throw new FormatException("The URL you specificed is not in the current format.");

    url = Uri.EscapeUriString(url);
    string reqUri = String.Format(@"http://is.gd/api.php?longurl={0}", url);

    WebRequest req = WebRequest.Create(reqUri);
    req.Timeout = 5000;
    using (System.IO.StreamReader reader =
        new System.IO.StreamReader(req.GetResponse().GetResponseStream()))
    {
        return reader.ReadLine();
    }
}
' VB.NET

Public Function Shorten(ByVal url As String) As String
    If (Not System.Text.RegularExpressions.Regex.IsMatch _
        (url, "(http|ftp|https)://[w-_]+(.[w-_]+)+([w-.,@?^=%&;:/~+#]*[w-@?^=%&;/~+#])?")) Then
        Throw New FormatException("The URL you specificed is not in the current format.")
    End If

    url = Uri.EscapeUriString(url)
    Dim reqUri As String = String.Format("http://is.gd/api.php?longurl={0}", url)

    Dim req As WebRequest = WebRequest.Create(reqUri)
    req.Timeout = 5000

    Dim reader As New System.IO.StreamReader(req.GetResponse().GetResponseStream())
    url = reader.ReadLine()
    reader.Close()

    Return url
End Function

Download

Download twittoo; our sample application

Bad Practices: Locking on Non-shared Objects in Multi-threaded Applications

Actually, I was having a problem synchronizing threads calling a function. If we could regenerate the bug, we would end up with code like this:

static void Main()
{
    Thread[] arr = new Thread[10];

    for (int i = 0; i < 10; i++)
    {
        arr[i] = new Thread(ThreadProc);
        arr[i].IsBackground = true;
        arr[i].Start(new object());
    }

    foreach (Thread t in arr)
        t.Join();
}

private static void ThreadProc(object obj)
{
    lock (obj)
    {
        int i = 0;
        while (i < 10)
        {
            Thread.Sleep(1);
            Console.WriteLine("Thread #{0},t{1}",
                Thread.CurrentThread.ManagedThreadId, i++);
        }
    }
}

And when we execute this code the results would be like this:

Thread #4,      2
Thread #3,      3
Thread #5,      1
Thread #7,      0
Thread #5,      2
Thread #6,      1
Thread #3,      4
Thread #4,      3
Thread #8,      1
Thread #9,      0

What is the problem with this code? It starts multiple threads and passes them a locking object and the object is locked successfully using the lock statement, so why threads overlap? Why the synchronization doesn’t take effect?

Well, after a long period and after pushing a new thread in the MSDN forums (we are all developers do make silly mistakes, aih? :P), I come up with the solution and discovered the drawback of the code.

The problem was that each time we start off a thread we pass it a new locking object different from the others:

        arr[i].Start(new object());

Therefore, every thread is locking on its own objects, so no thread synchronization take effect.

The solution is very easy, you should lock on a shared object; an object that is shared between all your threads accessing this block of code. Read more about thread synchronization here.

For example, we should change our code to the following:

private static object _lockThis = new object();
static void Main()
{
    Thread[] arr = new Thread[10];

    for (int i = 0; i < 10; i++)
    {
        arr[i] = new Thread(ThreadProc);
        arr[i].IsBackground = true;
        arr[i].Start();
    }

    foreach (Thread t in arr)
        t.Join();
}

private static void ThreadProc(object obj)
{
    lock (_lockThis)
    {
        int i = 0;
        while (i < 10)
        {
            Thread.Sleep(1);
            Console.WriteLine("Thread #{0},t{1}",
                Thread.CurrentThread.ManagedThreadId, i++);
        }
    }
}

Finally, this was one of the bad practices and mistakes me (and many others too) fall in. Honestly, I would like to start my Bad Practices series :”>. I would write about problems I came across and solutions I found for them. In addition, I’m thinking of starting the Questions series. I got a huge number of questions every week, if we could publish them I think it would be great for all.

Have a nice day!

Programmatically Enumerating, Attaching, and Detaching SQL Server Databases

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Contents

Contents of this article:

  • Contents
  • Overview
  • Introduction
  • Enumerating Databases on a Server
    • INFORMATION_SCHEMA.SCHEMATA System View
    • sys.sysdatabases System Table/View
    • sys.databases System View
    • sys.sp_databases Stored Procedure
  • Attaching Databases to the Server
    • CREATE DATABASE Statement
    • sys.sp_attach_db Stored Procedure
    • sys.sp_attach_single_file_db Stored Procedure
  • Detaching Databases from the Server
    • CREATE DATABASE Statement
    • sys.sp_detach_db Stored Procedure
  • Security Considerations

Overview

This writing is like an encyclopedia for the SQL statements and stored procedures used to enumerate, attach, and detach databases on a SQL Server instance. It lists the statements and stored procedures that you can use and discusses them in simple examples.

Introduction

Today we are going to talk about how to programmatically enumerate databases on a SQL Server instance and how you can attach and detach SQL Server databases.

Enumerating Databases on a Server

You can get a list of databases on a server using one of many ways:

  • INFORMATION_SCHEMA.SCHEMATA system view (SQL Server 2000 only)
  • sys.sysdatabases system table (a view in SQL Server 2005 and higher versions)
  • sys.databases system view (SQL Server 2005 and higher versions)
  • sys.sp_databases stored procedure

INFORMATION_SCHEMA.SCHEMATA System View

If you are using SQL Server 2000, you can query the system view INFORMATION_SCHEMA.SCHEMATA to get information about current databases on the server.

The following is the table diagram for INFORMATION_SCHEMA.SCHEMATA system view:

Actually, you don’t need to worry about any of the view columns, just the first column, CATALOG_NAME, that you need to worry about, it is the database (i.e. catalog) name.

The following code simply prints out the databases currently found on the default SQL Server instance in the current machine:

    // C# Code

    SqlConnection conn = new SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI");
    SqlCommand cmd = new SqlCommand("", conn);
    SqlDataReader rdr;

    cmd.CommandText = "SELECT DISTINCT    CATALOG_NAME    FROM    INFORMATION_SCHEMA.SCHEMATA";

    conn.Open();

    rdr = cmd.ExecuteReader();
    while (rdr.Read())
    {
        Console.WriteLine(rdr.GetString(0));
    }

    rdr.Dispose();
    cmd.Dispose();
    conn.Dispose();
' VB.NET

Dim conn As New SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI")
Dim cmd As New SqlCommand("", conn)
Dim rdr As SqlDataReader

cmd.CommandText = "SELECT DISTINCT    CATALOG_NAME    FROM    INFORMATION_SCHEMA.SCHEMATA"

conn.Open()

rdr = cmd.ExecuteReader()
While (rdr.Read())
    Console.WriteLine(rdr.GetString(0))
End While

rdr.Dispose()
cmd.Dispose()
conn.Dispose()

Again, this is for SQL Server 2000 only.

Check the MSDN documentation for INFORMATION_SCHEMA.SCHEMATA system view here.

sys.sysdatabases System Table/View

This is a system table specific to SQL Server 2000. In SQL Server 2005 it is provided for backward compatibility as a system view. Therefore, do not rely on this system view (or table) because it is expected to be removed in a future version of SQL Server.

The definition of this table/view is as following:

Only the first column, name, is the most important to us, it contains the database name. Other columns of importance (not for the subject of this topic) are:

  • dbid:
    Database ID.
  • sid:
    Security ID for the database.
  • crdate:
    Creation date of the database.
  • filename:
    Database filename.

You can change the line that sets the command text in the previous code to this line:

    // C# Code

    cmd.CommandText = "SELECT [name]    FROM    sys.sysdatabases";
    ' VB.NET Code

    cmd.CommandText = "SELECT [name]    FROM    sys.sysdatabases";

Again, using the sys.sysdatabases system table/view is not recommended because it would be removed in a future version of SQL Server.

Check the MSDN documentation for sys.sysdatabases system view/table here.

Check this MSDN article out: Mapping System Tables to System Views for more information about SQL Server 2000 tables mapped to SQL Server 2005 views or stored procedure.

sys.databases System View

This is the new version included in SQL Server 2005 (and higher versions) replaces the SQL Server 2000 sys.sysdatabases table.

This is a very lengthy system view, it includes tenths of columns, we are interested only on the first column, name, that contains the database name.

You can change the line that sets the command text in the first code to this line:

    // C# Code

cmd.CommandText = "SELECT [name]    FROM    sys. databases";
    ' VB.NET Code

cmd.CommandText = "SELECT [name]    FROM    sys. databases"

Check the MSDN documentation for sys.sysdatabases system view/table here.

sys.sp_databases Stored Procedure

This way is different from all others because it is not a system view or a system table, it is a system stored procedure.

This stored procedure accepts no parameters and returns a result set of three columns:

  • DATABASE_NAME:
    The name of the database.
  • DATABASE_SIZE:
    The size of the database (in kilobytes.)
  • REMARKS:
    Always NULL. For the Database Engine.

The following code demonstrates this stored procedure:

// C# Code

cmd.CommandText = "exec sys.sp_databases";
' VB.NET Code

cmd.CommandText = "exec sys.sp_databases"

Check the MSDN documentation for sys.sysdatabases system view/table here.

Attaching Databases to the Server

You can programmatically attach a database to the server in two ways:

  1. Using the CREATE DATABASE statement
  2. Using the sys.sp_attach_db system stored procedure
  3. Using the sys.sp_attach_single_file_db system stored procedure

CREATE DATABASE Statement

The CREATE DATABASE statement can be used to create databases into the server or to attach existing database files.

If you are going to attach a database, this statement should be formed as the following:

CREATE DATABASE database_name
    ON  [ ,...n ]
    FOR ATTACH [;]

The database_name is the name that you wish to give to the database. In addition, this statement takes filegroups of the database files.

Keep in mind that the database name should not be exist in the server or the function would fail.

The following example shows how you can attach the database database.mdf to the server and give it the name MyDatabase:

    // C# Code

    SqlConnection conn = new SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI");
    SqlCommand cmd = new SqlCommand("", conn);

    cmd.CommandText =
        "CREATE DATABASE 'MyDatabase' ON " +
        "PRIMARY ( FILENAME =  'database.mdf' ) " +
        "FOR ATTACH";

    conn.Open();

    cmd.ExecuteNonQuery();

    cmd.Dispose();
    conn.Dispose();
' VB.NET Code

Dim conn As New SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI")
Dim cmd As New SqlCommand("", conn)

cmd.CommandText = "CREATE DATABASE 'MyDatabase' ON " & _
    "PRIMARY ( FILENAME =  'database.mdf' ) " & _
    "FOR ATTACH"

conn.Open()

cmd.ExecuteNonQuery()

cmd.Dispose()
conn.Dispose()

If no log file (.LDF) can be found, SQL Server creates one for you.

The following code attaches the same database along with its log file. Just change the third line of the previous example that sets the command text with this line:

    // C# Code

    cmd.CommandText = "CREATE DATABASE 'MyDatabase' ON " +
        "PRIMARY ( FILENAME =  'database.mdf' ), " +
        "FILEGROUP MyDatabase_Log ( FILENAME = 'database.ldf')" +
        "FOR ATTACH";
' VB.NET Code

cmd.CommandText = "CREATE DATABASE 'MyDatabase' ON " & _
    "PRIMARY ( FILENAME =  'database.mdf' ), " & _
    "FILEGROUP MyDatabase_Log ( FILENAME = 'database.ldf')" & _
    "FOR ATTACH"

Check the MSDN documentation for the CREATE DATABASE statement here.

More about database files and filegroups can be found in the MSDN documentation here.

sys.sp_attach_db Stored Procedure

Another way that allows to attach a database to the server is the sys.sp_attach_db stored procedure. The definition of this stored procedure is as following:

sp_attach_db [ @dbname= ] 'dbname'
    , [ @filename1= ] 'filename_n' [ ,...16 ]

This function takes the database name as the first argument. In addition it accepts another 16 arguments (only the first is required) represent database files. The following code attaches the same database to the server. Again, just change the third line of the previous code to the following line:

    // C# Code

    cmd.CommandText = "exec sys.sp_attach_db    MyDatabase,    'database.mdf'";
' VB.NET Code

cmd.CommandText = "exec sys.sp_attach_db    MyDatabase,    'database.mdf'"

Check the MSDN documentation for the sys.sp_attach_db statement here.

sys.sp_attach_single_file_db Stored Procedure

This statement is the same as sys.sp_attach_db stored procedure. However, this statement accepts only one file, the database file.

Check the following code out:

    // C# Code

    cmd.CommandText = "exec sys.sp_attach_single_file_db    MyDatabase,    'database.mdf'";
' VB.NET Code

cmd.CommandText = "exec sys.sp_attach_single_file_db    MyDatabase,    'database.mdf'"

Check the MSDN documentation for the sys.sp_attach_single_file_db statement here.

Detaching Databases from the Server

Unlike attaching databases, you can detach a database from a server in only two ways:

  1. DROP DATABASE statement
  2. sys.sp_detach_db system stored procedure

DROP DATABASE Statement

This statement is used to remove one or more databases from SQL Server. It has the following syntax:

DROP DATABASE database_name [ ,...n ] [;]

The following code simply executes this statement against our database MyDatabase:

    // C# Code

    SqlConnection conn = new SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI");
    SqlCommand cmd = new SqlCommand("", conn);

    cmd.CommandText = "DROP DATABASE MyDatabase";

    conn.Open();

    cmd.ExecuteNonQuery();

    cmd.Dispose();
    conn.Dispose();
' VB.NET Code

Dim conn As New SqlConnection("Server=(local);Data Source=;Integrated Security=SSPI")
Dim cmd As New SqlCommand("", conn)

cmd.CommandText = "DROP DATABASE MyDatabase"

conn.Open()

cmd.ExecuteNonQuery()

cmd.Dispose()
conn.Dispose()

Check the MSDN documentation for the sys.sp_attach_single_file_db statement here.

sys.sp_detach_db Stored Procedure

Huh, the last one. This stored procedure is used to detach a database from the server. It has the following syntax:

sp_detach_db [ @dbname= ] 'database_name'

It accepts a single argument, the database name. The following code removes our database, MyDatabase, from the server:

    // C# Code

    cmd.CommandText = "sys.sp_detach_db MyDatabase";
' VB.NET Code

cmd.CommandText = "sys.sp_detach_db MyDatabase"

Check the MSDN documentation for the sys.sp_attach_single_file_db statement here.

Security Considerations

Every statement and stored procedure we discussed in this writing requires specific permissions. Check the MSDN documentation to get more information.

Creating a Simple Sheet Designer in C#

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Code: Geming.Sisc.msi

Contents

Contents of this article:

  • Contents
  • Introduction
  • Problem
  • Requirements
  • Solution
  • Snapshots
  • Component Design
  • Class Diagrams
  • Database Diagram
  • Characteristics
  • Background
  • Code Highlights
  • Download

Introduction

Today, we are going to create a simple application, actually a simple sheet designing tool. This tool gives the user the flexibility to design his sheets, reports, bills, invoices, and receipts (whatever.)

In this writing, we will refer to reports, bills, invoices, receipts, etc. with just the name sheets. For this, we will need to give a sheet a definition.

A sheet is just like an empty page (i.e. template) that contains sheet items. For example, a resume template is a sheet that has many items (name, birth date, address, etc.) A cash receipt is a sheet that has a few items (date, amount, charged to, received by, etc.)

We will go through this tool in a nice way. We will begin by a very simple system analysis and application design. After that, we will get into coding.

Actually, I€™m not an architect, and I think I will never be. So please, DO NOT blame me for this bad analysis. It is just an illustration to get you have a solid understanding of what our application is designed for.

Problem

The user is tied into the sheet designs that the developer has created for him. The user should have the ability to design his own sheets and/or to edit application defined sheets.

Requirements

User Requirements

The following is list of common user requirements:

  • The application should allow the user to create, edit, and delete sheets, and to group those sheets into categories he creates.
  • For the sake of simplicity, those categories would be only one level, no nested categories are allowed.
  • The application should be persistent. The data should be stored in a database and loaded when the user needs it.
  • The sheet should be loaded with right data and printed whenever the user asks.
  • Like Visual Studio, the user should have a Toolbox that has all types of sheet items. The user could insert a sheet item into his sheet by drawing it into the screen, dragging it from the toolbox, or just double-clicking its icon in the toolbox.
  • The user should have a ruler to allow him measuring sheet item dimensions. In addition, he should have a design-time-only grid.
  • The user should be allowed to set attributes of the sheet like the page setup, grid settings, background color, etc.
  • Not all sheet attributes are printed out; some are for design-mode only (like background color, and grid.)
  • Each type of sheet items should have its own attributes and the user should be allowed to change them too.
  • The user should be allowed to drag sheet items around the screen and position them.
  • The user should be allowed to cut, copy, paste, and delete sheet items.
  • Of course, the user should be allowed to print out the sheet, and to preview it too.
  • The application should be generic to be used in any system and in any application.
  • Other pieces of the system should have the possibility to interact with the sheet.

Functional Requirements

Common functional (developer) requirements are:

  • Abstraction. Component/Class abstraction should be considered into the design. The system should consist of several components each of which groups a related features together (e.g. interface objects, business objects, and data management.)
  • Extensibility. The system should be extensible. A good class hierarchy should be created, and derivation (inheritance) should be taken into consideration. In addition, every sheet item should be represented by a class in this hierarchy.
  • Technically, sheet items would be called shapes. Because they are just drawings into the sketch (or sheet.)
  • To allow the user to work with sheet items (shapes) and the application to populate the sheet with data, sheet items should not be drawn directly into the sheet page. Instead every item should represented by a class that has its own drawing routine.
  • The sheet should be a container for the shapes. All shapes are children of the sheet.
  • The sheet and rulers should be Windows controls to allow them to be hosted by a Windows Form.
  • Sheet items should be Windows controls too to allow them to be hosted by the sheet.
  • The sheet items should be owner-drawn controls and they should not be derived from existing Windows controls to help integrating them easily into the sheet.
  • For the sake of simplicity, the sheet and shapes should be serialized into XML and stored in the database.
  • The database should be a SQL Server database to allow faster communication and easier manipulation of XML data.
  • The database should have three tables for the three core system components, the category, the sheet, and the shape.
  • Every sheet item in a given sheet should have a unique name (or a tag) to allow other system components to interact with it (e.g. populate it with the right data.)
  • The developer should have the ability to control the quality of the painting process for each control or item independently.

Solution

After going through project requirements and considering a sophisticated system design, we got a nice plan for our project that would be called Geming SISC (Sheet Infrastructure Components.)

This project would be created using C# and .NET 2.0 (or future versions of course.)

Snapshots

The following are final snapshots of the application. Figure 1 shows a cash receipt sheet designed by labels, boxes, and lines. Figure 2 shows a simple resume template designed by only a few labels and a picture. The left pane of the application lists current sheets grouped by their categories.

Figure 01 - Geming SISC (Cash Receipt)
Figure 01 - Geming SISC (Cash Receipt)
Figure 2 - Geming SISC (Resume Template)
Figure 2 - Geming SISC (Resume Template)

As you know other system components could fill those fields up with correct information.

Component Design

Figure 3 shows the three components of our system.

Figure 3 - Geming SISC Component Design
Figure 3 - Geming SISC Component Design

The three core components of our system, Geming SISC are:

  • Geming.Sisc.Infrastructure:
    Contains the sheet control and shapes controls (label, text box, etc.) The sheet, rulers, and sheet items controls are all derived from System.Windows.Forms.Control.
  • Geming.Sisc.Data:
    Business objects that would be sent to the data among database manager objects. This component references the Geming.Sisc.Infrastructure component.
  • Geming.Sisc.Driver:
    The application interface that would be used to design sheets. It references the other two components.

Class Diagrams

Extensibility is one of the main goals of the system. A flexible class hierarchy should be considered as well as derivation (i.e. inheritance) of controls for other external objects.

The following is the class hierarchy for the sheet and the ruler controls.

Figure 4 - Sheet and Rulers Class Hierarchy
Figure 4 - Sheet and Rulers Class Hierarchy

As we can see, the base class is the System.Windows.Forms.Control class. This allows the sheet and rulers controls to be inserted into a Windows form or a Windows control.

The following figure, figure 5, shows the class hierarchy of sheet items (technically called shapes.)

Figure 5 - Sheet Items (Shapes) Class Hierarchy
Figure 5 - Sheet Items (Shapes) Class Hierarchy

As we can see, the base class for all shapes is the abstract Geming.Sisc.Infrastructure.ShapeBase class which inherits from System.Windows.Forms.Control class. All other shapes are derived from ShapeBase.

Two edit shapes were created, TextBoxShape that has a look like a Windows Text Box, and LabelShape that has a look like a normal Windows Label. Both are derived from the abstract EditableShapeBase.

Other shapes are BoxShape, LineShape, ImageShape, and CheckBoxShape.

All classes are serializable (implement System.Runtime.Serialization.ISerializable interface,) so they can easily converted into XML and pushed to the database.

For simplicity, we have developed just the box, line, image, check box, and two edit shapes (or sheet items.) You can go further and create any other shapes you like. In addition, you can create more specific items like CurrencyField, DateField, etc.

For more detailed class diagrams like those shows class members, check the application code.

Database Diagram

In its simplicity, database is defined as the following diagram illustrates:

Figure 6 - Geming SISC Database Diagram
Figure 6 - Geming SISC Database Diagram

Notice that, all data operations are carried out through stored procedures in the database.

The Shape.Value column is of the type xml to allow easier manipulation of XML data in the future.

Characteristics

Here are some of the characteristics (i.e. attributes) of the sheet (some are represented by properties):

  • Background color:
    The user should be able change the background color. Notice that the background color is not printed.
  • Non-printable grid:
    To help the user position sheet items. The user should be able to display them or not. In addition, the user can change the grid color.
  • Margin:
    The application should set the page margin based on print settings.
  • Title:
    Every sheet has a title, a description, and a category.

In addition, next is a list of some the characteristics of a shape (sheet item):

  • Selected:
    Is the shape currently selected or not. The user could select a shape by the mouse. A selection frame is drawn around the shape when selected.
  • Non-printable size grip:
    Each shape should have a size grip to allow the user to resize the shape.
  • Cloning:
    The shape should be able to be cloned; that is copied, to allow the copy and paste feature.

Background

Here are some refreshers of techniques we use in this system:

  • Painting

We will rely on custom painting in most situations. Therefore, well request help from System.Drawing classes specially the System.Drawing.Graphics class.

In some shapes like those mimic existing Windows controls (like the text box and the check box,) we will get help from classes in System.Windows.Forms.VisualStyles namespace to reflect the visual styles of Windows in our controls. In addition, System.Windows.Forms.ControlPaint class is used for drawing borders and selection rectangles.

  • Painting Quality

Every control in our project (sheet, rulers, and shapes) has a paining quality property that determines the quality of the drawing (low, medium, and high.) For this to work we will make use of some properties of the System.Drawing.Graphics object like those related to smoothing and anti-aliasing feature.

  • Serialization

All serialized objects should be marked with System.SerializableAttribute attribute. With help from System.Runtime.Serialization namespace we could customize the serialization process.

The core interface that would allow us to customize the serialization process is the System.Runtime.Serialization.ISerializable interface (implemented in all serializable classes.) Notice that, we should add the deserialization constructor to get correct deserialization.

  • Database

The database is a SQL Server client database with all operations included as stored procedures.

The system uses two-tier architecture; means that it accesses the database directly using just three objects, a connection, a command, and a data reader.

  • Design-Mode Support

For extending design-mode support we have created custom designer service for the Sheet class that inherits the System.Windows.Forms.Design.ParentControlDesigner class to allow nested controls in the Sheet object in the design-mode.

Code Highlights

In this section we will talk about significant blocks of code that would be of interest.

  • Painting Routines

The following is the code for OnPaint() routine overridden by only the ShapeBase class.

Listing 1 – ShapeBase.OnPaint() Method Code Listing
protected override void OnPaint(PaintEventArgs e)
{
    GraphicsManager.SetQuality(e.Graphics, PaintingQuality);

    ControlPaint.DrawBorder(e.Graphics, this.ClientRectangle, this.ForeColor, ButtonBorderStyle.Dotted);

    PaintShape(e.Graphics);

    if (Selected)
        DrawSelectionFrame(e.Graphics);

    ControlPaint.DrawSizeGrip(e.Graphics, this.BackColor, GetResizeGripRect());
}

This function first calls the GraphicsManager.SetQuality() function that sets the quality attributes of the Graphics object. We will get back to this function in about a moment.

After that the function paints the control border using the System.Windows.Forms.ControlPaint class.

Next comes the interesting point. The function calls the virtual function PaintShape() that a derived class overrides to provide its own drawing routines.

For example, the TextBoxShape class has this PaintShape() override:

Listing 2 – TextBoxShape.PaintShape() Method Code Listing
public override void PaintShape(Graphics dc)
{
    base.PaintShape(dc);

    using (Brush b = new SolidBrush(this.BackColor))
    dc.FillRectangle(b, this.ClientRectangle);

    using (Pen p = new Pen(SystemColors.ActiveCaption, 1))
    {
        p.Alignment = System.Drawing.Drawing2D.PenAlignment.Inset;
        Rectangle r = this.ClientRectangle;
        dc.DrawRectangle(p, r);
    }

    Rectangle rect = this.ClientRectangle;
    rect.Offset(2, 2);
    rect.Width -= 4; rect.Height -= 4;

    StringFormat format = new StringFormat();
    format.LineAlignment = this.Alignment;
    if (this.RightToLeft == RightToLeft.Yes)
        format.FormatFlags = StringFormatFlags.DirectionRightToLeft;

    using (Brush b = new SolidBrush(this.ForeColor))
        dc.DrawString(this.Text, this.Font, b, rect, format);
}

After that the OnPaint() function draws selection frame if the shape is currently selected.

Just before the function closes, it calls the ControlPaint.DrawSizeGrip() function to draw the sizing grip that the user would resize the shape from.

  • Painting Quality

In our library Geming.Sisc.Infrastructure, we have created a helper class, GraphicsManager, which contains only one function, SetQuality().

This function accepts two input parameters, the graphics object and the quality you wish to set to that object. The following is the code for the SetQuality() function.

Listing 3 – GraphicsManager.SetQuality() Method Code Listing
public static void SetQuality(System.Drawing.Graphics g, PaintingQuality quality)
{
    if (g == null)
        throw new ArgumentNullException("g");

    if (quality == PaintingQuality.High)
    {
        g.CompositingQuality = CompositingQuality.AssumeLinear;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;
        g.PixelOffsetMode = PixelOffsetMode.Half;
        g.SmoothingMode = SmoothingMode.AntiAlias;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
    }
    else if (quality == PaintingQuality.Medium)
    {
        g.CompositingQuality = CompositingQuality.HighQuality;
        g.InterpolationMode = InterpolationMode.Bilinear;
        g.PixelOffsetMode = PixelOffsetMode.HighQuality;
        g.SmoothingMode = SmoothingMode.HighQuality;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
    }
    else
    {
        g.CompositingQuality = CompositingQuality.Default;
        g.InterpolationMode = InterpolationMode.Default;
        g.PixelOffsetMode = PixelOffsetMode.Default;
        g.SmoothingMode = SmoothingMode.Default;
        g.TextRenderingHint =
        System.Drawing.Text.TextRenderingHint.SystemDefault;
    }
}

Download

The application Geming.Sisc along with its code is available for download here.

Download Geming.Sisc

Hard Links vs. Soft Links

Contents

Contents of this writing:

  • Contents
  • Overview
  • Introducing Hard Links
  • Introducing Soft Links
  • Creating a Hard Link
  • Creating a Soft Link

Overview

This writing talks about hard links and soft links; two of the nice features of NTFS file system.

You can divide file links into two categories:

  • Normal Links (shortcuts)
  • Symbolic Links:
    This divided into two subcategories:

    • Hard Links (for files)
    • Soft Links (for directories)

Introducing Hard Links

A hard link is a way to represent a single file (i.e. data volume) by more than one path. In other words, it is a way to create multiple directory entries for a single file.

Consider the following example, we could have two files, e.g. D:DocumentsBills.doc and D:FilesCompanyInvoices.doc, both files represent the same data, so when you change one file the other changes too!

A hard link is almost like a normal link (i.e. shortcut) but there is a big difference. If you delete the source file of a shortcut, the shortcut would be broken (points to a non-existing file.) A hard link on the other hand, would be still working fine if you delete the source file as if it was just a copy.

Notice that, hard disk space is not multiplied for each hard link. Because they are share the same data volume, they share the same size.

There is something that you should be aware of. File attributes are not updated for each of the hard links. This means that if you set a hard link (e.g. D:FilesCompanyInvoices.doc) to read-only, it is the only link that is set to this attribute. Other hard links including the source would be still read-write (unless you set them too.)

Hard links are only supported on NTFS file system; they are not supported by Win9x versions of Windows. Besides this, hard links creation is limited to the same local disk (or network drive) of the source file. For example, you cannot create a hard link on the C: drive for a file on the drive D: or on a network share.

Introducing Soft Links

Soft links (also called junctions,) are identical to hard links except that soft links are designated for directories not files.

For example, if you have one or more soft links to the directory D:Tools (e.g. D:Software and D:ProgramsTools,) all directories (including the source) would be updated when any one of them changes. That is, if you created a new file in one directory, it is created in all directory links, and so on.

Like hard links, soft links require NTFS file system. Plus, deleting the source directory does not affect other directories, all links share the same data volume, changing attributes of one directory does not be applied to others.

Unlike hard links, creating soft links is a special feature of Windows Vista and Windows Server 2008 (and future versions of course.) Therefore, it cannot be applied to earlier versions of Windows (like Windows XP or Windows Server 2003.)

Creating a Hard Link

Unfortunately, neither hard links nor soft links are supported by the .NET Framework. Therefore, you will need to dig into the Windows API to allow your application to consume this feature.

You can create a hard link using a single line of code using a simple call to the Win32 function, CreateHardLink(), that resides in the Kernel32.dll library. The definition of this function is as follows:

BOOL CreateHardLink(
    LPCTSTR lpFileName,
    LPCTSTR lpExistingFileName,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes
    );

This function accepts three arguments;

  • lpFileName:
    The path of the new hard link to be created. Notice that, it should be resides in the location (local disk or network share) as the source file.
  • lpExistingFileName:
    The path of the source file to create the hard link from.
  • lpSecurityAttributes:
    The security descriptor for the new file. Till now, it is reserved for future use and it should not be used.

This function returns TRUE if succeeded or FALSE otherwise.

Keeping the definition of the function in our minds, we can create its PInvoke method in C#. Simply, this is our C# PInvoke method:

[DllImport("Kernel32.dll", CharSet = CharSet.Unicode )]
static extern bool CreateHardLink(
    string lpFileName,
    string lpExistingFileName,
    IntPtr lpSecurityAttributes
    );

As you know, we have set the character encoding to Unicode because Windows NT allows Unicode file names. In addition, we have marshaled lpSecurityAttributes as System.IntPtr to allow passing it a null value using System.IntPtr.Zero.

Then, you simply call the function:

Change the source file to meet an existing file into your system. In addition, check that the directory of the new link exists or the call would fail.

Take into consideration that the function creates the new file for you, it should not be found or the function would fail (as the case with soft links.)

CreateHardLink(
    "D:\Files\Company\Invoices.doc",	// New Link
    "D:\Documents\Bills.doc",			// Source
    IntPtr.Zero);

And this is another link:

CreateHardLink(
    "D:\Important\Work Invoices.doc",			// New Link
    "D:\Files\Company\Invoices.doc",			// Source
    IntPtr.Zero);

As you see, it does not matter if you created the new link from the original file or from another link.

Now try to change any of the links, and check the others. They are all updated at the time you commit the change (save the file, for instance.)

Creating a Soft Link

Like hard links, you can create a soft link (a junction) using a single call to a Win32 API function; it is CreateSymbolicLink() function. Again, this function is only supported by Windows Vista and Windows Server 2008 (and future versions.)

Actually, the function CreateSymbolicLink() can be used to create both types of symbolic links, hard and soft links. The definition of this function is as follows:

BOOLEAN WINAPI CreateSymbolicLink(
    LPTSTR lpSymlinkFileName,
    LPTSTR lpTargetFileName,
    DWORD dwFlags
    );

This function accepts three arguments, the first and the second are identical to the CreateHardLink() function:

  • lpSymlinkFileName:
    The path of the new link (file or directory) that resides in the same drive (local disk or network share) of the target file.
  • lpTargetFileName:
    The path of the existing source (file or directory.)
  • dwFlags:
    The type of symbolic link. It can take the following values:

    • 0x0:
      The link is a hard link (source and destination are files.)
    • 0x1 = SYMBOLIC_LINK_FLAG_DIRECTORY:
      The link is a soft link (source and destination are directories.)

The following is the PInvoke method for our function:

[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
static extern bool CreateSymbolicLink(
    string lpSymlinkFileName,
    string lpTargetFileName,
    uint dwFlags
    );

const uint SYMBLOC_LINK_FLAG_FILE		= 0x0;
const uint SYMBLOC_LINK_FLAG_DIRECTORY 	= 0x1;

The following code creates two soft links for one directory:

Again, the source should be the only directory that exists in your machine or the function would fail.

CreateSymbolicLink(
    "D:\Software",         // New Link
    "D:\Tools",            // Source
    SYMBOLIC_LINK_FLAG_DIRECTORY);

// Source
CreateSymbolicLink(
    "D:\Programs\Tools",  // New Link
    "D:\Software",         // Source
    SYMBOLIC_LINK_FLAG_DIRECTORY);

Now try changing one directory, other links change too. Try deleting the source, nothing would be happen to the other links.

Creating a Sound Recorder in C and C#

هذه المقالة متوفرة أيضا باللغة العربية، اقرأها هنا.

Code: SMPREC – C.zip
Code: SimpleRec – C#.zip

Contents

Contents of this article:

  • Contents
  • Overview
  • Introduction
  • Sending MCI Commands
  • The Wait, Notify, and Test Flags
  • Handling MCI Errors
  • Recording from an Input Device
    • Opening the Device
    • Starting Recording
    • Pausing Recording
    • Resuming Recording
    • Stopping Recording
    • Retrieving Buffer Length
    • Saving the Recorded Buffer
    • Closing the Device
  • Playing a Sound File
    • Loading the File
    • Playing the File
    • Pausing
    • Resuming
    • Retrieving Current Position
    • Retrieving File Length
    • Seeking a Specific Position
    • Closing the Device
    • In a Nutshell
  • MCI and .NET
    • Creating the Managed Signature
    • Receiving MCI_MMNOTIFY
  • Appendix A: Setting Device Information [NEW]
  • Sample Code

Overview

This writing will focus on how you can record sound from an input device and how you can play sound files using MCI (Media Control Interface) in C and C#.

This writing does not involve a discussion or even an introduction to MCI. Instead, it provides technical discussion of what we will need to use to record and to play sound files. If you need an introduction to MCI refer to the MSDN documentation.

We will begin by a discussion to types and functions required to accomplish our tasks. Then we will look on how you can utilize those types and functions in your C or C# application.

Our demonstration examples in this writing will be in C. In the last section we will have a look at .NET and C#. Besides this, there are sample applications written by C and C# attached with the article.

Introduction

Icon to represent multimedia
Image via Wikipedia

Actually, there are many ways to record and play sound files. However, in this writing we will focus on MCI (Media Control Interface) as it is one of the high-level easy-to-use interfaces for controlling all media types.

You can control a multimedia hardware using MCI by either sending commands like Windows messages or by sending string messages to the MCI. The second approach is preferable in scripting languages. As we are concentrating on languages C and C#, we will consider the first approach only.

Because .NET Framework does not include classes for handling MCI (or multimedia at all,) we will need to dig into Windows SDK, specifically WinMM.dll (Windows Multimedia Library) that resides in the system directory.

In order to access this library from your C# application, you will need to create your own marshaling types and PInvoke (Platform Invocation) methods to allow your managed code to communicate with the unmanaged server. That is what the last section of this article is devoted for.

To access this library from your C application, just reference the library object file WinMM.lib in your project. Notice that all of the functions and structures related to the MCI are prefixed with mci.

In the next sections we will talk about the functions and structures that we need to be aware of before digging into coding.

Sending MCI Commands

The key function to Windows multimedia programming using MCI is mciSendCommand(). This function sends a command to the MCI. As we have said, you can program MCI using one of two approaches, you can send Windows-messages-like commands, and you can also send string messages to the MCI. The key function that we will use to send MCI commands is mciSendCommand(). To send string messages, you can use the function mciSendMessage() which is not covered here.

The definition of the function mciSendCommand() is as follows:

MCIERROR mciSendCommand(
  MCIDEVICEID IDDevice,
  UINT        uMsg,
  DWORD       fdwCommand,
  DWORD_PTR   dwParam
);

This function receives four input arguments:

  • IDDevice:
    The ID of the device to which to receive the command. For example, the ID of the input device when recording or the output device if playing. As you know, many devices could be connected to the PC. You can forget about this argument and just pass the function 0 to direct the message to the default device (selected in the Sound applet in the Control Panel.)
  • uMsg:
    The message (command) to be sent. Common messages are covered in the next few sections.
  • fdwCommand:
    Flags (options) of the message. Every message has its own options. However, all messages share the flags MCI_WAIT, MCI_NOTIFY, and MCI_TEST (covered soon.)
  • dwParam:
    A structure contains the specific parameter for the command message.

As a result, every command (message) has its name, flags, and structure parameter.

This function returns 0 if succeeded or an error code otherwise.

The Wait, Notify, and Test Flags

Common flags for all MCI messages are, MCI_WAIT, MCI_NOTIFY, and MCI_TEST.

The Wait flag, MCI_WAIT, indicates that the message should be processed synchronously; means that the function would not return until message processing finishes. For example, if you send a play message with the MCI_WAIT, your application would be suspended until the entire file finishes. Therefore, you should not use MCI_WAIT for play or record messages.

The Notify flag, MCI_NOTIFY, is the reverse of the Wait flag. It indicates that the message should be processed asynchronously; means that the function would return immediately and does not wait for the completion of the message. When the processing of the message finishes, it sends a MM_MCINOTIFY message to the windows specified in the dwCallback member of the message parameter. This flag should be used for play and record messages.

The wParam value of the MM_MCINOTIFY message is usually set to either MCI_NOTIFY_SUCCESSFUL to indicate command success or MCI_NOTIFY_FAILURE otherwise.

The Test flag, MCI_TEST, checks if the device can process this message, it does not process the message. The function would return 0 if the device can process the message or non-zero otherwise. You will use this flag in rare cases only.

Keep in mind that you should choose only one of the three flags, you cannot combine them.

If you didn’t specify any of these three flags, the call would be treated asynchronously and you will not be notified when it completes.

Handling MCI Errors

Every MCI command could succeed or fail. If the command succeeded, mciSendCommand() returns 0 (FALSE/false.) Otherwise, it returns the error code.

MCI defines error codes as constants that are prefixed with MCIERR_ like MCIERR_DEVICE_NOT_INSTALLED and MCIERR_FILE_NOT_FOUND (names are self-explanatory.) You can get the friendly error message of the code using the function mciGetErrorString(). The definition of this function is as follows:

BOOL mciGetErrorString(
  DWORD fdwError,
  LPTSTR lpszErrorText,
  UINT cchErrorText
);

This function accepts three arguments:

  • fdwError:
    Error code returned by mciSendCommand() function.
  • lpszErrorText:
    A string buffer that receives the description of the error.
  • cchErrorText:
    Length of the buffer in characters.

The following C example shows how you can display a friendly error message to the user if an error occurred:

	TCHAR szBuffer[256];
	DWORD dwErr;

	dwErr = mciSendCommand(/* arguments */);

	mciGetErrorStringW(dwErr, szBuffer, 256);
	// cchErrorText =
	//     sizeof(szBuffer) / sizeof(szBuffer[0])

Recording from an Input Device

To record from an input device using MCI, follow these steps:

  1. Open the input device to receive the data from.
  2. Order the MCI to start the recording process.
  3. When you finish, stop the recording process.
  4. Save the record buffer if applicable.
  5. Close the opened device.

Keep in mind that you should check whether an error occurred or not after sending each command. The previous approach to retrieve error messages would be very useful.

Opening the Device

To open a device just pass the open command MCI_OPEN to the mciSendCommand() function along with its flags and parameter.

The parameter of MCI_OPEN is the structure MCI_OPEN_PARMS. This structure contains information about the open command. The definition of this structure is as following:

typedef struct {
    DWORD_PTR    dwCallback;
    MCIDEVICEID  wDeviceID;
    LPCSTR       lpstrDeviceType;
    LPCSTR       lpstrElementName;
    LPCSTR       lpstrAlias;
} MCI_OPEN_PARMS;

Actually, you will make use of only the third and fourth members, lpstrDeviceType and lpstrElementName, of the structure when you open a device. lpstrDeviceType determines the type of the device (digital-audio, digital-video, etc.) that will be used. In our example that records and plays sound files, we will set this member to €œwaveaudio€ to indicate that we are going to work with waveform (WAV) data.

lpstrElementName on the other hand, should be set to an empty string (that is €œ€) if you are opening an input device for recording. If you want to play a file, set this member to the full path of that file.

Common flags of the command MCI_OPEN are:

  • The Wait, Notify, and Test flags:
    The Wait command is usually used for MCI_OPEN.
  • MCI_OPEN_ELEMENT:
    Mandatory. The lpstrDeviceType of the MCI_OPEN_PARMS is set. It is set to €œwaveaudio€ for WAV data.
  • MCI_OPEN_TYPE:
    Mandatory. The lpstrElementType of the MCI_OPEN_PARMS is set. It is set to an empty string if recording or a path to a file if you want to play it.

You will always combine the flags MCI_WAIT, MCI_OPEN_ELEMENT, and MCI_OPEN_TYPE for the MCI_OPEN command.

When the function returns, the wDeviceID member of the structure is set to the ID of the device opened. You should keep this ID for future calls on that device until you close it using the close command.

The following C code shows how you can open an input device for recording:

	MCI_OPEN_PARMS parmOpen;
	WORD wDeviceID;

	parmOpen.dwCallback       = 0;
	parmOpen.wDeviceID        = 0;	// the default device
	parmOpen.lpstrDeviceType  = TEXT("waveaudio");
	parmOpen.lpstrElementName = TEXT("");
	parmOpen.lpstrAlias       = 0;

	mciSendCommand(0, // the default device
		MCI_OPEN,
		MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
		(DWORD)&parmOpen);

	// Keep the device ID for future calls
	wDeviceID = parmOpen.wDeviceID;

Starting Recording

After you have opened the input device, you can order the MCI to start the recording process using the command MCI_RECORD. This command requires an opened input device and a parameter of type MCI_RECORD_PARMS. The definition of this structure is as following:

typedef struct {
    DWORD_PTR dwCallback;
    DWORD     dwFrom;
    DWORD     dwTo;
} MCI_RECORD_PARMS;

Members this structure defines are as following:

  • dwCallback:
    The window handle that should be called after the processing of the command finishes if MCI_NOTIFY is specified in command flags.
  • dwFrom:
    Indicates the position of the buffer (in thousands of a second) to start recording from. In most cases this would be set to zero.
  • dwTo:
    Indicates the position of the buffer to stop recording when it is reached. Unless you want to record for a given period, this member should be zero.

Common flags for MCI_RECORD are:

  • MCI_WAIT, MCI_NOTIFY, and MCI_TEST:
    Usually you will use the MCI_NOTIFY flag. If so, you should handle the MM_MCINOTIFY message.
  • MCI_FROM:
    Set if you used the dwFrom member of the parameter.
  • MCI_TO:
    Set if you used the dwTo member of the parameter.

If you want to record for a specific period, set dwTo member of the structure to that specific period (in thousands of seconds) and combine your flags with MCI_TO. When this period ends, MCI automatically stops the recording process and it sends a MM_MCINOTIFY message if you have set MCI_NOTIFY in the flags.

The following C example shows how you can start recording:

	MCI_RECORD_PARMS parmRec;

	parmRec.dwCallback = 0;
	parmRec.dwFrom	   = 0;
	parmRec.dwTo	   = 0;

	// We do not need a notification message
	// we will send a Stop command, when
	// we finish.
	mciSendCommand(wDeviceID, MCI_RECORD, 0, (DWORD)&parmRec);

The following code shows how you can record for a specific period:

	MCI_RECORD_PARMS parmRec;

	parmRec.dwCallback = hWnd;	// window handle
	parmRec.dwFrom	   = 0;
	parmRec.dwTo	   = 30000; // 30 seconds

	// Notify me when you finish the 30 seconds
	mciSendCommand(wDeviceID, MCI_RECORD,
		MCI_NOTIFY | MCI_TO, (DWORD)&parmRec);

Pausing Recording

To pause the recording process, just pass the MCI_PAUSE command to the MCI. This command accepts a parameter of the structure MCI_GENERIC_PARMS which is defined as following:

typedef struct {
    DWORD_PTR dwCallback;
} MCI_GENERIC_PARMS;

This structure contains only one member, dwCallback. As you know, if you specify MCI_NOTIFY in command flags, MCI will send a MM_MCINOTIFY message to the window specified in this member.

MCI_PAUSE does not have specific flags, just the Wait, Notify, and Test flags.

The following C example shows how you can pause the recording process. Notice that you should already be recording or an error would be returned by the mciSendCommand().

	MCI_GENERIC_PARMS parmGen;

	parmGen.dwCallback = 0;

	mciSendCommand(wDeviceID, MCI_PAUSE, MCI_WAIT, (DWORD)&parmGen);

Resuming Recording

To resume after pausing, you can send a MCI_RESUME command to MCI. This command is very similar to the MCI_PAUSE command. It accepts the same parameter and the same flags. The example is the same, just change command name.

Stopping Recording

After you finish recording you will need to stop the recording process. To accomplish this, pass the MCI_STOP command along with the device ID and its parameter to the MCI.

This command is the same as MCI_PAUSE and MCI_RESUME. It accepts the same flags and the same parameters, and the example is identical too, just change the command name.

Retrieving Buffer Length

How long have you been recording? This could be easily answered with the MCI_STATUS command. This command queries MCI and retrieves information about the current session.

MCI_STATUS accepts a structure parameter of type MCI_STATUS_PARMS which is defined as following:

typedef struct {
    DWORD_PTR dwCallback;
    DWORD     dwReturn;
    DWORD     dwItem;
    DWORD     dwTrack;
} MCI_STATUS_PARMS;

This structure defines the following members:

  • dwCallback:
    Discussed previously.
  • dwReturn:
    After returning from the call, it should contains the return value of the query.
  • dwItem:
    The item to query about.
  • dwTracks:
    Length or number of tracks (specific to some query items.)

Common flags that MCI_STATUS accepts:

  • The Wait, Notify, and Test flags:
    You will usually use the Wait flag.
  • MCI_STATUS_ITEM:
    Mandatory in most cases. Indicates that the dwItem of the structure is set.

If you want to query MCI about specific information, pass the MCI_STATUS command to the MCI along with MCI_STATUS_ITEM and MCI_WAIT flags, and set the dwItem field of the parameter to one of the following values (some are for output devices):

  • MCI_STATUS_LENGTH:
    Retrieves the total length of the buffer (in thousands of a second.)
  • MCI_STATUS_MODE:
    Retrieves current mode of the device which can be one of the following values (names are self-explanatory):

    • MCI_MODE_NOT_READY
    • MCI_MODE_PAUSE
    • MCI_MODE_PLAY
    • MCI_MODE_STOP
    • MCI_MODE_RECORD
    • MCI_MODE_SEEK
  • MCI_STATUS_NUMBER_OF_TRACKS:
    Retrieves the total number of tracks.
  • MCI_STATUS_POSITION:
    Retrieves current position (in thousands of a second.)
  • MCI_STATUS_READY:
    Returns TRUE (true in C#) if the device is ready or FALSE (false in C#) otherwise.

When the function returns, the dwReturn field of the structure should contain the result of the query item selected.

The following C example retrieves the length of the buffer recorded:

	MCI_STATUS_PARMS parmStatus;

	parmStatus.dwCallback = 0;
	parmStatus.dwReturn   = 0;
	parmStatus.dwItem     = MCI_STATUS_LENGTH;
	parmStatus.dwTrack    = 0;

	mciSendCommand(0, MCI_STATUS,
		MCI_WAIT | MCI_STATUS_ITEM, (DWORD)&parmStatus);

	// Display the number of seconds
	// parmStatus.dwReturn / 1000

Saving the Recorded Buffer

Before you close the device, you can save the current recorded buffer in an audio (waveform) file. The command MCI_SAVE orders MCI to save the buffer to the file specified in its structure parameter.

The parameter of MCI_SAVE is a MCI_SAVE_PARMS structure and it is defined as following:

typedef struct {
    DWORD_PTR  dwCallback;
    LPCTSTR    lpfilename;
} MCI_SAVE_PARMS;

This structure contains only two members, dwCallback (discussed before) and lpfilename. lpfilename points to a string buffer contains the name and full path of the file to save.

MCI_SAVE accepts few flags:

  • The Wait, Notify, and Test flags:
    You will always use the Wait (MCI_WAIT) flag.
  • MCI_SAVE_FILE:
    Mandatory. You will always set this flag. It indicates that lpfilename contains the path of the target file.

The following example saves the recorded buffer to the file €œrecording.wav€:

	MCI_SAVE_PARMS parmSave;

	parmSave.dwCallback = 0;
	parmSave.lpfilename = TEXT("recording.wav");
	// save to the current directory

	mciSendCommand(wDeviceID, MCI_SAVE,
		MCI_WAIT | MCI_SAVE_FILE, (DWORD)&parmSave);

Closing the Device

You strictly should always close the device when you finish working with it, and this is done through the MCI_CLOSE command which accepts a parameter of type MCI_GENERIC_PARMS (discussed before.)

MCI_CLOSE accepts only the Wait, Notify, and Test flags.

The following C example closes the opened device:

	MCI_GENERIC_PARMS parmsGen;

	parmsGen.dwCallback = 0;

	mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)&parmsGen);

Playing a Sound File

Keeping in mind what you have learnt from the previous section would be very helpful in our discussion on how to play sound files using MCI.

Actually, the same rules and commands applied in the previous section, will be applied here.

To play a sound file on MCI, you should follow these steps:

  1. Load the audio file. This automatically opens the output device.
  2. Order the MCI to start the playing process.
  3. Close the opened device when you finish.

Again, you should check whether an error occurred or not after each call to mciSendCommand().

Loading the File

As you know, to open a multimedia device, you pass MCI the MCI_OPEN command. To specify a file to be loaded, specify the full path of the file in the lpstrElementName field of the MCI_OPEN_PARMS structure.

The following C example shows how you can open an output device and load a file for playing:

	MCI_OPEN_PARMS parmOpen;
	WORD wDeviceID;

	parmOpen.dwCallback       = 0;
	parmOpen.wDeviceID        = 0;	// the default device
	parmOpen.lpstrDeviceType  = TEXT("waveaudio");
	parmOpen.lpstrElementName = TEXT("recording.wav");
	parmOpen.lpstrAlias       = 0;

	mciSendCommand(0, // the default device
		MCI_OPEN,
		MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
		(DWORD)&parmOpen);

	// Keep the device ID for future calls
	wDeviceID = parmOpen.wDeviceID;

Playing the File

To start playing the currently loaded file, you can use the MCI_PLAY command. This command accepts a structure parameter of type MCI_PLAY_PARMS which is defined as following:

typedef struct {
    DWORD_PTR dwCallback;
    DWORD     dwFrom;
    DWORD     dwTo;
} MCI_PLAY_PARMS;

At first sight, you notice that it is identical to the MCI_RECORD_PARMS. Actually, you are right. This structure defines the same members as the MCI_RECORD_PARMS structure. In addition, the description of the members is the same.

dwFrom specifies the position (in thousands of a second) where to start playing. dwTo on the other hand specifies the position (in thousands of a second too) where to end playing. If you set dwFrom, you will need to set the flag MCI_FROM. Conversely, if you set dwTo, you will need to set the flag MCI_TO. If you need to play the file from the start to the end, leave both members and do not specify either MCI_FROM or MCI_TO.

You will most likely combine MCI_PLAY flags with the Notify flag, MCI_NOTIFY, to allow the code to continue execution while the file plays. If you did so, your application would receive MM_MCINOTIFY message to the window specified in the dwCallback member of the parameter structure.

The following C example shows how to start playing a file:

	MCI_PLAY_PARMS parmPlay;

	// Play the file
	// from the start to the end

	parmRec.dwCallback = 0;
	parmRec.dwFrom	   = 0;
	parmRec.dwTo	   = 0;

	// notify me when you finish
	mciSendCommand(wDeviceID, MCI_PLAY,
		MCI_NOTIFY, (DWORD)&parmRec);

And the following code plays only three minutes from the file:

	MCI_PLAY_PARMS parmPlay;

	// play only 3 minutes
	parmRec.dwCallback = 0;
	parmRec.dwFrom	   = 0;
	// 3 * 60 * 1000
	parmRec.dwTo	   = 180000;

	// notify me when you finish
	mciSendCommand(wDeviceID, MCI_PLAY,
		MCI_NOTIFY | MCI_TO, (DWORD)&parmRec);

Pausing

As you know, you can pause the playback using the MCI_PAUSE command which accepts the MCI_GENERIC_PARMS parameter.

Resuming

To resume the playback after pausing, you can use the MCI_RESUME command discussed before.

Retrieving Current Position

To retrieve the current position of the file, pass the MCI_STATUS command along with its flags (MCI_WAIT and MCI_STATUS_ITEM) and its parameter (MCI_STATUS_PARMS) to the MCI. Do not forget to set dwItem member of the structure to MCI_STATUS_POSITION to retrieve the current position (in thousands of a second) in the dwReturn member of the structure. The following C example demonstrates this:

	MCI_STATUS_PARMS parmStatus;

	parmStatus.dwCallback = 0;
	parmStatus.dwReturn   = 0;
	parmStatus.dwItem     = MCI_STATUS_POSITION;
	parmStatus.dwTrack    = 0;

	mciSendCommand(wDeviceID, MCI_STATUS,
		MCI_WAIT | MCI_STATUS_ITEM, (DWORD)&parmStatus);

	// Display the current position
	// parmStatus.dwReturn / 1000

Retrieving File Length

Like retrieving current position, you can retrieve full file length the same way. However, you will need to specify the item MCI_STATUS_LENGTH instead.

Seeking a Specific Position

To change the current position, pass MCI the command MCI_SEEK. This command accepts the structure parameter MCI_SEEK_PARMS which is defined as following:

typedef struct {
    DWORD_PTR dwCallback;
    DWORD     dwTo;
} MCI_SEEK_PARMS;

This parameter defines the following members:

  • dwCallback:
    The window to be notified with the MCI_MMNOTIFY message if the call was carried out asynchronously using the MCI_NOTIFY flag.
  • dwTo:
    The position (in thousands of a second) to jump to.

The command MCI_SEEK accepts the following flags:

  • The Wait, Notify, and Test flags:
    You will always use the Wait flag.
  • MCI_SEEK_TO_END:
    If specified, it would jump to the end of the file.
  • MCI_SEEK_TO_START:
    If specified, it would jump to the start of the file.
  • MCI_TO:
    If specified, MCI would use the value in the dwTo member of the parameter structure.

The following C example jumps to the start of the file:

	MCI_SEEK_PARMS parmSeek;

	parmStatus.dwCallback = 0;
	parmStatus.dwTo	      = 0;

	mciSendCommand(wDeviceID, MCI_SEEK,
		MCI_WAIT | MCI_SEEK_TO_START, (DWORD)&parmSeek);

And the following example jumps to the third minute of the file:

	MCI_SEEK_PARMS parmSeek;

	parmStatus.dwCallback = 0;
	parmStatus.dwTo	      = 180000;

	mciSendCommand(wDeviceID, MCI_SEEK,
		MCI_WAIT | MCI_TO, (DWORD)&parmSeek);

Closing the Device

You should close the device as soon as you finish working with it, and this is done (as you know) through the MCI_CLOSE command.

In a Nutshell

The following table summarizes the commands that you will usually use with audio files along with their parameter structures and flags.

Command Input/Output Parameter Structure Commonly Used Flags
MCI_OPEN In/Out MCI_OPEN_PARMS MCI_WAIT, MCI_OPEN_ELEMENT, and MCI_OPEN_TYPE
MCI_RECORD In MCI_RECORD_PARMS (none) or MCI_NOTIFY
MCI_PLAY Out MCI_PLAY_PARMS MCI_NOTIFY
MCI_PAUSE In/Out MCI_GENERIC_PARMS MCI_WAIT
MCI_RESUME In/Out MCI_GENERIC_PARMS MCI_WAIT
MCI_STOP In/Out MCI_GENERIC_PARMS MCI_WAIT
MCI_SEEK Out MCI_SEEK_PARMS MCI_WAIT and MCI_TO / MCI_SEEK_TO_START / MCI_SEEK_TO_END
MCI_SAVE In MCI_SAVE_PARMS MCI_WAIT and MCI_SAVE_FILE
MCI_STATUS In/Out MCI_STATUS_PARMS MCI_WAIT and MCI_STATUS_ITEM
MCI_CLOSE In/Out MCI_GENERIC_PARMS MCI_WAIT

MCI and .NET

Creating the Managed Signature

Because .NET does not support MCI and it does not allow you to call unmanaged code directly, you will need to create your own marshaling types and PInvoke methods.

Keep in mind that you can get the handle of a window by using Control.Handle property.

The following class is the managed signature of our unmanaged structures and functions along with the required constants:

internal static class SafeNativeMethods
{
    // Constants

    public const string WaveAudio = "waveaudio";

    public const uint MM_MCINOTIFY = 0x3B9;

    public const uint MCI_NOTIFY_SUCCESSFUL = 0x0001;
    public const uint MCI_NOTIFY_SUPERSEDED = 0x0002;
    public const uint MCI_NOTIFY_ABORTED = 0x0004;
    public const uint MCI_NOTIFY_FAILURE = 0x0008;

    public const uint MCI_OPEN = 0x0803;
    public const uint MCI_CLOSE = 0x0804;
    public const uint MCI_PLAY = 0x0806;
    public const uint MCI_SEEK = 0x0807;
    public const uint MCI_STOP = 0x0808;
    public const uint MCI_PAUSE = 0x0809;
    public const uint MCI_RECORD = 0x080F;
    public const uint MCI_RESUME = 0x0855;
    public const uint MCI_SAVE = 0x0813;
    public const uint MCI_LOAD = 0x0850;
    public const uint MCI_STATUS = 0x0814;

    public const uint MCI_SAVE_FILE = 0x00000100;
    public const uint MCI_OPEN_ELEMENT = 0x00000200;
    public const uint MCI_OPEN_TYPE = 0x00002000;
    public const uint MCI_LOAD_FILE = 0x00000100;
    public const uint MCI_STATUS_POSITION = 0x00000002;
    public const uint MCI_STATUS_LENGTH = 0x00000001;
    public const uint MCI_STATUS_ITEM = 0x00000100;

    public const uint MCI_NOTIFY = 0x00000001;
    public const uint MCI_WAIT = 0x00000002;
    public const uint MCI_FROM = 0x00000004;
    public const uint MCI_TO = 0x00000008;

    // Structures

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_OPEN_PARMS
    {
        public IntPtr dwCallback;
        public uint wDeviceID;
        public IntPtr lpstrDeviceType;
        public IntPtr lpstrElementName;
        public IntPtr lpstrAlias;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_RECORD_PARMS
    {
        public IntPtr dwCallback;
        public uint dwFrom;
        public uint dwTo;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_PLAY_PARMS
    {
        public IntPtr dwCallback;
        public uint dwFrom;
        public uint dwTo;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_GENERIC_PARMS
    {
        public IntPtr dwCallback;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_SEEK_PARMS
    {
        public IntPtr dwCallback;
        public uint dwTo;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_SAVE_PARMS
    {
        public IntPtr dwCallback;
        public IntPtr lpfilename;
    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    public struct MCI_STATUS_PARMS
    {
        public IntPtr dwCallback;
        public uint dwReturn;
        public uint dwItem;
        public uint dwTrack;
    } ;

    // Functions

    [DllImport("winmm.dll", CharSet = CharSet.Ansi,
        BestFitMapping = true, ThrowOnUnmappableChar = true)]
    [return: MarshalAs(UnmanagedType.U4)]
    public static extern uint mciSendCommand(
        uint mciId,
        uint uMsg,
        uint dwParam1,
        IntPtr dwParam2);

    [DllImport("winmm.dll", CharSet = CharSet.Ansi,
        BestFitMapping = true, ThrowOnUnmappableChar = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool mciGetErrorString(
        uint mcierr,
        [MarshalAs(UnmanagedType.LPStr)]
        System.Text.StringBuilder pszText,
        uint cchText);
}

Receiving MCI_MMNOTIFY

In C, you can handle the MCI_MMNOTIFY message in the way you handle any other message. Just add the handler to the window procedure (WndProc) function.

In .NET, you do not have a WndProc function. However, .NET emulates this function using the protected Control.WndProc() function. You can override this function in your form and do the required processing. The following example demonstrates this:

public partial class MainForm : Form
{
    . . .

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == SafeNativeMethods.MM_MCINOTIFY)
        {
            // Handle the message
        }

        // DO NOT REMOVE the following line
        base.WndProc(ref m);
    }
}

Appendix A: Setting Device Information

After we received your feedbacks and comments about the article, we decided to add a small appendix to the end of the article about setting information (volume, channel, sampling rate, etc.) to a MCI device (a Waveform device of course.)

Like anything else in MCI, you can set device information using a MCI command (string/numeric), and this time it’s the MCI_SET command.

This command is used to set information about a specific device. This command requires an input parameter of the MCI_SET_PARMS structure. However, that input parameter might have extended members for specific devices. Because we are concentrating of Waveform devices, so we are going to use the MCI_WAVE_SET_PARMS structure that contains the extended members for our device and is defined as following:

typedef struct {
  DWORD_PTR dwCallback;
  DWORD     dwTimeFormat;
  DWORD     dwAudio;
  UINT      wInput;
  UINT      wOutput;
  WORD      wFormatTag;
  WORD      wReserved2;
  WORD      nChannels;
  WORD      wReserved3;
  DWORD     nSamplesPerSec;
  DWORD     nAvgBytesPerSec;
  WORD      nBlockAlign;
  WORD      wReserved4;
  WORD      wBitsPerSample;
  WORD      wReserved5;
} MCI_WAVE_SET_PARMS;

This structure contains all and every little piece of information that can be set to a device. I expect that you read the main article and you are already familiar with members like dwCallback (other members are self-explanatory) that we have talked about many times, and you are fine too with function calls and setting up input parameters, so I won’t get into the discussion of the structure or how you are going to use that command. However, if you need more help setting up the input parameters for the structure, you should take a brief look at the MCI_WAVE_SET_PARMS Structure documentation in the MSDN.

As you know, the MCI_WAVE_SET_PARMS unmanaged structure can be marshaled in C# as following:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MCI_WAVE_SET_PARMS
{
  public IntPtr dwCallback;
  public uint     dwTimeFormat;
  public uint     dwAudio;
  public uint      wInput;
  public uint      wOutput;
  public ushort      wFormatTag;
  public ushort      wReserved2;
  public ushort      nChannels;
  public ushort      wReserved3;
  public uint     nSamplesPerSec;
  public uint     nAvgBytesPerSec;
  public ushort      nBlockAlign;
  public ushort      wReserved4;
  public ushort      wBitsPerSample;
  public ushort      wReserved5;
}

Congratulations! You did set the device information! So how to get them back?

This can be done through the MCI_STATUS (discussed earlier) by setting up the MCI_STATUS_ITEM flag and setting the query item to the required information you need to query about (like MCI_DGV_STATUS_VOLUME to query about volume.)

More about the MCI_STATUS command can be found in the MSDN documentation.

Sample Code

Attached with this writing the sample applications, SampleRec (C#) and SMPREC (C).

Download “SMPREC – C.zip”

Download “SampleRec – C#.zip”

Marshaling with C# – Chapter 3: Marshaling Compound Types

Read the full book here.

Chapter Contents

Contents of this chapter:

  • Chapter Contents
  • Overview
  • Introduction
  • Marshaling Unmanaged Structures
    • How to Marshal a Structure
    • Handling Memory Layout Problem
    • Try It Out!
  • Marshaling Unions
    • A Short Speech About Unions
    • How to Marshal a Union
    • Unions with Arrays
    • Try It Out!
  • Value-Types and Reference-Types
  • Passing Mechanism
  • Real-World Examples
    • The DEVMODE Structure
    • Working with Display Settings
  • Summary

Overview

This chapter demonstrates how to marshal compound types. Compound types are those build of other types, for example structures and classes.

Like the previous chapter. This chapter breaks unmanaged compound types into two categories, structures and unions. We first discuss structures and then we will dive into unions and how to marshal them.

You might ask, why you have divided compound types into just two categories, structures and unions, I can create classes too? The answer is easy. For its simplicity, this book will focus primarily on Windows API. Therefore, you will find much of our talking about Win32 functions and structures. However, the same rules apply to classes and other unmanaged types.

Introduction

A compound type encapsulates related data together; it provides an organized and arranged container for transmitting a group of variables between the client application and the unmanaged server. It consists (usually) of variables of simple types and (optionally) other compound types. In addition, it could define other compound types inside.

Compound types come in two kinds:

  • Unmanaged Structures
  • Unmanaged Unions

An example of a structure is OSVERSIONINFOEX structure that encapsulates operating system version information together. For those who are somewhat familiar with DirectX, they may find that DirectX API relies heavily on structures.

As you know, because there is no compatibility between .NET and unmanaged code, data must undergo some conversion routines for transmitting from the managed code to the unmanaged server and vice versa, and compound types are no exception.

In the next section, we will focus of the first kind, structures.

Marshaling Unmanaged Structures

How to Marshal a Structure

Unmanaged structures can be marshaled as managed structures or even classes. Choosing between a managed structure and a class is up to you, there are no rules to follow. However, when marshaling as managed classes, there are some limitations with the passing mechanism as we will see later in this chapter.

When marshaling structures in the managed environment, you must take into consideration that while you access a variable into your by its name, Windows accesses it via its address (i.e. position) inside the memory, it does not care about field name, but it cares about its location and size. Therefore, the memory layout and size of the type are very crucial.

You can marshal an unmanaged structure in few steps:

  1. Create the marshaling type either a managed structure or a class.
  2. Add the type fields (variables) only. Again, layout and size of the type are very crucial. Therefore, fields must be ordered as they are defined, so that the Windows can access them correctly.
  3. Decorate your type with StructLayoutAttribute attribute specifying the memory layout kind.

Handling Memory Layout Problem

When marshaling an unmanaged structure, you must take care of how that type is laid-out into memory.

Actually, application memory is divided into blocks (in a 4-bytes base,) and every block has its own address. When you declare a variable or a type in your program it is stored inside the memory and got its memory address. Consequently, all data members inside a structure have their own addresses that are relative to the address of the beginning of the structure.

Consider the following structures:

Listing 3.1 SMALL_RECT and COORD Unmanaged Signature

typedef struct SMALL_RECT {
  SHORT Left;
  SHORT Top;
  SHORT Right;
  SHORT Bottom;
};

typedef struct COORD {
  SHORT X;
  SHORT Y;
};

When we declare those structures in our code they are laid-out into memory and got addresses like that:

Figure 3.1 How Memory is Laid-Out
Figure 3.1 How Memory is Laid-Out

Thus, you should keep in mind that the size and location of each of type members is very crucial and you strictly should take care of how this type is laid-out into the memory.

For now, you do not have to think about the last illustration. We will cover memory management in details in chapter 6.

For handling the memory layout problem, you must apply the StructLayoutAttribute attribute to your marshaling type specifying the layout kind using the LayoutKind property.

This property can take one of three values:

  • LayoutKind.Auto (Default):
    Lets the CLR chooses how the type is laid-out into memory. Setting this value prevents interoperation with this type, that means that you will not be able to marshal the unmanaged structure with this type, and if you tried, an exception will be thrown.
  • LayoutKind.Sequential:
    Variables of the type are laid-out sequentially. When setting this value ensure that all variables are on the right order as they are defined in the unmanaged structure.
  • LayoutKind.Explicit:
    Lets you control precisely each variable’s location inside the type. When setting this value, you must apply the FieldOffsetAttribute attribute to every variable in your type specifying the relative position in bytes of the variable to the start of the type. Note that when setting this value, order of variables becomes unimportant.

For the sake of simplicity, you should lay-out all of your types sequentially. However, when working with unions, you are required to explicitly control every variable’s location. Unions are covered in the next section.

We have said that you should add only the type members into the marshaling type, however, this is not always true. In structures where there is a member that you can set to determine the structure size (like the OPENFILENAME structure,) you can add your own members to the end of the structure. However, you should set the size member to the size of the entire structure minus the new members that you have added. This technique is discussed in details in chapter 6.

Try It Out!

The following example demonstrates how to marshal the famous structures SMALL_RECT and COORD. Both used earlier with the ScrollConsoleScreenBuffer() function in the last chapter. You can check code listing 3.1 earlier in this chapter for the definition of the structures.

Next is the managed signature for both the structures. Note that you can marshal them as managed classes too.

Listing 3.2 SMALL_RECT and COORD Managed Signature

    // Laying-out the structure sequentially
    [StructLayout(LayoutKind.Sequential)]
    //public class SMALL_RECT
    public struct SMALL_RECT
    {
        // Because we are laying the structure sequentially,
        // we preserve field order as they are defined.

        public UInt16 Left;
        public UInt16 Top;
        public UInt16 Right;
        public UInt16 Bottom;
    }

    // The same as SMALL_RECT applies to COORD
    [StructLayout(LayoutKind.Sequential)]
    //public struct COORD
    public struct COORD
    {
        public UInt16 X;
        public UInt16 Y;
    }

Marshaling Unions

A Short Speech About Unions

A union is a memory location that is shared by two or more different types of variables. A union provides a way for interpreting the same bit pattern in two or more different ways (or forms.)

In fact, unions share structures lots of characteristics, like the way they defined and marshaled. It might be helpful to know that, like structures, unions can be defined inside a structure or even as a single entity. In addition, unions can define compound types inside, like structures too.

To understand unions, we will take a simple example. Consider the following union:

Listing 3.3 SOME_CHARACTER Unmanaged Signature

typedef union SOME_CHARACTER {
  int i;
  char c;
};

This was a simple union defines a character. It declared two members, i and c, it defined them in the same memory location. Thus, it provides two ways for accessing the character, by its code (int) and by its value (char). For this to work it allocates enough memory storage for holding the largest member of the union and that member is called container. Other members will overlap with the container. In our case, the container is i because it is 4 bytes (on Win32, 16 on Win16), while c is only 1 byte. Figure 3.2 shows how the memory is allocated for the union.

Figure 3.2 SOME_CHARACTER Union
Figure 3.2 SOME_CHARACTER Union

Because the two members are sharing the same memory location, when you change one member the other is changed too. Consider the following C example:

Listing 3.4 Unions Example 1

int main()
{
	union CHARACTER ch;

	ch.i = 65;			// 65 for A
	printf("c = %c", ch.c);	// prints 'A'
	printf("n");

	ch.c += 32;			// 97 for a
	printf("i = %d", ch.i);	// prints '97'
	printf("n");

	return 0;
}

When you change any of the members of the union, other members change too because they are all share the same memory address.

Now consider the same example but with values that won’t fit into the char member:

Listing 3.5 Unions Example 2

int main()
{
	union CHARACTER ch;

	ch.i = 330;
	printf("c = %c", ch.c);	// prints 'J'
	printf("n");			// Ops!

	ch.c += 32;
	printf("i = %d", ch.i);	// prints '362'
	printf("n");

	return 0;
}

What happened? Because char is 1 bye wide, it interprets only the first 8 bits of the union that are equal to 32.

The same rule applies if you add another member to the union. See the following example. Notice that order of member declarations doesn’t matter.

Listing 3.6 Unions Example 3

int main()
{
	union {
		int i;
		char c;
		short n;
	} ch;

	ch.i = 2774186;

	printf("i = %d", ch.i);
	printf("n");
	printf("c = %i", (unsigned char)ch.c);
	printf("n");
	printf("n = %d", ch.n);
	printf("n");

	return 0;
}

Now, member i, the container, interprets the 32 bits. Member c, interprets the first 8 bits (notice that we converted it to unsigned char to not to show the negative value.) Member n, interprets the first high word (16 bits.)

You might ask: Why I need unions at all? I could easily use the cast operator to convert between data types!

The answer is very easy. Unions come very efficient when casting between types require much overhead. Consider the following example: You are about to write an integer to a file. Unfortunately, there are no functions in the C standard library that allow you to write an int to a file, and using fwrite() function requires excessive overhead. The perfect solution is to define a union that contains an integer and a character array to allow it to be interpreted as an integer and as a character array when you need to pass it to fwrite() for example. See the following code snippet:

Listing 3.7 Unions Example 4

typedef union myval{
	int i;
	char str[4];
};

In addition, unions offer you more performance than casts. Moreover, your code will be more readable and efficient when you use unions.

More on how unions are laid-out into memory in chapter 6.

How to Marshal a Union

You can marshal a union the same way as you marshal structures, except that because of the way that unions laid-out into memory, you will need to explicitly set variable positions inside the type.

Follow these steps to marshal a union:

  1. Create your marshaling type, no matter whether your marshaling type is a managed structure or class. Again, classes require special handling when passed as function arguments. Passing mechanism is covered soon.
  2. Decorate the type with the StructLayoutAttribute attribute specifying LayoutKind.Explicit for the explicit layout kind.
  3. Add the type fields. Do not add fields other than those defined in the unmanaged signature. Because we are controlling the type layout explicitly, order of fields is not important.
  4. Decorate every field with the FieldOffsetAttribute attribute specifying the absolute position in bytes of the member from the start of the type.

The following example demonstrates how to marshal our SOME_CHARACTER union.

Listing 3.8 SOME_CHARACTER Managed Signature

    // Unions require explicit memory layout
    [StructLayout(LayoutKind.Explicit)]
    //public class SOME_CHARACTER
    public struct SOME_CHARACTER
    {
        // Both members located on the same
        // position in the beginning of the union

        // This is the continer it is 4 bytes
        [FieldOffset(0)]
        [MarshalAs(UnmanagedType.U4)]
        public int i;
        // This is only 1 byte. Therefore, it is contained
        [FieldOffset(0)]
        public char c;
    }

    public static void Main()
    {
        SOME_CHARACTER character = new SOME_CHARACTER();

        // The code for letter 'A'
        character.i = 65;
        // Should prints 'A'
        Console.WriteLine("c = {0}", character.c);

        character.c = 'B';
        // Should prints 66
        Console.WriteLine("i = {0}", character.i);
    }

From the last code, we learn that…

  • Unions are marshaled like structures, they can be marshaled as either managed structures or classes.
  • Setting StructLayoutAttribute.LayoutKind to LayoutKind.Explicit allows us to exactly control the memory location of our members.
  • We use the FieldOffsetAttribute to specify the starting location in bytes of the field into the type in memory.
  • To create the union between the fields, we set both the fields to the same memory location.
  • In the example, member i occupies byte 0 through byte 4, and member c occupies byte 0 through byte 1.
  • If we do not need the benefits of unions, we can omit member c because it is contained inside the range of member i. However, we cannot omit member c because it is the container.
  • When we change either one of the union variables, the other variable changes too because they share the same memory address.

Unions with Arrays

Another example of a union is as following:

Listing 3.9 UNION_WITH_ARRAY Unmanaged Signature

typedef union UNION_WITH_ARRAY
{
    INT number;
    CHAR charArray[128];
};

This union must be marshaled in a special way because managed code does not permit value types and reference types to overlap.

As a refresher, a value-type is the type stored in the memory stack; it inherits from System.ValueType. All primitive data types, structures, and enumerations are considered value-types. On the other hand, reference-types are those types stored in the memory heap; they inherit from System.Object. Most types in .NET are reference-types (except System.ValueType and its descendents of course.)

That is, all value-types inherit -directly or indirectly- from System.ValueType.

As a result, we cannot union both members of our example, because whether marshaling the second variable charArray as an array, a System.String, or as a System.Text.StringBuilder, it is still a reference-type. Therefore, we have to leave the benefits of unions and marshal only a single member. For our example, we will create two marshaling types for our union, one with the first member marshaled, and the other with the other member.

As we know, the layout and size of the type inside the memory is the most crucial. Therefore, we must preserve the layout and size of our union. This union has a 128 bytes array as a container and only one member contained, and this member is only 2-bytes. Therefore, we have two choices, to marshal the union with the container member, or to marshal it with the contained member but to extend it enough to be as large as the container. In this example, we will take the two approaches.

Try It Out!

The following are two code segments. The first demonstrates how to marshal only the second member which is the container, while the second demonstrates how to marshal the first member.

Listing 3.10 UNION_WITH_ARRAY Union Managed Signature

    // Setting StructLayoutAttribute.CharSet
    // ensures the correct encoding for all
    // string members of the union in our example
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
    //public struct UNION_WITH_ARRAY_1
    public struct UNION_WITH_ARRAY_1
    {
        // As we know, character arrays can be marshaled
        // as either an array or as a string

        // Setting MarshalAsAttribute is required
        // for the array and the string

        //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)]
        //public char[] charArray;

        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string charArray;
    }

    // StructLayoutAttribute.Size determines
    // the size -in bytes- of the type.
    // If the size specified is larger than
    // members' size, the last member will be extended
    // Because this is only a single
    // member, we laid it out sequentially.
    [StructLayout(LayoutKind.Sequential, Size = 128)]
    //public class UNION_WITH_ARRAY_2
    public struct UNION_WITH_ARRAY_2
    {
        [MarshalAs(UnmanagedType.I2)]
        public short number;
    }

For more information about marshaling arrays, refer to the next chapter.

Value-Types and Reference-Types

In the realm of .NET, types are broken into two categories:

  • Value-Types:
    These types are stored in the memory stack. They are destroyed when their scope ends, therefore, they are short-lived. Types of this category are all types inherit from System.ValueType (like all primitive data types, structures, and enumerations.)
  • Reference-Types:
    These types are stored in the memory heap. They are controlled by the Garbage Collector (GC,) therefore, they may retain in memory for a long while. Reference-types are all types -directly or indirectly- inherit from System.Object (except System.ValueType and descendants of course.) All .NET classes fall in this category.

Stack and heap! Confused? Check chapter 6 for more details.

Talking about value-types and reference-types leads us to talk about the passing mechanism. And that is what the next section is devoted for.

Passing Mechanism

In the last chapter, we have talked about the passing mechanism with simple types and how it affects the call. Actually, all we have learnt is applied to the compound types too.

As a refresher, when a type passed by value, a copy of type passed to the function, not the value itself. Therefore, any changes to the type inside the function do not affect the original copy. On the other hand, passing a type by reference passes a pointer to the value to the function. In other words, the value itself is passed. Therefore, any changes to the type inside the function are seen by the caller.

Functions require the type passed to be passed either by value or by reference. Plus, they require the argument to be passed by reference only if the argument will be changed inside.

Moreover, an argument passed by reference can be passed either as Input/Output (In/Out) or Output (Out). In/Out arguments used by the function for receiving the input from the caller and posting the changes back to him. Therefore, In/Out arguments must be initialized before handing them to the function. On the other hand, output (Out) arguments are only used for returning output to the caller. Therefore, they do not require pre-initialization because the function will initialize them.

All of the information learnt from the last chapter is applied to this chapter too.

Compound types also can be passed by value or by reference. When passing by value, no changes need to be applied. On the other hand passing a type by reference requires some changes to the PInvoke method and the call itself.

If you are marshaling as a structure, you may add the ref modifier to the parameter. However, classes are -by default- reference-types. Thus, they are normally passed by reference and they cannot be passed by value. Therefore, they do not need the ref modifier.

On the other hand, if you are passing the type as output (Out,) you will need to add the out modifier whether it is a structure or a class.

As you know, you can decorate In/Out arguments with both InAttribute and OutAttribute attributes. For Out arguments, specify OutAttribute attribute only.

Notice that there is a big difference between managed and unmanaged classes. Unmanaged classes are -by default- value-types. Manager classes are reference-types.

The following example demonstrates the PInvoke method for the function GetVersionEx(). This function requires a single In/Out argument. That argument is of the type OSVERSIONINFO.

The function uses OSVERSIONINFO’s dwOSVersionInfoSize field as input from the caller for determining the type size, and it uses the remaining arguments as output for sending the version information back. Therefore, the function requires the argument to be passed by reference as In/Out.

Next is the definition of the function along with the structure:

Listing 3.11 GetVersionEx() Unmanaged Signature

BOOL GetVersionEx(
  OSVERSIONINFO lpVersionInfo
);

typedef struct OSVERSIONINFO{
  DWORD dwOSVersionInfoSize;
  DWORD dwMajorVersion;
  DWORD dwMinorVersion;
  DWORD dwBuildNumber;
  DWORD dwPlatformId;
  TCHAR szCSDVersion[128];
};

In addition, this is the managed version with the text code:

Listing 3.12 Retrieving System Version Information Sample

    [DllImport("Kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetVersionEx
        ([param: In, Out]
        // If a class remove the "ref" keyword
        ref OSVERSIONINFO lpVersionInfo);

    [StructLayout(LayoutKind.Sequential)]
    //public class OSVERSIONINFO
    public struct OSVERSIONINFO
    {
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dwOSVersionInfoSize;
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dwMajorVersion;
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dwMinorVersion;
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dwBuildNumber;
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dwPlatformId;

        // Can be marshaled as an array too
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
        public string szCSDVersion;
    }

    static void Main()
    {
        OSVERSIONINFO info = new OSVERSIONINFO();
        info.dwOSVersionInfoSize = (uint)Marshal.SizeOf(info);

        //GetVersionEx(info);
        GetVersionEx(ref info);

        Console.WriteLine("System Version: {0}.{1}",
            info.dwMajorVersion, info.dwMinorVersion);
    }

More about the passing mechanism in chapter 6.

Compound Types and Character Encoding

As you know, the size and layout of the marshaling type is the most important. If the compound type contains a textual data, sure special handling should be taken to ensure correct marshaling of the data.

You already know that the character encoding can be either ANSI or Unicode.

When a string is ANSI-encoded, every character reserves only a single byte of application memory. On the other hand, every character in a Unicode-encoded string reserves two bytes of the memory. Therefore, a string like €œC-Sharp€ with 7 characters reserves 7 bytes if ANSI-encoded and 14 bytes if Unicode-encoded.

You can determine the character encoding of the compound type by specifying the CharSet property of the StructLayoutAttribute attribute. This property can take one of several values:

  • CharSet.Auto (CLR Default):
    Strings encoding varies based on operating system; it is Unicode-encoded on Windows NT and ANSI-encoded on other versions of Windows.
  • CharSet.Ansi (C# Default):
    Strings are always 8-bit ANSI-encoded.
  • CharSet.Unicode:
    Strings are always 16-bit Unicode-encoded.
  • CharSet.None:
    Obsolete. Has the same behavior as CharSet.Ansi.

Take into consideration that if you have not set the CharSet property, CLR automatically sets it to CharSet.Auto. However, some languages override the default behavior. For example, C# defaults to CharSet.Ansi.

In addition, you can determine the character encoding at a granular level by specifying the CharSet property of the MarshalAsAttribute attribute applied to the member.

Real-World Examples

The DEVMODE Structure

Now, we are going to dig into real-world examples. In the first example, we are going to marshal one of the most complex compound structures in the Windows API, it is the DEVMODE structure.

If you have worked with GDI, you will be somewhat familiar with this structure. It encapsulates information about initialization and environment of a printer or a display device. It is required by many functions like EnumDisplaySettings(), ChangeDisplaySettings() and OpenPrinter().

The complexity of this structure comes because of few factors. Firstly, there are unions defined inside the structure. In addition, the definition of this structure defers from a platform to another. As we will see, the structure defines some members based on the operating system.

Here is the definition of DEVMODE structure along with the POINTL structure that is referenced by DEVMODE.

Listing 3.13 DEVMODE and POINTL Unmanaged Signature

typedef struct DEVMODE {
  BCHAR  dmDeviceName[CCHDEVICENAME];
  WORD   dmSpecVersion;
  WORD   dmDriverVersion;
  WORD   dmSize;
  WORD   dmDriverExtra;
  DWORD  dmFields;
  union {
    struct {
      short dmOrientation;
      short dmPaperSize;
      short dmPaperLength;
      short dmPaperWidth;
      short dmScale;
      short dmCopies;
      short dmDefaultSource;
      short dmPrintQuality;
    };
    POINTL dmPosition;
    DWORD  dmDisplayOrientation;
    DWORD  dmDisplayFixedOutput;
  };
  short  dmColor;
  short  dmDuplex;
  short  dmYResolution;
  short  dmTTOption;
  short  dmCollate;
  BYTE  dmFormName[CCHFORMNAME];
  WORD  dmLogPixels;
  DWORD  dmBitsPerPel;
  DWORD  dmPelsWidth;
  DWORD  dmPelsHeight;
  union {
    DWORD  dmDisplayFlags;
    DWORD  dmNup;
  }
  DWORD  dmDisplayFrequency;
#if(WINVER >;= 0x0400)
  DWORD  dmICMMethod;
  DWORD  dmICMIntent;
  DWORD  dmMediaType;
  DWORD  dmDitherType;
  DWORD  dmReserved1;
  DWORD  dmReserved2;
#if (WINVER >;= 0x0500) || (_WIN32_WINNT >;= 0x0400)
  DWORD  dmPanningWidth;
  DWORD  dmPanningHeight;
#endif
#endif /* WINVER >;= 0x0400 */
};
typedef struct POINTL {
  LONG x;
  LONG y;
};

You might have noticed that two unions are defined inside the structure. In addition, a structure is defined inside the first union! Moreover, the last 8 members are not supported in Windows NT. Plus, the very last two members, dmPanningWidth and dmPanningHeight, are not supported in Windows 9x (95/98/ME.)

When working with Windows API, you should take care of operating system compatibility. Some functions, for instance, are not supported on certain operating systems (e.g. most Unicode versions are not supported on Win9x.) Other functions take arguments that vary based on the OS (i.e. EnumPrinters() function.) If your application tried to call a function, for instance, that is not supported by the current operating system, the call would fail.

If you need your application to be portable to every platform, you will need to create three versions of the structure, one for Windows ME and its ascendants, one for Windows NT, and the last for Windows 2000 and higher versions. In addition, you will need to create three overloads of every function require DEVMODE structure; three overloads for the three structures. For the sake of simplicity, we will assume that you are working with Windows 2000 or a higher version. Thus, we will marshal all members of the structure.

The following is the managed version of both DEVMODE and POINTL structures:

Listing 3.14 DEVMODE and POINTL Managed Signature

    // Setting StructLayout.LayoutKind to LeyoutKind.Explicit to allow
    // precisely choosing of member position. It is required for unions
    // This structure is 156-bytes
    [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Ansi)]
    //public class DEVMODE
    public struct DEVMODE
    {
        // You can define the following constant
        // BUT OUTSIDE THE STRUCTURE
        // because you know that size and layout of the structure
        // is very important
        // CCHDEVICENAME = 32 = 0x50
        [FieldOffset(0)]
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
        public Char[] dmDeviceName;
        // In addition you can define the last character array
        // as following:
        //MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
        //public string dmDeviceName;

        // After the 32-bytes array
        [FieldOffset(32)]
        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmSpecVersion;

        [FieldOffset(34)]
        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmDriverVersion;

        [FieldOffset(36)]
        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmSize;

        [FieldOffset(38)]
        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmDriverExtra;

        [FieldOffset(40)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmFields;

        // ************ Union Start ************
        // Because DEVMODE_PRINT_SETTINGS is the hugest member and it is
        // 16-bytes, it is the container for other members
        // Remeber, you cannot emit the container
        [FieldOffset(44)]
        public DEVMODE_PRINT_SETTINGS dmSettings;

        // Positioned within DEVMODE_PRINT_SETTINGS
        // It is 8-bytes only
        [FieldOffset(44)]
        public POINTL dmPosition;

        // Positioned within DEVMODE_PRINT_SETTINGS
        [FieldOffset(44)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayOrientation;

        // Positioned within DEVMODE_PRINT_SETTINGS
        [FieldOffset(44)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayFixedOutput;
        // ************* Union End *************

        // Because DEVMODE_PRINT_SETTINGS structure
        // is 16-bytes, dmColor is positioned on byte 60
        [FieldOffset(60)]
        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmColor;

        [FieldOffset(62)]
        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmDuplex;

        [FieldOffset(64)]
        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmYResolution;

        [FieldOffset(66)]
        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmTTOption;

        [FieldOffset(70)]
        [MarshalAs(UnmanagedType.I2)]
        public Int16 dmCollate;

        // CCHDEVICENAME = 32 = 0x50
        [FieldOffset(72)]
        [MarshalAs(UnmanagedType.ByValArray,
            SizeConst = 32,
            ArraySubType = UnmanagedType.U1)]
        public Byte[] dmFormName;

        // After the 32-bytes array
        [FieldOffset(102)]
        [MarshalAs(UnmanagedType.U2)]
        public UInt16 dmLogPixels;

        [FieldOffset(104)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmBitsPerPel;

        [FieldOffset(108)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPelsWidth;

        [FieldOffset(112)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPelsHeight;

        // ************ Union Start ************
        // Because both members are 4-bytes, the union is 4-bytes
        // and its members are overlapped
        // Again, you cannot emit the container
        // Except if both are equal, you can emit anyone of them
        [FieldOffset(116)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayFlags;

        [FieldOffset(116)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmNup;
        // ************* Union End *************

        [FieldOffset(120)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDisplayFrequency;

        [FieldOffset(124)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmICMMethod;

        [FieldOffset(128)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmICMIntent;

        [FieldOffset(132)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmMediaType;

        [FieldOffset(136)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmDitherType;

        [FieldOffset(140)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmReserved1;

        [FieldOffset(144)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmReserved2;

        [FieldOffset(148)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPanningWidth;

        [FieldOffset(152)]
        [MarshalAs(UnmanagedType.U4)]
        public UInt32 dmPanningHeight;
    }

    // 16-bytes structure
    [StructLayout(LayoutKind.Sequential)]
    //public class DEVMODE_PRINT_SETTINGS
    public struct DEVMODE_PRINT_SETTINGS
    {
        public short dmOrientation;
        public short dmPaperSize;
        public short dmPaperLength;
        public short dmPaperWidth;
        public short dmScale;
        public short dmCopies;
        public short dmDefaultSource;
        public short dmPrintQuality;

    }

    // 8-bytes structure
    [StructLayout(LayoutKind.Sequential)]
    //public class POINTL
    public struct POINTL
    {
        public Int32 x;
        public Int32 y;
    }

Lengthy, isn’t it? DEVMODE is one of the lengthy and compound GDI structures. If you want to learn more about laying out structure into memory, refer to chapter 6 €œMemory Management.€

From the last code we learn that€¦

  • Whether the union defined as a single entity or inside a structure, you will need to lay-out the type explicitly into memory to allow defining two or more variables at the same memory location.
  • When setting the memory layout explicitly, we apply the FieldOffsetAttribute attribute to the variable specifying the location -in bytes- of the variable from the start of the type.
  • In the union that defines a structure inside, we marshaled the structure outside the union and referred it to be the container of other members. Chapter 6 demonstrates other techniques for laying-out structures into memory.

Working with Display Settings

The follows example shows how you can access and modify display settings programmatically using C# and Windows API. In this example we will create four functions, one retrieves current display settings, another enumerates available display modes, the third changes current display settings, and the last changes screen orientation (i.e. rotates the screen.)

For our example, we will use the DEVMODE and POINTL structures that we have marshaled previously. In addition, we will make use of two new Windows API functions, EnumDisplaySettings and ChangeDisplaySettings. The following is the unmanaged signature of both functions:

Listing 3.15 EnumDisplaySettings() and ChangeDisplaySettings() Unmanaged Signature

BOOL EnumDisplaySettings(
  LPCTSTR lpszDeviceName,            // display device
  DWORD iModeNum,                    // graphics mode
  [In, Out] LPDEVMODE lpDevMode      // graphics mode settings
);

LONG ChangeDisplaySettings(
  LPDEVMODE lpDevMode,               // graphics mode
  DWORD dwflags                      // graphics mode options
);

For more information about these functions, refer to the MSDN documentation.

The next is the managed version of the functions:

Listing 3.16 EnumDisplaySettings() and ChangeDisplaySettings() Managed Signature

    [DllImport("User32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern Boolean EnumDisplaySettings(
        [param: MarshalAs(UnmanagedType.LPTStr)]
        string lpszDeviceName,
        [param: MarshalAs(UnmanagedType.U4)]
        int iModeNum,
        [In, Out]
        ref DEVMODE lpDevMode);

    [DllImport("User32.dll")]
    [return: MarshalAs(UnmanagedType.I4)]
    public static extern int ChangeDisplaySettings(
        [In, Out]
        ref DEVMODE lpDevMode,
        [param: MarshalAs(UnmanagedType.U4)]
        uint dwflags);

Finally, those are our four functions that utilize the native functions:

Listing 3.17 Accessing/Modifying Display Settings Sample

    public static void GetCurrentSettings()
    {
        DEVMODE mode = new DEVMODE();
        mode.dmSize = (ushort)Marshal.SizeOf(mode);

        if (EnumDisplaySettings(null,
            ENUM_CURRENT_SETTINGS, ref mode) == true) // Succeeded
        {
            Console.WriteLine("Current Mode:nt" +
                "{0} by {1}, {2} bit, {3} degrees, {4} hertz",
                mode.dmPelsWidth, mode.dmPelsHeight,
                mode.dmBitsPerPel, mode.dmDisplayOrientation * 90,
                mode.dmDisplayFrequency);
        }
    }

    public static void EnumerateSupportedModes()
    {
        DEVMODE mode = new DEVMODE();
        mode.dmSize = (ushort)Marshal.SizeOf(mode);

        int modeIndex = 0; // 0 = The first mode

        Console.WriteLine("Supported Modes:");

        while (EnumDisplaySettings(null,
            modeIndex, ref mode) == true) // Mode found
        {
            Console.WriteLine("t{0} by {1}, {2} bit, " +
                "{3} degrees, " +
                "{4} hertz",
                mode.dmPelsWidth, mode.dmPelsHeight,
                mode.dmBitsPerPel, mode.dmDisplayOrientation * 90,
                mode.dmDisplayFrequency);

            modeIndex++; // The next mode
        }
    }

    public static void ChangeDisplaySettings
        (int width, int height, int bitCount)
    {
        DEVMODE originalMode = new DEVMODE();
        originalMode.dmSize = (ushort)Marshal.SizeOf(originalMode);

        // Retrieving current settings to edit them
        EnumDisplaySettings(null, ENUM_CURRENT_SETTINGS, ref originalMode);

        // Making a copy of the current settings
        // to allow reseting to the original mode
        DEVMODE newMode = originalMode;

        // Changing the settings
        newMode.dmPelsWidth = (uint)width;
        newMode.dmPelsHeight = (uint)height;
        newMode.dmBitsPerPel = (uint)bitCount;

        // Capturing the operation result
        int result = ChangeDisplaySettings(ref newMode, 0);

        if (result == DISP_CHANGE_SUCCESSFUL)
        {
            Console.WriteLine("Succeeded.n");

            // Inspecting the new mode
            GetCurrentSettings();

            Console.WriteLine();

            // Waiting for seeing the results
            Console.ReadKey(true);

            ChangeDisplaySettings(ref originalMode, 0);
        }
        else if (result == DISP_CHANGE_BADMODE)
            Console.WriteLine("Mode not supported.");
        else if (result == DISP_CHANGE_RESTART)
            Console.WriteLine("Restart required.");
        else
            Console.WriteLine("Failed. Error code = {0}", result);
    }

    public static void RotateScreen(bool clockwise)
    {
        // Retrieving current settings
        // ...

        // Rotating the screen
        if (clockwise)
            if (newMode.dmDisplayOrientation <; DMDO_270)
                newMode.dmDisplayOrientation++;
            else
                newMode.dmDisplayOrientation = DMDO_DEFAULT;
         else
            if (newMode.dmDisplayOrientation >; DMDO_DEFAULT)
                newMode.dmDisplayOrientation--;
            else
                newMode.dmDisplayOrientation = DMDO_270;

        // Swapping width and height;
        uint temp = newMode.dmPelsWidth;
        newMode.dmPelsWidth = newMode.dmPelsHeight;
        newMode.dmPelsHeight = temp;

        // Capturing the operation result
        // ...
    }

the Console Library

There are functionalities of console applications that are not accessible from the .NET Framework like clearing the console screen and moving a text around.

The following sample shows a tiny library for console applications. It contains some of the common functionalities of the console (like writing and reading data) along with new functionalities added.

Listing 3.18 The Console Library Sample

SafeNativeMethods.cs

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

/// <summary>
/// Safe native functions
/// </summary>
internal static class SafeNativeMethods
{
    /// <summary>
    /// Standard input device.
    /// </summary>
    public const int STD_INPUT_HANDLE = -10;
    /// <summary>
    /// Standard output device.
    /// </summary>
    public const int STD_OUTPUT_HANDLE = -11;
    /// <summary>
    /// Standard error device (usually the output device.)
    /// </summary>
    public const int STD_ERROR_HANDLE = -12;

    /// <summary>
    /// White space character for clearing the screen.
    /// </summary>
    public const char WHITE_SPACE = ' ';

    /// <summary>
    /// Retrieves a handle for the console standard input, output, or error device.
    /// </summary>
    /// <param name="nStdHandle">The standard device of which to retrieve handle for.</param>
    /// <returns>The handle for the standard device selected.
    /// Or an invalid handle if the function failed.</returns>
    [DllImport("Kernel32.dll")]
    public static extern IntPtr GetStdHandle([param: MarshalAs(UnmanagedType.I4)] int nStdHandle);

    /// <summary>
    /// Writes a character string to the console buffer starting from the current cursor position.
    /// </summary>
    /// <param name="hConsoleOutput">A handle for the opened output device.</param>
    /// <param name="lpBuffer">The string of which to write.</param>
    /// <param name="nNumberOfCharsToWrite">Number of characters to write.</param>
    /// <param name="lpNumberOfCharsWritten">Outputs the number of characters written.</param>
    /// <param name="lpReserved">Reserved.</param>
    /// <returns>True if succeeded, otherwise False.</returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool WriteConsole
        (IntPtr hConsoleOutput,
        string lpBuffer,
        [param: MarshalAs(UnmanagedType.U4)] uint nNumberOfCharsToWrite,
        [param: MarshalAs(UnmanagedType.U4)] [Out] out uint lpNumberOfCharsWritten,
        [param: MarshalAs(UnmanagedType.U4)]
        uint lpReserved);

    /// <summary>
    /// Read a character string from the console buffer starting from the current cursor position.
    /// </summary>
    /// <param name="hConsoleInput">A handle for the opened input device.</param>
    /// <param name="lpBuffer">The string read from the buffer.</param>
    /// <param name="nNumberOfCharsToRead">The number of characters to read.</param>
    /// <param name="lpNumberOfCharsRead">Outputs the number of characters read.</param>
    /// <param name="lpReserved">Reserved.</param>
    /// <returns>True if succeeded, otherwise False.</returns>
    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool ReadConsole(
        IntPtr hConsoleInput,
        StringBuilder lpBuffer,
        [param: MarshalAs(UnmanagedType.U4)] uint nNumberOfCharsToRead,
        [param: MarshalAs(UnmanagedType.U4)] [Out] out uint lpNumberOfCharsRead,
        [param: MarshalAs(UnmanagedType.U4)] uint lpReserved);

    /// <summary>
    /// Retrieves information about the console cursor such as the size and visibility.
    /// </summary>
    /// <param name="hConsoleOutput">A handle for the opened output device.</param>
    /// <param name="lpConsoleCursorInfo">The cursor info.</param>
    /// <returns>True if succeeded, otherwise False.</returns>
    [DllImport("kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetConsoleCursorInfo(
        IntPtr hConsoleOutput,
        [Out] out CONSOLE_CURSOR_INFO lpConsoleCursorInfo);

    /// <summary>
    /// Sets the console cursor properties as the size and visibility.
    /// </summary>
    /// <param name="hConsoleOutput">A handle for the opened output device.</param>
    /// <param name="lpConsoleCursorInfo">The cursor info.</param>
    /// <returns>True if succeeded, otherwise False.</returns>
    [DllImport("kernel32.dll")]

    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool SetConsoleCursorInfo(
        IntPtr hConsoleOutput,
        ref CONSOLE_CURSOR_INFO lpConsoleCursorInfo);

    /// <summary>
    /// Moves a block of data in a screen buffer.
    /// </summary>
    /// <param name="hConsoleOutput">A handle for the opened output device.</param>
    /// <param name="lpScrollRectangle">The coordinates of the block to move.</param>
    /// <param name="lpClipRectangle">The coordinates affected by the scrolling.</param>
    /// <param name="dwDestinationOrigin">The coordinates represents
    /// the new location of the block.</param>
    /// <param name="lpFill">Specifies the character and color info for the cells
    /// left empty after the move.</param>
    /// <returns>True if succeeded, otherwise False.</returns>
    /// <remarks>
    /// Because we are going to set the <paramref name="lpClipRectangle"/> to NULL,
    /// we marshaled it as IntPtr so we can set it to null using IntPtr.Zero.
    /// If you do need to set its value, you can marshal it as SMALL_RECT.
    /// </remarks>
    [DllImport("kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool ScrollConsoleScreenBuffer(
        IntPtr hConsoleOutput,
        ref SMALL_RECT lpScrollRectangle,
        IntPtr lpClipRectangle,
        COORD dwDestinationOrigin,
        ref CHAR_INFO lpFill);

    /// <summary>
    /// Retrieves information about the specified console screen buffer.
    /// </summary>
    /// <param name="hConsoleOutput">A handle for the device of which to get its
    /// information.</param>
    /// <param name="lpConsoleScreenBufferInfo">Outputs the information of the
    /// specified screen buffer.</param>
    /// <returns>True if succeeded, otherwise False.</returns>
    [DllImport("Kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool GetConsoleScreenBufferInfo
        (IntPtr hConsoleOutput,
        [Out] out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);

    /// <summary>
    /// Fills the console buffer with a specific character.
    /// </summary>
    /// <param name="hConsoleOutput">A handle for the opened output device.</param>
    /// <param name="cCharacter">The character of which to fill the buffer width.
    /// Setting this character to a white space means clearing the cells.</param>
    /// <param name="nLength">The number of cells to fill.</param>
    /// <param name="dwWriteCoord">The location of which to start filling.</param>
    /// <param name="lpNumberOfCharsWritten">Outputs the number of characters written.</param>
    /// <returns>True if succeeded, otherwise False.</returns>
    [DllImport("Kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool FillConsoleOutputCharacter
        (IntPtr hConsoleOutput,
        char cCharacter,
        [param: MarshalAs(UnmanagedType.U4)] uint nLength,
        COORD dwWriteCoord,
        [param: MarshalAs(UnmanagedType.U4)][Out] out uint lpNumberOfCharsWritten);

    /// <summary>
    /// Sets the console cursor to a specific position.
    /// </summary>
    /// <param name="hConsoleOutput">A handle for the opened output device.</param>
    /// <param name="dwCursorPosition">The new cursor position inside the console buffer.</param>
    /// <returns>True if succeeded, otherwise False.</returns>
    [DllImport("Kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool SetConsoleCursorPosition
        (IntPtr hConsoleOutput, COORD dwCursorPosition);
}

/// <summary>
/// Information about the screen buffer.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    /// <summary>
    /// The size of the buffer.
    /// </summary>
    public COORD dwSize;
    /// <summary>
    /// The location of the cursor inside the buffer.
    /// </summary>
    public COORD dwCursorPosition;
    /// <summary>
    /// Additional attributes about the buffer write the fore color and back color.
    /// </summary>
    [MarshalAs(UnmanagedType.U2)]
    public ushort wAttributes;
    /// <summary>
    /// The location and bounds of the window.
    /// </summary>
    public SMALL_RECT srWindow;
    /// <summary>
    /// The maximum size of the window.
    /// </summary>
    public COORD dwMaximumWindowSize;
}

/// <summary>
/// Coordinates (X, Y).
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
    /// <summary>
    /// The location from the left (X).
    /// </summary>
    [MarshalAs(UnmanagedType.I2)]
    public short X;
    /// <summary>
    /// The location from the top (Y).
    /// </summary>
    [MarshalAs(UnmanagedType.I2)]
    public short Y;
}

/// <summary>
/// Defines the coordinates of the upper left and right bottom coordinates of a rectangle.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
    /// <summary>
    /// The X-coordinate of the upper left corner of the rectangle.
    /// </summary>
    [MarshalAs(UnmanagedType.I2)]
    public short Left;
    /// <summary>
    /// The Y-coordinate of the upper left corner of the rectangle.
    /// </summary>
    [MarshalAs(UnmanagedType.I2)]
    public short Top;
    /// <summary>
    /// The X-coordinate of the lower right corner of the rectangle.
    /// </summary>
    [MarshalAs(UnmanagedType.I2)]
    public short Right;
    /// <summary>
    /// The Y-coordinate of the lower right corner of the rectangle.
    /// </summary>
    [MarshalAs(UnmanagedType.I2)]
    public short Bottom;
}

/// <summary>
/// Defines the console cursor info.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_CURSOR_INFO
{
    /// <summary>
    /// The size of the cursor. Usually 0.25 of the cell.
    /// </summary>
    [MarshalAs(UnmanagedType.U4)]
    public uint dwSize;
    /// <summary>
    /// If cursor is visible or not.
    /// </summary>
    [MarshalAs(UnmanagedType.Bool)]
    public bool bVisible;
}

/// <summary>
/// Defines a character information.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct CHAR_INFO
{
    /// <summary>
    /// The character.
    /// </summary>
    public char Char;
    /// <summary>
    /// Additional attributes of the character like fore color and back color.
    /// </summary>
    [MarshalAs(UnmanagedType.U2)]
    public ushort Attributes;
}

ConsoleLib.cs

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

// Console horizontal text alignment.
public enum ConsoleTextAlignment
{
    /// <summary>
    /// Text is left aligned.
    /// </summary>
    Left,
    /// <summary>
    /// Text is right aligned.
    /// </summary>
    Right,
    /// <summary>
    /// Text is centered.
    /// </summary>
    Center
}

/// <summary>
/// Console standard devices.
/// </summary>
public enum ConsoleStandardDevice
{
    /// <summary>
    /// The input device.
    /// </summary>
    Input = SafeNativeMethods.STD_INPUT_HANDLE,
    /// <summary>
    /// The output device.
    /// </summary>
    Output = SafeNativeMethods.STD_OUTPUT_HANDLE,
    /// <summary>
    /// The error device (usually the output device.)
    /// </summary>
    Error = SafeNativeMethods.STD_ERROR_HANDLE
}

/// <summary>
/// Extension methods for the console.
/// </summary>
public static class ConsoleExtensions
{
    /// <summary>
    /// Clears the screen buffer.
    /// </summary>
    public static void ClearScreen()
    {
        // Clearing the screen starting from the first cell
        COORD location = new COORD();
        location.X = 0;
        location.Y = 0;

        ClearScreen(location);
    }
    /// <summary>
    /// Clears the screen buffer starting from a specific location.
    /// </summary>
    /// <param name="location">The location of which to start clearing
    /// the screen buffer.</param>
    public static void ClearScreen(COORD location)
    {
        // Clearing the screen starting from the specified location
        // Setting the character to a white space means clearing it
        // Setting the count to 0 means clearing to the end, not a specific length
        FillConsoleBuffer(location, 0, SafeNativeMethods.WHITE_SPACE);
    }

    /// <summary>
    /// Fills a specific cells with a specific character starting from a specific location.
    /// </summary>
    /// <param name="location">The location of which to start filling.</param>
    /// <param name="count">The number of cells starting
    /// from the location to fill.</param>
    /// <param name="character">The character to fill with.</param>
    public static void FillConsoleBuffer(COORD location, uint count, char character)
    {
        // Getting the console output device handle
        IntPtr handle = GetStandardDevice(ConsoleStandardDevice.Output);

        uint length;

        // If count equals 0 then user require clearing all the screen
        if (count == 0)
        {
            // Getting console screen buffer info
            CONSOLE_SCREEN_BUFFER_INFO info = GetBufferInfo(ConsoleStandardDevice.Output);
            // All the screen
            length = (uint)(info.dwSize.X * info.dwSize.Y);
        }
        else
            length = count;

        // The number of written characters
        uint numChars;

        // Calling the Win32 API function
        SafeNativeMethods.FillConsoleOutputCharacter(handle, character,
            length, location, out numChars);

        // Setting the console cursor position
        SetCursorPosition(location);
    }

    /// <summary>
    /// Rettrieves a handle for a specific device.
    /// </summary>
    /// <param name="device">The device of which to retrieve the handle for.</param>
    /// <returns>The handle for the specified device.</returns>
    public static IntPtr GetStandardDevice(ConsoleStandardDevice device)
    {
        // Calling the Win32 API function
        return SafeNativeMethods.GetStdHandle((int)device);
    }

    /// <summary>
    /// Writes an empty line to the console buffer on the current position of the cursor.
    /// </summary>
    public static void WriteLine()
    {
        WriteLine(string.Empty);
    }
    /// <summary>
    /// Writes specific text followed by a line terminator to the console buffer on
    /// the current position of the cursor.
    /// </summary>
    /// <param name="txt">The text to write.</param>
    public static void WriteLine(string txt)
    {
        WriteLine(txt, ConsoleTextAlignment.Left);
    }
    /// <summary>
    /// Writes specific text followed by a line terminator to the console buffer on the
    /// current position of the cursor with the specified line alignemnt.
    /// </summary>
    /// <param name="txt">The text to write.</param>
    /// <param name="alignment">The horizontal alignment of the text.</param>
    public static void WriteLine(string txt, ConsoleTextAlignment alignment)
    {
        Write(txt + Environment.NewLine, alignment);
    }
    /// <summary>
    /// Writes specific text to the console buffer on the current position of the cursor.
    /// </summary>
    /// <param name="txt">The text to write.</param>
    public static void Write(string txt)
    {
        Write(txt, ConsoleTextAlignment.Left);
    }
    /// <summary>
    /// Writes specific text to the console buffer on the current position of the cursor
    /// with the specified line alignment.
    /// </summary>
    /// <param name="txt">The text to write.</param>
    /// <param name="alignment">The horizontal alignment of the text.</param>
    public static void Write(string txt, ConsoleTextAlignment alignment)
    {
        if (alignment == ConsoleTextAlignment.Left)
            InternalWrite(txt);
        else
        {
            // Determining the location of which to begin writing
            CONSOLE_SCREEN_BUFFER_INFO info = GetBufferInfo(ConsoleStandardDevice.Output);

            COORD pos = new COORD();

            if (alignment == ConsoleTextAlignment.Right)
                pos.X = (short)(info.dwSize.X - txt.Length);
            else // Center
                pos.X = (short)((info.dwSize.X - txt.Length) / 2);

            pos.Y = info.dwCursorPosition.Y;

            // Changing the cursor position
            SetCursorPosition(pos);

            // Now writing on the current position
            InternalWrite(txt);
        }
    }
    /// <summary>
    /// Writing a specific text to the console output buffer starting from the
    /// current cursor position.
    /// </summary>
    /// <param name="txt">The text to write.</param>
    private static void InternalWrite(string txt)
    {
        // Required for the WriteConsole() function
        // It is the number of characters written
        uint count;
        // Getting the output handle
        IntPtr handle = GetStandardDevice(ConsoleStandardDevice.Output);
        // Calling the Win32 API function
        SafeNativeMethods.WriteConsole(handle, txt, (uint)txt.Length, out count, 0);
    }

    /// <summary>
    /// Shows or hides the cursor.
    /// </summary>
    /// <param name="show">Specifies whether to show the cursor or not.</param>
    public static void ShowCursor(bool show)
    {
        CONSOLE_CURSOR_INFO info;
        // Getting the output device
        IntPtr handle = GetStandardDevice(ConsoleStandardDevice.Output);

        // Getting the cursor info
        SafeNativeMethods.GetConsoleCursorInfo(handle, out info);

        // Determining the visibility of the cursor
        info.bVisible = show;

        // Setting the cursor info
        SafeNativeMethods.SetConsoleCursorInfo(handle, ref info);
    }

    /// <summary>
    /// Read the next line from the input device.
    /// </summary>
    /// <returns></returns>
    public static string ReadText()
    {
        // The buffer
        // Maximum number of characters is 256
        StringBuilder buffer = new StringBuilder(256);
        // Required for the function call
        uint count;
        // Getting the input device that's used for receiving user input
        SafeNativeMethods.ReadConsole(GetStandardDevice(ConsoleStandardDevice.Input), buffer,
            (uint)buffer.Capacity, out count, 0);
        // Returning the user input cutting up the line terminator
        return buffer.ToString().Substring(0, (int)(count - Environment.NewLine.Length));
    }

    /// <summary>
    /// Retrieves the buffer info of the specified device.
    /// </summary>
    /// <param name="device">The device of which to retrieve its information.</param>
    /// <returns>The buffer info of the specified device.</returns>
    public static CONSOLE_SCREEN_BUFFER_INFO GetBufferInfo(ConsoleStandardDevice device)
    {
        // Returning the handle for the selected device
        IntPtr handle = GetStandardDevice(device);

        // Getting console screen buffer information
        CONSOLE_SCREEN_BUFFER_INFO info;
        SafeNativeMethods.GetConsoleScreenBufferInfo(handle, out info);

        return info;
    }

    /// <summary>
    /// Sets the cursor position in the buffer.
    /// </summary>
    /// <param name="pos">The coordinates of which to move the cursor to.</param>
    public static void SetCursorPosition(COORD pos)
    {
        // Getting the console output device handle
        IntPtr handle = SafeNativeMethods.GetStdHandle(SafeNativeMethods.STD_OUTPUT_HANDLE);

        // Moving the cursor to the new location
        SafeNativeMethods.SetConsoleCursorPosition(handle, pos);
    }

    /// <summary>
    /// Writes the buffer information to the screen.
    /// </summary>
    /// <param name="info">The information of which to write.</param>
    public static void WriteBufferInfo(CONSOLE_SCREEN_BUFFER_INFO info)
    {
        // Discovering console screen buffer information
        WriteLine("Console Buffer Info:");
        WriteLine("--------------------");

        WriteLine("Cursor Position:");
        WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "t{0}, {1}",
            info.dwCursorPosition.X, info.dwCursorPosition.Y));

        // Is this information right?
        WriteLine("Maximum Window Size:");
        WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "t{0}, {1}",
            info.dwMaximumWindowSize.X,
            info.dwMaximumWindowSize.Y));

        // Is this information right?
        WriteLine("Screen Buffer Size:");
        WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture, "t{0}, {1}",
            info.dwSize.X, info.dwSize.Y));

        WriteLine("Screen Buffer Bounds:");
        WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
            "t{0}, {1}, {2}, {3}",
            info.srWindow.Left, info.srWindow.Top,
            info.srWindow.Right, info.srWindow.Bottom));

        WriteLine("--------------------");
    }

    /// <summary>
    /// Writes the specific text followed by a line terminator to the left and moves
    /// it to the far right.
    /// </summary>
    /// <param name="txt">The text of which to write.</param>
    public static void MoveText(string txt)
    {
        // First, writing the text
        WriteLine(txt);

        // Getting the handle for the output device
        IntPtr handle = GetStandardDevice(ConsoleStandardDevice.Output);

        // Getting the screen buffer info for the output device
        CONSOLE_SCREEN_BUFFER_INFO screenInfo = GetBufferInfo(ConsoleStandardDevice.Output);

        // Selecting the text to be moved
        SMALL_RECT rect = new SMALL_RECT();
        rect.Left = 0; // The 1st cell
        rect.Top = (short)(screenInfo.dwCursorPosition.Y - 1); // The row of the text
        rect.Bottom = (short)(rect.Top); // Only a single line

        while (true)
        {
            // Moving to the right
            rect.Right = (short)(rect.Left + (txt.Length - 1));

            // Do not move it nore if we are in the far right of the buffer
            if (rect.Right == (screenInfo.dwSize.X - 1))
                break;

            // The character to fill the empty cells created after the move with
            CHAR_INFO charInfo = new CHAR_INFO();
            charInfo.Char = SafeNativeMethods.WHITE_SPACE; // For clearing the cells

            // Calling the API function
            SafeNativeMethods.ScrollConsoleScreenBuffer(handle, ref rect, IntPtr.Zero,
                new COORD() { X = (short)(rect.Left + 1), Y = rect.Top }, ref charInfo);

            // Blocking the thread for the user to see the effect
            System.Threading.Thread.Sleep(100);

            // Moving the rectangle
            rect.Left++;
        }
    }
}

Summary

After all, you learned that compound types are unmanaged structures and unions, and they called compound because they consisted of other types.

You learned that compound types can be marshaled as either a managed structure or a class. In addition, you learned how to lay-out the type into memory.

Again and again, the memory layout and size of the type is very crucial.

After that, you have worked with unions and learned that unions are simply a group of multiple variables share the same memory. In fact, it is the same memory location that is shared by one or more variables. Therefore, bits are represents in several ways.

Now it is the time for arrays. The next chapter discusses what arrays are and how to marshal them.