调用非托管(unmanaged)代码——第一部分 简单的DllImport
Managed世界是很美妙的,在Framework中,我拥有我想要的所有class。但是如果我想调用一些unmanaged代码呢?例如,我已经用C++写了一个DLL,而且想在C#中使用它。那就让我们看看下面的代码。我们的DLL以Cdecl的约定export一个函数,它用来对两个整数进行求和:
extern "C" __declspec(dllexport) __cdecl int sum(int a,int b);
当然,我们要在C#中重用该代码。我们必须recall,那是一种调用unmanaged代码的非“直接”的方式,但是我们必须通知编译器,我们要调用什么,如何调用,要调用的代码在哪。
[DllImport("TestDll.dll", EntryPoint="sum",
ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]
static extern int sum(int a,int b);
这会,我们就可以像调用一般的C#函数一样调用它了。
x=5;
y=7;
z=sum(x,y); // x will receive 12
下面是完整的C# client代码。
namespace WindowsApplication6
{
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Runtime.InteropServices;
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.Button button1;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.TextBox textBox2;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.TextBox textBox3;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.button1 = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.textBox2 = new System.Windows.Forms.TextBox();
this.label2 = new System.Windows.Forms.Label();
this.textBox3 = new System.Windows.Forms.TextBox();
this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(64, 192);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(144, 64);
this.button1.TabIndex = 0;
this.button1.Text = "call sum";
this.button1.Click += new System.EventHandler(this.button1_Click);
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(40, 120);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(72, 22);
this.textBox1.TabIndex = 1;
this.textBox1.Text = "2";
//
// label1
//
this.label1.Location = new System.Drawing.Point(128, 128);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(16, 16);
this.label1.TabIndex = 2;
this.label1.Text = "+";
//
// textBox2
//
this.textBox2.Location = new System.Drawing.Point(152, 120);
this.textBox2.Name = "textBox2";
this.textBox2.Size = new System.Drawing.Size(56, 22);
this.textBox2.TabIndex = 3;
this.textBox2.Text = "3";
//
// label2
//
this.label2.Location = new System.Drawing.Point(224, 120);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(24, 23);
this.label2.TabIndex = 4;
this.label2.Text = "=";
//
// textBox3
//
this.textBox3.Location = new System.Drawing.Point(248, 120);
this.textBox3.Name = "textBox3";
this.textBox3.Size = new System.Drawing.Size(112, 22);
this.textBox3.TabIndex = 5;
this.textBox3.Text = "5";
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(6, 15);
this.ClientSize = new System.Drawing.Size(576, 322);
this.Controls.AddRange(new System.Windows.Forms.Control[]
{
this.textBox3,
this.label2,
this.textBox2,
this.label1,
this.textBox1,
this.button1
});
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
#region My Code
#region Dll Imports
[DllImport("TestDll.dll", EntryPoint="sum",
ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]
static extern int sum(int a,int b);
#endregion
#region Button Click Events
private void button1_Click(object sender, System.EventArgs e)
{
textBox3.Text=(int.Parse(textBox1.Text)+int.Parse(textBox2.Text)).ToString();
}
#endregion
#endregion
}
}
在此,我定义了三个textbox,其中,textBox1和textBox2是两个操作数,textBox3是运算结果,并且button1调用sum()并更新结果。
sum()被定义为“static extern”,意味着外部链接函数。它不能被放置在class以外,因为C# 中不存在“alone”函数,而是每个函数必属于某个class。调用规则是Cdecl,因为C++函数以__cdecl属性进编译的。并且置exactspelling为false是说,编译器试图对函数名“decore(除芯)”,对于Unicode的,在函数名前冠以“W”;对于ANSI的,在函数名前冠以“A”。button1单击事件将两个参数由string解析为int,并调用sum(),然后把结果以string类型输出。
注意,我们可以通常的C++声明,而不用<extern "C">:
/*extern "C"*/ __declspec(dllexport) __cdecl int sum(int a,int b);
但是,在这种情况下,我们必须告知编译器以真实的,或者“decorated修饰”函数名。这可以通过使用DllImport属性中的EntryPoint参数来完成这项工作。
[DllImport("TestDll.dll", EntryPoint="sum",
ExactSpelling=false,CallingConvention=CallingConvention.Cdecl)]
static extern int sum(int a,int b);
这看似简单,因为“int”是同构(isomorphic)类型,也就是说,int在C#中和在C++中是一致的。当我们需要操作异构(non-isomorhic)类型的操作数,例如,String时,我们该怎么办呢?要记得,.NET中的String是一个类,而C++中的string是char *,或wchar_t * BSTR……。String或许可以嵌入一个结构体中,或者指向指针的指针(pointed by pointer),又或是更怪异的什么。让我们看几个调用string的函数吧:
[DllImport("Advapi32.dll", EntryPoint="GetUserName", ExactSpelling=false,
SetLastError=true)]
static extern bool GetUserName(
[MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,
[MarshalAs(UnmanagedType.LPArray)] Int32[] nSize );
这个函数接收两个参数:char *和int *。因为我们必须分配char *空间并通过指针接收string,我们就不能使用UnmanagedType.LPStr属性,因此我们以byte array传递ANSI string。int *更为简单——这是一个1个元素的Int32 array。让我们这样调用它:
private void button2_Click(object sender, System.EventArgs e)
{
byte[] str=new byte[20];
Int32[] len=new Int32[1];
len[0]=20;
GetUserName(str,len);
MessageBox.Show(System.Text.Encoding.ASCII.GetString(str));
}
我们为接收ANSI string分配了20个byte,每个元素都在Int32 array中,设置20作为最大的string长度并调用它。为了接从byte array中接收string,我用了Text.Encoding.ASCII class。
这一部分就到这。第二部分我们将讲讲更为复杂的interop。
这是我翻译的:原文,http://www.c-sharpcenter.com/Tutorial/UnManaged.htm