[轉帖]如何使用C#調用非託管DLL函數

來源:互聯網
上載者:User

由於工作需要,學習了GDI+編程的一些知識。其中看到了一個比較好的Demo,深入的瞭解後,卻發現自己對如何用C#調用非託管DLL函數也有了更好的理解,於是整理了一下,跟大家一起分享。

C#捕獲當前螢幕的例子

#region C#捕獲當前螢幕的例子
using System ;
using System.Drawing ;
using System.Collections ;
using System.ComponentModel ;
using System.Windows.Forms ;
using System.Data ;
using System.Drawing.Imaging ;
public class Form1 : Form
{
    private Button button1 ;
    private System.ComponentModel.Container components = null ;

    public Form1 ( )
    {
        //初始化表單中的各個組件
        InitializeComponent ( ) ;
    }
    // 清除程式中使用過的資源
    protected override void Dispose ( bool disposing )
    {
        if ( disposing )
        {
            if ( components != null )
            {
                components.Dispose ( ) ;
            }
        }
        base.Dispose ( disposing ) ;
    }
    private void InitializeComponent ( )
    {
        button1 = new Button ( );
        SuspendLayout ( ) ;
        button1.Location = new System.Drawing.Point ( 64 , 40 ) ;
        button1.Name = "button1" ;
        button1.Size = new System.Drawing.Size ( 80 , 32 ) ;
        button1.TabIndex = 0 ;
        button1.Text = "捕獲" ;
        button1.Click += new System.EventHandler ( button1_Click ) ;

        AutoScaleBaseSize = new System.Drawing.Size ( 6 , 14 ) ;
        ClientSize = new System.Drawing.Size ( 216 , 125 ) ;
        Controls.Add ( button1 ) ;
        MaximizeBox = false ;
        MinimizeBox = false ;
        Name = "Form1" ;
        Text = "C#捕獲當前螢幕!" ;
        ResumeLayout ( false ) ;

    }
    //聲明一個API函數
    [ System.Runtime.InteropServices.DllImportAttribute ( "gdi32.dll" ) ]
    private static extern bool BitBlt (
        IntPtr hdcDest , // 目標 DC的控制代碼
        int nXDest ,
        int nYDest ,
        int nWidth ,
        int nHeight ,
        IntPtr hdcSrc , // 源DC的控制代碼
        int nXSrc ,
        int nYSrc ,
        System.Int32 dwRop // 光柵的處理數值
        ) ;

    static void Main ( )
    {
        Application.Run ( new Form1 ( ) ) ;
    }
    private void button1_Click ( object sender , System.EventArgs e )
    {
        //獲得當前螢幕的大小
        Rectangle rect = new Rectangle ( ) ;
        rect = Screen.GetWorkingArea ( this ) ;
        //建立一個以當前螢幕為模板的圖象
        Graphics g1 = this.CreateGraphics ( ) ;
        //建立以螢幕大小為標準的位元影像
        Image MyImage = new Bitmap ( rect.Width , rect.Height , g1 ) ;
        Graphics g2 = Graphics.FromImage ( MyImage ) ;
        //得到螢幕的DC
        IntPtr dc1 = g1.GetHdc ( ) ;
        //得到Bitmap的DC
        IntPtr dc2 = g2.GetHdc ( ) ;
        //調用此API函數,實現螢幕捕獲
        BitBlt ( dc2 , 0 , 0 , rect.Width , rect.Height , dc1 , 0 , 0 , 13369376 ) ;
        //釋放掉螢幕的DC
        g1.ReleaseHdc ( dc1 ) ;
        //釋放掉Bitmap的DC
        g2.ReleaseHdc ( dc2 ) ;
        //以JPG檔案格式來儲存
        MyImage.Save ( @"c:\Capture.jpg" , ImageFormat.Jpeg );
        MessageBox.Show ( "當前螢幕已經儲存為C盤的capture.jpg檔案!" ) ;
    }
}
#endregion

上面的例子中,應用C#調用非託管DLL的函數如下:
//聲明一個API函數
 [ System.Runtime.InteropServices.DllImportAttribute ( "gdi32.dll" ) ]
 private static extern bool BitBlt (
  IntPtr hdcDest , // 目標 DC的控制代碼
  int nXDest ,
  int nYDest ,
  int nWidth ,
  int nHeight ,
  IntPtr hdcSrc , // 源DC的控制代碼
  int nXSrc ,
  int nYSrc ,
  System.Int32 dwRop // 光柵的處理數值
  ) ;

在上面這段代碼中,我將以分別介紹 DllImportAttribute屬性、extern關鍵字 、IntPtr類型 這三個方面,向大家介紹如何應用C#調用非託管DLL函數。

C# 如何使用 DllImport Attribute(屬性) 標識 DLL 和函數

System.Runtime.InteropServices.DllImportAttribute

從Managed 程式碼中訪問非託管 DLL 函數之前,需要知道該函數的名稱以及該 DLL 的名稱,然後為 DLL 的非託管函數 編寫 託管定義。

它將用到 static 和 extern 修飾符,此類型的公用靜態成員對於多線程操作是安全的。

DllImport 屬性提供非託管 DLL 函數的調用資訊。

樣本1  簡單DllImportAttribute 應用
using System.Runtime.InteropServices;
[DllImport("user32.dll")]     
public static extern int MessageBox(int hWnd, String text, String caption, uint type);

樣本2  如何將 DllImportAttribute 應用於方法。
using System.Runtime.InteropServices;
[DllImport(  "KERNEL32.DLL",
             EntryPoint="MoveFileW",
             SetLastError=true,
             CharSet=CharSet.Unicode,
             ExactSpelling=true,
             CallingConvention=CallingConvention.StdCall
          )
]
public static extern bool MoveFile(String src, String dst);

參數說明:

EntryPoint         指定要調用的 DLL 進入點。

SetLastError       判斷在執行該方法時是否出錯(使用 Marshal.GetLastWin32Error API 函數來確定)。
                   C#中預設值為 false。

CharSet            控制名稱及函數中字串參數的編碼方式。預設值為 CharSet.Ansi。

ExactSpelling      是否修改進入點以對應不同的字元編碼方式。

CallingConvention  指定用於傳遞方法參數的呼叫慣例。預設值為 WinAPI。
                   該值對應於基於32位Intel平台的 __stdcall。

BestFitMapping     是否啟用最佳映射功能,預設為 true。
                   最佳映射功能提供在沒有匹配項時,自動提供匹配的字元。
                   無法映射的字元通常轉換為預設的“?”。

PreserveSig        託管方法簽名是否轉換成返回 HRESULT,預設值為 true(不應轉換籤名)。
                   並且傳回值有一個附加的 [out, retval] 參數的非託管簽名。
                    
ThrowOnUnmappableChar     控制對轉換為 ANSI '?' 字元的不可映射的 Unicode 字元引發異常。

C# 關鍵字 extern 的使用

public static extern int MyMethod(int x);

外部修飾符 extern 用於指示外部實現方法,常與 DllImport 屬性一起使用(DllImport 屬性提供非託管 DLL 函數的調用資訊)。

若將 abstract 和 extern 修飾符一起使用來修改同一成員是錯誤的。extern 將方法在 C# 代碼的外部實現,而 abstract 意味著在此類中未提供此方法的實現。

因為外部方法聲明不提供具體實現,所以沒有方法體;
此方法聲明只是以一個分號結束,並且在簽名後沒有大括弧{ }。

樣本3  接收使用者輸入的字串並顯示在訊息框中

程式從 User32.dll 庫匯入的 MessageBox 方法。
using System;
using System.Runtime.InteropServices;
class MyClass
{
   [DllImport("User32.dll")]
   public static extern int MessageBox(int h, string m, string c, int type);

   public static int Main()
   {
      string myString;
      Console.Write("Enter your message: ");
      myString = Console.ReadLine();
      return MessageBox(0, myString, "My Message Box", 0);
   }
}

運行結果: 輸入"Hello"文本後,螢幕上將彈出一個包含該文本的訊息框。
Enter your message: Hello

樣本4  調用DLL進行計算

該樣本使用兩個檔案 CM.cs 和 Cmdll.c 來說明 extern。
C 檔案是從 C# 程式中調用的外部 DLL。

使用 Visual C++ 命令列將 Cmdll.c 編譯為 DLL:
cl /LD /MD Cmdll.c

檔案:Cmdll.c
// cmdll.c
// compile with: /LD /MD
int __declspec(dllexport) MyMethod(int i)
{
   return i*10;
}

使用命令列編譯 CM.cs:
csc CM.cs
這將建立可執行檔 CM.exe。

檔案:CM.cs
// cm.cs
using System;
using System.Runtime.InteropServices;
public class MyClass
{
   [DllImport("Cmdll.dll")]
   public static extern int MyMethod(int x);

   public static void Main()
   {
      Console.WriteLine("MyMethod() returns {0}.", MyMethod(5));
   }
}

運行此程式,MyMethod 將值 5 傳遞到 DLL 檔案,該檔案將此值乘以 10 返回。
運行結果:MyMethod() returns 50.

IntPtr 類型的說明

對於平台叫用,應讓參數為 IntPtr 類型,而不是 String 類型。使用 System.Runtime.InteropServices.Marshal 類所提供的方法,可將類型手動轉換為字串並手動將其釋放。

IntPtr 類型被設計成整數,其大小適用於特定平台。
                  在 32 位硬體和作業系統中將是 32 位;
                  在 64 位元硬體和作業系統中將是 64 位元。

IntPtr 類型由支援指標的語言使用,並作為在支援與不支援指標的語言間引用資料的一種通用方式。它也可用於保持控制代碼。例如,IntPtr 的執行個體廣泛地用在 System.IO.FileStream 類中來保持檔案控制代碼。

IntPtr 類型符合 CLS 規範,而 UIntPtr 類型卻不符合。只有 IntPtr 類型可用在公用語言運行庫中。此類型實現 ISerializable 介面。

其中引用了微軟官方的部分例子,特此聲名。希望多多指教,共同進步!

來源:http://www.cnblogs.com/xugang/archive/2007/12/23/1011597.html 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.