Professional Documents
Culture Documents
POCKET REFERENCE
MARSHALING WITH C#
POCKET REFERENCE
Mohammad Elsheimy
A freelance software developer and trainer
2
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
3
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Mohammad Elsheimy
Mohammad is a MCP, MCTS, and MCPD independent
consultant, trainer, and author. He was born in 1991 in KB,
Egypt.
This book discusses the marshaling process between managed and unmanaged
code using C#, devoted for developers who know the basics of unmanaged code
(especially Windows API) and platform invocation.
For the sake of simplicity, this book focuses on Windows API as the source for
unmanaged code. However, information in this book can be applied to any of the
unmanaged environments.
4
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
CONTENTS AT A GLANCE
Where to Go Next
References
Figures
Code Listings
Glossary
5
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
TABLE OF CONTENTS
What is Marshaling?
Why Marshaling?
Chapter Contents
Overview
Variants
Try It Out!
A Rule of Thumb
Marshaling Booleans
Try It Out!
Try It Out!
6
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Marshaling Handles
Generic Handles
Safe Handles
Critical Handles
Passing Mechanism
Additional Techniques
Encapsulation
Creating Wrappers
Real-World Examples
Summary
Chapter Contents
Overview
Introduction
Try It Out!
Marshaling Unions
Try It Out!
7
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Passing Mechanism
Real-World Examples
Summary
Where to Go Next
References
Figures
Code Listings
Glossary
8
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
9
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
CHAPTER 1
INTRODUCING MARSHALING
WHAT IS MARSHALING ?
WHY MARSHALING ?
You already know that there is no such compatibility between managed and
unmanaged environments. In other words, .NET does not contain such the types
HRESULT, DWORD, and HANDLE that exist in the realm of unmanaged code.
Therefore, you need to find a .NET substitute or create your own if needed. That
is what called marshaling.
10
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Marshaling comes handy when you are working with unmanaged code, whether
you are working with Windows API or COM components. It helps you
interoperating (i.e. working) correctly with these environments by providing a
way to share data between the two environments. Figure 1 shows the
marshaling process, where it fall, and how it is required in the communication
process between the two environments.
11
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
12
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
CHAPTER 2
MARSHALING SIMPLE TYPES
CHAPTER CONTENTS
Chapter Contents
Overview
o Variants
o Try It Out!
A Rule of Thumb
Marshaling Booleans
o Try It Out!
o Try It Out!
Marshaling Handles
o Generic Handles
o Safe Handles
13
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
o Critical Handles
Passing Mechanism
Additional Techniques
o Encapsulation
o Creating Wrappers
Real-World Examples
Summary
OVERVIEW
This chapter discusses the nitty-gritty part of marshaling process. It is the base
for the rest of discussion about marshaling. It is about marshaling simple data
types.
The first section of this chapter breaks data types into two categories, simple and
compound. Simple types (integers, booleans, etc.) are those that are not made of
other types. On the contrary, compound types (structures and classes) are those
types that require special handling and made of other types.
After that, we will dig into the discussion of simple types and we will break them
into two categories, blittable and non-blittable.
Before we end this chapter, we will discuss the passing mechanism and handles
in .NET Framework.
Simple (primitive/basic)
14
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Compound (complex)
Primitive data types are those that are not defined in terms of other data types.
They are the basis for all other types. Examples of managed primitives are
numbers like System.Byte, System.Int32, System.UInt32, and System.Double,
strings like System.Char and System.String, and handles like System.IntPtr.
Compound data types are those that built up of other data types. For example a
class or a structure that encapsulates simple types and other compound types.
We will use terms simple, primitive, and basic types to refer to base types
like integers, strings, etc. Terms compound, and complex types also will be
used interchangeably to refer to classes and structures.
Most data types have common representations in both managed and unmanaged
memory and do not require special handling. These types are called blittable
types because they do not require special handling when passed between
managed and unmanaged code. Other types that require special handling are
called non-blittable types. You can think that most of simple types are blittable
and all of compound types are non-blittable.
The following table lists the blittable data types exist in .NET (their counterparts
in unmanaged code will be covered soon):
Table 2.1 Blittable Types
Managed Type Description
System.SByte 8-bit signed integer.
15
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
You can marshal an unmanaged simple data type by tracking its definition then
finding its counterpart (marshaling type) in the managed environment based on
its definition (we will see how soon.)
The following table lists some of the unmanaged data types in Windows, their
C/C++ keywords, and their counterparts (marshaling types) in .NET. As you
might guess, by tracking each of these unmanaged types, we were able to find its
managed counterpart. Notice that so
Table 2.2 Numeric Data Types
Description Windows Type C/C++ Managed Type C#
Keyword Keyword
8-bit signed CHAR char System.SByte sbyte
integer
Notice that long and int defer from a platform to another and from a
compiler to another. In 32-bit versions of Windows, most compilers refer to
both long and int as 32-bit integers.
16
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Some types are based on the version of Windows. DWORD, for instance, is 32
bits on 32-bit versions and 64 bits on 64-bit versions of Windows. This
writing assumes that it is a 32-bit version of Windows.
Know that there is no difference between Windows data types and C/C++
data types. Windows data types are just aliases for the actual C types.
Do not be confused with the many types that refer to one thing, they are all
just names (aliases.) INT, INT32, LONG, and LONG32 are all 32-bit integers
for instance.
If you need to learn more about these types, check out the article Windows Data
Types in MSDN library.
In addition to the numeric data types, you will need to know how to marshal
unmanaged textual data types (a single character or a string.) However, these
types are non-blittable, so they require special handling.
As we have said, for the sake of simplicity, we will use Windows API as the base
for our discussion in this book. Therefore, you need to know that all Windows
Data Types (INT, DWORD, etc.) are just names (technically, typedefs) for the
actual C types. Therefore, many names may refer to one thing just as INT and
LONG.
17
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Thus, we can say that LONG is defined as C int and DWORD is defined as C
unsigned long.
INT and LONG are easy to marshal. However, there are primitive types that you
will need to track their definitions to know how to marshal it.
The next are some of the types defined as another types. You can think of these
types as aliases for the base types. Yet, some are platform-specific, and others
not.
HRESULT:
As you will see, plenty of functions return a HRESULT to represent the
status of the operation. If HRESULT equals to zero, then the function
succeeded, otherwise it represents the error code or status information
for the operation. HRESULT defined as LONG, and LONG in turn defined as
a 32-bit signed integer. Therefore, you can marshal HRESULT as
System.Int32.
HFILE:
A handle to a file opened using one of the Windows File IO functions like
OpenFile() function. This type is defined as INT, and INT in turn is defined
as a 32-bit signed integer. Therefore, you can marshal HFILE as
System.Int32. Although, HFILE defined as INT, handles should be
marshaled as System.IntPtr, which is internally encapsulates the raw
handle. To be clear, you would better marshal an unmanaged handle as a
System.Runtime.InteropServices.SafeHandle or CriticalHandle, this is the
ideal marshaling type for any handle. Hence, file handles best marshaled
as Microsoft.Win32.SafeHandles.SafeFileHandle that is derived from
SafeHandleZeroOrMinusOneIsInvalid that is in turn derived from the
abstract class System.Runtime.InteropServices.SafeHandle. For more
details about handles, refer to the section "Marshaling Handles" later in
this chapter.
18
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
In addition, there are types that are variable based on the operating system.
Examples are:
INT_PTR:
A pointer to a signed integer. Defined as INT64 if this is a 64-bit OS, or INT
otherwise.
LONG_PTR:
A pointer to a signed long. Defined as INT64 if this is a 64-bit OS, or LONG
otherwise.
UINT_PTR:
A pointer to an unsigned integer. Defined as DWORD64 if this is a 64-bit
OS, or DWORD otherwise.
ULONG_PTR:
A pointer to an unsigned long. Defined as DWORD64 if this is a 64-bit OS,
or DWORD otherwise.
Keep in mind that there is a big difference between a variable and a pointer to a
variable. A variable refers directly to its value into the memory. However, a
pointer contains an address of another value into the memory. Consider the
following illustration, Figure 2.1:
In the illustration above, the variable i contains the value 320 and you can get the
value from the variable directly. The pointer ptr on the other hand contains the
19
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
address of the variable i. Thus, it indirectly contains the value of the variable i.
That is why we cannot get the value of the pointer directly. We need to
dereference it first before retrieving its value.
In addition, for textual data types, there are types variable based on Unicode
definition (strings and buffers are covered soon.) Examples are:
PTSTR:
Defined as PWSTR if UNICODE defined, otherwise PSTR.
Notice that some types have special characters in their names. For example, A in
textual data types stands for ANSI, and W in stands for Wide, which means
Unicode. In addition, the letter T in textual information too means it varies based
on OS. Another example is the prefix P (lowercase,) it means a pointer, and LP
means a long pointer. LPC stands for long pointer to a constant.
VARIANTS
In addition, Win32 API defines the types VOID, LPVOID, and LPCVOID. VOID
indicates that the function does accept no arguments. Consider the following
function:
DWORD GetVersion(VOID);
It is required to tag the function with VOID if it does not accept any arguments
(that is one of the specifications of C89.) Notice that VOID is defined as void.
LPVOID and LPCVOID are defined as any type (variant). That means that they can
accept any value. They can be marshaled as integers, strings, handles, or even
compound types, anything you want. In addition, you can marshal them as
System.IntPtr, so you can set them to the address of any object in memory. In
addition, you can marshal them as pointers to object. For example, marshaling a
LPCVOID as System.Int32* (a pointer to an integer) in unsafe code. Moreover,
you can use unsafe code and marshal them as void*. Furthermore, you can
20
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
marshal them as System.Object, so you can set them to any type (refer to chapter
6 for more information about memory management and unsafe code.)
If you have worked with traditional Visual Basic, thinking about LPVOID
and LOCVOID as a Variant could help too much.
If you are interoperating with the traditional Visual Basic code, you can use
the same way we did on marshaling LPVOID and LPCVOID in marshaling
the type Variant.
TRY IT OUT!
Now, we will try to create the PInvoke method for the MessageBoxEx() function.
The example demonstrates how to control precisely the marshaling process
using the MarshalAsAttribute attribute. We will cover this attribute and more in
the last chapter of this book: "Controlling the Marshaling Process." Handles are
covered in the section: "Marshaling Handles" of this chapter.
The following example creates the PInvoke method for the MessageBoxEx()
function and calls it to display a friendly message to the user.
HWND hWnd,
LPCTSTR lpText,
LPCTSTR lpCaption,
UINT uType,
WORD wLanguageId
);
And here is the managed signature (the PInvoke method) of this function:
In order for the example to run you must add a using statement to
System.Runtime.InteropServices namespace. Be sure to add it for all
examples throughout this book.
Listing 2.2 MessageBoxEx() Managed Signature
// CharSet.Unicode defines the UNICODE.
21
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
// the whole function, or you can control
// MarshalAsAttribute attribute
[return: MarshalAs(UnmanagedType.I4)]
(IntPtr hWnd,
[param: MarshalAs(UnmanagedType.LPTStr)]
String lpText,
[param: MarshalAs(UnmanagedType.LPTStr)]
String lpCaption,
[param: MarshalAs(UnmanagedType.U4)]
UInt32 uType,
[param: MarshalAs(UnmanagedType.U2)]
UInt16 wLanguageId);
A RULE OF THUMB
Keep in mind that. .NET Framework allows you to take a granular level of control
over the marshaling process and that would be very complicated. However,
things can be so simple.
You can ignore attributes in most cases and just use the counterparts and CLR
will do its best. Likely, you are not required to use managed signed integers for
22
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
unmanaged equivalents. You can use managed signed integers for unmanaged
unsigned integers and vice versa. You can also marshal a SHORT as System.Char!
The key point is that as long as the managed marshal type occupies the same
memory size as the unmanaged type, you are in safe. However, keeping things in
its right position helps avoiding undesirable errors that maybe very difficult to
know and handle.
Another thing that you should keep in mind that the information in this book can
be applied to any unmanaged environment. You can apply this information when
interoperating with Windows API, C/C++ libraries, Visual Basic, COM, OLE,
ActiveX, etc. However, for the sake of simplicity, we will talk about the Windows
API as the source of the unmanaged code.
MARSHALING BOOLEANS
In general, marshaling simple data types is very easy and booleans are no
exception. However, Booleans are non-blittable types. Therefore, they require
some handling.
There are some notes about marshaling booleans in the managed environment.
The first thing to mention about is that Windows defines two types of Boolean
variables:
1. BOOL:
Defined as INT, therefore, it is 4-bytes wide.
2. BOOLEAN:
Defined as BYTE, therefore it is only 1-byte.
Both can be set to non-zero to indicate a true (TRUE) value, and zero otherwise
(FALSE.)
Again, the two types exist only in the Windows SDK. Other environments
may define other types with similar names.
While it is true that BOOL and BOOLEAN are best marshaled as System.Boolean,
BOOL can be marshaled as System.Int32 too, because it is defined as a 32-bit
integer. On the other hand, BOOLEAN can be marshaled as System.Byte or
System.U1, because it is defined as 8-bits integer. Do you remember our rule of
thumb?
23
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Take into consideration that whether you are marshaling your Boolean type to
System.Boolean, System.Int32, or System.Byte, it is recommended that you apply
MarshalAsAttribute attribute to the variable to specify the underlying
unmanaged type. For example, to specify that the underlying type is BOOL,
specify UnmanagedType.Bool (recommended) or UnmanagedType.I4 in the
MarshalAsAttribute constructor. On the other hand, BOOLEAN can be specified
as UnmanagedType.U1. If you omit MarshalAsAttribute, CLR assumes the default
behavior for System.Boolean, which is 2 bytes wide. For more information about
MarshalAsAttribute attribute, see the last chapter: "Controlling the Marshaling
Process."
TRY IT OUT!
[return: MarshalAs(UnmanagedType.Bool)]
// [return: MarshalAs(UnmanagedType.I4)]
Handles covered soon. For now, it is OK to know that all handles marshaled
to System.IntPtr.
This section discusses how to marshal strings and buffers. We will use the terms
string and buffer interchangeably to refer to a sequence of characters.
Two types exist in the managed environment for marshaling unmanaged string
buffers. They are System.String and System.Text.StringBuilder. Of course, they
24
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Every time you use one of the methods of System.String class or you pass a
System.String to a function, normally, you create a new string object in memory,
which requires a new allocation of memory space for the new object. In addition,
if the function changes the string you will not get the results back. That is why
System.String is called immutable. On the other hand, StringBuilder does not
require re-allocating of space unless you exceed its capacity. Besides the talk
about marshaling, you should use StringBuilder to accommodate performance
issues if you often change the same string many times.
To keep System.String immutable, the marshaler copies the contents of the string
to another buffer before calling the function, and then it passes that buffer to the
function. If you were passing the string by reference, the marshaler copies the
contents of the buffer into the original string when returning from the function.
Another point to mention is that you can pass array of System.Char in place of a
System.String or System.Text.StringBuilder. In other words, you can marshal
unmanaged strings as managed arrays of System.Char (or System.Int16, do you
remember?)
25
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Unicode-encoded, it can hold one of 65536 characters, which is very good for
most languages.
If you need more information about Unicode, you can check the official site
of Unicode, www.Unicode.org. In addition, Programming Windows 5th by
Charles Petzold includes a must-read introduction of Unicode and
character sets.
For controlling character encoding when marshaling unmanaged types, you may
take one of two approaches or you can combine them as needed. You can control
the encoding of the overall function (i.e. at the function level,) or you can drill
down and control the encoding process at a granular level by controlling every
argument separately (the second approach is required in certain cases e.g.
MultiByteToWideChar() function.)
For changing the encoding of the overall function, DllImportAttribute offers the
property CharSet that indicates the encoding (character set) for the strings and
arguments of the function. This property can take one of several values:
CharSet.Unicode:
Strings are always 16-bit Unicode-encoded.
CharSet.None:
Obsolete. Has the same behavior as CharSet.Ansi.
Take into consideration that if you have not set the CharSet property, CLR
automatically sets it to CharSet.Auto. However, some languages override the
default behavior. For example, C# defaults to CharSet.Ansi.
It is worth mentioning that plenty of functions that accept strings and buffers are
just names (technically typedefs)! They are not real functions, they are entry-
points (aliases) for the real functions. For example, ReadConsole() function is
nothing except an entry point redirects the call to the right function, either
ReadConsoleA() if ANSI is defined, or ReadConsoleW() if Unicode is defined (A
stands for ANSI, and W stands for Wide which means Unicode.) Therefore, you
can actually bypass this entry-point by changing the PInvoke method name to
match the right function or by changing DllImportAttribute.EntryPoint to the
name of the required function. In both cases, setting DllImportAttribute.CharSet
along with is no use.
26
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
If you want to control the encoding at a granular level, you can apply the
MarshalAsAttribute attribute to the argument specifying the underlying
unmanaged type.
Usually, you will need to unify the character encoding of all your native functions
and types. This is, all the functions should be either Unicode or ANSI. Under rare
occasions, some functions would be different in character encoding.
It is worth mentioning that, for fixed-length strings you will need to set the
SizeConst property of MarshalAsAttribute to the buffer length.
These techniques are not limited to arguments only! You can use them with
variables of compound types too. We will look at compound types in the
following chapter.
TRY IT OUT!
DWORD nStdHandle
);
BOOL ReadConsole(
HANDLE hConsoleInput,
DWORD nNumberOfCharsToRead,
LPVOID lpReserved
);
DWORD GetLastError(void);
DWORD FormatMessage(
DWORD dwFlags,
LPCVOID lpSource,
DWORD dwMessageId,
27
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
DWORD dwLanguageId,
DWORD nSize,
va_list* Arguments
);
And this is the managed version along with the driver code.
Listing 2.6 Reading from the Console Screen Buffer Example
// For retrieving a handle to a specific console device
[DllImport("Kernel32.dll")]
[param: MarshalAs(UnmanagedType.U4)]
int nStdHandle);
[return: MarshalAs(UnmanagedType.Bool)]
IntPtr hConsoleInput,
// [param: MarshalAs(UnmanagedType.AsAny)]
StringBuilder lpBuffer,
[param: MarshalAs(UnmanagedType.U4)]
uint nNumberOfCharsToRead,
28
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
[param: MarshalAs(UnmanagedType.AsAny)]
uint lpReserved);
[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.U4)]
[return: MarshalAs(UnmanagedType.U4)]
// Options
[param: MarshalAs(UnmanagedType.U4)]
uint dwFlags,
// [param: MarshalAs(UnmanagedType.AsAny)]
[param: MarshalAs(UnmanagedType.U4)]
uint lpSource,
[param: MarshalAs(UnmanagedType.U4)]
uint dwMessageId,
// Language ID (Reserved)
[param: MarshalAs(UnmanagedType.U4)]
uint dwLanguageId,
[param: MarshalAs(UnmanagedType.U4)]
uint nSize,
// Additional options
29
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
[param: MarshalAs(UnmanagedType.U4)]
uint Arguments);
// Message Options
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_IGNORE_INSERTS |
FORMAT_MESSAGE_FROM_SYSTEM;
// Message Source
uint noCharacters;
string errMsg;
FormatMessage(FORMAT_MESSAGE_FLAGS,
FORMAT_MESSAGE_FROM_HMODULE,
GetLastError(),
30
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
0, // Means NULL
out errMsg,
0, // Maximum length
Console.WriteLine("ERROR:\n{0}", errMsg);
builder.ToString().Substring(0,
builder.Length - Environment.NewLine.Length));
// Invalid handle
handle = GetStdHandle(12345);
string errMsg;
FormatMessage(FORMAT_MESSAGE_FLAGS,
FORMAT_MESSAGE_FROM_HMODULE,
GetLastError(),
0, // Means NULL
out errMsg,
0, // Maximum length
31
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Console.WriteLine("ERROR: {0}", errMsg);
builder.ToString().Substring(0,
builder.Length - Environment.NewLine.Length));
32
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Does this seem strange for you? Start by consulting the documentation
about binary notation.
MARSHALING HANDLES
GENERIC HANDLES
There are plenty of type handles in unmanaged code, here is some of them:
HANDLE:
This is the most widely used handle type in the unmanaged environment.
It represents a generic handle.
HWND:
Most widely used with Windows application. It is a handle to a window or
a control.
HFILE:
A handle to a file opened by any of Windows File IO functions like
OpenFile() function.
HMENU:
A handle to a menu or menu item.
Again, from all you have seen, you may have noticed that most types
identified by a prefix or a suffix. For example, handles prefixed with the
letter H, while some pointers have the suffix _PTR, or the prefix P or LP.
While strings with letter W are Unicode-encoded, and strings with letter T
are OS-based.
33
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
but it provides more control over the object in memory. For more information
about memory management, see chapter 6: Memory Management.
In addition, starting from version 2.0, new managed types for working with
unmanaged handles added to the .NET Framework. A new namespace
Microsoft.Win32.SafeHandles that contains most of the new types has been
added too. Other types exist in System.Runtime.InteropServices. These types
called managed handles.
There are two kinds of managed handles safe and critical handles.
SAFE HA NDLES
34
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
As the diagram illustrates, SafeHandle is the base class that represents any safe
handle. It inherits from
System.Runtime.ConstrainedExecution.CriticalFinalizerObject that ensures the
finalization process. The following are the most common members of
SafeHandle:
IsClosed:
Returns a value indicates whether the handle is closed.
IsInvalid:
Abstract. If overridden, returns a value indicates whether the handle is
invalid or not.
35
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
ReleaseHandle():
Protected Abstract. Use to provide handle clean-up code. This function
should returns true if successfully released, or false otherwise. In the case
of false, it generates a ReleaseHandleFailed Managed Debugging Assistant
(MDA) exception that will not interrupt your code but provides you with a
bad sign about it. Keep in mind that ReleaseHandle() called internally by
Dispose().
SetHandle():
Protected. Sets the handle to the specified pre-existing handle.
SetHandleAsInvalid():
Sets the handle as invalid so it is no longer used.
DangerousGetHandle():
Returns System.IntPtr that represents the handle. Beware that if you have
called SetHandleAsInvalid() before calling DangerousGetHandle(), it
returns the original handle not the invalid one.
DangerousRelease():
Manually releasing the handle in unsafe manner. It is recommended using
Close() or Dispose() methods instead.
DangerousAddRef():
Increments the reference count of the handle. It is not recommended
using neither DangerousRelease() nor DangerousAddRef(), use safe
methods instead. However, when working with COM, you will find
yourself using these functions
Do not use unsafe methods unless you really need to use it because they pass
the protection level offered by safe handles.
Because SafeHandle is abstract, you must either implement it or use one of its
implementation classes. Only two classes from the new namespace
Microsoft.Win32.SafeHandles implement SafeHandle, both are abstract too:
SafeHandleMinusOneIsInvalid:
Represents a safe handle of which a value of -1 indicates that the handle is
invalid. Therefore, IsInvalid returns true only if the handle equals to -1.
SafeHandleZeroOrMinusOneIsInvalid:
Represents a safe handle of which a value of 0 or -1 indicates that the
handle is invalid. So, IsInvalid returns true only if the handle equals to 0
or -1.
36
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Notice that, choosing between the two implementations is up to the type of the
underlying handle. If it considered invalid if set to -1, use
SafeHandleMinusOneIsInvalid. If it considered invalid if set to 0 or -1, use
SafeHandleZeroOrMinusOneIsInvalid. Using the right class for the handle
ensures that methods like IsInvalid() returns correct results. It also ensures that
CLR will mark the handle as garbage only if it is invalid.
If you need to provide a safe handle for your object, you will need to inherit from
SafeHandleMinusOneIsInvalid, SafeHandleZeroOrMinusOneIsInvalid, or even
from SafeHandle. Be aware that, you will always need to override the
ReleaseHandle() method because neither SafeHandleMinusOneIsInvalid nor
SafeHandleZeroOrMinusOneIsInvalid does override it.
SafeFileHandle:
A wrapper class for an IO device handle (e.g. HFILE.) This class internally
overrides the ReleaseHandle() and calls the unmanaged CloseHandle()
function to close the handle. Use when working with HFILE handles in
Windows File IO functions like OpenFile() and CreateFile(). Internally,
System.FileStream uses a HFILE as SafeFileHandle, and it exposes a
constructor that accepts SafeFileHandle.
SafeWaitHandle:
If you are working with unmanaged thread synchronization objects like a
Mutex or an Event, then this should be the desired marshaling type for
synchronization objects' handles.
Now, we are going to create a file using CreateFile() function with SafeFileHandle
for the marshaling process. The definition of CreateFile() is as following:
Listing 2.7 CreateFile() Unmanaged Signature
HANDLE CreateFile(
LPCTSTR lpFileName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposition,
DWORD dwFlagsAndAttributes,
HANDLE hTemplateFile
);
37
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
string lpFileName,
uint dwDesiredAccess,
uint dwShareMode,
IntPtr lpSecurityAttributes,
uint dwCreationDisposition,
uint dwFlagsAndAttributes,
SafeFileHandle hTemplateFile);
SafeFileHandle handle =
CreateFile("C:\\MyFile.txt",
38
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
GENERIC_WRITE,
FILE_SHARE_NONE,
IntPtr.Zero, // NULL
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
// SafeHandleZeroOrMinusOneIsInvalid
if (handle.IsInvalid) // 0 or -1
return;
writer.WriteLine("Hello, World!");
writer.Close();
/*
* StreamWriter.Close()
* - StreamWriter.BaseStream.Close()
* - - FileStream.SafeFileHandle.Close()
* - - - SafeHandleZeroOrMinusOneIsInvalid
* .Close()
39
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
* - - - - SafeHandle.Close()
* - - - - - SafeHandle.ReleaseHandle()
*/
The next example demonstrates how to create your custom safe handle. This
custom safe handle represents a handle invalid only if equals to zero. Although,
you can extend the functionality of either SafeHandleMinusOneIsInvalid or
SafeHandleZeroOrMinusOneIsInvalid, we have inherited SafeHandle directly.
Code is very simple:
Listing 2.9 Custom Safe Handle Example
public sealed class SafeHandleZeroIsInvalid : SafeHandle
[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public SafeHandleZeroIsInvalid
: base(IntPtr.Zero, ownsHandle)
this.SetHandle(preexistingHandle);
40
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
get
// this.handle.ToInt32() == 0
return CloseHandle(this.handle);
Until now, I do not have an answer for why a handle could be invalid only if
it is set to zero! Maybe you will need this for your custom handles. However,
this is just an illustration.
CRITICAL HANDLES
Critical handles are the same as safe handles, except that they do not perform
reference counting, so they do not provide protection from recycling security
attacks.
41
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
As the diagram illustrates, CriticalHandle is the base class that represents any
critical handle. It inherits from
System.Runtime.ConstrainedExecution.CriticalFinalizerObject that ensures the
finalization process. The members of CriticalHandle are the same as SafeHandle,
except that it does not include the Dangerous-prefixed methods because critical
handles themselves are dangerous because they do not provide the necessary
protection. For more information about CriticalHandle members, refer to
members of SafeHandle discussed previously.
Because CriticalHandle is abstract, you must either implement it or use one of its
implementation classes. Only two classes from the new namespace
Microsoft.Win32.SafeHandles implement CriticalHandle, both are abstract too:
CriticalHandleMinusOneIsInvalid:
Represents a critical handle of which a value of -1 indicates that the
handle is invalid. Therefore, IsInvalid returns true only if the handle
equals to -1.
42
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
CriticalHandleZeroOrMinusOneIsInvalid:
Represents a critical handle of which a value of 0 or -1 indicates that the
handle is invalid. So, IsInvalid returns true only if the handle equals to 0
or -1.
Examples are the same as SafeHandle, only to change the type name.
PASSING MECHANISM
When passing an argument to a function, the function may require either passing
the argument by value or by reference. If the function intends to change
argument value, it requires it to be passed by reference, otherwise, by value. This
is what called passing mechanism.
It is worth mentioning that Windows API does not support reference arguments.
Instead, if a function requires an argument to be passed by reference, it declares
it as a pointer so that caller can see the applied changes. Other code such as COM
libraries can require either a pointer or a reference argument. In either cases,
you can safely apply the changes required. You can also marshal a pointer
argument as System.IntPtr or as the unsafe void* for example.
43
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
A great deal of functions require Out arguments specially for returning results or
status information. Good examples are ReadConsole() and WriteConsole() that
require by-reference Out arguments for returning the characters read/written.
The following is the unmanaged signature for the WriteConsole() function.
Listing 2.10 WriteConsole() Unmanaged Signature
BOOL WriteConsole(
HANDLE hConsoleOutput,
VOID lpBuffer,
DWORD nNumberOfCharsToWrite,
LPDWORD lpNumberOfCharsWritten,
LPVOID lpReserved
);
And this is the managed version along with the driver code:
Listing 2.11 Writing to Console Screen Example
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
IntPtr hConsoleOutput,
String lpBuffer,
[param: MarshalAs(UnmanagedType.U4)]
UInt32 nNumberOfCharsToWrite,
[param: MarshalAs(UnmanagedType.U4)]
[param: MarshalAs(UnmanagedType.AsAny)]
object lpReserved);
[DllImport("Kernel32.dll")]
[param: MarshalAs(UnmanagedType.U4)]
44
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Int32 nStdHandle);
uint noCharactersWritten;
WriteConsole(handle,
textToWrite,
(uint)textToWrite.Length,
out noCharactersWritten,
null);
noCharactersWritten);
Finally yet importantly, chapter 6 provides you with more granular and down-
level details about the memory management and the passing mechanism.
ADDITIONAL TECHNIQUES
Here we will talk about techniques that should be taken into consideration when
working with unmanaged code, they are encapsulation, creating wrappers,
working with nullable arguments, and working out CLS problem.
ENCAPSULATION
If the function requires an argument that can be set to a value or more, you can
define these values (constants or typedefs) in an enumeration so you can easily
access every set of values separately; that technique called encapsulation
45
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
(grouping.) The following example shows the MessageBoxEx() example, the most
suitable function for the example:
Listing 2.12 Message Box Example
[DllImport("User32.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.I4)]
(IntPtr hWnd,
[param: MarshalAs(UnmanagedType.LPTStr)]
String lpText,
[param: MarshalAs(UnmanagedType.LPTStr)]
String lpCaption,
[param: MarshalAs(UnmanagedType.U4)]
UInt32 uType,
[param: MarshalAs(UnmanagedType.U2)]
UInt16 wLanguageId);
MB_OK = 0x0,
MB_OKCANCEL = 0x1,
MB_ABORTRETRYIGNORE = 0x2,
MB_YESNOCANCEL = 0x3,
MB_YESNO = 0x4,
MB_RETRYCANCEL = 0x5,
MB_HELP = 0x4000,
MB_ICONHAND = 0x10,
MB_ICONQUESTION = 0x20,
MB_ICONEXCLAMATION = 0x30,
MB_ICONASTERISK = 0x40,
MB_ICONERROR = MB_ICONHAND,
MB_ICONSTOP = MB_ICONHAND,
46
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
MB_ICONWARNING = MB_ICONEXCLAMATION,
MB_ICONINFORMATION = MB_ICONASTERISK,
MB_DEFBUTTON1 = 0x0,
MB_DEFBUTTON2 = 0x100,
MB_DEFBUTTON3 = 0x200,
MB_DEFBUTTON4 = 0x300,
MB_APPLMODAL = 0x0,
MB_SYSTEMMODAL = 0x1000,
MB_TASKMODAL = 0x2000,
MB_SETFOREGROUND = 0x10000,
MB_DEFAULT_DESKTOP_ONLY = 0x20000,
MB_SERVICE_NOTIFICATION_NT3X = 0x40000,
MB_TOPMOST = 0x40000,
MB_RIGHT = 0x80000,
MB_RTLREADING = 0x100000,
MB_SERVICE_NOTIFICATION = 0x200000,
IDOK = 1,
IDCANCEL = 2,
IDABORT = 3,
IDRETRY = 4,
IDIGNORE = 5,
47
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
IDYES = 6,
IDNO = 7,
IDCLOSE = 8,
IDHELP = 9,
IDTRYAGAIN = 10,
IDCONTINUE = 11,
"MyApplication",
(UInt32)MB_BUTTON.MB_YESNOCANCEL |
(UInt32)MB_ICON.MB_ICONQUESTION |
(UInt32)MB_DEF_BUTTON.MB_DEFBUTTON3 |
(UInt32)MB_SPECIAL.MB_TOPMOST,
0);// Reserved
Console.WriteLine("ERROR");
else
if (ret == MB_RETURN.IDYES)
48
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
You could also change the names of the constants to friendly names.
Figure 2.4 shows the message box resulted from running of the last code.
[param: MarshalAs(UnmanagedType.U4)]
CONSOLE_STD_HANDLE nStdHandle);
STD_INPUT_HANDLE = -10,
STD_OUTPUT_HANDLE = -11,
STD_ERROR_HANDLE = -12
IntPtr handle;
handle =
GetStdHandle(CONSOLE_STD_HANDLE.STD_INPUT_HANDLE);
if (handle == IntPtr.Zero)
Console.WriteLine("Failed!");
else
Console.WriteLine("Succeeded!");
49
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
CREATING WRAPPERS
Exposing PInvoke methods to the outside the assembly is not a good practice. It
is always recommended that you group your PInvoke methods into an internal
class, and that class should be named as NativeMethods, SafeNativeMethods or
UnsafeNativeMethods. For more information about this, check Code Analyzing
Rules in MSDN documentation. Read Move PInvokes to Native Methods Class
article.
The following code segment illustrates the wrapper method for our
MessageBoxEx() function:
Listing 2.14 Message Box Example Revised
public static MB_RETURN MessageBox
"MyApplication",
(UInt32)buttons |
(UInt32)icon |
(UInt32)defaultButton |
(UInt32)modality |
(UInt32)options,
0);
if (result == 0)
return (MB_RETURN)result;
50
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Some function arguments are nullable. Means that they can take a NULL (null in
C#) value. To pass a NULL value to an argument, you can marshal this argument
as System.IntPtr, so you can set it to System.IntPtr.Zero to represent a NULL
value. Another trick here is creating an overload for the function, in which the
first is marshaled as the argument type, and the other is marshaled as
System.IntPtr. Thus, if you pass a System.IntPtr.Zero, CLR directs the call to the
function with System.IntPtr. Conversely, passing a value to the argument, directs
the call to the function with the correct type. The following code segment
demonstrates this technique:
[return: MarshalAs(UnmanagedType.Bool)]
IntPtr hConsoleOutput,
SMALL_RECT lpScrollRectangle,
SMALL_RECT lpClipRectangle,
COORD dwDestinationOrigin,
CHAR_INFO lpFill);
[return: MarshalAs(UnmanagedType.Bool)]
IntPtr hConsoleOutput,
SMALL_RECT lpScrollRectangle,
IntPtr lpClipRectangle,
COORD dwDestinationOrigin,
CHAR_INFO lpFill);
...
You should know that some types are non-CLS-compliant and you should avoid
exposing them outside the assembly. For example, the famous System.UInt32 is
non-CLS-compliant, and you strictly should not expose it.
51
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Being non-CLS-compliant means that the type violates with CLS (Common
Language Specifications) specifications. Following CLS specifications helps the
interoperation of .NET languages. It helps avoiding some actions like declaring
specific types or following uncommon naming conventions.
Why to avoid such these acts? This helps the big goal of .NET Framework, the
interoperation of .NET languages. Some languages for example does not support
variable names beginning with an underscore (_) others do. Therefore, following
the CLS specifications allows your assembly to be callable from any other
assembly build with any language easily.
To force the check of CLS specification, you can decorate the assembly with
System.CLSCompliantAttribute attribute -specifying true,- and that would result
in compiler warnings whenever you try to expose non-CLS-compliant type out.
To work out this CLS dilemma, for functions require UInt32 as an argument, you
can create a wrapper that behaves as an entry-point to the private non-CLS-
compliant method. That wrapper method accepts, for instance, System.Int32 and
converts it internally to System.UInt32.
For structures, you can declare the structure as internal and continue using it the
normal way.
Again, you could replace all non-CLS-compliant types like System.UInt32 with
CLS-compliant equivalents like System.Int32 and take advantage of easily
distributing your types and assembly. However, that would not be easy in all
cases.
REAL-WORLD EXAMPLES
The following are some real-world examples that solve problems that you might
face while developing your application. Those problems can be solved only via
interoperability with unmanaged code.
The following code swaps mouse buttons programmatically. It makes the left
button acts like the right button (e.g. opens the context menu) and vice versa.
52
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Listing 2.16 Swapping Mouse Buttons Sample
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
SwapMouseButton(true);
SwapMouseButton(false);
The following code shows how to turn on the screen saver programmatically.
Listing 2.19 Dragging a Form without a Title Bar Sample
[DllImport("User32.dll")]
(IntPtr hWnd,
uint Msg,
uint wParam,
uint lParam);
HWND_DESKTOP = 0x0,
HWND_BROADCAST = 0xFFFF
53
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
}
SendMessage(
new IntPtr((int)SpecialHandles.HWND_BROADCAST),
WM_SYSCOMMAND,
SC_SCREENSAVE,
0);
The following code allows the form to be dragged from its body. This code is a
good example for the wrapper creating technique discussed earlier.
Listing 2.18 Dragging a Form without a Title Bar Sample
SafeNativeMethods.cs
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.I4)]
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)]
54
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
public const uint WM_NCLBUTTONDOWN = 0xA1; // 161
HelperMethods.cs
SafeNativeMethods.ReleaseCapture();
SafeNativeMethods.SendMessage
(hWnd, SafeNativeMethods.WM_NCLBUTTONDOWN,
SafeNativeMethods.HTCAPTION, 0);
MainForm.cs
HelperMethods.MoveObject(this.Handle);
55
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
SUMMARY
The last word to say is that MarshalAsAttribute is not required all the time.
Sometimes it is optional, and other times it is required.
For example, if you marshal blittable data types like DWORD, you can safely
ignore MarshalAsAttribute. Conversely, if you are marshaling non-blittable data
types like booleans and strings, you will need to use the MarshalAsAttribute to
ensure correct marshaling process. However, it is always better giving the CLR
and other developers a notation about the underlying data type by apply the
MarshalAsAttribute attribute to blittable data types too.
Finally yet importantly, this chapter was the key for the gate to the
interoperation with unmanaged environments. It discussed the most important
part of the marshaling process, marshaling the simple types, which you will
always need to keep it into your mind.
Next, you will learn how to work with compound types and marshal them in your
managed environment.
56
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
57
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
CHAPTER 3
M A R S H A L I N G C O M P O U N D T Y P ES
CHAPTER CONTENTS
Chapter Contents
Overview
Introduction
o Try It Out!
Marshaling Unions
o Try It Out!
Passing Mechanism
Real-World Examples
Summary
58
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
OVERVIEW
Like the previous chapter. This chapter breaks unmanaged compound types into
two categories, structures and unions. We first discuss structures and then we
will dive into unions and how to marshal them.
You might ask, why you have divided compound types into just two categories,
structures and unions, I can create classes too? The answer is easy. For its
simplicity, this book will focus primarily on Windows API. Therefore, you will
find much of our talking about Win32 functions and structures. However, the
same rules apply to classes and other unmanaged types.
INTRODUCTION
Unmanaged Structures
Unmanaged Unions
59
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
When marshaling structures in the managed environment, you must take into
consideration that while you access a variable into your by its name, Windows
accesses it via its address (i.e. position) inside the memory, it does not care about
field name, but it cares about its location and size. Therefore, the memory layout
and size of the type are very crucial.
2. Add the type fields (variables) only. Again, layout and size of the type are
very crucial. Therefore, fields must be ordered as they are defined, so that
the Windows can access them correctly.
When marshaling an unmanaged structure, you must take care of how that type
is laid-out into memory.
Actually, application memory is divided into blocks (in a 4-bytes base,) and every
block has its own address. When you declare a variable or a type in your
program it is stored inside the memory and got its memory address.
Consequently, all data members inside a structure have their own addresses that
are relative to the address of the beginning of the structure.
SHORT Left;
SHORT Top;
SHORT Right;
SHORT Bottom;
};
60
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
SHORT X;
SHORT Y;
};
When we declare those structures in our code they are laid-out into memory and
got addresses like that:
Thus, you should keep in mind that the size and location of each of type members
is very crucial and you strictly should take care of how this type is laid-out into
the memory.
For now, you do not have to think about the last illustration. We will cover
memory management in details in chapter 6.
For handling the memory layout problem, you must apply the
StructLayoutAttribute attribute to your marshaling type specifying the layout
kind using the LayoutKind property.
61
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
LayoutKind.Auto (Default):
Lets the CLR chooses how the type is laid-out into memory. Setting this
value prevents interoperation with this type, that means that you will not
be able to marshal the unmanaged structure with this type, and if you
tried, an exception will be thrown.
LayoutKind.Sequential:
Variables of the type are laid-out sequentially. When setting this value
ensure that all variables are on the right order as they are defined in the
unmanaged structure.
LayoutKind.Explicit:
Lets you control precisely each variables location inside the type. When
setting this value, you must apply the FieldOffsetAttribute attribute to
every variable in your type specifying the relative position in bytes of the
variable to the start of the type. Note that when setting this value, order of
variables becomes unimportant.
For the sake of simplicity, you should lay-out all of your types sequentially.
However, when working with unions, you are required to explicitly control every
variables location. Unions are covered in the next section.
We have said that you should add only the type members into the
marshaling type, however, this is not always true. In structures where there
is a member that you can set to determine the structure size (like the
OPENFILENAME structure,) you can add your own members to the end of
the structure. However, you should set the size member to the size of the
entire structure minus the new members that you have added. This
technique is discussed in details in chapter 6.
TRY IT OUT!
Next is the managed signature for both the structures. Note that you can marshal
them as managed classes too.
Listing 3.2 SMALL_RECT and COORD Managed Signature
// Laying-out the structure sequentially
[StructLayout(LayoutKind.Sequential)]
62
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
{
[StructLayout(LayoutKind.Sequential)]
public UInt16 X;
public UInt16 Y;
MARSHALING UNIONS
In fact, unions share structures lots of characteristics, like the way they defined
and marshaled. It might be helpful to know that, like structures, unions can be
defined inside a structure or even as a single entity. In addition, unions can
define compound types inside, like structures too.
63
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Listing 3.3 SOME_CHARACTER Unmanaged Signature
typedef union SOME_CHARACTER {
int i;
char c;
};
This was a simple union defines a character. It declared two members, i and c, it
defined them in the same memory location. Thus, it provides two ways for
accessing the character, by its code (int) and by its value (char). For this to work
it allocates enough memory storage for holding the largest member of the union
and that member is called container. Other members will overlap with the
container. In our case, the container is i because it is 4 bytes (on Win32, 16 on
Win16), while c is only 1 byte. Figure 3.2 shows how the memory is allocated for
the union.
Because the two members are sharing the same memory location, when you
change one member the other is changed too. Consider the following C example:
Listing 3.4 Unions Example 1
int main()
printf("\n");
printf("\n");
return 0;
}
64
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
When you change any of the members of the union, other members change too
because they are all share the same memory address .
Now consider the same example but with values that wont fit into the char
member:
Listing 3.5 Unions Example 2
int main()
ch.i = 330;
printf("\n"); // Ops!
ch.c += 32;
printf("\n");
return 0;
}
What happened? Because char is 1 bye wide, it interprets only the first 8 bits of
the union that are equal to 32.
The same rule applies if you add another member to the union. See the following
example. Notice that order of member declarations doesnt matter.
Listing 3.6 Unions Example 3
int main()
union {
int i;
char c;
short n;
} ch;
ch.i = 2774186;
65
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
printf("\n");
printf("\n");
printf("\n");
return 0;
Now, member i, the container, interprets the 32 bits. Member c, interprets the
first 8 bits (notice that we converted it to unsigned char to not to show the
negative value.) Member n, interprets the first high word (16 bits.)
You might ask: Why I need unions at all? I could easily use the cast operator to
convert between data types !
The answer is very easy. Unions come very efficient when casting between types
require much overhead. Consider the following example: You are about to write
an integer to a file. Unfortunately, there are no functions in the C standard library
that allow you to write an int to a file, and using fwrite() function requires
excessive overhead. The perfect solution is to define a union that contains an
integer and a character array to allow it to be interpreted as an integer and as a
character array when you need to pass it to fwrite() for example. See the
following code snippet:
Listing 3.7 Unions Example 4
typedef union myval{
int i;
char str[4];
};
In addition, unions offer you more performance than casts. Moreover, your code
will be more readable and efficient when you use unions.
You can marshal a union the same way as you marshal structures, except that
because of the way that unions laid-out into memory, you will need to explicitly
set variable positions inside the type.
66
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
3. Add the type fields. Do not add fields other than those defined in the
unmanaged signature. Because we are controlling the type layout
explicitly, order of fields is not important.
[StructLayout(LayoutKind.Explicit)]
[FieldOffset(0)]
[MarshalAs(UnmanagedType.U4)]
public int i;
[FieldOffset(0)]
public char c;
67
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
character.i = 65;
character.c = 'B';
// Should prints 66
To create the union between the fields, we set both the fields to the same
memory location.
When we change either one of the union variables, the other variable
changes too because they share the same memory address.
INT number;
CHAR charArray[128];
};
68
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
This union must be marshaled in a special way because managed code does not
permit value types and reference types to overlap.
As we know, the layout and size of the type inside the memory is the most
crucial. Therefore, we must preserve the layout and size of our union. This union
has a 128 bytes array as a container and only one member contained, and this
member is only 2-bytes. Therefore, we have two choices, to marshal the union
with the container member, or to marshal it with the contained member but to
extend it enough to be as large as the container. In this example, we will take the
two approaches.
TRY IT OUT!
The following are two code segments. The first demonstrates how to marshal
only the second member which is the container, while the second demonstrates
how to marshal the first member.
Listing 3.10 UNION_WITH_ARRAY Union Managed Signature
// Setting StructLayoutAttribute.CharSet
69
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
// Setting MarshalAsAttribute is required
// StructLayoutAttribute.Size determines
[MarshalAs(UnmanagedType.I2)]
For more information about marshaling arrays, refer to the next chapter.
Value-Types:
These types are stored in the memory stack. They are destroyed when
their scope ends, therefore, they are short-lived. Types of this category
are all types inherit from System.ValueType (like all primitive data types,
structures, and enumerations.)
70
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
Reference-Types:
These types are stored in the memory heap. They are controlled by the
Garbage Collector (GC,) therefore, they may retain in memory for a long
while. Reference-types are all types -directly or indirectly- inherit from
System.Object (except System.ValueType and descendants of course.) All
.NET classes fall in this category.
Talking about value-types and reference-types leads us to talk about the passing
mechanism. And that is what the next section is devoted for.
PASSING MECHANISM
In the last chapter, we have talked about the passing mechanism with simple
types and how it affects the call. Actually, all we have learnt is applied to the
compound types too.
All of the information learnt from the last chapter is applied to this chapter too.
If you are marshaling as a structure, you may add the ref modifier to the
parameter. However, classes are -by default- reference-types. Thus, they are
normally passed by reference and they cannot be passed by value. Therefore,
they do not need the ref modifier.
71
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
On the other hand, if you are passing the type as output (Out,) you will need to
add the out modifier whether it is a structure or a class.
As you know, you can decorate In/Out arguments with both InAttribute and
OutAttribute attributes. For Out arguments, specify OutAttribute attribute only.
The following example demonstrates the PInvoke method for the function
GetVersionEx(). This function requires a single In/Out argument. That argument
is of the type OSVERSIONINFO.
OSVERSIONINFO lpVersionInfo
);
DWORD dwOSVersionInfoSize;
DWORD dwMajorVersion;
DWORD dwMinorVersion;
DWORD dwBuildNumber;
DWORD dwPlatformId;
TCHAR szCSDVersion[128];
[return: MarshalAs(UnmanagedType.Bool)]
72
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
[StructLayout(LayoutKind.Sequential)]
[MarshalAs(UnmanagedType.U4)]
[MarshalAs(UnmanagedType.U4)]
[MarshalAs(UnmanagedType.U4)]
[MarshalAs(UnmanagedType.U4)]
[MarshalAs(UnmanagedType.U4)]
info.dwOSVersionInfoSize = (uint)Marshal.SizeOf(info);
//GetVersionEx(info);
GetVersionEx(ref info);
info.dwMajorVersion, info.dwMinorVersion);
73
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
As you know, the size and layout of the marshaling type is the most important. If
the compound type contains a textual data, sure special handling should be taken
to ensure correct marshaling of the data.
You already know that the character encoding can be either ANSI or Unicode.
You can determine the character encoding of the compound type by specifying
the CharSet property of the StructLayoutAttribute attribute. This property can
take one of several values:
CharSet.Unicode:
Strings are always 16-bit Unicode-encoded.
CharSet.None:
Obsolete. Has the same behavior as CharSet.Ansi.
Take into consideration that if you have not set the CharSet property, CLR
automatically sets it to CharSet.Auto. However, some languages override the
default behavior. For example, C# defaults to CharSet.Ansi.
REAL-WORLD EXAMPLES
Now, we are going to dig into real-world examples. In the first example, we are
going to marshal one of the most complex compound structures in the Windows
API, it is the DEVMODE structure.
74
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
If you have worked with GDI, you will be somewhat familiar with this structure.
It encapsulates information about initialization and environment of a printer or a
display device. It is required by many functions like EnumDisplaySettings(),
ChangeDisplaySettings() and OpenPrinter().
The complexity of this structure comes because of few factors. Firstly, there are
unions defined inside the structure. In addition, the definition of this structure
defers from a platform to another. As we will see, the structure defines some
members based on the operating system.
Here is the definition of DEVMODE structure along with the POINTL structure
that is referenced by DEVMODE.
Listing 3.13 DEVMODE and POINTL Unmanaged Signature
typedef struct DEVMODE {
BCHAR dmDeviceName[CCHDEVICENAME];
WORD dmSpecVersion;
WORD dmDriverVersion;
WORD dmSize;
WORD dmDriverExtra;
DWORD dmFields;
union {
struct {
short dmOrientation;
short dmPaperSize;
short dmPaperLength;
short dmPaperWidth;
short dmScale;
short dmCopies;
short dmDefaultSource;
short dmPrintQuality;
};
POINTL dmPosition;
DWORD dmDisplayOrientation;
DWORD dmDisplayFixedOutput;
};
short dmColor;
short dmDuplex;
75
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
short dmYResolution;
short dmTTOption;
short dmCollate;
BYTE dmFormName[CCHFORMNAME];
WORD dmLogPixels;
DWORD dmBitsPerPel;
DWORD dmPelsWidth;
DWORD dmPelsHeight;
union {
DWORD dmDisplayFlags;
DWORD dmNup;
DWORD dmDisplayFrequency;
DWORD dmICMMethod;
DWORD dmICMIntent;
DWORD dmMediaType;
DWORD dmDitherType;
DWORD dmReserved1;
DWORD dmReserved2;
DWORD dmPanningWidth;
DWORD dmPanningHeight;
#endif
};
LONG x;
LONG y;
};
You might have noticed that two unions are defined inside the structure. In
addition, a structure is defined inside the first union! Moreover, the last 8
members are not supported in Windows NT. Plus, the very last two members,
76
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
When working with Windows API, you should take care of operating system
compatibility. Some functions, for instance, are not supported on certain
operating systems (e.g. most Unicode versions are not supported on Win9x.)
Other functions take arguments that vary based on the OS (i.e.
EnumPrinters() function.) If your application tried to call a function, for
instance, that is not supported by the current operating system, the call
would fail.
If you need your application to be portable to every platform, you will need to
create three versions of the structure, one for Windows ME and its ascendants,
one for Windows NT, and the last for Windows 2000 and higher versions. In
addition, you will need to create three overloads of every function require
DEVMODE structure; three overloads for the three structures. For the sake of
simplicity, we will assume that you are working with Windows 2000 or a higher
version. Thus, we will marshal all members of the structure.
The following is the managed version of both DEVMODE and POINTL structures:
Listing 3.14 DEVMODE and POINTL Managed Signature
// Setting StructLayout.LayoutKind to LeyoutKind.Explicit to allow
// is very important
// CCHDEVICENAME = 32 = 0x50
[FieldOffset(0)]
// as following:
77
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
[FieldOffset(32)]
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(34)]
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(36)]
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(38)]
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(40)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(44)]
// It is 8-bytes only
[FieldOffset(44)]
78
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
[FieldOffset(44)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(44)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(60)]
[MarshalAs(UnmanagedType.I2)]
[FieldOffset(62)]
[MarshalAs(UnmanagedType.I2)]
[FieldOffset(64)]
[MarshalAs(UnmanagedType.I2)]
[FieldOffset(66)]
[MarshalAs(UnmanagedType.I2)]
[FieldOffset(70)]
[MarshalAs(UnmanagedType.I2)]
79
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
// CCHDEVICENAME = 32 = 0x50
[FieldOffset(72)]
[MarshalAs(UnmanagedType.ByValArray,
SizeConst = 32,
ArraySubType = UnmanagedType.U1)]
[FieldOffset(102)]
[MarshalAs(UnmanagedType.U2)]
[FieldOffset(104)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(108)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(112)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(116)]
[MarshalAs(UnmanagedType.U4)]
80
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
[FieldOffset(116)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(120)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(124)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(128)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(132)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(136)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(140)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(144)]
[MarshalAs(UnmanagedType.U4)]
81
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
[FieldOffset(148)]
[MarshalAs(UnmanagedType.U4)]
[FieldOffset(152)]
[MarshalAs(UnmanagedType.U4)]
// 16-bytes structure
[StructLayout(LayoutKind.Sequential)]
// 8-bytes structure
[StructLayout(LayoutKind.Sequential)]
public Int32 x;
public Int32 y;
82
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
As we have said earlier in the previous chapter, this writing assumes 32-bit
versions of Windows. For instance, in the DEVMODE example, we have
assumed that DWORDs are 4 bytes. If you want to port your application to a
64-bit machine, DWORDs should be considered as 8 bytes.
Lengthy, isn't it? DEVMODE is one of the lengthy and compound GDI structures.
If you want to learn more about laying out structure into memory, refer to
chapter 6 Memory Management.
Whether the union defined as a single entity or inside a structure, you will
need to lay-out the type explicitly into memory to allow defining two or
more variables at the same memory location.
The follows example shows how you can access and modify display settings
programmatically using C# and Windows API. In this example we will create four
functions, one retrieves current display settings, another enumerates available
display modes, the third changes current display settings, and the last changes
screen orientation (i.e. rotates the screen.)
For our example, we will use the DEVMODE and POINTL structures that we have
marshaled previously. In addition, we will make use of two new Windows API
functions, EnumDisplaySettings and ChangeDisplaySettings. The following is the
unmanaged signature of both functions:
Listing 3.15 EnumDisplaySettings() and ChangeDisplaySettings() Unmanaged Signature
BOOL EnumDisplaySettings(
);
LONG ChangeDisplaySettings(
83
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
DWORD dwflags // graphics mode options
);
[return: MarshalAs(UnmanagedType.Bool)]
[param: MarshalAs(UnmanagedType.LPTStr)]
string lpszDeviceName,
[param: MarshalAs(UnmanagedType.U4)]
int iModeNum,
[In, Out]
ref DEVMODE lpDevMode);
[DllImport("User32.dll")]
[return: MarshalAs(UnmanagedType.I4)]
[In, Out]
[param: MarshalAs(UnmanagedType.U4)]
uint dwflags);
Finally, those are our four functions that utilize the native functions:
Listing 3.17 Accessing/Modifying Display Settings Sample
public static void GetCurrentSettings()
mode.dmSize = (ushort)Marshal.SizeOf(mode);
if (EnumDisplaySettings(null,
Console.WriteLine("Current Mode:\n\t" +
84
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
"{0} by {1}, {2} bit, {3} degrees, {4} hertz",
mode.dmPelsWidth, mode.dmPelsHeight,
mode.dmDisplayFrequency);
mode.dmSize = (ushort)Marshal.SizeOf(mode);
Console.WriteLine("Supported Modes:");
while (EnumDisplaySettings(null,
"{4} hertz",
mode.dmPelsWidth, mode.dmPelsHeight,
mode.dmDisplayFrequency);
85
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
originalMode.dmSize = (ushort)Marshal.SizeOf(originalMode);
newMode.dmPelsWidth = (uint)width;
newMode.dmPelsHeight = (uint)height;
newMode.dmBitsPerPel = (uint)bitCount;
if (result == DISP_CHANGE_SUCCESSFUL)
Console.WriteLine("Succeeded.\n");
GetCurrentSettings();
Console.WriteLine();
Console.ReadKey(true);
86
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
else if (result == DISP_CHANGE_RESTART)
Console.WriteLine("Restart required.");
else
// ...
if (clockwise)
newMode.dmDisplayOrientation++;
else
newMode.dmDisplayOrientation = DMDO_DEFAULT;
else
newMode.dmDisplayOrientation--;
else
newMode.dmDisplayOrientation = DMDO_270;
newMode.dmPelsWidth = newMode.dmPelsHeight;
newMode.dmPelsHeight = temp;
// ...
87
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
There are functionalities of console applications that are not accessible from the
.NET Framework like clearing the console screen and moving a text around.
The following sample shows a tiny library for console applications. It contains
some of the common functionalities of the console (like writing and reading
data) along with new functionalities added.
Listing 3.18 The Console Library Sample
SafeNativeMethods.cs
using System;
using System.Runtime.InteropServices;
using System.Text;
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
88
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
public const char WHITE_SPACE = ' ';
/// <summary>
/// Retrieves a handle for the console standard input, output, or error
device.
/// </summary>
[DllImport("Kernel32.dll")]
/// <summary>
/// Writes a character string to the console buffer starting from the
current cursor position.
/// </summary>
[return: MarshalAs(UnmanagedType.Bool)]
(IntPtr hConsoleOutput,
string lpBuffer,
[param: MarshalAs(UnmanagedType.U4)]
uint lpReserved);
89
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
/// <summary>
/// Read a character string from the console buffer starting from the
current cursor position.
/// </summary>
[return: MarshalAs(UnmanagedType.Bool)]
IntPtr hConsoleInput,
StringBuilder lpBuffer,
/// <summary>
/// Retrieves information about the console cursor such as the size and
visibility.
/// </summary>
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
IntPtr hConsoleOutput,
90
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
[Out] out CONSOLE_CURSOR_INFO lpConsoleCursorInfo);
/// <summary>
/// Sets the console cursor properties as the size and visibility.
/// </summary>
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
IntPtr hConsoleOutput,
/// <summary>
/// </summary>
/// <param name="lpFill">Specifies the character and color info for the
cells left empty after the move.</param>
/// <remarks>
/// If you do need to set its value, you can marshal it as SMALL_RECT.
/// </remarks>
[DllImport("kernel32.dll")]
91
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
[return: MarshalAs(UnmanagedType.Bool)]
IntPtr hConsoleOutput,
IntPtr lpClipRectangle,
COORD dwDestinationOrigin,
/// <summary>
/// </summary>
[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
(IntPtr hConsoleOutput,
/// <summary>
/// </summary>
92
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
(IntPtr hConsoleOutput,
char cCharacter,
COORD dwWriteCoord,
/// <summary>
/// </summary>
[DllImport("Kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
/// <summary>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
/// <summary>
/// </summary>
/// <summary>
93
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
/// The location of the cursor inside the buffer.
/// </summary>
/// <summary>
/// Additional attributes about the buffer write the fore color and back
color.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
/// <summary>
/// </summary>
/// <summary>
/// </summary>
/// <summary>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
/// <summary>
/// </summary>
[MarshalAs(UnmanagedType.I2)]
public short X;
/// <summary>
/// </summary>
[MarshalAs(UnmanagedType.I2)]
94
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
public short Y;
/// <summary>
/// Defines the coordinates of the upper left and right bottom coordinates
of a rectangle.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
/// <summary>
/// </summary>
[MarshalAs(UnmanagedType.I2)]
/// <summary>
/// </summary>
[MarshalAs(UnmanagedType.I2)]
/// <summary>
/// </summary>
[MarshalAs(UnmanagedType.I2)]
/// <summary>
/// </summary>
[MarshalAs(UnmanagedType.I2)]
/// <summary>
95
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
/// </summary>
[StructLayout(LayoutKind.Sequential)]
/// <summary>
/// </summary>
[MarshalAs(UnmanagedType.U4)]
/// <summary>
/// </summary>
[MarshalAs(UnmanagedType.Bool)]
/// <summary>
/// </summary>
[StructLayout(LayoutKind.Sequential)]
/// <summary>
/// </summary>
/// <summary>
/// Additional attributes of the character like fore color and back
color.
/// </summary>
[MarshalAs(UnmanagedType.U2)]
96
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
ConsoleLib.cs
using System;
using System.Runtime.InteropServices;
using System.Text;
/// <summary>
/// </summary>
Left,
/// <summary>
/// </summary>
Right,
/// <summary>
/// </summary>
Center
/// <summary>
/// </summary>
97
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
/// <summary>
/// </summary>
Input = SafeNativeMethods.STD_INPUT_HANDLE,
/// <summary>
/// </summary>
Output = SafeNativeMethods.STD_OUTPUT_HANDLE,
/// <summary>
/// </summary>
Error = SafeNativeMethods.STD_ERROR_HANDLE
/// <summary>
/// </summary>
/// <summary>
/// </summary>
location.X = 0;
location.Y = 0;
ClearScreen(location);
/// <summary>
/// </summary>
98
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
/// <param name="location">The location of which to start clearing the
screen buffer.</param>
FillConsoleBuffer(location, 0, SafeNativeMethods.WHITE_SPACE);
/// <summary>
/// </summary>
uint length;
if (count == 0)
CONSOLE_SCREEN_BUFFER_INFO info =
GetBufferInfo(ConsoleStandardDevice.Output);
99
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
else
length = count;
uint numChars;
SafeNativeMethods.FillConsoleOutputCharacter(handle, character,
length, location, out numChars);
SetCursorPosition(location);
/// <summary>
/// </summary>
return SafeNativeMethods.GetStdHandle((int)device);
/// <summary>
/// Writes an empty line to the console buffer on the current position
of the cursor.
/// </summary>
WriteLine(string.Empty);
/// <summary>
100
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
/// </summary>
WriteLine(txt, ConsoleTextAlignment.Left);
/// <summary>
/// </summary>
/// <summary>
/// Writes specific text to the console buffer on the current position
of the cursor.
/// </summary>
Write(txt, ConsoleTextAlignment.Left);
/// <summary>
/// Writes specific text to the console buffer on the current position
of the cursor with the specified line alignment.
/// </summary>
101
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
if (alignment == ConsoleTextAlignment.Left)
InternalWrite(txt);
else
CONSOLE_SCREEN_BUFFER_INFO info =
GetBufferInfo(ConsoleStandardDevice.Output);
if (alignment == ConsoleTextAlignment.Right)
else // Center
pos.Y = info.dwCursorPosition.Y;
SetCursorPosition(pos);
InternalWrite(txt);
/// <summary>
/// Writing a specific text to the console output buffer starting from
the current cursor position.
/// </summary>
uint count;
102
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
// Getting the output handle
/// <summary>
/// </summary>
CONSOLE_CURSOR_INFO info;
info.bVisible = show;
/// <summary>
/// </summary>
/// <returns></returns>
103
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
// The buffer
uint count;
// Getting the input device that's used for receiving user input
SafeNativeMethods.ReadConsole(GetStandardDevice(ConsoleStandardDevice.Input)
, buffer,
/// <summary>
/// </summary>
CONSOLE_SCREEN_BUFFER_INFO info;
return info;
/// <summary>
104
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
/// </summary>
IntPtr handle =
SafeNativeMethods.GetStdHandle(SafeNativeMethods.STD_OUTPUT_HANDLE);
SafeNativeMethods.SetConsoleCursorPosition(handle, pos);
/// <summary>
/// </summary>
WriteLine("--------------------");
WriteLine("Cursor Position:");
WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
"\t{0}, {1}",
info.dwCursorPosition.X, info.dwCursorPosition.Y));
WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
"\t{0}, {1}",
info.dwMaximumWindowSize.X,
info.dwMaximumWindowSize.Y));
105
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
"\t{0}, {1}",
info.dwSize.X, info.dwSize.Y));
WriteLine(string.Format(System.Globalization.CultureInfo.InvariantCulture,
"\t{0}, {1}, {2}, {3}",
info.srWindow.Left, info.srWindow.Top,
info.srWindow.Right, info.srWindow.Bottom));
WriteLine("--------------------");
/// <summary>
/// Writes the specific text followed by a line terminator to the left
and moves it to the far right.
/// </summary>
WriteLine(txt);
CONSOLE_SCREEN_BUFFER_INFO screenInfo =
GetBufferInfo(ConsoleStandardDevice.Output);
106
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
SMALL_RECT rect = new SMALL_RECT();
while (true)
break;
// The character to fill the empty cells created after the move
with
System.Threading.Thread.Sleep(100);
rect.Left++;
SUMMARY
107
MARSHALING WITH C# POCKET REFERENCE
2010 MOHAMMAD ELSHEIMY
After all, you learned that compound types are unmanaged structures and
unions, and they called compound because they consisted of other types.
Again and again, the memory layout and size of the type is very crucial.
After that, you have worked with unions and learned that unions are simply a
group of multiple variables share the same memory. In fact, the same memory
location is shared by one or more variables. Therefore, bits are represents in
several ways.
Now it is the time for arrays. The next chapter discusses what arrays are and
how to marshal them.
108