Calling Native Code: Quick Start


This section describes the minimum steps needed to call a function in a native DLL binary. Before jumping directly into the tutorial, there are several fundamental assumptions we are making about the DLL into which we are calling.

The most fundamental requirement is that the DLL is compiled for the CPU and Windows CE OS version for the device on which you plan to use it. It is an easy mistake to make to accidentally use the DLL binary for the wrong platform and then wonder why you get exceptions when you try to call into from native code.

It is assumed that the DLL does not have C++-style mangled names. To understand what this means, consider what happens when a group of functions written in standard C are compiled into a DLL. The resulting DLL has a table indicating which functions are available to call and where in the DLL binary the function code begins. A DLL written with the C++ programming language has the table as well, but the names of the functions that are exported can be mangled. For example, the function MyFunction1 might be mangled to look like MyFunctioned87883ffe1 . The name mangling supports the polymorphic features of the C++ language, where a class can override a method name from a parent. Unfortunately, it can be problematic when trying to call the function from managed code.

HOW TO TELL IF A DLL HAS NAME MANGLING

You can tell if a DLL has name mangling by using the DEPENDS dependency walker utility that comes with Visual Studio. To use the dependency walker utility, start a Visual Studio command line, and change to the directory holding the DLL you want to examine. Then type DEPENDS [dllname] . The dependency walker application shows all of the native function names exported by the DLL. Figure 12.1 shows the dependency walker examining the SquareAnInt.dll file, which is used later in this chapter. As Figure 12.1 shows, it is easy to see whether the function names look "clean," or whether they have been mangled. The SquareAnInt.dll exports one function, SquareAnInt , whose name appears cleanly in the dependency walker utility.

Figure 12.1. The dependency walker demonstrates that SquareAnInt.dll does not have mangled names.

graphics/12fig01.jpg


All of the Windows CE OS functions are held in DLLs that do not mangle their names. Also, all of the binary DLLs that are included with the examples do not mangle their names. If you are building the DLL that you plan to call into from managed code, then consult your compiler's documentation to see how to disable name mangling.

It is assumed that the DLL has been placed in a location where the .NET CF runtime can find it. Two safe choices are in the \Windows directory or in the same directory as the managed application which will call into the DLL.

Calling Native Code with Minimal Steps

If you have a DLL that adheres to all of the preceding restrictions, then you are ready to call functions in the DLL from managed code. This section examines the minimum steps you need to take in your managed application in order to call native code functions from managed code. The next section focuses on the specifics of passing various data types into native code in the next section after we understand the basics of calling into native code from managed code.

To call a native function from managed code, first declare the native function in the class where it will be called. In the declaration, specify what data types are passed into and out of the function, what the return type is, and what DLL the function is located in. After the native function is declared, it can be used in the class just as any other class method. From a programmer's perspective, the declaration creates the illusion that the native function is simply a class method.

When the class actually does call the native function, the runtime locates the DLL holding the native function. It loads the DLL and marshals the input arguments into the native function. The native code is then executed, and the output arguments and return values are marshalled back into the managed side. As long as everything goes well, this process is transparent to the user . Otherwise, an exception is thrown. Table 12.1 shows exceptions commonly thrown while trying to call native code.

Table 12.1. Exceptions Thrown When Calling into Native Code

EXCEPTION

CAUSE(S)

MissingMethodException

(1) The DLL file holding the function could not be found.

(2) The DLL file was found, but it does not contain the function specified in the declaration of the native code function. Make sure that the DLL exports the function by using the DEPENDS utility.

NotSupportedException

The managed application attempted to pass by value an item greater than 32 bits long. For example, an attempt was made to pass a 64-bit long integer into a native function. See the section "Marshalling the Fundamental Data Types" for more information.

The C# keyword for declaring the function is the DllImport . For Visual Basic, use the Declare Sub construct.

The code in Listing 12.1 is derived from the ComputeSquare sample application. The code snippet shows the declaration and usage for the SquareAnInt function, which is held in SquareAnInt.dll . It does not return a value, and it accepts an integer and a reference to an integer ( int * , in the C code that implements SquareAnInt ) as input. The value of the integer passed in is squared and written to the referenced integer as an out parameter.

Listing 12.1 Declaration and usage for the SquareAnInt function
 C# public class Form1 : System.Windows.Forms.Form {         private System.Windows.Forms.Button btnComputeSquare;         private System.Windows.Forms.Label label1;         private System.Windows.Forms.Label label2;         private System.Windows.Forms.TextBox txtInput;         private System.Windows.Forms.TextBox txtResult;         private System.Windows.Forms.MainMenu mainMenu1;         [DllImport("SquareAnInt.dll")]         private static extern void SquareAnInt(int input,                 ref int output);         // Rest of the class declarations deleted for brevity         // ...         // ...         // Usage of the SquareAnInt function occurs as part of         // btnComputeSquare_Click         private void btnComputeSquare_Click(object sender,                  System.EventArgs e)         {                 int output = 0;                 int input = Convert.ToInt32(this.txtInput.Text);                 SquareAnInt(input, ref output);                 this.txtResult.Text = Convert.ToString(output);         } // Rest of class cut for brevity... VB Public Class Form1     Inherits System.Windows.Forms.Form     Friend WithEvents txtResult As System.Windows.Forms.TextBox     Friend WithEvents txtInput As System.Windows.Forms.TextBox     Friend WithEvents label2 As System.Windows.Forms.Label     Friend WithEvents label1 As System.Windows.Forms.Label     Friend WithEvents btnComputeSquare As System.Windows.Forms.Button     Friend WithEvents MainMenu1 As System.Windows.Forms.MainMenu     Declare Sub SquareAnInt Lib "SquareAnInt.dll"             (ByVal input As Int32, ByRef output As Int32)         ' Rest of the class declarations deleted for brevity         ' ...         ' ...         ' Usage of the SquareAnInt function occurs as part of         ' btnComputeSquare_Click     Private Sub btnComputeSquare_Click(ByVal sender As             System.Object, ByVal e As System.EventArgs)             Handles btnComputeSquare.Click         Dim output As Integer = 0         Dim input As Integer = Convert.ToInt32(Me.txtInput.Text)         SquareAnInt(input, output)         Me.txtResult.Text = Convert.ToString(output)     End Sub 
Computing a Square with Native Code ”Sample Application

The ComputeSquare sample application is a complete end-to-end demonstration of how to call native code from a managed application. The managed application is called SquareAnInt, and it is in the directory \SampleApplications\Chapter12 . There are C# and Visual Basic versions.

The SquareAnInt application uses the SquareAnInt.dll binary to compute the square of a 32-bit integer passed into it. The source code for SquareAnInt.dll is held in the directory \SampleApplications\Chapter12\NativeBinaries\SquareAnInt .

Because SquareAnInt.dll is native code, a version for each supported CPU and Windows CE OS version is required. There are multiple subdirectories in the \SampleApplications\Chapter12\ NativeBinaries\SquareAnInt directory. Each holds the native DLL file for a specific CPU type and OS version. For example, the 400_x86 directory holds the binaries for Windows CE 4.x running on 80x86 hardware. The popular Compaq iPaq running PocketPC 2002 would use the binaries in the 300_ARM directory.

To use the SquareAnInt application, open the managed project, build it, and deploy it to your device. Copy SquareAnInt.dll from the appropriate folder to your device. You can choose the \Windows directory or the directory where the SquareAnInt.exe file resides.

When you launch the application, it shows a simple interface into which you can enter an integer value. Click the button labeled Computer Square, and the integer value is squared by calling into the SquareAnInt.dll library and by using the native function, SquareAnInt .



Microsoft.NET Compact Framework Kick Start
Microsoft .NET Compact Framework Kick Start
ISBN: 0672325705
EAN: 2147483647
Year: 2003
Pages: 206

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net