C# 中dllimport 應用

C#中得DllImpor

大家在實際工作學習C#的時候,可能會問:爲什麼我們要爲一些已經存在的功能(比如Windows中的一些功能,C++中已經編寫好的一些方法)要重新編寫代碼,C#有沒有方法可以直接都用這些原本已經存在的功能呢?答案是肯定的,大家可以通過C#中的DllImport直接調用這些功能。
DllImport所在的名字空間 using System.Runtime.InteropServices;
MSDN中對DllImportAttribute的解釋是這樣的:可將該屬性應用於方法。DllImportAttribute 屬性提供對從非託管 DLL 導出的函數進行調用所必需的信息。作爲最低要求,必須提供包含入口點的 DLL 的名稱。
DllImport 屬性定義如下:
namespace System.Runtime.InteropServices
{
  [AttributeUsage(AttributeTargets.Method)]
  public class DllImportAttribute: System.Attribute
  {
   public DllImportAttribute(string dllName) {...}
   public CallingConvention CallingConvention;
   public CharSet CharSet;
   public string EntryPoint;
   public bool ExactSpelling;
   public bool PreserveSig;
   public bool SetLastError;
   public string Value { get {...} }
  }
}
  說明:
  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的用法:
DllImport("MyDllImport.dll")]
private static extern int mySum(int a,int b);

一 在C#程序設計中使用Win32類庫
常用對應類型:
1、DWORD 是 4 字節的整數,因此我們可以使用 int 或 uint 作爲 C# 對應類型。
2、bool 類型與 BOOL 對應。

示例一:調用 Beep() API 來發出聲音
Beep() 是在 kernel32.lib 中定義的,在MSDN 中的定義,Beep具有以下原型:
BOOL Beep(DWORD dwFreq, // 聲音頻率
DWORD dwDuration // 聲音持續時間);
用 C# 編寫以下原型:
[DllImport("kernel32.dll")]
public static extern bool Beep(int frequency, int duration);

示例二:枚舉類型和常量
MessageBeep() 是在 user32.lib 中定義的,在MSDN 中的定義,MessageBeep具有以下原型:
BOOL MessageBeep(UINT uType // 聲音類型
);

用C#編寫一下原型:
public enum BeepType
{
  SimpleBeep = -1,
  IconAsterisk = 0x00000040,
  IconExclamation = 0x00000030,
  IconHand = 0x00000010,
  IconQuestion = 0x00000020,
  Ok = 0x00000000,
}
uType 參數實際上接受一組預先定義的常量,對於 uType 參數,使用 enum 類型是合乎情理的。
[DllImport("user32.dll")]
public static extern bool MessageBeep(BeepType beepType);

示例三:處理結構
有時我需要確定我筆記本的電池狀況。Win32 爲此提供了電源管理函數,搜索 MSDN 可以找到GetSystemPowerStatus() 函數。
BOOL GetSystemPowerStatus(
  LPSYSTEM_POWER_STATUS lpSystemPowerStatus
);
此函數包含指向某個結構的指針,我們尚未對此進行過處理。要處理結構,我們需要用 C# 定義結構。我們從非託管的定義開始:
typedef struct _SYSTEM_POWER_STATUS {
BYTE  ACLineStatus;
BYTE  BatteryFlag;
BYTE  BatteryLifePercent;
BYTE  Reserved1;
DWORD BatteryLifeTime;
DWORD BatteryFullLifeTime;
} SYSTEM_POWER_STATUS, *LPSYSTEM_POWER_STATUS;
   然後,通過用 C# 類型代替 C 類型來得到 C# 版本。
struct SystemPowerStatus
{
  byte ACLineStatus;
  byte batteryFlag;
  byte batteryLifePercent;
  byte reserved1;
  int batteryLifeTime;
  int batteryFullLifeTime;
}
這樣,就可以方便地編寫出 C# 原型:
[DllImport("kernel32.dll")]
public static extern bool GetSystemPowerStatus(
  ref SystemPowerStatus systemPowerStatus);
   在此原型中,我們用“ref”指明將傳遞結構指針而不是結構值。這是處理通過指針傳遞的結構的一般方法。
   此函數運行良好,但是最好將 ACLineStatus 和 batteryFlag 字段定義爲 enum:
  enum ACLineStatus: byte
   {
    Offline = 0,
    Online = 1,
    Unknown = 255,
   }
   enum BatteryFlag: byte
   {
    High = 1,
    Low = 2,
    Critical = 4,
    Charging = 8,
    NoSystemBattery = 128,
    Unknown = 255,
   }
請注意,由於結構的字段是一些字節,因此我們使用 byte 作爲該 enum 的基本類型

示例四:處理字符串


二 C# 中調用C++代碼
int 類型
[DllImport(“MyDLL.dll")]
//返回個int 類型
public static extern int mySum (int a1,int b1);
//DLL中申明
extern “C” __declspec(dllexport) int WINAPI mySum(int a2,int b2)
{
//a2 b2不能改變a1 b1
//a2=..
//b2=...
return a+b;
}

//參數傳遞int 類型
public static extern int mySum (ref int a1,ref int b1);
//DLL中申明
extern “C” __declspec(dllexport) int WINAPI mySum(int *a2,int *b2)
{
//可以改變 a1, b1
*a2=...
*b2=...
return a+b;
}


DLL 需傳入char *類型
[DllImport(“MyDLL.dll")]
//傳入值
public static extern int mySum (string astr1,string bstr1);
//DLL中申明
extern “C” __declspec(dllexport) int WINAPI mySum(char * astr2,char * bstr2)
{
//改變astr2 bstr 2 ,astr1 bstr1不會被改變
return a+b;
}


DLL 需傳出char *類型
[DllImport(“MyDLL.dll")]
// 傳出值
public static extern int mySum (StringBuilder abuf, StringBuilder bbuf );
//DLL中申明
extern “C” __declspec(dllexport) int WINAPI mySum(char * astr,char * bstr)
{
//傳出char * 改變astr bstr -->abuf, bbuf可以被改變
return a+b;
}

DLL 回調函數

BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam)

 

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;
}
}

DLL 傳遞結構
BOOL PtInRect(const RECT *lprc, POINT pt);

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 XXXX {
[DllImport("User32.dll")]
public static extern bool PtInRect(ref Rect r, Point p);
}

 

 

---------------------------------------------------------------

 

(2)關於unsigned char * pBuffer,這個unsigned char *其實有2個轉換可選,有時可以使用byte[],有時則是StringBuilder,這就要集體問題具體分析了。例如:

 

int GetErrorMessage_ex6 (int ErrorNr, unsigned long BufLen, unsigned char* pBuffer);

void copy_buffer_ex6 (unsigned char * pTargetBuffer, unsigned char *pSourceBuffer, unsigned long Amount);

  前者就轉換成StringBuilder後者是byte[]。

  (3)有些變量雖是整型但是可以用枚舉,而且用枚舉感覺更合適

  例如

int db_read_ex6 (unsigned short BlkNr, unsigned char DatType, unsigned

short StartNr, unsigned long * pAmount, unsigned long BufLen, unsigned

char * pReadBuffer, unsigned long * pDatLen)中,unsigned char DatType其實指的是“0x02 = BYTE, 0x04 = WORD, 0x06 = DWORD default: DatType = 0x02”等數據類型

,因此可以翻譯成

public enum DatType : byte//PLC數據類型

        {

            BYTE = 0x02,

            WORD = 0x04,

            DWORD = 0x06,

        }

  (4)對了,如果是對象型的引用,比如unsigned char*轉成byte[],是不需要加ref,但如果是c++ int轉 c# int 則要加ref關鍵字。

  要說明的就是這些,下面請各位看官看看我的轉換代碼吧,還請見教:

    public class Prodave6

    {

        #region 常值定義(用於極限值)

        public const int MAX_CONNECTIONS = 64; // 64 is default in PRODAVE

        public const int MAX_DEVNAME_LEN = 128;// e.g. "S7ONLINE"

        public const int MAX_BUFFERS = 64; // 64 for blk_read() and blk_write()

        public const int MAX_BUFFER = 65536; // Transfer buffer for error text)

        #endregion

        #region 結構體定義

        public struct CON_TABLE_TYPE//待連接plc地址屬性表

        {

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]

            //public CON_ADR_TYPE Adr; // connection address

            public byte[] Adr; // connection address

            // MPI/PB station address (2)

            // IP address (192.168.0.1)

            // MAC address (08-00-06-01-AA-BB)

            public byte AdrType; // Type of address: MPI/PB (1), IP (2), MAC (3)

            public byte SlotNr; // Slot number

            public byte RackNr; // Rack number

        }

        public enum DatType : byte//PLC數據類型

        {

            BYTE = 0x02,

            WORD = 0x04,

            DWORD = 0x06,

        }

        public enum FieldType : byte//PLC區域類型

        {

            //Value types as ASCII characters區域類型對應的ASCII字符

            //data byte (d/D)

            d = 100,

            D = 68,

            //input byte (e/E)

            e = 101,

            E = 69,

            //output byte (a/A)

            a = 97,

            A = 65,

            //memory byte (m/M)

            m = 109,

            M = 77,

            //timer word (t/T),

            t = 116,

            T = 84,

        }

        #endregion

        #region PLC基本函數

        [DllImport("Prodave6.dll")]//連接PLC操作

        //參數:連接號(0-63)、常值"S7ONLINE"、待連接plc地址屬性表長度(字節爲單位,常值9)、待連接plc地址屬性表

        public extern static int LoadConnection_ex6(int ConNr, string pAccessPoint, int ConTableLen, ref  CON_TABLE_TYPE pConTable);

        [DllImport("Prodave6.dll")]//斷開PLC操作

        //參數:連接號(0-63)

        public extern static int UnloadConnection_ex6(UInt16 ConNr);

        [DllImport("Prodave6.dll")]//激活PLC連接操作

        //參數:連接號(0-63)

        public extern static int SetActiveConnection_ex6(UInt16 ConNr);

        [DllImport("Prodave6.dll")]//PLC db區讀取操作

        //參數:data block號、要讀取的數據類型、起始地址號、需要讀取類型的數量、緩衝區長度(字節爲單位)、緩衝區、緩衝區數據交互的長度

        public extern static int db_read_ex6(UInt16 BlkNr, DatType DType, UInt16 StartNr, ref UInt32 pAmount, UInt32 BufLen,

            byte[] pBuffer, ref UInt32 pDatLen);

        [DllImport("Prodave6.dll")]//PLC db區寫入操作

        //參數:data block號、要寫入的數據類型、起始地址號、需要寫入類型的數量、緩衝區長度(字節爲單位)、緩衝區

        public extern static int db_write_ex6(UInt16 BlkNr, DatType Type, UInt16 StartNr, ref UInt32 pAmount, UInt32 BufLen,

            byte[] pBuffer);

        [DllImport("Prodave6.dll")]//PLC 任意區讀取操作

        //參數:要讀取的區類型、data block號(DB區特有,默認爲0)、起始地址號、需要讀取類型的數量、

        //緩衝區長度(字節爲單位)、緩衝區、緩衝區數據交互的長度

        public extern static int field_read_ex6(FieldType FType, UInt16 BlkNr, UInt16 StartNr, UInt32 pAmount, UInt32 BufLen,

            byte[] pBuffer, ref UInt32 pDatLen);

        [DllImport("Prodave6.dll")]//PLC 任意區寫入操作

        //參數:要寫入的區類型、data block號(DB區特有,默認爲0)、起始地址號、需要寫入類型的數量、

        //緩衝區長度(字節爲單位)、緩衝區

        public extern static int field_write_ex6(FieldType FType, UInt16 BlkNr, UInt16 StartNr, UInt32 pAmount, UInt32 BufLen,

            byte[] pBuffer);

        [DllImport("Prodave6.dll")]//PLC M區某字節的某位讀取操作

        //參數:M區字節號、位號、當前的值(0/1)

        public extern static int mb_bittest_ex6(UInt16 MbNr, UInt16 BitNr, ref int pValue);

        [DllImport("Prodave6.dll")]//PLC M區某字節的某位寫入操作

        //參數:M區字節號、位號、要寫入的值(0/1)

        public extern static int mb_setbit_ex6(UInt16 MbNr, UInt16 BitNr, byte Value);

        #endregion

        #region PLC200用數據傳輸函數

        [DllImport("Prodave6.dll")]//200系列PLC 任意區讀取操作

        //參數:要讀取的區類型、data block號(DB區特有,默認爲0)、起始地址號、需要讀取類型的數量、

        //緩衝區長度(字節爲單位)、緩衝區、緩衝區數據交互的長度

        public extern static int as200_field_read_ex6(FieldType FType, UInt16 BlkNr, UInt16 StartNr, UInt32 pAmount, UInt32 BufLen,

            byte[] pBuffer, ref UInt32 pDatLen);

        [DllImport("Prodave6.dll")]//200系列PLC 任意區寫入操作

        //參數:要寫入的區類型、data block號(DB區特有,默認爲0)、起始地址號、需要寫入類型的數量、

        //緩衝區長度(字節爲單位)、緩衝區

        public extern static int as200_field_write_ex6(FieldType FType, UInt16 BlkNr, UInt16 StartNr, UInt32 pAmount, UInt32 BufLen,

            byte[] pBuffer);

        [DllImport("Prodave6.dll")]//200系列PLC M區某字節的某位讀取操作

        //參數:M區字節號、位號、當前的值(0/1)

        public extern static int as200_mb_bittest_ex6(UInt16 MbNr, UInt16 BitNr, ref int pValue);

        [DllImport("Prodave6.dll")]//200系列PLC M區某字節的某位寫入操作

        //參數:M區字節號、位號、要寫入的值(0/1)

        public extern static int as200_mb_setbit_ex6(UInt16 MbNr, UInt16 BitNr, byte Value);

        #endregion

        #region PLC數據轉換函數

        [DllImport("Prodave6.dll")]//診斷錯誤信息操作

        //參數:錯誤代號、緩衝區大小(字節爲單位)、緩衝區

        public extern static int GetErrorMessage_ex6(int ErrorNr, UInt32 BufLen, [MarshalAs(UnmanagedType.LPStr)] StringBuilder pBuffer);

        [DllImport("Prodave6.dll")]//S7浮點數轉換成PC浮點數

        //參數:S7浮點數、PC浮點數

        public extern static int gp_2_float_ex6(UInt32 gp, ref float pieee);

        [DllImport("Prodave6.dll")]//PC浮點數轉換成S7浮點數

        //參數:PC浮點數、S7浮點數

        public extern static int float_2_gp_ex6(float ieee, ref UInt32 pgp);

        [DllImport("Prodave6.dll")]//檢測某字節的某位的值是0或1

        //參數:字節值、位號

        public extern static int testbit_ex6(byte Value, int BitNr);

        [DllImport("Prodave6.dll")]//檢測某字節的byte值轉換成int數組

        //參數:byte值、int數組(長度爲8)

        public extern static void byte_2_bool_ex6(byte Value, int[] pBuffer);

        [DllImport("Prodave6.dll")]//檢測某字節的int數組轉換成byte值

        //參數:int數組(長度爲8)

        public extern static byte bool_2_byte_ex6(int[] pBuffer);

        [DllImport("Prodave6.dll")]//交換數據的高低字節——16位數據

        //參數:待交換的數據

        public extern static UInt16 kf_2_integer_ex6(UInt16 wValue);//16位數據——WORD

        [DllImport("Prodave6.dll")]//交換數據的高低字節——32位數據

        //參數:待交換的數據

        public extern static UInt32 kf_2_long_ex6(UInt32 dwValue);//32位數據——DWORD

        [DllImport("Prodave6.dll")]//交換數據緩衝區的的高低字節區,例如pBuffer[0]與pBuffer[1],pBuffer[2]與pBuffer[3]交換

        //參數:待交換的數據緩衝區,要交換的字節數,如Amount=pBuffer.Length,則交換全部緩衝

        public extern static void swab_buffer_ex6(byte[] pBuffer, UInt32 Amount);

        [DllImport("Prodave6.dll")]//複製數據緩衝區

        //參數:目的數據緩衝區,源數據緩衝區,要複製的數量(字節爲單位)

        public extern static void copy_buffer_ex6(byte[] pTargetBuffer, byte[] pSourceBuffer, UInt32 Amount);

        [DllImport("Prodave6.dll")]//把二進制數組傳換成BCD碼的數組——16位數據

        //參數:要處理的數組,要處理的字節數,轉換前是否先交換高低字節,轉換後是否要交換高低字節

        //InBytechange爲1則轉換BCD碼之前,先交換高低字節

        //OutBytechange爲1則轉換BCD碼之後,再交換高低字節

        //如果InBytechange和OutBytechange都沒有置1,則不發生高低位的交換

        //16位數據BCD碼值的許可範圍是:+999 —— -999

        public extern static void ushort_2_bcd_ex6(UInt16[] pwValues, UInt32 Amount, int InBytechange, int OutBytechange);//16位數據——WORD

        [DllImport("Prodave6.dll")]//把二進制數組傳換成BCD碼的數組——32位數據

        //參數:要處理的數組,要處理的字節數,轉換前是否先交換高低字節,轉換後是否要交換高低字節

        //InBytechange爲1則轉換BCD碼之前,先交換高低字節

        //OutBytechange爲1則轉換BCD碼之後,再交換高低字節

        //如果InBytechange和OutBytechange都沒有置1,則不發生高低位的交換

        //32位數據BCD碼值的許可範圍是:+9 999 999 —— -9 999 999

        public extern static void ulong_2_bcd_ex6(UInt32[] pdwValues, UInt32 Amount, int InBytechange, int OutBytechange);//32位數據——DWORD

        [DllImport("Prodave6.dll")]//把BCD碼的數組傳換成二進制數組——16位數據

        //參數:要處理的數組,要處理的字節數,轉換前是否先交換高低字節,轉換後是否要交換高低字節

        //InBytechange爲1則轉換BCD碼之前,先交換高低字節

        //OutBytechange爲1則轉換BCD碼之後,再交換高低字節

        //如果InBytechange和OutBytechange都沒有置1,則不發生高低位的交換

        //16位數據BCD碼值的許可範圍是:+999 —— -999

        public extern static void bcd_2_ushort_ex6(UInt16[] pwValues, UInt32 Amount, int InBytechange, int OutBytechange);//16位數據——WORD

        [DllImport("Prodave6.dll")]//把BCD碼的數組傳換成二進制數組——32位數據

        //參數:要處理的數組,要處理的字節數,轉換前是否先交換高低字節,轉換後是否要交換高低字節

        //InBytechange爲1則轉換BCD碼之前,先交換高低字節

        //OutBytechange爲1則轉換BCD碼之後,再交換高低字節

        //如果InBytechange和OutBytechange都沒有置1,則不發生高低位的交換

        //32位數據BCD碼值的許可範圍是:+9 999 999 —— -9 999 999

        public extern static void bcd_2_ulong_ex6(UInt32[] pdwValues, UInt32 Amount, int InBytechange, int OutBytechange);//32位數據——DWORD

        [DllImport("Prodave6.dll")]//查看64個連接中哪些被佔用,哪些已經建立

        //參數:傳輸緩衝的字節長度,64位長度的數組(0或1)

        public extern static void GetLoadedConnections_ex6(UInt32 BufLen, int[] pBuffer);

        #endregion

        #region 自定義輔助函數

        public  static UInt16 bytes_2_word(byte dbb0, byte dbb1)//將高低2個byte轉換成1個word

        {

            UInt16 dbw0;

            dbw0 = (UInt16)(dbb0 * 256 + dbb1);

            return dbw0;

        }

        public static UInt32 bytes_2_dword(byte dbb0, byte dbb1, byte dbb2, byte dbb3)//將高低4個byte轉換成1個dword

        {

            UInt32 dbd0;

            dbd0 = (UInt32)(dbb0 *16777216 + dbb1 * 65536 + dbb2 * 256 + dbb3);

            return dbd0;

        }

        public static UInt32 words_2_dword(UInt16 dbw0, UInt16 dbw2)//將高低2個word轉換成1個dword

        {

            UInt32 dbd0;

            dbd0 = (UInt32)(dbw0 * 65536 + dbw2);

            return dbd0;

        }

        public static byte[] word_2_bytes(UInt16 dbw0)//將word拆分爲2個byte

        {

            byte[] bytes = new byte[2];

            bytes[0] = (byte)(dbw0 / 256);

            bytes[1] = (byte)(dbw0 % 256);

            return bytes;

        }

        public static byte[] dword_2_bytes(UInt32 dbd0)//將dword拆分爲4個byte

        {

            byte[] bytes = new byte[4];

            bytes[0] = (byte)(dbd0 / 16777216);

            dbd0 = dbd0 % 16777216;

            bytes[1] = (byte)(dbd0 / 65536);

            dbd0 = dbd0 % 65536;

            bytes[2] = (byte)(dbd0 / 256);

            bytes[3] = (byte)(dbd0 % 256);

            return bytes;

        }

        public static UInt16[] dword_2_words(UInt32 dbd0)//將dword拆分爲2個word

        {

            UInt16[] words = new UInt16[2];

            words[0] = (UInt16)(dbd0 / 65536);

            words[1] = (UInt16)(dbd0 % 65536);

            return words;

        }

        #endregion

   

    }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章