C#與DLL和COM的混合編程(1)-C#調用C++寫的非託管的DLL中匯出的函數

來源:互聯網
上載者:User

C#調用C++寫的非託管的DLL中匯出的函數

Platform invoke是一個使得Managed 程式碼(managed code)能夠調用DLL中實現的非託管函數(unmanaged functions)的服務(service),例如:那些Win32 API中的函數。它定位(locate)並且調用(invoke)匯出的函數,在需要的時候,跨越互動邊界列集(marshal)它的參數(integers, strings, arrays, structures等)。
Platform invoke 在運行時(run time)依賴中繼資料(metadata)來定位匯出函數並列集參數.說明了這個過程:

 

 

下面介紹調用DLL非託管函數的過程

1. 指定DLL中的函數
至少,你要制定一個函數的名字和包含這個函數的DLL
注意ANSI和Unicode版本函數的差別
還可以改變DLL中的函數的名字,例如把MessageBoxA改為MsgBox


using System.Runtime.InteropServices;
public class Win32
{
    [DllImport("user32.dll", EntryPoint = "MessageBoxA")]
     public static extern int MsgBox(int hWnd, String text, String caption,
                                    uint type);
}


2. 建立一個類來包含DLL中的函數
你可以使用一個已存在的類,或者為每個非託管函數建立一個單獨的類,或者為一組非託管函數建立一個類
在一個託管類中封裝(Wrapping)常用的DLL函數是一種封裝平台相關功能的有效方法,雖然在某些情況下它(Wrapping)並不是必須的。提供一個封裝類是一種方便的方法,因為定義DLL的函數很麻煩又容易出錯。如果你使用Visual Basic 或者C#,你必須在C#的Class或者Visual Basic的module內聲明DLL的函數
在類的內部,為每一個你想調用的DLL函數定義一個static的方法。定義可以包含其他的資訊:例如字元集(character set)或者傳遞方法參數的調用規則(calling convention),如果忽略了這些資訊,將使用預設的設定。
一份完整的聲明和預設設定的列表 see Creating Prototypes in Managed Code.
一旦封裝完成,你可以像調用其他的static方法一樣調用這些方法。Platform invoke自動處理下面的調出函數

當為platform invoke設計一個託管的類的時候,要考慮類和DLL函數之間的關係。例如:
• 在一個已經存在的類中聲明DLL函數
• 為每一個DLL函數建立一個單獨的類,使得函數獨立並且容易找到
• 建立一個包含一系列相關DLL函數的類來邏輯分組並且減少開銷


3. 在Managed 程式碼中建立函數簽名(signature)
 [C#] 使用 DllImportAttribute 來制定DLL和函數。把方法標記為static and extern.

4. 


 [C#]
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct Point {
    public int x;
    public int y;
}  

[StructLayout(LayoutKind.Explicit)]
public struct Rect {
    [FieldOffset(0)] public int left;
    [FieldOffset(4)] public int top;
    [FieldOffset(8)] public int right;
    [FieldOffset(12)] public int bottom;
}  

class Win32API {
    [DllImport("User32.dll")]
    public static extern bool PtInRect(ref Rect r, Point p);
}

[C#]
[StructLayout(LayoutKind.Sequential)]
public class MySystemTime {
    public ushort wYear;
    public ushort wMonth;
    public ushort wDayOfWeek;
    public ushort wDay;
    public ushort wHour;
    public ushort wMinute;
    public ushort wSecond;
    public ushort wMilliseconds;
}
class Win32API {
    [DllImport("Kernel32.dll")]
    public static extern void GetSystemTime(MySystemTime st);
}

 

  
回呼函數的理想用到的情形是一個任務重複的執行。另一個通常的用法是enumeration函數,例如:EnumFontFamilies,EnumPrinters, 和EnumWindows
實現一個回呼函數
下面的流程說明了怎樣在一個託管的應用中使用P/Invoke列印本機上的每一個視窗的handle。特別的是,這個例子使用EnumWindows函數來處理視窗的列表並且,一個託管的回呼函數(named CallBack)用來列印視窗的handle的值


[C#]
using System;
using System.Runtime.InteropServices;

public delegate bool CallBack(int hwnd, int lParam);
public class EnumReportApp {
    [DllImport("user32")]
    public static extern int EnumWindows(CallBack x, int y);
    public static void Main()
    {
        CallBack myCallBack = new CallBack(EnumReportApp.Report);
        EnumWindows(myCallBack, 0);
    }
   public static bool Report(int hwnd, int lParam) {
        Console.Write("Window handle is ");
        Console.WriteLine(hwnd);
        return true;
    }
}

實現一個回呼函數
(1). 在實現之間,查看一下EnumWindows函數. EnumWindows的(signature)如下:
BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam)
一個線索是這個函數需要一個callback的參數lpEnumFunc
(2). 建立一個託管的函數。例子聲明了一個delegate類型(called CallBack),包含兩個參數(hwnd and lparam).
(3). 建立一個代理(delegate)然後把它作為EnumWindows的參數傳遞給EnumWindows,Platform invoke自動轉換delegate為回呼函數。
(4). 確定垃圾收集器在回呼函數完成之前不會回收delegate .當你把delegate當作參數傳遞時,或者傳遞一個包含delegate的結構時,在調用的過程中,他不會被回收,這樣,在下面這個enumeration的例子中,callback函數在函數調用返回之前完成,所以託管的調用者不需要額外的工作。
如果,callback在函數調用返回之後可以被調用,託管的調用者必須保證在回呼函數完成之前delegate沒有被垃圾收集。防止垃圾收集的資訊參見Interop Marshaling• 實現回呼函數(Implementing Callback Functions)
回呼函數是在託管應用中的代碼來協助非託管的DLL函數完成一個功能。下面的例子說明了回呼函數的元素和怎樣在Managed 程式碼中實現回調。
回呼函數基礎
從Managed 程式碼中調用DLL中的函數,建立一個DLL中函數的託管的定義然後調用它,這個過程是很直接的
使用DLL的需要回調的函數需要一些額外的步驟。首先,必須通過尋找協助確定一個函數是否需要回調,然後,在你的Managed 程式碼中建立回呼函數,最後,調用DLL的函數,傳遞指向回呼函數的指標。描述了這個過程聲明傳遞Classes
可以傳遞類的成員給一個非託管DLL的函數,由於類的成員是有固定的布局(layout)。下面的例子說明了怎樣傳遞MySystemTime類順序定義的成員給User32.dll中的GetSystemTime函數
GetSystemTime 的函數簽名(signature)如下
void GetSystemTime(SYSTEMTIME* SystemTime);調用DLL中的函數.
調用你的託管類的方法像你調用其他的方法一樣,傳遞結構體和實現回呼函數的情形例外
• 傳遞結構體(Passing Structures)
聲明傳遞結構
下面的例子顯示了怎樣在Managed 程式碼中定義一個Point和Rect 結構,然後把它們當作參數傳遞給User32.dll 中的PtInRect函數。
PtInRect的函數簽名(signature)如下
BOOL PtInRect(const RECT *lprc, POINT pt);
注意必須傳遞一個 Rect的引用,因為這個函數需要一個指向RECT類型的指標

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.