The .NET Compact Framework can marshal simple structures between the managed and native worlds. Unlike the full desktop .NET Framework, there are strong restrictions governing what kinds of structures can be marshalled automatically.
First, the .NET Compact Framework marshals all simple structures by reference. Thus, the native side sees parameters that are structures as pointers to the structure. As such, the native code can alter the contents of structures, and the alterations are visible as side effects on the managed side.
Another important rule is that only references to “shallow” structures can be marshalled automatically. This means that the structure can contain an arbitrary number of fundamental data types, as described in previous sections, but the structure cannot contain strings or nested structures. For example, the first structure shown below can be marshalled automatically, but the second cannot.
The following is a structure that can be automatically marshalled:
C# public struct MarshallableStruct { public int m_Int; public char m_Char; public Int32 m_DWORD; public bool m_Bool; } VB Public Structure MarshallableStruct Public m_Int As Integer Public m_Char As Char Public m_DWORD As Int32 Public m_Bool As Boolean End Structure
The following is a structure that cannot be automatically marshalled:
C# public struct NestedStruct { public int m_Int; public char m_Char; } public struct CannotAutoMarshal { public int m_Int; public NestedStruct m_Nested; public Int32 m_DWORD; public bool m_Bool; } VB Public Structure NestedStruct Public m_Int As Integer Public m_Char As Char End Structure Public Structure CannotAutoMarshal Public m_Int As Integer Public m_Nested As NestedStruct Public m_DWORD As Int32 Public m_Bool As Boolean End Structure
Complex structures that cannot be marshalled automatically and structures with strings in them can be passed to native code, but doing so requires manual marshalling. The next section describes how to do manual marshalling.
Once you understand the rules, passing shallow structures into native code is not significantly different from passing fundamental data types into native code. You need to declare the native function that is passed the structure. You also need to create a structure in managed code that has a compatible layout to the structure you create in native code. For example, if your managed structure is composed of two Int32 values, then you also need a structure that houses two 32-bit integers on the native side.
The code in Listing 12.4 is derived from the MarshalShallowStruct sample application. It shows the structure and method declarations in C# and Visual Basic to set up for a call to a native C function called ManipulateStruct. It also shows the corresponding C code for the ManipulateStruct.
C# // Structure declaration public struct ShallowStruct { public int m_Int; public char m_Char; public Int32 m_DWORD; public bool m_Bool; } // Method declaration // Method invocation on native function // Set up input... ShallowStruct in_Struct = new ShallowStruct(); in_Struct.m_Int = Convert.ToInt32(4); in_Struct.m_Char = Convert.ToChar('C'), in_Struct.m_DWORD = Convert.ToInt32(4434); in_Struct.m_Bool = true; ShallowStruct out_Struct = new ShallowStruct(); // Call native function ManipulateStruct(ref in_Struct, ref out_Struct); // out_Struct has been altered by the native code implementation // of ManipulateStruct VB ' Structure declaration Public Structure ShallowStruct Public m_Int As Integer Public m_Char As Char Public m_DWORD As Int32 Public m_Bool As Boolean End Structure ' Method declaration Declare Sub ManipulateStruct Lib "ManipulateStruct.dll" (ByRef in_ShallowStruct As ShallowStruct, ByRef out_ShallowStruct As ShallowStruct) ' Method invocation on native function ' Set up input... Dim in_Struct As ShallowStruct = New ShallowStruct in_Struct.m_Int = Convert.ToInt32(4) in_Struct.m_Char = Convert.ToChar("C") in_Struct.m_DWORD = Convert.ToInt32(4434) in_Struct.m_Bool = True Dim out_Struct As ShallowStruct = New ShallowStruct ' Call native function ManipulateStruct(in_Struct, out_Struct) ' out_Struct has been altered by the native code implementation ' of ManipulateStruct Native C implementation for ManipulateStruct /* Structure declaration */ struct ShallowStruct { int m_Int; char m_Char; DWORD m_DWORD; bool m_Bool; }; /* ManipulateStruct function implementation */ void __cdecl ManipulateStruct(ShallowStruct * in_ShallowStruct, ShallowStruct * out_ShallowStruct) { out_ShallowStruct->m_Int = in_ShallowStruct->m_Int + 1; out_ShallowStruct->m_Char = in_ShallowStruct->m_Char + 1; out_ShallowStruct->m_DWORD = in_ShallowStruct->m_DWORD + 1; out_ShallowStruct->m_Bool = (in_ShallowStruct->m_Bool != true); } |
The MarshalShallowStruct sample application demonstrates an end-to-end scenario for calling a native function and passing automatically marshalled shallow structures as parameters. The sample application is located in the folder SampleApplicationsChapter12. There are C# and Visual Basic versions.
MarshalShallowStruct uses a native DLL, ManipulateStruct.dll. The source code for this DLL is in the folder SampleApplicationsChapter12NativeBinariesManipulateStruct. Just like the SquareAnInt sample application, there are also pre-compiled DLL binaries in the SampleApplicationsChapter12NativeBinariesManipulateStruct directory. Each binary is in its own subdirectory according to the hardware and operating system for which the DLL was built.
To use the MarshalShallowStruct application, build and deploy the managed project to your device. Then copy the appropriate ManipulateStruct.dll library to your device, either to the Windows directory or the directory where the managed executable, ManipulateStruct.exe, resides. You can now launch the application.
When MarshalShallowStruct launches, it shows a form with four fields: one for an integer, one for a character, one for a DWORD value, and one for a Boolean value. Each holds a default value. When you click the button labeled Update Struct, the application passes a structure holding each field's value to the native function ManipulateStruct() that resides in ManipulateStruct.dll.
The ManipulateStruct() function increments each field in the shallow structure passed to it. When the function returns, the managed application updates the main form to display the new values that were set by the native code.
18.118.128.105