« 上一篇下一篇 »

C# 中dllimport 应用

    大家在实际工作学习C#的时候,可能会问:为什么我们要为一些已经存在的功能(比如Windows中的一些功能,C++中已经编写好的一些方法)要重新编写代码,C#有没有方法可以直接都用这些原本已经存在的功能呢?答案是肯定的,大家可以通过C#中的DllImport直接调用这些功能。

    DllImport所在的名字空间usingSystem.Runtime.InteropServices;

    MSDN中对DllImportAttribute的解释是这样的:可将该属性应用于方法。DllImportAttribute属性提供对从非托管DLL导出的函数进行调用所必需的信息。作为最低要求,必须提供包含入口点的DLL的名称。

    DllImport属性定义如下:

    namespaceSystem.Runtime.InteropServices

    {

    [AttributeUsage(AttributeTargets.Method)]

    publicclassDllImportAttribute:System.Attribute

    {

    publicDllImportAttribute(stringdllName){...}

    publicCallingConventionCallingConvention;

    publicCharSetCharSet;

    publicstringEntryPoint;

    publicboolExactSpelling;

    publicboolPreserveSig;

    publicboolSetLastError;

    publicstringValue{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")]

    privatestaticexternintmySum(inta,intb);

    一在C#程序设计中使用Win32类库

    常用对应类型:

    1、DWORD是4字节的整数,因此我们可以使用int或uint作为C#对应类型。

    2、bool类型与BOOL对应。

    示例一:调用Beep()API来发出声音

    Beep()是在kernel32.lib中定义的,在MSDN中的定义,Beep具有以下原型:

    BOOLBeep(DWORDdwFreq,//声音频率

    DWORDdwDuration//声音持续时间);

    用C#编写以下原型:

    [DllImport("kernel32.dll")]

    publicstaticexternboolBeep(intfrequency,intduration);

    示例二:枚举类型和常量

    MessageBeep()是在user32.lib中定义的,在MSDN中的定义,MessageBeep具有以下原型:

    BOOLMessageBeep(UINTuType//声音类型

    );

    用C#编写一下原型:

    publicenumBeepType

    {

    SimpleBeep=-1,

    IconAsterisk=0x00000040,

    IconExclamation=0x00000030,

    IconHand=0x00000010,

    IconQuestion=0x00000020,

    Ok=0x00000000,

    }

    uType参数实际上接受一组预先定义的常量,对于uType参数,使用enum类型是合乎情理的。

    [DllImport("user32.dll")]

    publicstaticexternboolMessageBeep(BeepTypebeepType);

    示例三:处理结构

    有时我需要确定我笔记本的电池状况。Win32为此提供了电源管理函数,搜索MSDN可以找到GetSystemPowerStatus()函数。

    BOOLGetSystemPowerStatus(

    LPSYSTEM_POWER_STATUSlpSystemPowerStatus

    );

    此函数包含指向某个结构的指针,我们尚未对此进行过处理。要处理结构,我们需要用C#定义结构。我们从非托管的定义开始:

    typedefstruct_SYSTEM_POWER_STATUS{

    BYTE ACLineStatus;

    BYTE BatteryFlag;

    BYTE BatteryLifePercent;

    BYTE Reserved1;

    DWORD BatteryLifeTime;

    DWORD BatteryFullLifeTime;

    }SYSTEM_POWER_STATUS,*LPSYSTEM_POWER_STATUS;

    然后,通过用C#类型代替C类型来得到C#版本。

    structSystemPowerStatus

    {

    byteACLineStatus;

    bytebatteryFlag;

    bytebatteryLifePercent;

    bytereserved1;

    intbatteryLifeTime;

    intbatteryFullLifeTime;

    }

    这样,就可以方便地编写出C#原型:

    [DllImport("kernel32.dll")]

    publicstaticexternboolGetSystemPowerStatus(

    refSystemPowerStatussystemPowerStatus);

    在此原型中,我们用“ref”指明将传递结构指针而不是结构值。这是处理通过指针传递的结构的一般方法。

    此函数运行良好,但是最好将ACLineStatus和batteryFlag字段定义为enum:

    enumACLineStatus:byte

    {

    Offline=0,

    Online=1,

    Unknown=255,

    }

    enumBatteryFlag:byte

    {

    High=1,

    Low=2,

    Critical=4,

    Charging=8,

    NoSystemBattery=128,

    Unknown=255,

    }

    请注意,由于结构的字段是一些字节,因此我们使用byte作为该enum的基本类型

    示例四:处理字符串

    二C#中调用C++代码

    int类型

    [DllImport(“MyDLL.dll")]

    //返回个int类型

    publicstaticexternintmySum(inta1,intb1);

    //DLL中申明

    extern“C”__declspec(dllexport)intWINAPImySum(inta2,intb2)

    {

    //a2b2不能改变a1b1

    //a2=..

    //b2=...

    returna+b;

    }

    //参数传递int类型

    publicstaticexternintmySum(refinta1,refintb1);

    //DLL中申明

    extern“C”__declspec(dllexport)intWINAPImySum(int*a2,int*b2)

    {

    //可以改变a1,b1

    *a2=...

    *b2=...

    returna+b;

    }

    DLL需传入char*类型

    [DllImport(“MyDLL.dll")]

    //传入值

    publicstaticexternintmySum(stringastr1,stringbstr1);

    //DLL中申明

    extern“C”__declspec(dllexport)intWINAPImySum(char*astr2,char*bstr2)

    {

    //改变astr2bstr2,astr1bstr1不会被改变

    returna+b;

    }

    DLL需传出char*类型

    [DllImport(“MyDLL.dll")]

    //传出值

    publicstaticexternintmySum(StringBuilderabuf,StringBuilderbbuf);

    //DLL中申明

    extern“C”__declspec(dllexport)intWINAPImySum(char*astr,char*bstr)

    {

    //传出char* 改变astr bstr-->abuf,bbuf可以被改变

    returna+b;

    }

    DLL回调函数

    BOOLEnumWindows(WNDENUMPROClpEnumFunc,LPARAMlParam)

    usingSystem;

    usingSystem.Runtime.InteropServices;

    publicdelegateboolCallBack(inthwnd,intlParam);//定义委托函数类型

    publicclassEnumReportApp

    {

    [DllImport("user32")]

    publicstaticexternintEnumWindows(CallBackx,inty);

    publicstaticvoidMain(){

    CallBackmyCallBack=newCallBack(EnumReportApp.Report);EnumWindows(myCallBack,0);

    }

    publicstaticboolReport(inthwnd,intlParam)

    {

    Console.Write("Windowhandleis");

    Console.WriteLine(hwnd);returntrue;

    }

    }

    DLL传递结构

    BOOLPtInRect(constRECT*lprc,POINTpt);

    usingSystem.Runtime.InteropServices;

    [StructLayout(LayoutKind.Sequential)]

    publicstructPoint{

    publicintx;

    publicinty;

    }

    [StructLayout(LayoutKind.Explicit)]

    publicstructRect

    {

    [FieldOffset(0)]publicintleft;

    [FieldOffset(4)]publicinttop;

    [FieldOffset(8)]publicintright;

    [FieldOffset(12)]publicintbottom;

    }

    ClassXXXX{

    [DllImport("User32.dll")]

    publicstaticexternboolPtInRect(refRectr,Pointp);

    }

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

    (2)关于unsignedchar*pBuffer,这个unsignedchar*其实有2个转换可选,有时可以使用byte[],有时则是StringBuilder,这就要集体问题具体分析了。例如:

    intGetErrorMessage_ex6(intErrorNr,unsignedlongBufLen,unsignedchar*pBuffer);

    voidcopy_buffer_ex6(unsignedchar*pTargetBuffer,unsignedchar*pSourceBuffer,unsignedlongAmount);

    前者就转换成StringBuilder后者是byte[]。

    (3)有些变量虽是整型但是可以用枚举,而且用枚举感觉更合适

    例如

    intdb_read_ex6(unsignedshortBlkNr,unsignedcharDatType,unsigned

    shortStartNr,unsignedlong*pAmount,unsignedlongBufLen,unsigned

    char*pReadBuffer,unsignedlong*pDatLen)中,unsignedcharDatType其实指的是“0x02=BYTE,0x04=WORD,0x06=DWORDdefault:DatType=0x02”等数据类型

    ,因此可以翻译成

    publicenumDatType:byte//PLC数据类型

    {

    BYTE=0x02,

    WORD=0x04,

    DWORD=0x06,

    }

    (4)对了,如果是对象型的引用,比如unsignedchar*转成byte[],是不需要加ref,但如果是c++int转c#int则要加ref关键字。

    要说明的就是这些,下面请各位看官看看我的转换代码吧,还请见教:

    publicclassProdave6

    {

    #region常值定义(用于极限值)

    publicconstintMAX_CONNECTIONS=64;//64isdefaultinPRODAVE

    publicconstintMAX_DEVNAME_LEN=128;//e.g."S7ONLINE"

    publicconstintMAX_BUFFERS=64;//64forblk_read()andblk_write()

    publicconstintMAX_BUFFER=65536;//Transferbufferforerrortext)

    #endregion

    #region结构体定义

    publicstructCON_TABLE_TYPE//待连接plc地址属性表

    {

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

    //publicCON_ADR_TYPEAdr;//connectionaddress

    publicbyte[]Adr;//connectionaddress

    //MPI/PBstationaddress(2)

    //IPaddress(192.168.0.1)

    //MACaddress(08-00-06-01-AA-BB)

    publicbyteAdrType;//Typeofaddress:MPI/PB(1),IP(2),MAC(3)

    publicbyteSlotNr;//Slotnumber

    publicbyteRackNr;//Racknumber

    }

    publicenumDatType:byte//PLC数据类型

    {

    BYTE=0x02,

    WORD=0x04,

    DWORD=0x06,

    }

    publicenumFieldType:byte//PLC区域类型

    {

    //ValuetypesasASCIIcharacters区域类型对应的ASCII字符

    //databyte(d/D)

    d=100,

    D=68,

    //inputbyte(e/E)

    e=101,

    E=69,

    //outputbyte(a/A)

    a=97,

    A=65,

    //memorybyte(m/M)

    m=109,

    M=77,

    //timerword(t/T),

    t=116,

    T=84,

    }

    #endregion

    #regionPLC基本函数

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

    //参数:连接号(0-63)、常值"S7ONLINE"、待连接plc地址属性表长度(字节为单位,常值9)、待连接plc地址属性表

    publicexternstaticintLoadConnection_ex6(intConNr,stringpAccessPoint,intConTableLen,ref CON_TABLE_TYPEpConTable);

    [DllImport("Prodave6.dll")]//断开PLC操作

    //参数:连接号(0-63)

    publicexternstaticintUnloadConnection_ex6(UInt16ConNr);

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

    //参数:连接号(0-63)

    publicexternstaticintSetActiveConnection_ex6(UInt16ConNr);

    [DllImport("Prodave6.dll")]//PLCdb区读取操作

    //参数:datablock号、要读取的数据类型、起始地址号、需要读取类型的数量、缓冲区长度(字节为单位)、缓冲区、缓冲区数据交互的长度

    publicexternstaticintdb_read_ex6(UInt16BlkNr,DatTypeDType,UInt16StartNr,refUInt32pAmount,UInt32BufLen,

    byte[]pBuffer,refUInt32pDatLen);

    [DllImport("Prodave6.dll")]//PLCdb区写入操作

    //参数:datablock号、要写入的数据类型、起始地址号、需要写入类型的数量、缓冲区长度(字节为单位)、缓冲区

    publicexternstaticintdb_write_ex6(UInt16BlkNr,DatTypeType,UInt16StartNr,refUInt32pAmount,UInt32BufLen,

    byte[]pBuffer);

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

    //参数:要读取的区类型、datablock号(DB区特有,默认为0)、起始地址号、需要读取类型的数量、

    //缓冲区长度(字节为单位)、缓冲区、缓冲区数据交互的长度

    publicexternstaticintfield_read_ex6(FieldTypeFType,UInt16BlkNr,UInt16StartNr,UInt32pAmount,UInt32BufLen,

    byte[]pBuffer,refUInt32pDatLen);

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

    //参数:要写入的区类型、datablock号(DB区特有,默认为0)、起始地址号、需要写入类型的数量、

    //缓冲区长度(字节为单位)、缓冲区

    publicexternstaticintfield_write_ex6(FieldTypeFType,UInt16BlkNr,UInt16StartNr,UInt32pAmount,UInt32BufLen,

    byte[]pBuffer);

    [DllImport("Prodave6.dll")]//PLCM区某字节的某位读取操作

    //参数:M区字节号、位号、当前的值(0/1)

    publicexternstaticintmb_bittest_ex6(UInt16MbNr,UInt16BitNr,refintpValue);

    [DllImport("Prodave6.dll")]//PLCM区某字节的某位写入操作

    //参数:M区字节号、位号、要写入的值(0/1)

    publicexternstaticintmb_setbit_ex6(UInt16MbNr,UInt16BitNr,byteValue);

    #endregion

    #regionPLC200用数据传输函数

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

    //参数:要读取的区类型、datablock号(DB区特有,默认为0)、起始地址号、需要读取类型的数量、

    //缓冲区长度(字节为单位)、缓冲区、缓冲区数据交互的长度

    publicexternstaticintas200_field_read_ex6(FieldTypeFType,UInt16BlkNr,UInt16StartNr,UInt32pAmount,UInt32BufLen,

    byte[]pBuffer,refUInt32pDatLen);

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

    //参数:要写入的区类型、datablock号(DB区特有,默认为0)、起始地址号、需要写入类型的数量、

    //缓冲区长度(字节为单位)、缓冲区

    publicexternstaticintas200_field_write_ex6(FieldTypeFType,UInt16BlkNr,UInt16StartNr,UInt32pAmount,UInt32BufLen,

    byte[]pBuffer);

    [DllImport("Prodave6.dll")]//200系列PLCM区某字节的某位读取操作

    //参数:M区字节号、位号、当前的值(0/1)

    publicexternstaticintas200_mb_bittest_ex6(UInt16MbNr,UInt16BitNr,refintpValue);

    [DllImport("Prodave6.dll")]//200系列PLCM区某字节的某位写入操作

    //参数:M区字节号、位号、要写入的值(0/1)

    publicexternstaticintas200_mb_setbit_ex6(UInt16MbNr,UInt16BitNr,byteValue);

    #endregion

    #regionPLC数据转换函数

    [DllImport("Prodave6.dll")]//诊断错误信息操作

    //参数:错误代号、缓冲区大小(字节为单位)、缓冲区

    publicexternstaticintGetErrorMessage_ex6(intErrorNr,UInt32BufLen,[MarshalAs(UnmanagedType.LPStr)]StringBuilderpBuffer);

    [DllImport("Prodave6.dll")]//S7浮点数转换成PC浮点数

    //参数:S7浮点数、PC浮点数

    publicexternstaticintgp_2_float_ex6(UInt32gp,reffloatpieee);

    [DllImport("Prodave6.dll")]//PC浮点数转换成S7浮点数

    //参数:PC浮点数、S7浮点数

    publicexternstaticintfloat_2_gp_ex6(floatieee,refUInt32pgp);

    [DllImport("Prodave6.dll")]//检测某字节的某位的值是0或1

    //参数:字节值、位号

    publicexternstaticinttestbit_ex6(byteValue,intBitNr);

    [DllImport("Prodave6.dll")]//检测某字节的byte值转换成int数组

    //参数:byte值、int数组(长度为8)

    publicexternstaticvoidbyte_2_bool_ex6(byteValue,int[]pBuffer);

    [DllImport("Prodave6.dll")]//检测某字节的int数组转换成byte值

    //参数:int数组(长度为8)

    publicexternstaticbytebool_2_byte_ex6(int[]pBuffer);

    [DllImport("Prodave6.dll")]//交换数据的高低字节——16位数据

    //参数:待交换的数据

    publicexternstaticUInt16kf_2_integer_ex6(UInt16wValue);//16位数据——WORD

    [DllImport("Prodave6.dll")]//交换数据的高低字节——32位数据

    //参数:待交换的数据

    publicexternstaticUInt32kf_2_long_ex6(UInt32dwValue);//32位数据——DWORD

    [DllImport("Prodave6.dll")]//交换数据缓冲区的的高低字节区,例如pBuffer[0]与pBuffer[1],pBuffer[2]与pBuffer[3]交换

    //参数:待交换的数据缓冲区,要交换的字节数,如Amount=pBuffer.Length,则交换全部缓冲

    publicexternstaticvoidswab_buffer_ex6(byte[]pBuffer,UInt32Amount);

    [DllImport("Prodave6.dll")]//复制数据缓冲区

    //参数:目的数据缓冲区,源数据缓冲区,要复制的数量(字节为单位)

    publicexternstaticvoidcopy_buffer_ex6(byte[]pTargetBuffer,byte[]pSourceBuffer,UInt32Amount);

    [DllImport("Prodave6.dll")]//把二进制数组传换成BCD码的数组——16位数据

    //参数:要处理的数组,要处理的字节数,转换前是否先交换高低字节,转换后是否要交换高低字节

    //InBytechange为1则转换BCD码之前,先交换高低字节

    //OutBytechange为1则转换BCD码之后,再交换高低字节

    //如果InBytechange和OutBytechange都没有置1,则不发生高低位的交换

    //16位数据BCD码值的许可范围是:+999——-999

    publicexternstaticvoidushort_2_bcd_ex6(UInt16[]pwValues,UInt32Amount,intInBytechange,intOutBytechange);//16位数据——WORD

    [DllImport("Prodave6.dll")]//把二进制数组传换成BCD码的数组——32位数据

    //参数:要处理的数组,要处理的字节数,转换前是否先交换高低字节,转换后是否要交换高低字节

    //InBytechange为1则转换BCD码之前,先交换高低字节

    //OutBytechange为1则转换BCD码之后,再交换高低字节

    //如果InBytechange和OutBytechange都没有置1,则不发生高低位的交换

    //32位数据BCD码值的许可范围是:+9999999——-9999999

    publicexternstaticvoidulong_2_bcd_ex6(UInt32[]pdwValues,UInt32Amount,intInBytechange,intOutBytechange);//32位数据——DWORD

    [DllImport("Prodave6.dll")]//把BCD码的数组传换成二进制数组——16位数据

    //参数:要处理的数组,要处理的字节数,转换前是否先交换高低字节,转换后是否要交换高低字节

    //InBytechange为1则转换BCD码之前,先交换高低字节

    //OutBytechange为1则转换BCD码之后,再交换高低字节

    //如果InBytechange和OutBytechange都没有置1,则不发生高低位的交换

    //16位数据BCD码值的许可范围是:+999——-999

    publicexternstaticvoidbcd_2_ushort_ex6(UInt16[]pwValues,UInt32Amount,intInBytechange,intOutBytechange);//16位数据——WORD

    [DllImport("Prodave6.dll")]//把BCD码的数组传换成二进制数组——32位数据

    //参数:要处理的数组,要处理的字节数,转换前是否先交换高低字节,转换后是否要交换高低字节

    //InBytechange为1则转换BCD码之前,先交换高低字节

    //OutBytechange为1则转换BCD码之后,再交换高低字节

    //如果InBytechange和OutBytechange都没有置1,则不发生高低位的交换

    //32位数据BCD码值的许可范围是:+9999999——-9999999

    publicexternstaticvoidbcd_2_ulong_ex6(UInt32[]pdwValues,UInt32Amount,intInBytechange,intOutBytechange);//32位数据——DWORD

    [DllImport("Prodave6.dll")]//查看64个连接中哪些被占用,哪些已经建立

    //参数:传输缓冲的字节长度,64位长度的数组(0或1)

    publicexternstaticvoidGetLoadedConnections_ex6(UInt32BufLen,int[]pBuffer);

    #endregion

    #region自定义辅助函数

    public staticUInt16bytes_2_word(bytedbb0,bytedbb1)//将高低2个byte转换成1个word

    {

    UInt16dbw0;

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

    returndbw0;

    }

    publicstaticUInt32bytes_2_dword(bytedbb0,bytedbb1,bytedbb2,bytedbb3)//将高低4个byte转换成1个dword

    {

    UInt32dbd0;

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

    returndbd0;

    }

    publicstaticUInt32words_2_dword(UInt16dbw0,UInt16dbw2)//将高低2个word转换成1个dword

    {

    UInt32dbd0;

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

    returndbd0;

    }

    publicstaticbyte[]word_2_bytes(UInt16dbw0)//将word拆分为2个byte

    {

    byte[]bytes=newbyte[2];

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

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

    returnbytes;

    }

    publicstaticbyte[]dword_2_bytes(UInt32dbd0)//将dword拆分为4个byte

    {

    byte[]bytes=newbyte[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);

    returnbytes;

    }

    publicstaticUInt16[]dword_2_words(UInt32dbd0)//将dword拆分为2个word

    {

    UInt16[]words=newUInt16[2];

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

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

    returnwords;

    }

    #endregion

    }