C#呼叫Delphi的dll之詳解

weixin_33816946發表於2015-10-11

C#呼叫Delphi介面方法,有兩種解決辦法:   

  一、將Delphi程式編譯成一個COM元件,然後在C#裡引用COM元件。

  二、非託管呼叫Dephi的DLL檔案。

 

      這裡我們主要講解一下第二種方法,講第二種方法之前首先講解下DllImport。

     DllImport是System.Runtime.InteropServices名稱空間下的一個屬性類,其功能是提供從非託管DLL匯出的函式的必要呼叫資訊。

     DllImport屬性應用於方法,要求最少要提供包含入口點的dll的名稱。
     DllImport的定義如下:
 

 

程式碼
 1      [AttributeUsage(AttributeTargets.Method)]
 2      public class DllImportAttribute: System.Attribute
 3      {
 4       public DllImportAttribute(string dllName) {…} //定位引數為dllName
 5       public CallingConvention CallingConvention; //入口點呼叫約定
 6       public CharSet CharSet;                                   //入口點採用的字元接
 7       public string EntryPoint;                                  //入口點名稱
 8       public bool ExactSpelling;                               //是否必須與指示的入口點拼寫完全一致,預設false
 9       public bool PreserveSig;                                  //方法的簽名是被保留還是被轉換
10       public bool SetLastError;                                  //FindLastError方法的返回值儲存在這裡
11       public string Value { get {…} }
12      } 
13 

 

  上面DLL的名字有時需要寫上路徑的如[DllImport(@"C:\OJ\Bin\Judge.dll")]這樣指定DLL的絕對路徑就可以正常裝載。

      假如沒有路徑的話,DllImport會按照順序自動去尋找的地方: 
     1、exe所在目錄 
     2、System32目錄 
     3、環境變數目錄
     所以只需要你把引用的DLL 拷貝到這三個目錄下, 就可以不用寫路徑了。

     說明:    
    1、DllImport只能放置在方法宣告上。   
    2、DllImport具有單個定位引數:指定包含被匯入方法的 dll 名稱的 dllName 引數。   
    3、DllImport具有五個命名引數:    
        a、CallingConvention 引數指示入口點的呼叫約定。如果未指定 CallingConvention,則使用預設值 CallingConvention.Winapi。    
        b、CharSet 引數指示用在入口點中的字符集。如果未指定 CharSet,則使用預設值 CharSet.Auto。   
        c、EntryPoint 引數給出 dll 中入口點的名稱。如果未指定 EntryPoint,則使用方法本身的名稱。    
        d、ExactSpelling 引數指示 EntryPoint 是否必須與指示的入口點的拼寫完全匹配。如果未指定 ExactSpelling,則使用預設值 false。    
      e、PreserveSig 引數指示方法的簽名應當被保留還是被轉換。當簽名被轉換時,它被轉換為一個具有 HRESULT返回值和該返回值的一個名為 retval 的附加輸出引數的簽名。如果未指定 PreserveSig,則使用預設值 true。    
      f、SetLastError 引數指示方法是否保留 Win32"上一錯誤"。如果未指定 SetLastError,則使用預設值 false。    
   4、它是一次性屬性類。    
   5、此外,用 DllImport 屬性修飾的方法必須具有 extern 修飾符。

 

      下面講解下如何呼叫:

      用DllImport來呼叫的  一般是用非託管的。
  具體形式如下:1.[DllImport("WZFSE.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]。

  其中第一個引數是指要引用DLL的名字, 這個名字應該是個常量(否則會出錯)。


     要想在自己C#頁面中引用,那就得在頁面中申明這個函式。

  下面緊接著他的申明函式:
  2.public static extern void InitDll(IntPtr handle, bool methodAddress);(Dephi裡怎麼定義的函式在C#這裡就要怎麼定義:即Dephi的申明函式轉換成C#的宣告函式)。
  --申明一個函式就要引用下他的DLL 如1和2是緊密連在一起的。即再寫一個函式就相應的應用起對應的DLL。

 

  下面是引數的引用:即Delphi的型別向C#的轉換。

     第一個引數型別:IntPtr這個型別可以申明為其他語言的控制程式碼,指標等。
     若要實現其他語言類似C++的函式指標形式, 這時我們考慮用C#的委託來實現。

  

  下面說一下:如何將Dephi的窗體顯示在自己的頁面中(且不能顯示Delphi窗體的標題欄,實現無縫的結合)。

  將dephi的窗體簽入到自己的C#系統裡 還有一點比較重要,我們是呼叫Delphi的窗體,此時顯示在我們C#窗體中會有Delphi的窗體,  

這時我們怎麼辦呢,  怎麼去除Delphi中的窗體呢?  這時我們就需要用API函式了。 因為WINDOWS API是一種比較底層的語言,可以通過它進行操作。

 在C#中是這麼引用的: [DllImport("user32.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
                 public static extern void MoveWindow(IntPtr handler, int x, int y, int width, int height, bool repaint);

  下面插入一個類,這裡麵包含了怎麼引用dephi的dll 以及怎麼申明:

程式碼
 1  public class CompliancePlatDLL
 2     {
 3         public static string strPath = "";
 4         /// <summary>
 5         /// 初始化
 6         /// </summary>
 7         /// <param name="handle"></param>
 8         /// <param name="methodAddress"></param>
 9         [DllImport("WZFSE.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
10         public static extern void InitDll(IntPtr handle, bool methodAddress);
11         /// <summary>
12         /// 載入相應的服務
13         /// </summary>
14         /// <param name="str"></param>
15         /// <param name="str2"></param>
16         /// <param name="i"></param>
17         /// <returns></returns>
18         [DllImport("WZFSE.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
19         public static extern IntPtr wzLoadModule(string str, string str2, int i);
20         /// <summary>
21         /// 去除相應的服務
22         /// </summary>
23         /// <param name="handle"></param>
24         /// <returns></returns>
25         [DllImport("WZFSE.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
26         public static extern bool wzUnloadModule(IntPtr handle);
27 
28         #region API函式
29         /// <summary>
30         /// API函式 設定主輔窗體
31         /// </summary>
32         /// <param name="child"></param>
33         /// <param name="parent"></param>
34         [DllImport("user32.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
35         public static extern void SetParent(IntPtr child, IntPtr parent);
36         /// <summary>
37         /// API函式 移動窗體
38         /// </summary>
39         /// <param name="handler"></param>
40         /// <param name="x"></param>
41         /// <param name="y"></param>
42         /// <param name="width"></param>
43         /// <param name="height"></param>
44         /// <param name="repaint"></param>
45         [DllImport("user32.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
46         public static extern void MoveWindow(IntPtr handler, int x, int y, int width, int height, bool repaint);
47 
48         [DllImport("user32.dll", EntryPoint = "GetWindowLong")]
49         public static extern long GetWindowLong(IntPtr hwnd, int nIndex);
50         /// <summary>
51         /// API函式 去除窗體的標題欄
52         /// </summary>
53         /// <param name="hwnd"></param>
54         /// <param name="nIndex"></param>
55         /// <param name="dwNewLong"></param>
56         /// <returns></returns>
57         [DllImport("user32.dll", EntryPoint = "SetWindowLong")]
58         public static extern long SetWindowLong(IntPtr hwnd, int nIndex, long dwNewLong);
59         public const int GWL_EXSTYLE = -16;
60         public const int WS_EX_TRANSPARENT = 0x20;
61         public const int WS_EX_LAYERED = 0x80000;
62         public const int LWA_ALPHA = 2;
63         public const int WS_CAPTION = 0xC00000;
64         #endregion
65     }

 

       其中API中的SetWindowLong這個方法是可以實現去除窗體的標題欄的,  具體呼叫SetWindowLong(common.p, GWL_EXSTYLE, GetWindowLong(handle, GWL_EXSTYLE) & (~WS_CAPTION));

  但一般完整利用API函式的呼叫是這樣的

程式碼
 1          decallback de1 = new decallback(iscallback);//利用委託
 2                InitDll(this.Handle, de1(this.Handle));//初始化
 3                IntPtr p = wzLoadModule("DoRiskSetup", "", 0);//取得控制程式碼
 4                if (p != (IntPtr)0)//判斷該控制程式碼不是彈出窗體時
 5                {
 6                    //去除dephi窗體的標題欄
 7                    SetParent(p, panel1.Handle);
 8                    SetWindowLong(p, GWL_EXSTYLE, GetWindowLong(p, GWL_EXSTYLE) & (~WS_CAPTION));
 9                    MoveWindow(p, 0, 0, panel1.ClientSize.Width, panel1.ClientSize.Height, false);
10                }

   SetWindowLong(IntPtr handle, int t,long l) 第一個引數為控制程式碼,是你調的dephi窗體的控制程式碼,第二個引數為整型,在dephi用常量GWL_EXSTYLE表示,表示要顯示的樣式,在C#中翻譯過來的他值為(-16),而第三個函則為長整型和第二個引數一起表示要去除第一個引數控制程式碼窗體的標題欄在Delphi中表示為:GetWindowLong(handle,GWL_EXSTYLE) and (not WS_CAPTION) 在C#中則翻譯為:GetWindowLong(handle,-16)&(~0xC00000),handle是指要呼叫的Delphi窗體的控制程式碼,GetWindowLong這個函式是獲得該窗體的相關資訊。大體上是這個用法,如有不懂大家可以提出來 共同探討。

     一般型別對應如下:

  Dephi-->C#

  intger -->int

  longint -->long

  pchar -->string

  THandle -->IntPtr  

相關文章