C#.Net調用非託管的DLL

來源:互聯網
上載者:User

一、DLL介紹:

動態連結程式庫(DLL,即“Dynamic Link Library”)是Microsoft Windows最重要的組成元素之一,開啟windows系統檔案夾,會發現很多DLL檔案,windows就是將一些主要的系統功能以DLL模組的形式實現。動態連結程式庫是不能直接執行的,也不能接收訊息,它是一個獨立的檔案,其中包含被程式或其他DLL調用來完成一定操作的函數(方法)。但這些函數不是執行程式本身的一部分,而是根據進程的需要按需載入,此時才能發揮作用。

二、C#.Net調用基本格式:

[DLLImport(“DLL檔案路徑”)]

修飾符 extern 傳回值類型 方法名稱(參數列表) 如:

[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "SetLocalTime")]
      public static extern int SetSystemTime(ref SystemTime lpSystemTime);

PS:

1、DLL檔案必須位於程式目前的目錄或系統定義的查詢路徑中(即:系統內容變數中Path所設定的路徑)。

2、DLLImport會按照順序去尋找DLL檔案(程式目前的目錄>System32目錄>環境變數Path所設定路徑)。

3、傳回型別變數、方法名稱、參數列表一定要與DLL檔案中的定義相一致。

4、Asp.net DLLImport路徑----使用第三方非託管的DLL(Charles.dll)組件的時候,當把Charles.dll拷貝到Bin目錄下,提示仍然提示仍然找不到該dll.(而這樣[DLLImport(@“C:\ProgramDir\Charles.dll”)]可以正常載入)。Asp.Net Team的官方解決方案如下:

首先需要確認引用了哪些組件?哪些是託管的?那些是非託管的?

託管的很方便,直接被使用的需要引用,間接使用的需要拷貝到Bin目錄下。非託管的就特殊處理(實際上你拷貝到bin是沒有任何作用的,因為CLR會把檔案拷貝到一個臨時目錄下,然後在那運行Web,而CLR只會拷貝託管檔案,這就是為什麼把非託管的DLL放到bin目錄下仍然提示找不到該模組)。

解決方案:首先在伺服器上建立一個建立的目錄,假設是(C:\ProgramDir\WinDLL\).然後在環境變數中,給Path變數添加這個目錄,最後把非託管的DLL檔案都拷貝到該目錄下。或者更乾脆把DLL放到System32目錄中。對於自己部署的應用程式,這樣的確能很好的解決問題。然而如果我們用的是虛擬空間,我們有沒有辦法吧註冊Path變數或者把我們自己的DLL拷貝System32目錄下。同時我們也不一定知道我們DLL的實體路徑.

DLLImport裡面只能用字元常量,而不能使用Server.MapPath來確認物理絕對路徑。

這樣的話我們需要動態取得我們DLL的實體路徑(Server.MapPath),並通過API來取得DLL裡面的函數(先載入LoadLibrary後獲得函數地址GetProcAddress)。相關的API如下:

Public Class CustomDLLInvoke

{

[DLLImport(“kernel32.dll”)]

private extern static IntPtr LoadLibrary(string path);

[DLLImport(kernel32.dll)]

private extern static IntPtr GetProcAddress(IntPtr lib,String funcName);

[DLLImport(Kernel32.dll)]

private extern static bool FreeLibrary(IntPtr lib);

private IntPtr MLib;

public CustomDLLInvoke(string dllPath)

{MLib=LoadLibrary(DLLPath)}

~CustomDLLInvoke(){FreeLibrary(MLib);}

public Delegate Invoke(string APIName,Type t)

{IntPtr api=GetProAddress(MLib,APIName);return (Delegate)Marshal.GetDelegateForFunctionPointer(api,t);}

}

三、訊息回調

函數的兩種調用方式:

StdCall:stdcall呼叫慣例又稱為passcal呼叫慣例,其呼叫慣例申明的文法為:int_stdcall function(int a,int b).stdcall的呼叫慣例意味著:1.參數從右向左壓入堆棧。2.函數自身修改堆棧。3.函數名自動加前置的底線,後面緊跟一個@符號。其後緊跟著參數的尺寸。在C#中,函數只支援stdcall的調用方式。

Cdecl:cdecl呼叫慣例又稱為C呼叫慣例,是C語言預設的呼叫慣例,它的定義文法是:int_cdecl function(int a,int b).cdecal呼叫慣例的參數壓棧順序和stdcall是一樣的。參數首先由右向左壓入堆棧。所不同的是,函數本身不清理堆棧,調用者複製清理堆棧。由於這種變化,C呼叫慣例允許函數的參數的個數是不固定的,這也是C語言的一大特色。VC++ 6.0預設使用cdecl的調用方式。

所以當C#通過訊息回調dll函數時候,由於函數調用的約定不同,函數不能正確的調用。

採取方式:1.修改dll檔案,支援stdCall的調用方式。2.手工方式修改.il中間檔案(C#不支援_cdecl修飾符,可.net中間檔案.il是支援cdecl的調用方式。)

3.用VC++ 7.0給windows dll封裝一層外殼。(在VC++ 7.0中,可以通過_cdecl修飾符,指定函數的調用方式)

四、複雜類型Demo樣本(修改PC機的系統時間)

public struct SystemTime
   {
       public short wYear;
       public short wMonth;
       public short wDayOfWeek;
       public short wDay;
       public short wHour;
       public short wMinute;
       public short wSecond;
       public short wMilliseconds;
   }

[DllImport("kernel32.dll", SetLastError = true, EntryPoint = "SetLocalTime")]
    public static extern int SetSystemTime(ref SystemTime lpSystemTime);

[DllImport("kernel32.dll", SetLastError = true)]
    public static extern long GetLastError();

    private void button1_Click(object sender, EventArgs e)
    {
          DateTime setdt=DateTime.Parse(this.textBox1.Text);
          SystemTime st = new SystemTime();
          st.wYear = (short)setdt.Year;
          st.wMonth = (short)setdt.Month;
          st.wDay =(short) setdt.Day;
          st.wHour = (short)setdt.Hour;
          st.wMinute =(short)setdt.Minute;
          st.wSecond =(short) setdt.Second;
          int result = SetSystemTime(ref st);
          if (result == 1)
          {
              MessageBox.Show("修改成功!");
          }
          else
          {
              long errorCode = GetLastError();
              //int code = Marshal.GetLastWin32Error();//.net常用這種方式代替GetLastError API
              MessageBox.Show("修改失敗,Win32錯誤碼是{0},請查看GetLastError傳回值的意義列表或調用FormatMessage查看" + errorCode.ToString());
          }
     }

PS:

1、SetLocalTime與SetSystemTime的區別:SetLocalTime的用法與SetSystemTime基本相同,差別在於SetSystemTime所帶的參數指定的是UTC時間(國際標準時間),也就是說,針對我們地區的電腦(東八區),這樣的話,使用SysteSystemTime設定後,系統的時間,會比參數所設定的時間快8個小時)

2、GetLastError傳回值的意義(http://blog.chinaunix.net/u2/82288/showart_1335456.html)

Best Regards,

Charles Chen

相關文章

聯繫我們

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