Setting Device Information in MCI

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

Interested in MCI multimedia processing? First check this article out if you didn’t:

Creating a Sound Recorder in C and C#

After we received your feedbacks and comments about th.e 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.

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”

Moving a Form without the Title Bar

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

Sample: Geming.Samples.FormDragging.zip

What is that?

Today, we are talking about how to move a form without its title bar.

You might have noticed that some applications with fancy UIs do not allow the user to move the window from its title bar. Honestly, some hide the overall title bar from the user. An example of these applications is Microsoft Windows Media Player -when in skin mode,- and Microsoft Windows Live Messenger. Both applications allow you to drag their windows using the client area not the title bar.

In this lesson, you will learn how to do this trick to move the form without its title bar.

At the end of this lesson, a sample application is available for download. It is a simple application with an outstanding UI illustrates how to move the form using its client area.

How?

Theoretically, you cannot drag any form without its title bar. However, we can simulate the dragging process which implies clicking with the left mouse button on the title bar and moving the cursor without releasing the left mouse button.

You can simulate this process using the SendMessage() function. This function is used to send a specific message (you can say command/order) to a specific object -specified by its handle.- This specific message can be attached with another messages (commands) to produce a new command. For example, in our lesson we will attach the message WM_NCLBUTTONDOWN with the message HTCAPTION to simulate the left mouse button clicking on the caption (title bar) of a window (form.)

Honestly, every object is a window. Every button -even the Close button- is a window, the menu item is a window, the status bar is a window, and the tooltip is another window!

The definition of SendMessage() is as following:

LRESULT SendMessage(
    HWND hWnd,
    UINT Msg,
    WPARAM wParam,
    LPARAM lParam
    );

This function takes four arguments:

  • hWnd:
    The handle of the object (window.) to send the message to. Can be set to a window handle, HWND_DESKTOP (to send the message to the desktop,) HWND_BROADCAST (to send the message to all windows.)
  • Msg:
    The primary message that will be sent.
  • wParam:
    A secondary message to be attached to the primary message. Set to 0 to indicate NULL.
  • lParam:
    Another message that can be attached to the primary message. Set to 0 to indicate NULL. If wParam was not set, you cannot set lParam.

The return value of this function depends on the message sent. In our example, the function may return 0 if succeeded, or non-zero if failed.

It is worth mentioning that, in our example, another function must be called before SendMessage(); it is the ReleaseCapture() function. This function releases the mouse capture from a window (object) and restores the normal mouse processing. A window (object) that has captured the mouse receives all mouse input. Therefore, calling the ReleaseCapture() allows our form to release this mouse input (left-clicking) simulated.

The ReleaseCapture() function is very simple; its definition is as following:

BOOL ReleaseCapture(VOID);

This function returns TRUE if succeeded, or FALSE otherwise.

Let’s Code!

Now, we are going to put things together and mix them up.

The first step is to hide the title bar of the form. This can be done through the FormBorderStyle property of the form. You can set this property to FormBorderStyle.None to hide the toolbar and borders of the form.

The next step to take is to create the PInvoke methods for the functions. Here is the full code:

    // C# Code

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.I4)]
static extern int SendMessage(
    IntPtr hWnd,
    [param: MarshalAs(UnmanagedType.U4)]
    uint Msg,
    [param: MarshalAs(UnmanagedType.U4)]
    uint wParam,
    [param: MarshalAs(UnmanagedType.I4)]
    int lParam);

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ReleaseCapture();

const uint WM_NCLBUTTONDOWN = 0xA1; // 161
const uint HTCAPTION = 2;
' VB.NET
Declare Function SendMessage Lib "user32.dll" ( _
    ByVal hWnd As IntPtr, _
    ByVal Msg As Integer, _
    ByVal wParam As Integer, _
    ByVal lParam As Integer) As Integer

Declare Auto Function ReleaseCapture _
    Lib "user32.dll" () As Boolean

Const WM_NCLBUTTONDOWN As Integer = 161
Const HTCAPTION As Integer = 2

Again and again, PInvoke stands for Platform Invocation; it is the CLR service allows .NET to call unmanaged code. This service requires special changes to the PInvoke method. Also, specific rules applied when PInvoking an unmanaged function.

The last code demonstrates the PInvoke methods for the functions, and the constants required.

The last step is to add the implementation code required to the MouseDown event of any control that you wish to allow the user to drag the form from. For instance, you can add the code to the MouseDown event of a PictureBox control that will act as the new and fancy title bar. In addition, you can add the code to the form’s MouseDown event to allow the user to drag the form from any point of its client area.

The following code demonstrates how to allow the user to drag the form from its client area:

    // Code

private void MainForm_MouseDown
    (object sender, MouseEventArgs e)
{
    // Releasing the mouse capture
    // to allow the form to receive
    // the order
    ReleaseCapture();
    // Ordering the form
    // Simulating left mouse button clicking
    // on the title bar
    SendMessage(this.Handle, // Form handle
        WM_NCLBUTTONDOWN, // Simulating the click
        HTCAPTION, // Attaching it to the title bar
        0); // No further options required
}
' VB.NET
Private Sub Form1_Load(ByVal sender As Object,ByVal e As EventArgs) _
    Handles MyBase.Load
    ReleaseCapture()
    SendMessage(Me.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0)
End Sub

It is better to check the return value of every call to determine whether the operation succeeded or failed.

A problem occurred!

It is wonderful to allow the user to drag the form from its client area. But, what if the user tried to drag the form from a label on the client area? It will not be dragged.

To overcome this situation, you can add the same code to the MouseDown event of the label. In this case, for better code maintenance and to avoid code repetition, you can define a method that contains the code, and call it from the MouseDown event of every control that you need to allow the user to drag the form from such as a label or a PictureBox. Another solution is to create a single MouseDown event handler for all controls that you are interested in.

Well! What’s next?

You can use this technique when creating applications with hot skins to allow the user to drag the form from its client area or from a specific control like a PictureBox that acts as the new hot title bar.

It is worth mentioning that you would better follow the guidelines when interoperating with unmanaged code. For example, PInvoke methods and constants should be declared inside an internal class called -in our example- SafeNativeMethods. In addition, it is better creating a wrapper method for the PInvoke methods. For example, instead of calling many functions to drag the form, you can create a single function named -for instance- MoveForm() that acts as a wrapper for the PInvoke methods. And that results in code that is more readable and easier to maintain. Plus, it prevents code repetition.

For more information about the functions, consult the MSDN documentation:

WOW! A code sample!

This is a very simple application that illustrates moving a form using its client area.

This sample created using Microsoft Visual Studio 2008 and .NET Framework 2.0.

The following is a snapshot from the application:

Moving Form without Title Bar Sample Snapshot

Download Here

Changing Display Settings Programmatically

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

Code: Geming.DisplayMgr.msi

Introduction

Previously, we have talked about how to change screen resolution and color system via DirectX. Today, we are talking about how to change all display settings -not the resolution and color system only- via API. We will change screen resolution (bounds,) color system (bit count,) rotation (orientation,) and refresh rate (frequency) via API with C# and the .NET Framework.

Overview

This lesson firstly discusses the required functions and structures. Then, it focuses on how to retrieve current display settings. After that, it discusses how to get all modes supported by your display. As you already know, a mode is a combination of may display settings including bounds, bit count, orientation, and frequency; therefore, we will refer to display settings as display mode.

Finally, this lesson discusses how to change the current display settings. Along with the discussion, you will learn additional techniques like how to PInvoke Win32 API functions, and to marshal unmanaged data types.

In addition, this lesson comes with a sample application used for changing the display settings.

Now, we are going to discuss the required functions and structure and how to use them. After that, we will focus on the implementation code. Get ready.

EnumDisplaySettings() Function

This function resides in user32.dll. It is used to retrieve one of the modes supported by a graphics device.

The definition of this function is as following:

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

This function accepts only three parameters:

  • lpszDeviceName:
    Specifies the display device name that will be used to retrieve its modes. This parameter can be either NULL to indicate the default device, or the name of the display device. You can enumerate display devices using the EnumDisplayDevices() function.
  • iModeNum:
    Specifies the type of information to retrieve. It could be either a mode index or one of these values:

    • ENUM_CURRENT_SETTINGS = -1
      Retrieves current display mode.
    • ENUM_REGISTRY_SETTINGS = -2
      Retrieves current display mode stored on the registry.
  • lpDevMode:
    A reference (In/Out) parameter represents the DEVMODE object encapsulates the retrieved display mode information. The DEVMODE’s dmSize member is used for input to represents the structure size, while other members are used for output.

As you might expect, to retrieve the current mode (settings) of the current display device, you will need to pass a NULL value as the lpszDeviceName parameter to indicate the current display, and the value -1 to the iModeNum parameter to indicate the current mode.

Unfortunately, EnumDisplaySettings() can return only one mode per call, and that mode is encapsulated into the DEVMODE object. To retrieve all modes (or few) supported by a display device, you need to call EnumDisplaySettings() many times specifying iModeNum as the mode index. Mode indexes start from zero. Therefore, to retrieve all modes, you will need to call the EnumDisplaySettings() function many times specifying 0 for iModeNum on the first time, and increment that index every call until EnumDisplaySettings() returns FALSE, which indicates that the previous mode was the last mode supported.

If you want to retrieve a mode (or all modes) supported by other display device, you will need to change the lpszDeviceName to the name of that device. You can get a list of all devices connected using the EnumDisplayDevices() function.

Now, it is the time for the PInvoke method. We can PInvoke this function in C# as following:

[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);

What is Platform Invocation (PInvoke)? You already know, there is no such compatibility between managed code (.NET) and unmanaged code (Win32 API in our case.) Therefore, they cannot call each other directly. Rather, you make use of the PInvoke service to call unmanaged functions from the managed environment.

What is Marshaling? Marshaling is another service of the CLR. Again, there is no such compatibility between managed code and unmanaged code. Therefore, they cannot communicate directly. To send data between the managed client and unmanaged server, and vice versa, you will need to use marshaling to allow sending and receiving of the data. Marshaling converts managed data types to unmanaged data and vice versa.

As you suggested, now we are going to talk about the DEVMODE structure.

DEVMODE Structure

This structure encapsulates information about a printer or a display device.

This structure is fairly a complex structure, but we will try to break it down to be simple and easier to marshal.

The definition of this structure is as following:

typedef struct _devicemode {
  TCHAR 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;
    } ;
    struct {
      POINTL dmPosition;
      DWORD  dmDisplayOrientation;
      DWORD  dmDisplayFixedOutput;
    } ;
  } ;
  short dmColor;
  short dmDuplex;
  short dmYResolution;
  short dmTTOption;
  short dmCollate;
  TCHAR 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
} DEVMODE, *PDEVMODE, *LPDEVMODE

Really complex, Isn’t it? Yeah, DEVMODE is one of the large and complex structures.

You might have noticed that two unions defined inside the structure. In addition, a structure is defined inside the first union. Notice that this structure is only available if it is a printer device. Plus, the union defined the structure also is for printer devices only. Therefore, for display devices, you can omit the structure, and define other members of the union sequentially, no additional work is required.

In addition, the last eight members are not supported by Windows NT, while the last two members are not supported by Windows ME and its ascendants. To solve this dilemma and support all versions, you can define 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 the function for the three structures. For simplicity, we will marshal the whole structure as for Windows 2000 and higher versions.

Notice that there are arrays that are defined with the length CCHFORMNAME which equals 32.

Last but not least, the second union of the structure defined two members inside, dmDisplayFlags and dmNup. For simplicity, we will take away the union and one of its members and define the other. Because both members are 4-bytes wide, we can omit anyone of them.

We can marshal that structure in C# as following:

    [StructLayout(LayoutKind.Sequential,
CharSet = CharSet.Ansi)]
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
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string dmDeviceName;
    // In addition you can define the last character array
    // as following:
    //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
    //public Char[] dmDeviceName;

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

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

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

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

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

    public POINTL dmPosition;

    [MarshalAs(UnmanagedType.U4)]
    public UInt32 dmDisplayOrientation;

    [MarshalAs(UnmanagedType.U4)]
    public UInt32 dmDisplayFixedOutput;

    [MarshalAs(UnmanagedType.I2)]
    public Int16 dmColor;

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

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

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

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

    // CCHDEVICENAME = 32 = 0x50
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string dmFormName;
    // Also can be defined as
    //[MarshalAs(UnmanagedType.ByValArray,
    //    SizeConst = 32, ArraySubType = UnmanagedType.U1)]
    //public Byte[] dmFormName;

    [MarshalAs(UnmanagedType.U2)]
    public UInt16 dmLogPixels;

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

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

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

    [MarshalAs(UnmanagedType.U4)]
    public UInt32 dmDisplayFlags;

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

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

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

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

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

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

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

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

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

We will cover the PONTL structure soon.

Actually, these dozens of MarshalAsAttribute attributes are not all required. Honestly, it is not required for marshaling DWORD into UInt32 because they are counterparts. On the hand, MarshalAsAttribute attribute must be applied to arrays.

From all of those members, we are interested only in a few:

  • dmPelsWidth and dmPelsHeight:
    Represents the bounds (width and height) of the display. These values can be used also to determine whether the display orientation is portrait or landscape.
  • dmBitsPerPel:
    Represents the bit count (color system) of the display.
  • dmDisplayOrientation:
    Represents the orientation (rotation) of the display. This member can be one of these values:

    • DMDO_DEFAULT = 0
      The display is in the natural orientation. It is the default.
    • DMDO_90 = 1
      The display is rotated 90 degrees (measured clockwise) from DMDO_DEFAULT.
    • DMDO_180 = 2
      The display is rotated 180 degrees (measured clockwise) from DMDO_DEFAULT.
    • DMDO_270 = 3
      The display is rotated 270 degrees (measured clockwise) from DMDO_DEFAULT.
  • dmDisplayFrequency:
    Represents the frequency (refresh rate) of the display.

POINTL Structure

The DEVMODE’s dmPosition member represents the location that display device in reference to the desktop area. It is always located at (0, 0). This member is of the structure POINTL which represents the coordinates (x and y) of a point. As you might expect, this structure is very simple. It is defined as following:

typedef struct POINTL {
    LONG x;
    LONG y;
};

We can marshal this structure easily as following:

    [StructLayout(LayoutKind.Sequential)]
public struct POINTL
{
    [MarshalAs(UnmanagedType.I4)]
    public int x;
    [MarshalAs(UnmanagedType.I4)]
    public int y;
}

However, for code clarity, we have a workaround. You can omit the POINTL structure and replace the dmPosition member with two members, you can call them dmPositionX and dmPositionY, and that will work fine because you know that the size and layout (position of members) of structures is very important. And doing that will not break this rule.

ChangeDisplaySettings() Function

This function resides in user32.dll. It is used to change display settings to the mode specified, but only if the mode is valid.

The definition of this function is as following:

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

This function accepts only two arguments:

  • lpDevMode:
    A reference (In/Out) argument of the type DEVMODE represents the new settings (mode) that will be applied to the display device. After retrieving the current settings using the EnumDisplaySettings() function, you can change the desired members of the DEVMODE object and use this function to change the display device to the new settings. Again, this argument is In/Out argument which means that it is used for input and output. DEVMODE’s dmSize member is used for input and other members are used for output.
  • dwflags:
    Indicates how the mode should be changed. Actually, in this example, we are not interested in this argument, so we will set it to zero. If you want more help on this argument, consult the MSDN documentation.

The return value of this function varies based on the success or failure of the settings change. This function can return one of several values including:

  • DISP_CHANGE_SUCCESSFUL = 0
    Indicates that the function succeeded.
  • DISP_CHANGE_BADMODE = -2
    The graphics mode is not supported.
  • DISP_CHANGE_FAILED = -1
    The display driver failed the specified graphics mode.
  • DISP_CHANGE_RESTART = 1
    The computer must be restarted for the graphics mode to work.

Consult MSDN documentation to know more about the ChangeDisplaySettings() return value. The last section of this lesson is devoted for this.

Another point of interest is that ChangeDisplaySettings() changes only the default display. If you want to change another display device, you can use the ChangeDisplaySettingsEx() function. It is very similar to ChangeDisplaySettings(). Consult MSDN documentation for more help.

Now, it is the time for creating the PInvoke method for the ChangeDisplaySettings() function.

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

Now, we are going to mix things together and talk about the implementation code. Get ready.

Retrieving Current Display Mode

The code that obtains the current display settings is very easy. We use the EnumDisplaySettings() function passing it ENUM_CURRENT_SETTINGS (-1) in the iModeNum parameter to get the current settings, and NULL in the lpszDeviceName parameter to indicate the current display device.

Here is the code.

Code abbreviated for clarity.

    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);
    }
}

Enumerating Supported Display Modes

As a refresher, to get the current mode or even another supported mode of the display device, you make use of the EnumDisplaySettings() function. If you pass ENUM_CURRENT_SETTINGS (-1) in the iModeNum parameter, you get the current mode. On the other hand, you can enumerate through the list of supported modes by passing the mode index in this parameter. We start by 0 which indicates the first mode, and increment it every call to enumerate through the list of the supported modes. If the function returns FALSE, that means that the mode with the index specified is not found. Therefore, the previous mode was the last one. Here is the code.

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
    }
}

Changing Display Mode

Now, we are going to change the current display settings. This can be done through the ChangeDispalySettings() function.

Changing Screen Resolution and Bit Count

The following code example loads the current settings and changes only the resolution and the bit count. Actually, you are free to change all settings or few of them that is up to you. However, for the sake of simplicity, we are going to change the screen resolution and bit count in this section, and the orientation in the next section.

static void Main()
{
    // Changing the display resolution
    // to 800 by 600
    // and the color system (bit count)
    // to 16-bit
    ChangeDisplaySettings(800, 600, 16);
}

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);
}

Changing Screen Orientation

Now we are going to change the screen orientation clockwise and anti-clockwise.

static void Main()
{
    // 0 degrees ( DMDO_DEFAULT = 0 )

    Console.WriteLine
        ("Press any key to rotate the screen . . .");
    Console.ReadKey(true);
    Console.WriteLine();

    RotateScreen(true); // 90 degrees (	DMDO_90 = 1 )
    Console.WriteLine
        ("Press any key to rotate the screen . . .");
    Console.ReadKey(true);
    Console.WriteLine();

    RotateScreen(true); // 180 degrees ( DMDO_180 = 2 )
    Console.WriteLine
        ("Press any key to rotate the screen . . .");
    Console.ReadKey(true);
    Console.WriteLine();

    RotateScreen(true); // 270 degrees ( DMDO_270 = 3 )
    Console.WriteLine
        ("Press any key to rotate the screen . . .");
    Console.ReadKey(true);
    Console.WriteLine();

    RotateScreen(true); // 0 degrees ( DMDO_DEFAULT = 0 )
}

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

    // Rotating the screen
    if (clockwise)
        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
    // ...
}

Sample Application

The code sample is a simple application used to change display settings and to rotate the screen.

This is a snapshot of the application:

Display Settings Sample Snashot

Download Here

References

It is pleasure receiving your feedbacks and comments.

Programmatically Turning on the Screen Saver

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

Overview

This lesson focuses on how to programmatically turn on the screen saver.

Background

In Windows, you can turn it on automatically by leaving the machine inactive for a specific period. You can control this period from the Screen Saver options from the desktop properties dialog. The following figure shows the Screen Saver Settings dialog.

Screen Saver Settings

Programmatically turning on the screen saver

In this section we will learn how to turn on the screen saver in .NET and C#. Of course you can write the code in any language you prefer, but here we will write it in C#.

You can turn on the screen saver by sending the WM_SYSCOMMAND message with the parameter SC_SCREENSAVE.

Sending a message can be done using the SendMessage() function that resides in the User32.dll library.

The definition of this function is as follows:

LRESULT SendMessage(
    HWND hWnd,
    UINT Msg
    WPARAM wParam,
    LPARAM lParam
    );

This function takes four arguments:

  • hWnd:
    Handle to the window to send the message to. You can set this argument to a window handle, the desktop handle (HWND_DESKTOP), or the handle for all top-level windows (HWND_BROADCAST).
  • Msg:
    The message to send.
  • wParam:
    Additional message-specific options.
  • lParam:
    Additional message-specific options.

This function returns a value specific to the message sent. Usually, it returns non-zero if it succeed or zero otherwise.

Here is the full code:

// C# Code

[DllImport("User32.dll")]
static extern int SendMessage
    (IntPtr hWnd,
    uint Msg,
    uint wParam,
    uint lParam);

const uint WM_SYSCOMMAND = 0x112;
const uint SC_SCREENSAVE = 0xF140;
const uint HWND_BROADCAST = 0xFFFF;

static void Main()
{
    SendMessage(
    new IntPtr((int)HWND_BROADCAST),
    WM_SYSCOMMAND,
    SC_SCREENSAVE,
    0);
}
' VB.NET Code

Declare Auto Function SendMessage Lib "user32.dll" _
    (ByVal hWnd As IntPtr, _
    ByVal Msg As UInt32, _
    ByVal wParam As UInt32, _
    ByVal lParam As UInt32) As Int32

Const WM_SYSCOMMAND As UInt32 = &h212
Const SC_SCREENSAVE As UInt32 = &HF140
Const HWND_BROADCAST As UInt32 = &HFFFF

Sub Main()
    SendMessage( _
        New IntPtr(CInt(HWND_BROADCAST)), _
        WM_SYSCOMMAND, _
        SC_SCREENSAVE, _
        0)
End Sub

Code explanation

First, we created our PInvoke method. This method is decorated by the DllImportAttribute attribute specifying the library which the method resides in. Also PInvoke methods must be declared as “static” and “extern”.

Because LRESULT defined as a signed 32-bit integer, it is marshaled as System.Int32 in .NET. Also, because of System.IntPtr is the best type for marshaling any Win32 raw handle, we have used it for the first argument. UINT, WPARAM, AND LPARAM are all defined as an unsigned 32-bit integer, so we have marshaled them as System.UInt32. HWND_BROADCAST represents the handle for all top-level windows, so we have sent them the order to turn on the screen saver.

PInvoke stands for Platform Invocation, it is the process of creating a wrapper for the .NET to interact with unmanaged functions.

Marshaling is the process of creating a bridge between .NET types and unmanaged types.

You can use PostMessage() in place of SendMessage() if you want to send the message asynchronously and don’t want to wait for a response.

Read more about PInvoking and Marshaling in other API lessons.

Clearing the Console Screen using API

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

Code: Geming.Suite.Console.zip

What you learn?

In addition to clearing the console screen, this lesson teaches you some about PInvoking, marshaling, and memory management. Also you will learn additional techniques like clearing a specific portion of the screen, and changing the cursor position. Moreover, you will dig into IL and see how System.Console.Clear() method do it. More than that you will learn how to reverse-engineer a .NET assembly and discover the code inside.

In addition, the example shows how to perform I/O operations on console using Win32 API calls, and how to show/hide the console cursor. In addition, it demonstrates how to move a text around the console screen.

Foreword

I am one of the huge supporters of console when it comes to the fact that we need simple and fast application such as tools and utilities.

One of the common needs of console is performing clear-screens (CLSs) when necessary.

When dealing with the Command Prompt you can order it to clear the screen by submitting the command “cls” and pressing Enter.

But, programmatically, you have no way to order the command “cls,” so you must find a substitute (or more.)

Starting from version 2.0 of the .NET Framework, System.Console has been added a new method, it is the Clear() method for clearing the console screen.

For versions before 2.0, or if you need to do it the hard-way, or even if you want to have more control over the clearing progress such as clearing a specific portion of the screen, you must dig into Win32 API and call special functions for doing so.

Clearing the console screen done through four Win32 API functions, GetStdHandle(), GetConsoleScreenBufferInfo(), FillConsoleOutputCharacter() and SetConsoleCursorPosition(). It is worth noting that all these functions located in Kernel32.dll library.

Be sure that the System.Console.Clear() method in .NET 2.0 and descendant call these four functions -and more- initially.

GetStdHandle() function

This method is the gate for dealing with console via the API. Almost every console operation requires a call to GetStdHandle() first.

GetStdHandle() simply returns the handle for the standard input, output, error device (“stream” in .NET methodology.) This function takes a single argument specifies which standard device (stream) we wish to retrieve the handle for.

The syntax of this function -in C- is as following:

HANDLE GetStdHandle(
    DWORD nStdHandle
    );

The nStdHandle argument can take one of three values:

  • STD_INPUT_HANDLE = -10
    Specifies the standard input device (stream.)
  • STD_OUTPUT_HANDLE = -11
    Specifies the standard output device (stream.)
  • STD_ERROR_HANDLE = -12
    Specifies the standard error device (stream.) It is always the output device (stream.)

As its names implies, input device used for receiving user input, output device used for writing output to the user, and error device used too for writing error information to the user. In .NET, you access these devices (streams) via nice wrappers. They are the static properties In, Out, and Error of the Console class. I guess that after playing with these nice wrappers you now know what standard devices are.

Because there’s no such way for .NET to interoperate with Win32 API directly, you need to create wrappers for the Win32 API functions you use, and this is done through PInvoking.

PInvoke stands for Platform Invocations. It is the mechanism for the .NET to interoperate with its “girlfriend” Win32 API.

The PInvoke method for GetStdHandle is as following – code assumes that you add a using statement for the System.Runtime.InteropServices namespace:

[DllImport("Kernel32.dll")]
public static extern IntPtr GetStdHandle(int nStdHandle);

Code explanation:

The DllImport attribute is required for PInvoke methods so that you can specify the DLL which the function resides in. Also DllImport have a very important role when you need to change the name of the method. If you need to change the name of the PInvoke method then you must set the property EntryPoint of DllImportAttribute to the original name of the function in the DLL. If you have not set this property, .NET will search the DLL for the method and will throw a System.EntryPointNotFoundException exception if it is not found.
“static” and “extern” are modifiers required for PInvoke methods.
Because some data types do not exist in .NET, you need to find a substitute. In other words, you need to marshal unmanaged Win32 data types to the new managed .NET types. So we have marshaled HANDLE to System.IntPtr and DWORD to System.Int32.

Only a single output handle serves the console application (the same for input and error devices,) so do not close that opened handle using the CloseHandle() function because it will result that you cannot write to the console any more, unless you open it again.

A note about marshaling

Because there’re plenty of Win32 data types that do not have correspondents in .NET, you must do Marshaling. Marshaling is the process of creating a bridge between .NET types and unmanaged types. Marshaling converts .NET types into unmanaged types. As we have seen in the last code, .NET does not contain DWORD, and because DWORD is a 32-bit unsigned integer we have to marshal it as System.UInt32. However, we marshaled it in GetStdHandle() as System.Int32! While unsigned integers (inculding DWORD) do not accept negative numbers, GetStdHandle() requires DWORD. Actually, it is OK because negative values can be stored in DWORD in a special way. For example, -10 stored as FFFFFFF6, -11 stored as FFFFFFF5, and so on. And that what GetStdHandle() actually does.

In addition, you may notice that we have marshaled HANDLE as System.IntPtr, because IntPtr is the best type fits to any Win32 handle. Moreover, .NET supports managed handles via the abstract SafeHandle and CriticalHandle classes.

It is worth mentioning that System.Runtime.InteropServices.MarshalAsAttribute attribute can have a noticeable effect over the marshaling process.

Good resources describe the marshaling process and the Win32 data types can be found in the references section.

Managed code is the .NET code that runs inside the CLR (Common Language Runtime,) because CLR manages and controls this code. Conversely, unmanaged code is the code other than the managed code. It is legal that you write code in .NET that runs outside the CLR such as direct memory management and that code called Unsafe Code because it is error-prone and may result to an unpredictable behavior. Also, MC++ allows you to write unmanaged code together with the managed code.

GetConsoleScreenBufferInfo() function

The third method we will use is the GetConsoleScreenBufferInfo(). This method retrieves information about a specific console screen buffer (in other words, device.) This information is like console screen buffer size, the location and bounds of the console window, and the cursor position inside the screen.

The definition of this function is as following:

BOOL GetConsoleScreenBufferInfo(
    HANDLE hConsoleOutput,
    [out] SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo
    );

typedef struct CONSOLE_SCREEN_BUFFER_INFO {
    COORD dwSize;
    COORD dwCursorPosition;
    WORD wAttributes;
    SMALL_RECT srWindow;
    COORD dwMaximumWindowSize;
};

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

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

Lengthy, isn’t it? Yeah, that’s true. Beside the GetConsoleScreenBufferInfo(), there’re three more structures, because the type of second argument of the function is the first structure, and the first structure in turn references the second and third structures.

The CONSOLE_SCREEN_BUFFER_INFO structure represents the console information. This information is:

  • The size of the console screen buffer in character rows and columns.
  • The position of the cursor in the console screen in character rows and columns.
  • Characteristics of the character written to the console like the fore color and back color.
  • The location and bounds of the console window.
  • The maximum size for the console window if we take into account the font size and the screen buffer size.

What is screen buffer and window size and what is the difference? Start the Command Prompt and right click its icon in the taskbar and go to the layout tab of the properties dialog box. Take some time playing with the values in this tab.
Window size is the size of the console window -like any other window.- Screen buffer size determines the amount of text which the console screen can hold, so you always see the vertical scroll bar because the buffer height is bigger than the window height.

Here’s the PInvoke method and the marshaling types because .NET does not have such those three structures:

[DllImport("Kernel32.dll")]
public static extern int GetConsoleScreenBufferInfo
    (IntPtr hConsoleOutput,
    out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);

[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    public COORD dwSize;
    public COORD dwCursorPosition;
    public ushort wAttributes;
    public SMALL_RECT srWindow;
    public COORD dwMaximumWindowSize;
}

[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
    public short X;
    public short Y;
}

[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
    public short Left;
    public short Top;
    public short Right;
    public short Bottom;
}

Code explanation:

Because .NET does not contain such these three structures, and we do not have any substitutes, so we have to marshal it, we have created it manually and mapped unmanaged to the managed data types. And because memory layout of such these object is very important, we added the StructLayout attribute and specified LayoutKind.Sequential that informs the marshaller that this structure will laid-out in memory sequentially so the first field comes before the second and so on. Also you might notice that WORD unmanaged data type is an unsigned 32-bit integer so we marshaled it as UInt32. Also SHORT is signed 16-bit integer so it is very easy to marshal it as Int16. Refer to the references section for more information about Win32 data types.

BOOL defined as a 32-bit signed integer, so we have marshaled the return value of the function as Int32 not Boolean because Boolean reserves 4-bits only. It will work well with Boolean, but it is better using Int32. Though, if you want to use Boolean, is helpful decorating the return value with the MarshalAs attribute.

Also, it is very efficient to use the System.Runtime.InteropServices.InAttribute and System.Runtime.InteropServices.OutAttribute to give a notation to the marshaller. The code sample demonstrates this.

A note about memory management

You already know that every object reserves space in memory. And the memory itself divided into two parts, Heap and Stack. Objects -directly or indirectly- inherit from System.ValueType are stack-based (like structures, enumerations and primitive data types.) Conversely, most objects  -directly or indirectly- inherit from System.Object are heap-based (like most objects.) And you know also that heap objects managed by the Garbage Collector (GC) and they may remain in the memory for a long time -actually even if you called System.GC many times.- On the other hand, stack objects are removed from the memory immediately when their scope ends. In addition, you need to know that stack is filled downwards. See figure 1.

Stack Memory

When interoperating with unmanaged code and marshaling types between the .NET and Win32 API (for instance) you need to know how to layout your type in memory. Unmanaged code assumes that types are laid sequentially based on the order of fields. For our example, the CONSOLE_SCREEN_BUFFER_INFO must be laid-out so dwSize comes before dwCursorPosition and so on. See figure 2.

CONSOLE_SCREEN_BUFFER_INFO in Memory

We can dig into more details and see how Windows will store the CONSOLE_SCREEN_BUFFER_INFO structure in the stack and how it will be rendered. See figure 3.

console_screen_buCONSOLE_SCREEN_BUFFER_INFO in Memory _ DetailsCONSOLE_SCREEN_BUFFER_INFO in Memory _ Details

From the diagrams we learn that:

  • Objects are stored downwards in the stack by the order they were created.
  • Object containing objects also stored downwards by the order they were declared.
  • Every object has a size. And this size determined by its containing objects -if it is not a primitive type of course.-

You can get the size of an object using two ways, the sizeof keyword, and System.Runtime.InteropServices.Marshal.SizeOf method. The second method is preferred. Do you know why? Try, think, and then answer.

Now, we know why the StructLayout attribute is required. And why sequential layout is required. But what if you prefer to change the order of fields? Answer is very simple. Change the LayoutKind to Explicit and decorate every field with FieldOffset attribute and specify its location from the beginning of the structure. In the following code fields are reversed but they perfectly laid-out in memory using the FieldOffset attribute:

[StructLayout(LayoutKind.Explicit)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    [FieldOffset(18)]
    public COORD dwMaximumWindowSize;
    [FieldOffset(10)]
    public SMALL_RECT srWindow;
    [FieldOffset(8)]
    public ushort wAttributes;
    [FieldOffset(4)]
    public COORD dwCursorPosition;
    [FieldOffset(0)]
    public COORD dwSize;
}

If you set the LayoutKind to Auto, you will not be able to interoperate with unmanaged code using the type. Also if you have omitted the whole StructLayoutAttribute.

Also, from the diagrams illustrated -specially the last one- we can also write our CONSOLE_SCREEN_BUFFER_INFO structure to be as following and remove the other two structures:

[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    public short dwSizeX;
    public short dwSizeY;
    public short dwCursorPositionX;
    public short dwCursorPositionY;
    public ushort wAttributes;
    public short srWindowLeft;
    public short srWindowTop;
    public short srWindowRight;
    public short srWindowBottom;
    public short dwMaximumWindowSizeX;
    public short dwMaximumWindowSizeY;
}

Sounds odd, isn’t it? You can also set the LayoutKind to Explicit and start working.

It is also possible to union two fields -or more- into one. For example, merging the two 16-bit integers dwSizeX and dwSizeY into one 32-bit integer, dwSize. It will work very well! In addition, you can divide them in your code using System.Collections.Specialized.BitVector32 structure.

A look inside the memory

Now, this time we are going to do something interesting. We are going to look on our structure in the memory.

For simplicity, we are going to use these types:

[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    public COORD dwSize;
    public COORD dwCursorPosition;
    public ushort wAttributes;
    public SMALL_RECT srWindow;
    public COORD dwMaximumWindowSize;
}

[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
    public short X;
    public short Y;
}

[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
    public short Left;
    public short Top;
    public short Right;
    public short Bottom;
}

Next, we will initialize the structures with some data to see how it stored in the memory.

unsafe static void Main()
{
    CONSOLE_SCREEN_BUFFER_INFO info
        = new CONSOLE_SCREEN_BUFFER_INFO();

    info.dwSize = new COORD();
    info.dwSize.X = 0xA1;
    info.dwSize.Y = 0xA2;

    info.dwCursorPosition = new COORD();
    info.dwCursorPosition.X = 0xB1;
    info.dwCursorPosition.Y = 0xB2;

    info.wAttributes = 0xFFFF;

    info.srWindow = new SMALL_RECT();
    info.srWindow.Left = 0xC1;
    info.srWindow.Top = 0xC2;
    info.srWindow.Right = 0xC3;
    info.srWindow.Bottom = 0xC4;

    info.dwMaximumWindowSize = new COORD();
    info.dwMaximumWindowSize.X = 0xD1;
    info.dwMaximumWindowSize.Y = 0xD2;

    uint memoryAddress = (uint)&info;

    Console.WriteLine(
        "Memory Address: 0x{0:X}",
        memoryAddress);

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

    // You can add a break point on the last line,
    // or you can use this function to break the code.
    System.Diagnostics.Debugger.Break();
}

This code assumes that you enabled the unsafe code from the Build tab of the project properties. Also, it assumes that you run your code in the debugging mode by pressing F5, or by clicking Start Debugging from the Debug menu.

Now, and after breaking the code, click Debug -> Windows -> Memory -> Memory 1 to open a memory window. Figure 4 shows how to open a memory window. And figure 5 shows the memory window opened. Click on the figures to enlarge.

Showing Memory Window

Memory Window

Now, you can locate your structure in memory by typing its memory address. Figure 6 shows the structure in the memory window.

Memory Window + CONSOLE_SCREEN_BUFFER_INFO

Now, take your time looking on how the structure -and its contained structures- laid-out in memory.

FillConsoleOutputCharacter() function

Last but not least, this is the fourth function we will use. This function fills a specific console buffer portion with a specific character. Passing a white space as the character means clearing this portion.

The syntax of this function is as following:

BOOL FillConsoleOutputCharacter(
    HANDLE hConsoleOutput,
    TCHAR cCharacter,
    DWORD nLength,
    COORD dwWriteCoord,
    [out] LPDWORD lpNumberOfCharsWritten
    );

This function takes four input arguments and a single output one, and it returns a value determines whether the function succeeded or failed. If the return value is non-zero (true) then the function succeeded, otherwise failed (that’s for GetConsoleBufferInfo() and SetConsoleCursorPosition() also.)

The five arguments are:

  • hConsoleOutput:
    A handle to an opened console output device to write to.
  • cCharacter:
    The character which to fill the buffer portion with.
  • nLength:
    The number of characters to write.
  • dwWriteCoord:
    A COORD structure defines where to begin writing (the first cell.)
  • lpNumberOfCharsWritten:
    An output parameters determines the number of characters written.

Here’s the PInvoke method of this function:

We have omitted the COORD structure because we have created it earlier.

[DllImport("Kernel32.dll")]
public static extern int FillConsoleOutputCharacter
    (IntPtr hConsoleOutput, char cCharacter, uint nLength,
    COORD dwWriteCoord, out uint lpNumberOfCharsWritten);

Notice that the unmanaged data types DWORD and LPDOWRD are marshaled to System.UInt32. For more information about unmanaged data types see the references section.

SetConsoleCursorPosition() function

This function is used to set the cursor position in the console screen buffer.

The syntax of this function is as following:

BOOL SetConsoleCursorPosition(
    HANDLE hConsoleOutput,
    COORD dwCursorPosition
    );

This function takes two arguments the first is a handle for an opened console output device. The second is a value specifies the new cursor position. Note that the new cursor position must be inside the console screen buffer.

The PInvoke method for this function is:

[DllImport("Kernel32.dll")]
public static extern int SetConsoleCursorPosition
    (IntPtr hConsoleOutput, COORD dwCursorPosition);

Putting things together

After all, we have learned the required functions and created the PInvoke method, so we can mix it up and start coding. Here’s the full code:

public const int STD_OUTPUT_HANDLE = -11;
public const char WHITE_SPACE = ' ';

[DllImport("Kernel32.dll")]
public static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("Kernel32.dll")]
public static extern int GetConsoleScreenBufferInfo
    (IntPtr hConsoleOutput,
    out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);

[DllImport("Kernel32.dll")]
public static extern int FillConsoleOutputCharacter
    (IntPtr hConsoleOutput, char cCharacter, uint nLength,
    COORD dwWriteCoord, out uint lpNumberOfCharsWritten);

[DllImport("Kernel32.dll")]
public static extern int SetConsoleCursorPosition
    (IntPtr hConsoleOutput, COORD dwCursorPosition);

[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    public COORD dwSize;
    public COORD dwCursorPosition;
    public ushort wAttributes;
    public SMALL_RECT srWindow;
    public COORD dwMaximumWindowSize;
}

[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
    public short X;
    public short Y;
}

[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
    public short Left;
    public short Top;
    public short Right;
    public short Bottom;
}

Clearing the console screen

And this is the code that clears the console screen:

static void Main()
{
    Console.WriteLine("Writing some text to clear.");

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

    ClearConsoleScreen();
}

public static void ClearConsoleScreen()
{
    // Getting the console output device handle
    IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);

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

    // Discovering console screen buffer info
    Console.WriteLine("Console Buffer Info:");
    Console.WriteLine("--------------------");
    Console.WriteLine("Cursor Position:");
    Console.WriteLine("t{0}, {1}",
    info.dwCursorPosition.X, info.dwCursorPosition.Y);
    // Is this information right?
    Console.WriteLine("Maximum Window Size:");
    Console.WriteLine("t{0}, {1}",
    info.dwMaximumWindowSize.X,
    info.dwMaximumWindowSize.Y);
    // Is this information right?
    Console.WriteLine("Screen Buffer Size:");
    Console.WriteLine("t{0}, {1}",
    info.dwSize.X, info.dwSize.Y);
    Console.WriteLine("Screen Buffer Bounds:");
    Console.WriteLine("t{0}, {1}, {2}, {3}",
    info.srWindow.Left, info.srWindow.Top,
    info.srWindow.Right, info.srWindow.Bottom);
    Console.WriteLine("--------------------");

    // Location of which to begin clearing
    COORD location = new COORD();
    location.X = 0;
    location.Y = 0;
    // What about clearing starting from
    // the second line
    // location.Y = 1;

    // The number of written characters
    uint numChars;

    FillConsoleOutputCharacter(handle, WHITE_SPACE,
    (uint)(info.dwSize.X * info.dwSize.Y),
    location, out numChars);

    // The new cursor location
    COORD cursorLocation = new COORD();
    cursorLocation.X = 0;
    cursorLocation.Y = 0;

    SetConsoleCursorPosition(handle, cursorLocation);
}

Also we can step further and write code that clears a specific portion of the console screen buffer, try this code:

static void Main()
{
    // Require the user to enter his password
    AuthenticateUser();
}

public static void AuthenticateUser()
{
    Console.WriteLine("Please enter your password:");
    Console.Write("> "); // Two characters right

    string input = Console.ReadLine();

    while (input != "MyPassword")
    {
        COORD location = new COORD();
        // The third character
        location.X = 2;
        // The second line
        location.Y = 1;
        ClearConsoleScreen(location);
        input = Console.ReadLine();
    }

    // User authenticated
    Console.WriteLine("Authenticated!");
}

public static void ClearConsoleScreen(COORD location)
{
    // Getting the console output device handle
    IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);

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

    // The number of written characters
    uint numChars;

    FillConsoleOutputCharacter(handle, WHITE_SPACE,
        (uint)(info.dwSize.X * info.dwSize.Y),
        location, out numChars);

    SetConsoleCursorPosition(handle, location);
}

A look inside the .NET library

Here we will look inside the library mscorlib.dll and see how it implements System.Console.Clear() method. This library is the core library that every .NET application must reference, it defines the core classes and components that are essential for every application. Also, it contains classes that are required by the CLR (Common Language Runtime.)

If you are using .NET 2.0 or higher you can continue this section, otherwise, you may pass this section because the Clear() method is new with the .NET 2.0.

MSIL, CIL, and IL all refer to the same thing the intermediate Language.

MSCORLIB stands for Microsoft Common Object Runtime Library.

Using the IL Disassembler

.NET Framework comes with a tool that allows you to reverse-engineer any assembly and view its MSIL (Microsoft Intermediate Language) instructions. This tool called IL Disassembler (ILDasm). You can find this tool in the .NET install directory in %ProgramFiles%Microsoft SDKsWindowsBin for the version 3.5, and Common7bin for versions before.

After opening ILDasm.exe you can open the file mscorlib.dll inside. Click File -> Open and select the library file which located in %WinDir%Microsoft.NETFramework. Figure 7 shows IL Disassembler with the mscorlib.dll opened.

ILDASM + mscorlib_dll

Now open the namespace System, then step down to the System.Console class and double-click the Clear() method to show the IL instructions. Take a look on how the Clear() method do it.

Using another tools

If MSIL seems odd, you may try another perfect tools that can reverse-engineer your assembly to your preferred language (like C# or VB.NET for instance.)

Some famous tools are Lutz Roeder’s .NET Reflector and XenoCode Fox.

For me, I prefer the first tool because it is faster, simpler, and supports many features.

Of course you can reflect (reverse-engineer) an assembly and learn nice tricks from the code inside.

References

Code Sample

Download the Console Library here.

Programmatically Swapping Mouse Buttons

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

Swapping mouse buttons means swapping the two buttons, making the right button acts like the left one and vice versa. This is done -for normal users of course- using the Mouse properties dialog in the control panel. See the next figure.

For we developers you need to do this programmatically, and this is done by a very very trivial -not a joke- Win32 API function, SwapMouseButton (resides on user32.dll). The syntax of the function -in C- is as follows:

BOOL SwapMouseButton(
    BOOL fSwap
    );

This function simply takes a single BOOL argument and returns a BOOL value too.

If fSwap is TRUE then the right mouse button will do the primary functions like the left button -by default- does, and the left button will do the secondary functions that the right button -by default- does.

If the function swapped the buttons, then it will return FALSE, otherwise TRUE.

BOOL can take one of values TRUE (non-zero) or FALSE (zero).

In .NET Framework, you need to write a wrapper to this API function and this wrapper is like this:

// C# Code
[DllImport("user32.dll")]
static extern bool SwapMouseButton(bool fSwap);
' VB.NET
Declare Auto Function SwapMouseButton Lib "user32.dll" _
    (ByVal fSwap As Boolean) As Boolean

The DllImport attribute defines the DLL that contains the function.

The MarshalAs attributes defines how .NET types will be mapped to Win32 types (this process called marshaling). We applied this attribute to the return value of the function and to the single argument of the function.

The static extern modifiers are required for any PInvoke function.

PInvoke stands for Platform Invokation. It’s the process of wrapping an API function to .NET code.

Marshaling is the process of creating a bridge between .NET types and unmanaged types.

For unmanaged BOOL, .NET makes the marshaling process automatically, so you can safely remove the MarshalAsAttribute attributes.

Now it’s the time for the code for calling the function.

// C# Code

public void MakeRightButtonPrimary()
{
    SwapMouseButton(true);
}

public void MakeLeftButtonPrimary()
{
    SwapMouseButton(false);
}
' VB.NET Code

Public Sub MakeRightButtonPrimary()
    SwapMouseButton(True)
End Sub

Public Sub MakeLeftButtonPrimary()
    SwapMouseButton(False)
End Sub

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.