Windows Vista File and Registry Virtualization

Enabling UAC (User Access Control) feature in Windows Vista, Administrator users in Windows Vista, by default, don’t have administrative privileges. Every Windows process has two security tokens associated with it, one with normal user privileges and one with admin privileges. With applications that require administrative privileges, the user can elevate the application to run with Administrator rights. And that process called Elevation.

As you expect, it’s the least-privilege principle well-recognized for security pros and people who use Linux.

User can elevate an application either by clicking “Run as Administrator” from the context menu of the application icon, or even by editing the Compatibility tab in the properties of the application file.
Also, while an application running it can ask the user to provide administrative permission to complete a specific operation (a good example is switching to the All Users mode in Task Manager).

Compatibility Options

Windows Vista keeps track of the compatibility options edited for an application by adding a compatibility flag to the registry at HKCUSoftwareMicrosoftWindows NTCurrentVersionAppCompatFlagsLayers.

Try changing any of the compatibility options for an application and see how Windows tracks that.

Because UAC feature of Windows Vista, it doesn’t allow users to access some folders like Program Files and Windows folder. Also it doesn’t allow them to access the registry without administrative permission.

But, there’re lots of applications that write lots of data to the Program Files folder for instance. And Windows Vista must keep them away from doing such these operations without administrative permission -you can imagine the amount of applications that require administrative privileges-. So to handle this dilemma, Windows Vista has a new technique called Virtualization.

When a program tries to write to the Program Files folder for instance, Windows Vista redirects it to a special virtual store so that the application can read/write data without generating errors (because of course it doesn’t have the permission).

As we would see in the next example Windows Vista uses this technique with registry too.

For folders, Virtualization called File Virtualization. For registry, it’s called Registry Virtualization.

File Virtualization

To see virtualization in action let’s try this example:

string programFiles =
    Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
string appDir = Path.Combine(programFiles, "MyApplication");

if (Directory.Exists(appDir) == false)
    Directory.CreateDirectory(appDir);

string file = Path.Combine(appDir, "SampleFile.txt");

File.WriteAllText(file, "Hello, World!");

When you run the example it doesn’t write to C:Program FilesMyApplication. Instead it writes to the Program Files virtual store in C:UsersAppDataLocalVirtualStoreProgram FilesMyApplication

Note that if you are running your Visual Studio instance in elevated mode and run your application it gets the elevated mode from Visual Studio. So you need to run it manually from its icon.

Try changing the application so it writes to Windows folder. And check the virtual store folder.

Registry Virtualization

Virtualization is not only done with folders but also with registry entries. If the application tries to write to the registry key Software in HKEY_LOCAL_MACHINE hive, it is redirected to the HKEY_CURRENT_USER hive. Instead of writing to HKLMSoftware{Manufacturer}, it writes to the registry Virtual Store HKCUSoftwareClassesVirtualStoreMACHINESOFTWARE{Manufacturer}.

File and registry virtualization is available only for 32-bit applications. This feature is not available for 64-bit applications on Windows Vista.

Don’t use virtualization as a feature of your application. It is better to fix your application than to write to Program Files folder and the HKLM hive without elevated user privileges. Redirection is only a temporary means to fix broken applications.

Creating Transacted Files

Lastly but not last, and after a long while, Windows Vista introduced a way to create transacted files or even to write to registry.

While market grows remarkably in the last years, its requirements increase as well. And every day you face a new problem that you must overcome to accommodate market requirements.

Transacted operations are one of the commonly demanded requirements by market.

While it’s true that you can easily do database operations transactionally, it’s not enough. Sometimes you will need to write files or make changes to registry in a transactional way.

With previous versions of Windows, you weren’t able to create a file transactionally without writing a huge amount of disorganized code to create it manually. You couldn’t use normal transactions or even COM+ Enterprise Services to create this type of transactions.

With Windows Vista you can easily create transacted files the common way you used to create normal files.

Unfortunately, from .NET 3.5 you cannot create transacted files. So you need to dive into API to create it.

Not Windows Vista only that supports this type of transactions, Windows Server 2008 also supports it.

Creating a transacted file function

To create a transacted file you can use the Kernel23.dll new function CreateFileTransacted.

This function takes the same arguments as the normal CreateFile function plus three more arguments that we are interested of the first one of them, the transaction handle that will be used while creating the file and writing to it.

The definition of this function in C is as follows:

HANDLE CreateFileTransacted(
    LPCTSTR lpFileName,
    DWORD dwDesiredAccess,
    DWORD dwShareMode,
    LPSECURITY_ATTRIBUTES lpSecurityAttributes,
    DWORD dwCreationDisposition,
    DWORD dwFlagsAndAttributes,
    HANDLE hTemplateFile,
    HANDLE hTransaction,
    PUSHORT pusMiniVersion,
    PVOID pExtendedParameter);

We are interested in 6 parameters of these 9.

  • lpFileName:
    The path of the file being created.
  • dwDesiredAccess:
    Defines how file will be accessed. Read only (0x80000000), right only (0x40000000), or both read and write (0xC0000000).
  • dwShareMode:
    Defines the sharing mode -during the operation- for the file. Enabling reading file contents (0x1), writing to it (0x2), reading and writing (0x3), or deleting the file (0x4).
  • dwCreationDisposition:
    Action to take if file exist or do not exist. This argument can take one of the values:
    0x1: Create the file and throw an error if it exists.
    0x2: Always create the file even if it exists.
    0x3: Open the file and throw an error if it doesn’t exist.
    0x4: Open the file or create it if it doesn’t exist.
    0x5: Open the file and clear its contents. Or throw an error if it doesn’t exist.
  • dwFlagsAndAttributes:
    Any additional options. This can be a combination of file attributes like Archive, Hidden, and Encrypted. Also it supports combining options like deleting the file after closing it.
  • hTransaction:
    A handle to our created transaction that we wish to use it in this operation. To get the handle you may take a step further into COM.

Also there’re three arguments that we are not interested on, so you can safely pass it a NULL value:

  • lpSecurityAttributes:
    A SECURITY_ATTRIBUTES object contains an optional security descriptor information for the file.
  • hTemplateFile:
    A handle to a file to read it’s attributes and options to fill the arguments automatically.
  • pusMiniVersion:
    The miniversion to be opened. When creating transacted file -by specifying the hTransaction argument, this must be NULL.
  • pExtendedParameter:
    Reserved.

For a complete discussion of this API function, see MSDN Library.

PInvoking CreateFileTransacted API function

PInvoke is a service that enables you to call unmanaged functions in DLLs, such as those in the Win32 API like the CreateFileTransacted API function last mentioned.

PInvoke stands for Platform Invokation.

In order to PInvoke a function you need to know some things:

  1. Where this function resides (which DLL).
  2. Function definition and order of arguments -if found.-
  3. How to marshal between unmanaged types in .NET types.

Marshaling in .NET is the process of creating a bridge between new .NET types and legacy COM types. This is done by finding equivalents for those legacy types in the new data types or creating it if needed.

Most of the COM data types have equivalents in .NET, also it’s very easy to create your own.

For the function on hands we could write it in C# as following:

[DllImport("kernel32.dll")]
    public static extern
    IntPtr CreateFileTransacted(
    string lpFileName,
    uint dwDesiredAccess,
    uint dwShareMode,
    IntPtr lpSecurityAttributes,
    uint dwCreationDisposition,
    uint dwFlagsAndAttributes,
    IntPtr hTemplateFile,
    IntPtr hHandle,
    IntPtr pusMiniVersion,
    IntPtr pExtendedParameter);

Code explanation:
static extern modifiers are required for creating PInvoke functions.
DllImport attribute used to define the DLL that contains the function.
System.IntPtr is the managed type equivalent to unmanaged HANDLE.
LPCSTR can be easily replaced by the managed System.String.
In unmanaged code DWORD is a 32-bit unsigned integer, so we could safely replace it with System.UInt32.

Because we need to pass a NULL value to the LPSECURITY_ATTRIBUTES argument we marshaled it as IntPtr, so we can use IntPtr.Zero to pass NULL values.
We did that too with the last two arguments.

For creating the PInvoke methods easily, you could use the Red Gate’s PInvoke.NET Visual Studio add-in.
Also you should visit the interop wiki.

After we create our PInvoke method we can call it. But after that we need to create the transaction and get its handle to fill the arguments of the method.

To get a transaction handle we need to dive into COM and call a method to get the handle. This method is wrapped to .NET using COM Interop.

[ComImport]
[Guid("79427A2B-F895-40e0-BE79-B57DC82ED231")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IKernelTransaction
{ void GetHandle(out IntPtr ktmHandle); }

Don’t forget adding a using System.Runtime.InteropServices statement.

Now we can call the method:

private const uint FILE_ATTRIBUTE_NORMAL = 0x80;
private const uint GENERIC_WRITE = 0x40000000;
private const uint CREATE_ALWAYS = 0x2;
private const uint FILE_SHARE_NONE = 0x0;

static void Main()
{
    using (TransactionScope scope = new TransactionScope())
    using (FileStream stream = CreateTransactedFile("D:SampleFile.txt"))
    using (StreamWriter writer = new StreamWriter(stream))
    {
        writer.WriteLine("Hello, World!");

        // To commit the transaction use the followind line
        scope.Complete();
    }
}

public static FileStream CreateTransactedFile(string fileName)
{
    IKernelTransaction ktx = (IKernelTransaction)
        TransactionInterop.GetDtcTransaction(Transaction.Current);

    IntPtr txHandle;
    ktx.GetHandle(out txHandle);

    IntPtr fileHandle =
        CreateFileTransacted(fileName,
        GENERIC_WRITE, FILE_SHARE_NONE, IntPtr.Zero,
        CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
        IntPtr.Zero, txHandle, IntPtr.Zero, IntPtr.Zero);

    return new FileStream(fileHandle, FileAccess.Write);
}

Don’t forget adding a reference to System.Transactions and a using statement.

Code explanation:
Constants define the input for the function arguments.
In method CreateTransactedFile() we get the handle of the ambient transaction created in method Main(). Then we call the CreateFileTransacted() function which returns a pointer to the create file. Lastly we created the FileStream object with the created the file handle and used it to create a StreamWriter object to write textual data to the file.

Calling Commit() of the transaction object ensures that all operations completed successfully and now we can apply it. Otherwise, nothing would be done. That’s the main characteristic of transactions. All or nothing.

You don’t need to close the transaction handle manually because when disposing the TransactionScope object it will close its handle automatically.
If you are interested in closing it manually you can use the .NET wrapper System.Runtime.InteropServices.Marshal.Release() method. Or try the difficult way using the CloseHandle unmanaged handle.

Transactional support not limited to creating a file only. Most file system operation now support transactions, some examples are CopyFileTransacted, CreateDirectoryTransacted, CreateHardLinkTransacted, DeleteFileTransacted, MoveFileTransacted, RemoveDirectoryTransacted, and SetFileAttributesTransacted.

The complete example can be downloaded here.

Microsoft Small Basic

Microsoft Small Basic

Small Basic is a project that’s aimed at bringing “fun” back to programming. By providing a small and easy to learn programming language in a friendly and inviting development environment, Small Basic makes programming a breeze. Ideal for kids and adults alike, Small Basic helps beginners take the first step into the wonderful world of programming.

  • Small Basic derives its inspiration from the original BASIC programming language, and is based on the Microsoft .NET platform. It is really small with just 15 keywords and uses minimal concepts to keep the barrier to entry as low as possible.
  • The Small Basic development environment is simple, yet provides powerful modern environment features like Intellisenseâ„¢ and instant context sensitive help.
  • Small Basic allows third-party libraries to be plugged in with ease, making it possible for the community to extend the experience in fun and interesting ways.

Go now and download Microsoft Small Basic 0.3.1 for Free:
http://www.microsoft.com/downloads/details.aspx?FamilyID=B006D58D-C2C7-44AD-936B-E7E2D7DE793E&displaylang=en

Also you may download Microsoft .NET Framework 3.5 SP1 as a reqirement for starting Microsoft Small Basic:
http://www.microsoft.com/downloads/details.aspx?FamilyId=AB99342F-5D1A-413D-8319-81DA479AB0D7&displaylang=en

Microsoft Small Basic Blog:
http://blogs.msdn.com/smallbasic/

Microsoft Small Basic is one of DevLabs projects. Checkout DevLabs projects:
http://msdn.microsoft.com/en-us/devlabs/dd125421.aspx

Microsoft Small Basic Snapshots:

Microsoft Small Basic Snapshot (0)
Microsoft Small Basic Snapshot (6) Microsoft Small Basic Snapshot (2) Microsoft Small Basic Snapshot (4)
Microsoft Small Basic Snapshot (3) Microsoft Small Basic Snapshot (5) Microsoft Small Basic Snapshot (1)

Creating/Extending a Culture

Haven’t you ever used a culture and want to extend it by changing e.g. the currency symbol? Haven’t you ever want to create a custom culture for formatting purposes? Answers will vary depending on the developer and the users targeted by his application.

Overtime, more and more cultures are started to be supported by the .NET Framework. However, not all cultures of the world are available in .NET. If you want to use a culture that is not available or you want to support a minority with a region you will need to create your custom culture.

Custom cultures and regions can be created with the class System.Globalization.CultureAndRegionInfoBuilder that resides in the assembly sysglobl.dll (you will need to reference it of course.)

Now, we are going to create a custom culture that extends the U.S. English culture. This culture is for New York. We’ll step further and change some of the characteristics of the base culture (en-US) to accommodate our needs for a good example.

We’ll start by referencing the assembly sysglobl.dll and add a using statement for the namespace System.Globalization.

sysglobl.dll is very tiny assembly. It contains only one class CultureAndRegionInfoBuilder and one enumeration CultureAndRegionModifiers that we will talk about it soon.

Next, we will instantiate a new instance of CultureAndRegionInfo class. That class is the key class for creating/extending a culture. Plus, it allows you to register/unregister your “baby.” It is required to register your custom culture on the system before you start using it.

CultureAndRegionInfoBuilder builder =
new CultureAndRegionInfoBuilder("en-US-NY",
CultureAndRegionModifiers.None);

The constructor of CultureAndRegionInfoBuilder class requires two parameters. The first is the name of the new culture. The second can one of the three values defined by the CultureAndRegionModifiers enumeration. And that parameter is used to identify the new culture.

The values of CultureAndRegionModifiers are:

  • None:
    For new cultures and the cultures that extends existing ones.
  • Neutral:
    For neutral cultures such as English, French, etc.
  • Replacement:
    For the cultures that you intend to be a replacement of existing culture in .NET Framework or even Windows. A replacement culture is like en-US that would replace the existing culture English (United States.)

In cultures world, there’s a unique name for every culture. And that name follows the naming convention xx-XX. The first lowercase xx is for language like en (English,) fr (French,) es (Spanish,) and so on. The second uppercase XX is for the country/region like US, FR, and ES. So an example like de-DE is the unique name German in Germany.

There’re exceptions to these rules. First, there’re languages like Dhivehi and Konkani that are abbreviated to three letters like div and kok. In addition, there’re two neutral cultures zh-CHS and zh-CHT for Simplified and Traditional Chinese; those have three letters for the country. In addition, there’re cultures that have the suffix -Cyrl and -Latn for defining the script Cyrillic or Latin. See RFC 1766 Standards.

After instantiating a new object of type CultureAndRegionInfoBuilder you need to initialize its properties with valid values. For the example in our hands, we will load the values from the parent culture en-US because we intend to extend it. Actually, there’s no rules to admit in loading the values for the new culture. You can load it from existing culture or you can write it yourself. In addition, we will set the Parent property to the parent culture that we wish to extend.

CultureInfo parent = new CultureInfo("en-US");
builder.LoadDataFromCultureInfo(parent);
builder.LoadDataFromRegionInfo(new RegionInfo("US"));
builder.Parent = parent;

You might review the properties of both CultureInfo and RegionInfo and compare it to the properties of CultureAndRegionInfoBuilder to know exactly why we are loading both objects.

Now comes the hardest part, changing the properties to accommodate our needs and setting the name of the new culture.

builder.RegionEnglishName = "New York";
builder.RegionNativeName = "New York";
builder.CultureEnglishName = "New York (United States)";
builder.CultureNativeName = "New York (United States)";
builder.NumberFormat.CurrencySymbol =  "*";

In the last few lines we changed the region “English” and its native name “English” to “New York”.

In addition, we changed the currency symbol from the dollar sign $ to an asterisk *.

Notice the difference between the English name and native name. The native name is the name of the culture in the native language that the culture is supposed to support. For example, “French” is the English name is and native name is “français”.

Here comes the hottest part, registering your new culture:

builder.Register();

You might get an exception of type InvalidOperationException if you tried to re-register it or if you chose a name that’s existed and you are not creating a replacement culture.

Now, Test your new culture:

CultureInfo newYork = new CultureInfo("en-US-NY");
double money = 100.99;
Console.WriteLine(money.ToString("C", newYork));
// "C" means currency formatting

Congratulations! You created a new culture and you can see it in action.

There’re some things that you need to take into account:

  1. You can unregister your created culture by using the static method Unregister of CultureAndRegionInfoBuilder class.
    CultureAndRegionInfoBuilder.Unregister("en-US-NY");
  2. After creating the desired culture and you don’t want to register it immediately (for example) you can save it as XML and load it later.
    // Saving the culture
    builder.Save("D:New York Culture.xml");
    // Loading the saved culture
    CultureAndRegionInfoBuilder.CreateFromLdml
    ("D:\New York Culture.xml");
  3. Windows saves the custom cultures you create in the folder %windir%Globalization.
  4. You are not ended using the custom culture in your .NET code only. You can go to Regional and Language Settings in Control Panel and change the culture to your created one and that affects the entire system.

Need more?

Links are subject to change if you found a bad link in our site please report it to us as soon as possible.

Download the example

LINQ: Deferred Execution

The following example shows how query execution is deferred until the results is enumerated.

 static void TryLinq()
 {
     int i = 0;
     int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

     // fake values for the query only (10 values)

     var result = from n in numbers select Increment(ref i);

     Console.WriteLine("After query i = {0}", i); // i still 0

     Console.WriteLine();

     Console.WriteLine("Enumerating results:");
     foreach (var v in result)
     {
         Console.WriteLine("v = {0},ti = {1}", v, i);
         // i is incremented every loop
     }

     Console.WriteLine("Press any key to continue . . .");
     Console.ReadKey(true);

     // Result:-

     // After query i = 0
     //
     // Enumerating results:
     // v = 1,  i = 1
     // v = 2,  i = 2
     // .............
     // v = 9,  i = 9
     // v = 10, i = 10

     // What you get?
     // Deferred-Execution / Lazy-Execution
     // - Query doesn't execute until you
     //   begin retrieving it's results.
     // - Every time you try get a value
     //   the query executes on this value only.
 }

 static int Increment(ref int i) { return ++i; }

Next is an example shows how you can execute the query immediately.

static void TryLinq()
{
    int i = 0;
    int[] numbers = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    // fake values for the query only (10 values)

    var result =
        (from n in numbers select Increment(ref i)).ToList();
    // The last call tries to get the value immediately.

    Console.WriteLine("After query i = {0}", i); // i is 10

    Console.WriteLine();

    Console.WriteLine("Enumerating results:");
    foreach (var v in result)
    {
        Console.WriteLine("v = {0},ti = {1}", v, i);
        // i still 10 every loop
    }

    Console.WriteLine("Press any key to continue . . .");
    Console.ReadKey(true);

    // Result:-

    // After query i = 10
    //
    // Enumerating results:
    // v = 1,  i = 10
    // v = 2,  i = 10
    // .............
    // v = 9,  i = 10
    // v = 10, i = 10

    // What you get?
    // Deferred-Execution / Lazy-Execution
    // - Query doesn't execute until you
    //   begin retrieving it's results.
    // - Every time you try get a value the
    //   query executes on this value only.
    // - You can immediate-execute the query by
    //   calling some conversation methods like
    //   ToList or ToQuery.
}

static int Increment(ref int i) { return ++i; }

What you get?
Deferred-Execution / Lazy-Execution

  • Query doesn’t execute until you begin retrieving it’s results.
  • Every time you try get a value the query executes on this value only.
  • You can execute the query immediately by calling a conversation method like ToList() or ToQuery().

Creating a Message Box Directly using API

Download the example (C#)

.NET Framework contains holes that you can’t overcome using the managed code. Then you must dig into the API to overcome this.

Some of the overcomes in the .NET is the way that you can’t show a message box that contains a Help button!

Figure 1 shows an error message box while loading a project in Visual Basic 6.

Visual Basic File Could Not Be Loaded

To overcome this, API provides you with two functions for creating a message box.

These functions are MessageBox and MessageBoxEx.
Confused?
Yeah, They are two distinct functions, but they’re the same on how they work, and also they share the same input parameters, except that the MessageBoxEx includes a parameter in the last called wLanguageId. But it’s reserved. Means that you can safely ignore this parameter.

The syntax of both functions (in C) is as follows:

int MessageBox(
    HWND hWnd,
    LPCTSTR lpText,
    LPCTSTR lpCaption,
    UINT uType);

int MessageBoxEx(
    HWND hWnd,
    LPCTSTR lpText,
    LPCTSTR lpCaption,
    UINT uType,
    WORD wLanguageId );

Parameters:
hWnd:
A handle to the owner window of the message box. If this parameter is NULL, the message box has no owner window.
lpText:
A string contains the message to be displayed.
lpCaption:
A string contains the message box caption. If this parameter is null, the Default title Error is used.
uType:
Specifies the contents and behavior of the dialog box. (we will return to discuss this later)
wLanguageId (MessageBoxEx):
Reserved.

uType:
This parameter specifies the contents and behavior of the message box, and can be a combination of flags from the following groups of flags:

  • To indicate the buttons displayed in the message box, specify one of the following values:
    MB_OK = 0x0 (default)
    OK button.
    MB_OKCANCEL = 0x1
    OK and Cancel buttons.
    MB_ABORTRETRYIGNORE = 0x2
    Abort, Retry, and Ignore buttons.
    MB_YESNOCANCEL = 0x3
    Yes, No, and Cancel buttons.
    MB_YESNO = 0x4
    Yes and No buttons.
    MB_RETRYCANCEL = 0x5
    Retry and Cancel buttons.
    MB_HELP = 0x4000
    Help button. This flag is the only that can be used with a combination with any other buttons flag.
  • To display an icon in the message box, specify one of the following values:
    MB_ICONHAND = 0x10
    A stop-sign icon appears in the message box.
    MB_ICONQUESTION = 0x20
    A stop-sign icon appears in the message box.
    MB_ICONEXCLAMATION = 0x30
    An exclamation-point icon appears in the message box.
    MB_ICONASTERISK = 0x40
    An icon consisting of a lowercase letter i in a circle appears in the message box.
    MB_ICONERROR = MB_ICONHAND
    MB_ICONSTOP = MB_ICONHAND
    MB_ICONWARNING = MB_ICONEXCLAMATION
    MB_ICONINFORMATION = MB_ICONASTERISK
  • To indicate the default button, specify one of the following values:
    MB_DEFBUTTON1 = 0x0 (default)
    The first button is the default.
    MB_DEFBUTTON2 = 0x100
    The second button is the default.
    MB_DEFBUTTON3 = 0x200
    The third button is the default.
    MB_DEFBUTTON4 = 0x300
    The fourth button is the default. (Will be the Help button if you specify MB_HELP)
  • To indicate the modality of the dialog box, specify one of the following values:
    MB_APPLMODAL = 0x0 (default)
    The user must respond to the message box before continuing work in the window identified by the hWnd parameter. However, the user can move to the windows of other threads and work in those windows.
    MB_SYSTEMMODAL = 0x1000
    Same as MB_APPLMODAL except that the message box has the is shown as top most. Use system-modal message boxes to notify the user of serious, potentially damaging errors that require immediate attention (for example, running out of memory). This flag has no effect on the user’s ability to interact with windows other than those associated with hWnd.
    MB_TASKMODAL = 0x2000
    Same as MB_APPLMODAL except that all the top-level windows belonging to the current thread are disabled if the hWnd parameter is NULL. Use this flag when the calling application or library does not have a window handle available but still needs to prevent input to other windows in the calling thread without suspending other threads.
  • To specify other options, use one or more of the following values:
    MB_DEFAULT_DESKTOP_ONLY = 0x20000
    The message is displayed on the default desktop.
    MB_TOPMOST = 0x40000
    The message is shown top most. (with the WS_EX_TOPMOST window style)
    MB_RIGHT = 0x80000
    The text is right-justified.
    MB_RTLREADING = 0x100000
    Displays message and caption text using right-to-left reading order on Hebrew and Arabic systems.
    MB_SERVICE_NOTIFICATION = 0x200000
    The message is displayed on the active desktop, even if no user logged on to the computer.

Return Value:
Both MessageBox and MessageBoxEx return an integer.
If the function fails, the return value is zero.
If the function succeeds, the return value is one of the following value:

  • IDOK = 1
    The user clicked the OK button.
  • IDCANCEL = 2
    The user clicked the Cancel button or pressed the ESC key.
  • IDABORT = 3
    The user clicked the Abort button.
  • IDRETRY = 4
    The user clicked the Retry button.
  • IDIGNORE = 5
    The user clicked the Ignore button.
  • IDYES = 6
    The user clicked the Yes button.
  • IDNO = 7
    The user clicked the No button.
  • IDTRYAGAIN = 10
    The user clicked the Try Again button.
  • IDCONTINUE = 11
    The user clicked the Continue button.

But what if user clicked the Help button?
There’s no return value named for clicking the help button! That’s because clicking the Help button doesn’t close the message box! Instead it’s sending a WM_HELP message to the owner which equals pressing F1.
So, You can handle this in .NET using the HelpRequested event.

Creating a .NET Example
First, Create the flags that can be used to specify message box options.

// Flags

public static class MBIcons
{
    public const int MB_ICONHAND = 0x10;
    public const int MB_ICONQUESTION = 0x20;
    public const int MB_ICONEXCLAMATION = 0x30;
    public const int MB_ICONASTERISK = 0x40;

    public const int MB_ICONERROR = MB_ICONHAND;
    public const int MB_ICONSTOP = MB_ICONHAND;
    public const int MB_ICONWARNING = MB_ICONEXCLAMATION;
    public const int MB_ICONINFORMATION = MB_ICONASTERISK;
}
public static class MBDefButton
{
    public const int MB_DEFBUTTON1 = 0x0;
    public const int MB_DEFBUTTON2 = 0x100;
    public const int MB_DEFBUTTON3 = 0x200;
    public const int MB_DEFBUTTON4 = 0x300;
}
public static class MBButton
{
    public const int MB_OK = 0x0;
    public const int MB_OKCANCEL = 0x1;
    public const int MB_ABORTRETRYIGNORE = 0x2;
    public const int MB_YESNOCANCEL = 0x3;
    public const int MB_YESNO = 0x4;
    public const int MB_RETRYCANCEL = 0x5;

    public const int MB_HELP = 0x4000;
}
public static class MBModal
{
    public const int MB_APPLMODAL = 0x0;
    public const int MB_SYSTEMMODAL = 0x1000;
    public const int MB_TASKMODAL = 0x2000;
}
public static class MBOptions
{
    public const int MB_DEFAULT_DESKTOP_ONLY = 0x20000;
    public const int MB_TOPMOST = 0x40000;
    public const int MB_RIGHT = 0x80000;
    public const int MB_RTLREADING = 0x100000;
    public const int MB_SERVICE_NOTIFICATION = 0x200000;
}

Second, Create the message box return value enumeration.

public enum MBReturn
{
    IDOK = 1,
    IDCANCEL = 2,
    IDABORT = 3,
    IDRETRY = 4,
    IDIGNORE = 5,
    IDYES = 6,
    IDNO = 7,
    IDTRYAGAIN = 10,
    IDCONTINUE = 11,
}

Third, Create the P/Invoke methods.
P/Invoke stands for Platform Invocation a way for calling unmanaged functions.

[DllImport("user32.dll")]
public static extern int MessageBox(
    IntPtr hWnd, string lpText,
    string lpCaption, uint uType);

[DllImport("user32.dll")]
public static extern int MessageBoxEx(
    IntPtr hWnd, string lpText,
    string lpCaption, uint uType,
    ushort wLanguageId);

Code Explanation:
DllImport Attribute:
This attribute is added to the P/Invoke method to indicate the DLL that expose that function. In the case of these functions the DLL is user32.dll.
static extern modifiers:
P/Invoke methods must specify the two modifiers.
extern means that the method is implemented externally, while static means that that method belongs to the type itself not the object.
IntPtr struct:
The managed type that can be used to represent a pointer or an handle in the unmanaged code.

Marshaling
In unmanaged code there’s some types that doesn’t exist in the managed environment, But using marshaling you can overcome this problem.
Marshaling is the process of converting a managed type into unmanaged type.
We have seen in the last code some examples of this.
An example is HWND which is a pointer to a handle in the memory. Which .NET doesn’t provide this type! But, It provides a similar type named IntPtr.
Another example is the WORD which is 16-bit unsigned integer. But, The .NET Framework includes an equivalent thats UInt16 or simply ushort (in C#).

Finally, Call the unmanaged function.

static void Main()
{
    // You can replace IntPtr.Zero
    // with Form.Handle if you want to
    // specify the form as an owner
    MBReturn ret = (MBReturn)MessageBoxEx(IntPtr.Zero,
        "'C:StockPrjcrviewer.dll' could not be loaded" +
        "--Continue Loading Project?", "Microsoft Visual Basic",
        MBButton.MB_YESNO | MBButton.MB_HELP |
        MBIcons.MB_ICONQUESTION | MBSpecial.MB_TOPMOST |
        MBDefButton.MB_DEFBUTTON1, 0);
    Console.WriteLine("User clicked: {0}", ret.ToString());
}

Congratulations!

You have completed the lesson.

Download the example (C#)

Working with Strings with Combining Characters

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

Contents

Contents of this article:

  • Contents
  • Introduction
  • Writing Arabic Diacritics
  • Using the Character Map Application
  • Enumerating a String with Only Base Characters
  • Enumerating a String with Combining Characters
  • Comparing Strings
  • Try it out!

Introduction

In some languages, like Arabic and Hebrew, you combine some characters with combining characters based on the pronunciation of the word.

Combining characters are characters (like diacritics, etc.) that are combined with base characters to change the pronunciation of the word (sometimes called vocalization.)

Some examples of combining characters are diacritics:

Base Character Combining Character(s) Result
1
Combining a single character

Arabic Letter Teh
Arabic Letter Teh
0x062A

Arabic Damma
Arabic Damma
0x064F
Arabic Letter Teh + Damma.gif
Letter Teh + Damma
2
Combining two characters
Arabic Letter Teh
Arabic Letter Teh
0x062A

Arabic Shadda
Arabic Shadda
0x0651

Arabic Fathatan
Arabic Fathatan
0x064B

Arabic Letter Teh + Shadda + Fathatan
Letter Teh + Shadda + Fathatan

When you combine a character with another one then you end up with two characters. When you combine two characters with a base one you end up with 3 characters combined in one, and so on.

Writing Arabic diacritics

The following table summarizes up the Arabic diacritics and the keyboard shortcut for each character:

Unicode Representation Character Name Shortcut
0x064B Arabic Fathatan Fathatan Shift + W
0x064C Arabic Dammatan Dammatan Shift + R
0x064D Arabic Kasratan Kasratan Shift + S
0x064E Arabic Fatha Fatha Shift + Q
0x064F Arabic Damma Damma Shift + E
0x0650 Arabic Kasra Kasra Shift + A
0x0651 Arabic Shadda Shadda Shift + ~
0x0652 Arabic Sukun Sukun Shift + X

Using the Character Map Application

Microsoft Windows comes with an application that help you browsing the characters that a font supports. This application is called, Character Map.

You can access this application by typing charmap.exe into Run, or pressing Start->Programs->Accessories->System Tools->Character Map.

Character Map application

Enumerating a String with Base Characters

Now we are going to try an example. This example uses a simple word,Word Muhammad (Mohammad; the name of the Islam prophet.)

Word Muhammad Details

This word (with the diacritics) is consisted of 9 characters, sequentially as following:

  1. Meem
  2. Damma (a combining character combined with the previous Meem)
  3. Kashida
  4. Hah
  5. Meem
  6. Shadda (a combining character)
  7. Fatha (a combining character both Shadda and Fatha are combined with the Meem)
  8. Kashida
  9. Dal

After characters combined with their bases we end up with 6 characters, sequentially as following:

  1. Meem (have a Damma above)
  2. Kashida
  3. Hah
  4. Meem (have a Shadda and a Fatha above)
  5. Kashida
  6. Dal

The following code simply enumerates the string and displays a message box with each character along with its index:

// C#

string name = "مُـحمَّـد"
string result = String.Empty;

for (int i = 0; i < name.Length; i++)
result += String.Format("{0}t{1}b", i, name(i));

MessageBox.Show(result);

' VB.NET

Dim name As String = "مُـحمَّـد"
Dim result As String = String.Empty

For i As Integer = 0 To name.Length – 1
result &= String.Format("{0}{1}{2}{3}", i, vbTab, name(i), vbNewLine)
Next

MessageBox.Show(result)

What we get? When enumerating the string, we enumerate its base characters only.

Enumerating a String with Combining Characters

.NET Framework provides a way for enumerating strings with combining characters, it is via the TextElementEnumerator and StringInfo types (both reside in namespace System.Globalization.)

The following code demonstrates how you can enumerate a string along with its combining characters:

// C#

string name = "مُـحمَّـد";
string result = String.Empty;

TextElementEnumerator enumerator =
StringInfo.GetTextElementEnumerator(name);

while (enumerator.MoveNext())
result += String.Format("{0}t{1}b",
enumerator.ElementIndex, enumerator.Current);

MessageBox.Show(result);
' VB.NET

Dim name As String = "مُـحمَّـد"
Dim result As String = String.Empty

Dim enumerator As TextElementEnumerator = _
StringInfo.GetTextElementEnumerator(name)

While enumerator.MoveNext()
result &= String.Format("{0}{1}{2}{3}", _
enumerator.ElementIndex, vbTab, _
enumerator.Current, vbNewLine)
End While

MessageBox.Show(result)

Comparing Strings

Sometimes, you will be faced with a situation where you need to compare two identical strings differ only by their diacritics (combining characters) for instance. If you were to compare them using the common way (using String.Compare for instance) they would be different because of the combining characters.

To overcome this you will need to use a special overload of String.Compare method:

The Kashida, isn’t of the Arabic alphabets. It’s most likely be a space! So the option CompareOptions.IgnoreSymbols ignores it from comparison.

// C#

string name1 = "محمد";
string name2 = "مُـحمَّـد";

// 1st check
if (name1 == name2)
MessageBox.Show("Strings are identical");
else
MessageBox.Show("Strings are different!");

// 2nd check
if (String.Compare(name1, name2) == 0)
MessageBox.Show("Strings are identical");
else
MessageBox.Show("Strings are different!");

// 3rd
if (String.Compare(name1, name2,
System.Threading.Thread.CurrentThread.CurrentCulture,
CompareOptions.IgnoreSymbols) == 0)
MessageBox.Show("Strings are identical");
else
MessageBox.Show("Strings are different!");
' VB.NET

Dim name1 As String = "محمد"
Dim name2 As String = "مُـحمَّـد"

' 1st check
If (name1 = name2) Then
MessageBox.Show("Strings are identical")
Else
MessageBox.Show("Strings are different!")
End If

' 2nd check
If (String.Compare(name1, name2) = 0) Then
MessageBox.Show("Strings are identical")
Else
MessageBox.Show("Strings are different!")
End If

' 3rd check
If (String.Compare(name1, name2, _
System.Threading.Thread.CurrentThread.CurrentCulture, _
CompareOptions.IgnoreSymbols) = 0) Then
MessageBox.Show("Strings are identical")
Else
MessageBox.Show("Strings are different!")
End If