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 易学 阅读(5897) 评论(9) 编辑 收藏

Feedback

#1楼2004-06-16 17:46hBifTs
呵呵,不错啊~
加油~再接再励
 回复 引用   
mmm,最大的问题是代码的背景颜色,看起来有点刺眼...
 回复 引用   
#3楼2004-06-16 21:42Ying-Shen

似乎不用这么麻烦的吧。
这样就可以了:
当然实际上是不会去 P/Invoke 这个API的,你可以从Environment.UserName得到当前用户名啊
namespace
ConsoleApplication2

{

     #region Using directives

 

     using System;

     using System.Text;

     using System.Runtime.InteropServices;

 

     #endregion

 

     class Program

     {

         static void Main(string[] args)

         {

              StringBuilder sb = new StringBuilder( UNLEN + 1 );

              uint size = (uint)sb.Capacity;

 

              if (!GetUserName(sb, ref size))

              {

                   Console.WriteLine("failed :{0}", Marshal.GetLastWin32Error());

              }

              Console.WriteLine(sb.ToString ());

              Console.Read();

         }

         const int UNLEN = 256;//defined in lmcons.h

         [DllImport("advapi32.dll",SetLastError = true )]

         private static extern bool GetUserNameW( [In,Out] StringBuilder buffer, ref uint size);

     }

}

 

 回复 引用   
#4楼2004-06-17 18:24willieCHEN
翻译嘛,力求保持原作风格,其中存在不少的错误,我还没来得及修正。还需同仁群箦群力。
 回复 引用   
#5楼2005-06-21 17:57lobo
呵呵
byte[]来接收字符串成功了
试了好久,别的方法好像都不行
 回复 引用   
#6楼2009-04-09 16:36virus      
public partial class DLLDemoForm1 : Form
{
public DLLDemoForm1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
DLLDemo.fn1();
}
}

public class DLLDemo
{[DllImport("FUKeyDll.dll",EntryPoint="fn1")]
extern public static int fn1();
}
我这个老是提示找不到模块
 回复 引用 查看   
#7楼2009-04-09 16:36virus      
无法加载 DLL“FUKeyDll.dll”: 找不到指定的模块。
 回复 引用 查看   
#8楼2009-04-09 16:40virus      
#6楼 2009-04-09 16:36 virus
public partial class DLLDemoForm1 : Form
{
public DLLDemoForm1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
DLLDemo.fn1();
}
}

public class DLLDemo
{[DllImport("FUKeyDll.dll",EntryPoint="fn1")]
extern public static int fn1();
}
我这个老是提示找不到模块
回复 引用 查看 删除 修改
 回复 引用 查看   
#9楼2009-04-10 09:50virus      
最近老是碰到问题啊,提示“无法加载 DLL“FUKeyDll.dll”: 找不到指定的模块。 (异常来自 HRESULT:0x8007007E)。”,

这类的问题该如何排除呢
谢谢
 回复 引用 查看