EDA驿站

去浮躁,求真知;重实干,勤与研

  博客园  :: 首页  :: 新随笔  ::  ::  :: 管理

调用非托管(unmanaged)代码——第一部分 简单的DllImport

Managed世界是很美妙的,在Framework中,我拥有我想要的所有class。但是如果我想调用一些unmanaged代码呢?例如,我已经用C++写了一个DLL,而且想在C#中使用它。那就让我们看看下面的代码。我们的DLLCdecl的约定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,其中,textBox1textBox2是两个操作数,textBox3是运算结果,并且button1调用sum()并更新结果。

sum()被定义为“static extern”,意味着外部链接函数。它不能被放置在class以外,因为C# 中不存在“alone”函数,而是每个函数必属于某个class。调用规则是Cdecl,因为C++函数以__cdecl属性进编译的。并且置exactspellingfalse是说,编译器试图对函数名“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)类型,也就是说,intC#中和在C++中是一致的。当我们需要操作异构(non-isomorhic)类型的操作数,例如,String时,我们该怎么办呢?要记得,.NET中的String是一个类,而C++中的stringchar *,或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 stringint *更为简单——这是一个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分配了20byte,每个元素都在Int32 array中,设置20作为最大的string长度并调用它。为了接从byte array中接收string,我用了Text.Encoding.ASCII class

 

这一部分就到这。第二部分我们将讲讲更为复杂的interop

这是我翻译的:原文,http://www.c-sharpcenter.com/Tutorial/UnManaged.htm

posted on 2004-06-16 16:03  易学  阅读(7005)  评论(9编辑  收藏  举报